mirror of
https://github.com/Tencent/tmagic-editor.git
synced 2025-04-06 03:57:56 +08:00
252 lines
6.6 KiB
TypeScript
252 lines
6.6 KiB
TypeScript
/*
|
||
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
||
*
|
||
* Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.
|
||
*
|
||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
* you may not use this file except in compliance with the License.
|
||
* You may obtain a copy of the License at
|
||
*
|
||
* http://www.apache.org/licenses/LICENSE-2.0
|
||
*
|
||
* Unless required by applicable law or agreed to in writing, software
|
||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
* See the License for the specific language governing permissions and
|
||
* limitations under the License.
|
||
*/
|
||
|
||
import { random } from 'lodash-es';
|
||
|
||
import type { Id, MApp, MContainer, MNode, MPage } from '@tmagic/schema';
|
||
import { NodeType } from '@tmagic/schema';
|
||
import type StageCore from '@tmagic/stage';
|
||
import { getNodePath, isNumber, isPage, isPop } from '@tmagic/utils';
|
||
|
||
import { Layout } from '@editor/type';
|
||
|
||
export const COPY_STORAGE_KEY = '$MagicEditorCopyData';
|
||
|
||
export const generateId = (type: string | number): string => `${type}_${random(10000, false)}`;
|
||
|
||
/**
|
||
* 获取所有页面配置
|
||
* @param app DSL跟节点
|
||
* @returns 所有页面配置
|
||
*/
|
||
export const getPageList = (app: MApp): MPage[] => {
|
||
if (app.items && Array.isArray(app.items)) {
|
||
return app.items.filter((item: MPage) => item.type === NodeType.PAGE);
|
||
}
|
||
return [];
|
||
};
|
||
|
||
/**
|
||
* 获取所有页面名称
|
||
* @param pages 所有页面配置
|
||
* @returns 所有页面名称
|
||
*/
|
||
export const getPageNameList = (pages: MPage[]): string[] => pages.map((page: MPage) => page.name || 'index');
|
||
|
||
/**
|
||
* 新增页面时,生成页面名称
|
||
* @param {Object} pageNameList 所有页面名称
|
||
* @returns {string}
|
||
*/
|
||
export const generatePageName = (pageNameList: string[]): string => {
|
||
let pageLength = pageNameList.length;
|
||
|
||
if (!pageLength) return 'index';
|
||
|
||
let pageName = `page_${pageLength}`;
|
||
while (pageNameList.includes(pageName)) {
|
||
pageLength += 1;
|
||
pageName = `page_${pageLength}`;
|
||
}
|
||
|
||
return pageName;
|
||
};
|
||
|
||
/**
|
||
* 新增页面时,生成页面名称
|
||
* @param {Object} app 所有页面配置
|
||
* @returns {string}
|
||
*/
|
||
export const generatePageNameByApp = (app: MApp): string => generatePageName(getPageNameList(getPageList(app)));
|
||
|
||
/**
|
||
* 复制页面时,需要把组件下关联的弹窗id换测复制出来的弹窗的id
|
||
* @param {number} oldId 复制的源弹窗id
|
||
* @param {number} popId 新的弹窗id
|
||
* @param {Object} pageConfig 页面配置
|
||
*/
|
||
const updatePopId = (oldId: Id, popId: Id, pageConfig: MPage) => {
|
||
pageConfig.items?.forEach((config) => {
|
||
if (config.pop === oldId) {
|
||
config.pop = popId;
|
||
return;
|
||
}
|
||
|
||
if (config.popId === oldId) {
|
||
config.popId = popId;
|
||
return;
|
||
}
|
||
|
||
if (Array.isArray(config.items)) {
|
||
updatePopId(oldId, popId, config as MPage);
|
||
}
|
||
});
|
||
};
|
||
|
||
/**
|
||
* 将组件与组件的子元素配置中的id都设置成一个新的ID
|
||
* @param {Object} config 组件配置
|
||
*/
|
||
/* eslint no-param-reassign: ["error", { "props": false }] */
|
||
export const setNewItemId = (config: MNode, parent?: MPage) => {
|
||
const oldId = config.id;
|
||
|
||
config.id = generateId(config.type || 'component');
|
||
config.name = `${config.name?.replace(/_(\d+)$/, '')}_${config.id}`;
|
||
|
||
// 只有弹窗在页面下的一级子元素才有效
|
||
if (isPop(config) && parent?.type === NodeType.PAGE) {
|
||
updatePopId(oldId, config.id, parent);
|
||
}
|
||
|
||
if (config.items && Array.isArray(config.items)) {
|
||
config.items.forEach((item) => setNewItemId(item, config as MPage));
|
||
}
|
||
};
|
||
|
||
/**
|
||
* @param {Object} node
|
||
* @returns {boolean}
|
||
*/
|
||
export const isFixed = (node: MNode): boolean => node.style?.position === 'fixed';
|
||
|
||
export const getNodeIndex = (node: MNode, parent: MContainer): number => {
|
||
const items = parent?.items || [];
|
||
return items.findIndex((item: MNode) => `${item.id}` === `${node.id}`);
|
||
};
|
||
|
||
export const toRelative = (node: MNode) => {
|
||
node.style = {
|
||
...(node.style || {}),
|
||
position: 'relative',
|
||
top: 0,
|
||
left: 0,
|
||
};
|
||
return node;
|
||
};
|
||
|
||
const setTop2Middle = (node: MNode, parentNode: MNode, stage: StageCore) => {
|
||
const style = node.style || {};
|
||
const height = style.height || 0;
|
||
|
||
if (!stage || style.top || !parentNode.style || !isNumber(height)) return style;
|
||
|
||
const { height: parentHeight } = parentNode.style;
|
||
|
||
if (isPage(parentNode)) {
|
||
const { scrollTop = 0, wrapperHeight } = stage.mask;
|
||
style.top = (wrapperHeight - height) / 2 + scrollTop;
|
||
} else {
|
||
style.top = (parentHeight - height) / 2;
|
||
}
|
||
|
||
return style;
|
||
};
|
||
|
||
export const initPosition = (node: MNode, layout: Layout, parentNode: MNode, stage: StageCore) => {
|
||
if (layout === Layout.ABSOLUTE) {
|
||
node.style = {
|
||
position: 'absolute',
|
||
...setTop2Middle(node, parentNode, stage),
|
||
};
|
||
return node;
|
||
}
|
||
|
||
if (layout === Layout.RELATIVE) {
|
||
return toRelative(node);
|
||
}
|
||
|
||
return node;
|
||
};
|
||
|
||
export const setLayout = (node: MNode, layout: Layout) => {
|
||
node.items?.forEach((child: MNode) => {
|
||
if (isPop(child)) return;
|
||
|
||
child.style = child.style || {};
|
||
|
||
// 是 fixed 不做处理
|
||
if (child.style.position === 'fixed') return;
|
||
|
||
if (layout !== Layout.RELATIVE) {
|
||
child.style.position = 'absolute';
|
||
} else {
|
||
toRelative(child);
|
||
child.style.right = 'auto';
|
||
child.style.bottom = 'auto';
|
||
}
|
||
});
|
||
return node;
|
||
};
|
||
|
||
export const change2Fixed = (node: MNode, root: MApp) => {
|
||
const path = getNodePath(node.id, root.items);
|
||
const offset = {
|
||
left: 0,
|
||
top: 0,
|
||
};
|
||
|
||
path.forEach((value) => {
|
||
offset.left = offset.left + globalThis.parseFloat(value.style?.left || 0);
|
||
offset.top = offset.top + globalThis.parseFloat(value.style?.top || 0);
|
||
});
|
||
|
||
node.style = {
|
||
...(node.style || {}),
|
||
...offset,
|
||
};
|
||
return node;
|
||
};
|
||
|
||
export const Fixed2Other = async (
|
||
node: MNode,
|
||
root: MApp,
|
||
getLayout: (parent: MNode, node?: MNode) => Promise<Layout>,
|
||
) => {
|
||
const path = getNodePath(node.id, root.items);
|
||
const cur = path.pop();
|
||
const offset = {
|
||
left: cur?.style?.left || 0,
|
||
top: cur?.style?.top || 0,
|
||
right: '',
|
||
bottom: '',
|
||
};
|
||
|
||
path.forEach((value) => {
|
||
offset.left = offset.left - globalThis.parseFloat(value.style?.left || 0);
|
||
offset.top = offset.top - globalThis.parseFloat(value.style?.top || 0);
|
||
});
|
||
|
||
const parent = path.pop();
|
||
if (!parent) {
|
||
return toRelative(node);
|
||
}
|
||
|
||
const layout = await getLayout(parent);
|
||
if (layout !== Layout.RELATIVE) {
|
||
node.style = {
|
||
...(node.style || {}),
|
||
...offset,
|
||
position: 'absolute',
|
||
};
|
||
return node;
|
||
}
|
||
|
||
return toRelative(node);
|
||
};
|