change pkg
This commit is contained in:
201
packages/dooringx-lib/src/components/blocks.tsx
Normal file
201
packages/dooringx-lib/src/components/blocks.tsx
Normal file
@@ -0,0 +1,201 @@
|
||||
import { IBlockType } from '../core/store/storetype';
|
||||
import { CSSProperties, PropsWithChildren, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { innerDrag } from '../core/innerDrag';
|
||||
import { BlockResizer } from '../core/resizeHandler';
|
||||
import { contextMenuEvent } from '../core/contextMenu';
|
||||
import React from 'react';
|
||||
import { transfer } from '../core/transfer';
|
||||
import { UserConfig } from '../config';
|
||||
import styles from '../index.less';
|
||||
interface BlockProps {
|
||||
data: IBlockType;
|
||||
context: 'edit' | 'preview';
|
||||
config: UserConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* 用来从component里拿到渲染进行渲染,由于异步拉代码,所以需要等待代码拉取完毕
|
||||
* @param {*} props
|
||||
* @returns
|
||||
*/
|
||||
function Blocks(props: PropsWithChildren<BlockProps>) {
|
||||
const [state, setState] = useState<JSX.Element | null>(null);
|
||||
|
||||
const [previewState, setPreviewState] = useState({
|
||||
top: props.data.top,
|
||||
left: props.data.left,
|
||||
height: props.data.height,
|
||||
width: props.data.width,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const fn = () => props.config.getComponentRegister().getComp(props.data.name);
|
||||
const data = fn();
|
||||
let unregist = () => {};
|
||||
let newdata = { ...props.data };
|
||||
if (props.context === 'preview') {
|
||||
newdata = {
|
||||
...props.data,
|
||||
top: previewState.top,
|
||||
left: previewState.left,
|
||||
height: previewState.height,
|
||||
width: previewState.width,
|
||||
};
|
||||
}
|
||||
|
||||
if (data) {
|
||||
setState(data.render(newdata, props.context, props.config.getStore(), props.config));
|
||||
} else {
|
||||
const callback = () => {
|
||||
const tmp = fn();
|
||||
setState(tmp.render(newdata, props.context, props.config.getStore(), props.config));
|
||||
unregist();
|
||||
};
|
||||
unregist = props.config.getComponentRegister().on(props.data.name, callback);
|
||||
}
|
||||
return () => {
|
||||
unregist();
|
||||
};
|
||||
}, [props.data, props.context, props.config, previewState]);
|
||||
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const innerDragData = useMemo(() => {
|
||||
return { ...innerDrag(props.data, ref) };
|
||||
}, [props.data]);
|
||||
|
||||
useEffect(() => {
|
||||
const fn = () => {
|
||||
const { top, left, width, height } = transfer(
|
||||
props.data.top,
|
||||
props.data.left,
|
||||
props.data.height,
|
||||
props.data.width,
|
||||
props.data.fixed
|
||||
);
|
||||
|
||||
setPreviewState({ top, left, width, height });
|
||||
};
|
||||
fn();
|
||||
window.addEventListener('resize', fn);
|
||||
return () => {
|
||||
window.removeEventListener('resize', fn);
|
||||
};
|
||||
}, [
|
||||
previewState.height,
|
||||
previewState.left,
|
||||
previewState.top,
|
||||
previewState.width,
|
||||
props.data.height,
|
||||
props.data.left,
|
||||
props.data.top,
|
||||
props.data.width,
|
||||
props.data.fixed,
|
||||
]);
|
||||
|
||||
const animatecss = useMemo(() => {
|
||||
const animate = props.data.animate;
|
||||
if (Object.keys(animate).length > 0) {
|
||||
return `animate__animated ${animate.animate ?? ''} ${animate.delay ?? ''} ${
|
||||
animate.speed ?? ''
|
||||
}`;
|
||||
}
|
||||
return '';
|
||||
}, [
|
||||
props.data.animate.animate,
|
||||
props.data.animate.delay,
|
||||
// props.data.animate.duration,
|
||||
props.data.animate.speed,
|
||||
]);
|
||||
const animateCount = useMemo(() => {
|
||||
const animate = props.data.animate;
|
||||
|
||||
if (Object.keys(animate).length > 0) {
|
||||
return { animationIterationCount: animate.animationIterationCount };
|
||||
}
|
||||
return { animationIterationCount: 1 };
|
||||
}, [props.data.animate.animationIterationCount]);
|
||||
|
||||
const render = useMemo(() => {
|
||||
// 如果是编辑模式下,则需要包裹不能选中层,位移层,缩放控制层,平面移动层。
|
||||
if (state && props.context === 'edit') {
|
||||
const style: CSSProperties = props.data.canDrag ? { pointerEvents: 'none' } : {};
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
className={
|
||||
props.data.focus && props.data.position !== 'static' ? styles.yh_block_focus : ''
|
||||
}
|
||||
style={{
|
||||
position: props.data.position,
|
||||
top: props.data.top,
|
||||
left: props.data.left,
|
||||
width: props.data.width,
|
||||
height: props.data.height,
|
||||
zIndex: props.data.zIndex,
|
||||
display: props.data.display,
|
||||
}}
|
||||
{...innerDragData}
|
||||
onContextMenu={(e) => {
|
||||
if (props.data.name !== 'modalMask') {
|
||||
contextMenuEvent(e, ref);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{props.data.position !== 'static' && (
|
||||
<div className={animatecss} style={{ ...style, ...animateCount }}>
|
||||
{state}
|
||||
</div>
|
||||
)}
|
||||
{/* 这里暂不考虑布局影响 */}
|
||||
{props.data.position === 'static' && props.data.display !== 'inline' && (
|
||||
<div
|
||||
className={animatecss}
|
||||
style={{
|
||||
pointerEvents: 'none',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
...animateCount,
|
||||
}}
|
||||
>
|
||||
{state}
|
||||
</div>
|
||||
)}
|
||||
{props.data.position === 'static' && props.data.display === 'inline' && (
|
||||
<span style={{ pointerEvents: 'none' }}>{state}</span>
|
||||
)}
|
||||
<BlockResizer data={props.data} rect={ref}></BlockResizer>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div
|
||||
className={animatecss}
|
||||
style={{
|
||||
position: props.data.fixed ? 'fixed' : props.data.position,
|
||||
top: previewState.top,
|
||||
left: previewState.left,
|
||||
width: previewState.width,
|
||||
height: previewState.height,
|
||||
zIndex: props.data.zIndex,
|
||||
display: props.data.display,
|
||||
...animateCount,
|
||||
}}
|
||||
>
|
||||
{state}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}, [
|
||||
state,
|
||||
props.context,
|
||||
props.data,
|
||||
innerDragData,
|
||||
previewState.top,
|
||||
previewState.left,
|
||||
previewState.width,
|
||||
previewState.height,
|
||||
]);
|
||||
return render;
|
||||
}
|
||||
export default Blocks;
|
||||
131
packages/dooringx-lib/src/components/container.tsx
Normal file
131
packages/dooringx-lib/src/components/container.tsx
Normal file
@@ -0,0 +1,131 @@
|
||||
import { containerDragResolve } from '../core/crossDrag';
|
||||
import { containerFocusRemove } from '../core/focusHandler';
|
||||
import { innerContainerDrag } from '../core/innerDrag';
|
||||
import { NormalMarkLineRender } from '../core/markline';
|
||||
import { scaleState } from '../core/scale/state';
|
||||
import { IStoreData } from '../core/store/storetype';
|
||||
import { wrapperMoveState } from './wrapperMove/event';
|
||||
import { CSSProperties, PropsWithChildren, useMemo } from 'react';
|
||||
import Blocks from './blocks';
|
||||
import { containerResizer } from '../core/resizeHandler/containerResizer';
|
||||
import React from 'react';
|
||||
import UserConfig from '../config';
|
||||
import styles from '../index.less';
|
||||
import { getRealHeight } from '../core/transfer';
|
||||
import { IconFont } from '../core/utils/icon';
|
||||
interface ContainerProps {
|
||||
state: IStoreData;
|
||||
context: 'edit' | 'preview';
|
||||
config: UserConfig;
|
||||
editContainerStyle?: CSSProperties;
|
||||
previewContainerStyle?: CSSProperties;
|
||||
}
|
||||
function Container(props: PropsWithChildren<ContainerProps>) {
|
||||
const { editContainerStyle, previewContainerStyle } = props;
|
||||
|
||||
const transform = useMemo(() => {
|
||||
if (props.context === 'edit') {
|
||||
return `scale(${scaleState.value}) translate(${wrapperMoveState.needX}px, ${wrapperMoveState.needY}px)`;
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}, [props.context]);
|
||||
|
||||
const bgColor = () => {
|
||||
const isEdit = props.config.getStoreChanger().isEdit();
|
||||
if (isEdit) {
|
||||
return 'rgba(255,255,255,1)';
|
||||
} else {
|
||||
return props.state.globalState.containerColor;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{props.context === 'edit' && (
|
||||
<>
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
height: `${props.state.container.height + 60}px`,
|
||||
width: `${props.state.container.width}px`,
|
||||
transform: `scale(${scaleState.value}) translate(${wrapperMoveState.needX}px, ${wrapperMoveState.needY}px)`,
|
||||
}}
|
||||
>
|
||||
<div style={{ display: 'flex' }}>
|
||||
<div
|
||||
id="yh-container"
|
||||
className={styles.yh_container}
|
||||
style={{
|
||||
height: `${props.state.container.height}px`,
|
||||
width: `${props.state.container.width}px`,
|
||||
backgroundColor: bgColor(),
|
||||
position: 'relative',
|
||||
overflow: 'hidden',
|
||||
...editContainerStyle,
|
||||
}}
|
||||
{...(props.context === 'edit' ? containerDragResolve : null)}
|
||||
{...(props.context === 'edit' ? innerContainerDrag() : null)}
|
||||
{...(props.context === 'edit' ? containerFocusRemove() : null)}
|
||||
>
|
||||
{props.context === 'edit' && <NormalMarkLineRender></NormalMarkLineRender>}
|
||||
{props.state.block.map((v) => {
|
||||
return (
|
||||
<Blocks
|
||||
config={props.config}
|
||||
key={v.id}
|
||||
data={v}
|
||||
context={props.context}
|
||||
></Blocks>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
height: '50px',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
width: `${props.state.container.width}px`,
|
||||
}}
|
||||
>
|
||||
<IconFont
|
||||
type="icon-suofang"
|
||||
onMouseDown={containerResizer.onMousedown}
|
||||
style={{ fontSize: '20px', cursor: 's-resize' }}
|
||||
></IconFont>
|
||||
{/* <BoxPlotFilled
|
||||
onMouseDown={containerResizer.onMousedown}
|
||||
style={{ fontSize: '20px', cursor: 's-resize' }}
|
||||
rotate={90}
|
||||
/> */}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{props.context === 'preview' && (
|
||||
<div
|
||||
id="yh-container-preview"
|
||||
className={styles.yh_container_preview}
|
||||
style={{
|
||||
height: `${getRealHeight(props.state.container.height)}px`,
|
||||
width: `100%`,
|
||||
position: 'relative' as 'absolute' | 'relative',
|
||||
overflow: 'hidden',
|
||||
backgroundColor: bgColor(),
|
||||
transform: transform,
|
||||
...previewContainerStyle,
|
||||
}}
|
||||
>
|
||||
{props.state.block.map((v) => {
|
||||
return (
|
||||
<Blocks key={v.id} config={props.config} data={v} context={props.context}></Blocks>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
export default Container;
|
||||
281
packages/dooringx-lib/src/components/control.tsx
Normal file
281
packages/dooringx-lib/src/components/control.tsx
Normal file
@@ -0,0 +1,281 @@
|
||||
import {
|
||||
CompressOutlined,
|
||||
DeleteOutlined,
|
||||
FullscreenExitOutlined,
|
||||
FullscreenOutlined,
|
||||
GatewayOutlined,
|
||||
MenuOutlined,
|
||||
SyncOutlined,
|
||||
UnorderedListOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import { Button, Divider, Form, Input, List, Modal, Popconfirm, Popover } from 'antd';
|
||||
import React, { CSSProperties, PropsWithChildren, useState } from 'react';
|
||||
import { UserConfig } from '..';
|
||||
import { IBlockType, IStoreData } from '../core/store/storetype';
|
||||
import { deepCopy, arrayMove, changeItem, changeLayer, focusEle } from '../core/utils';
|
||||
|
||||
import { SortEnd, SortableContainer, SortableElement, SortableHandle } from 'react-sortable-hoc';
|
||||
import { wrapperMoveState } from './wrapperMove/event';
|
||||
export interface ControlProps {
|
||||
config: UserConfig;
|
||||
style?: CSSProperties;
|
||||
}
|
||||
|
||||
const DragHandle = SortableHandle(() => <MenuOutlined />);
|
||||
const SortableItem = SortableElement(
|
||||
({ value }: { value: { value: IBlockType; config: UserConfig } }) => (
|
||||
<div
|
||||
style={{
|
||||
userSelect: 'none',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
width: 430,
|
||||
}}
|
||||
>
|
||||
<div style={{ width: 30, textAlign: 'center', cursor: 'move' }}>
|
||||
<DragHandle></DragHandle>
|
||||
</div>
|
||||
<Divider type="vertical"></Divider>
|
||||
<div style={{ width: 100, textAlign: 'center' }}>
|
||||
{value.config.getComponentRegister().getMap()[value.value.name].display}
|
||||
</div>
|
||||
<Divider type="vertical"></Divider>
|
||||
<div style={{ width: 50, textAlign: 'center' }}>{value.value.id.slice(-6)}</div>
|
||||
<Divider type="vertical"></Divider>
|
||||
<div style={{ width: 50, textAlign: 'center' }}>{value.value.position}</div>
|
||||
<Divider type="vertical"></Divider>
|
||||
<div style={{ width: 200 }}>
|
||||
<Popconfirm
|
||||
title="确认变更为绝对定位吗?"
|
||||
onConfirm={() => {
|
||||
changeItem(value.config.getStore(), value.value.id, 'position', 'absolute');
|
||||
}}
|
||||
>
|
||||
<Button type="link" title="切换绝对定位" icon={<FullscreenOutlined />}></Button>
|
||||
</Popconfirm>
|
||||
<Popconfirm
|
||||
title="确认变更为静态定位吗?"
|
||||
onConfirm={() => {
|
||||
changeItem(value.config.getStore(), value.value.id, 'position', 'static');
|
||||
}}
|
||||
>
|
||||
<Button type="link" title="切换静态定位" icon={<FullscreenExitOutlined />}></Button>
|
||||
</Popconfirm>
|
||||
<Button
|
||||
type="link"
|
||||
title="选中聚焦"
|
||||
icon={<CompressOutlined />}
|
||||
onClick={() => {
|
||||
focusEle(value.config.getStore(), value.value.id);
|
||||
}}
|
||||
></Button>
|
||||
<Popconfirm
|
||||
title="确认删除操作吗?"
|
||||
onConfirm={() => {
|
||||
changeLayer(value.config.getStore(), value.value.id, 'delete');
|
||||
}}
|
||||
>
|
||||
<Button icon={<DeleteOutlined />} title="删除" type="link"></Button>
|
||||
</Popconfirm>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
const SortableList = SortableContainer(
|
||||
({ items }: { items: { data: IBlockType[]; config: UserConfig } }) => {
|
||||
return (
|
||||
<div>
|
||||
{items.data.map((value, index: number) => (
|
||||
<SortableItem key={value.id} index={index} value={{ value, config: items.config }} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export function Control(props: PropsWithChildren<ControlProps>) {
|
||||
const { style } = props;
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [configVisible, setConfigVisible] = useState(false);
|
||||
const [form] = Form.useForm();
|
||||
|
||||
const data = props.config.getStore().getData().block;
|
||||
|
||||
const onSortEnd = (sort: SortEnd) => {
|
||||
const { oldIndex, newIndex } = sort;
|
||||
const newblocks: IBlockType[] = arrayMove(data, oldIndex, newIndex);
|
||||
// 这里要判断是否edit ,如果edit时,只要看第一个是不是container,不是则不移动
|
||||
const isEdit = props.config.getStoreChanger().isEdit();
|
||||
if (isEdit) {
|
||||
const firstType = newblocks[0].name;
|
||||
if (firstType !== 'modalMask') {
|
||||
return;
|
||||
}
|
||||
}
|
||||
const store = props.config.getStore();
|
||||
const cloneData: IStoreData = deepCopy(store.getData());
|
||||
cloneData.block = newblocks;
|
||||
store.setData(cloneData);
|
||||
};
|
||||
|
||||
const content =
|
||||
data.length === 0 ? (
|
||||
<div>暂时没有组件</div>
|
||||
) : (
|
||||
<div style={{ maxHeight: 300, overflow: 'auto' }}>
|
||||
<SortableList
|
||||
distance={2}
|
||||
useDragHandle
|
||||
items={{
|
||||
data,
|
||||
config: props.config,
|
||||
}}
|
||||
onSortEnd={onSortEnd}
|
||||
axis="y"
|
||||
></SortableList>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className="ant-menu"
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
...style,
|
||||
}}
|
||||
>
|
||||
<Popover style={{ minWidth: '208px' }} content={content} trigger="click">
|
||||
<Button icon={<UnorderedListOutlined />}></Button>
|
||||
</Popover>
|
||||
|
||||
{/* <Button icon={<FolderOpenOutlined />}></Button> */}
|
||||
<Popover
|
||||
placement="left"
|
||||
content={
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
flexDirection: 'column',
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setVisible(true);
|
||||
}}
|
||||
>
|
||||
新建弹窗
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setConfigVisible(true);
|
||||
}}
|
||||
>
|
||||
弹窗配置
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<Button icon={<GatewayOutlined />}></Button>
|
||||
</Popover>
|
||||
|
||||
<Button
|
||||
icon={<SyncOutlined />}
|
||||
onClick={() => {
|
||||
wrapperMoveState.needX = 0;
|
||||
wrapperMoveState.needY = 0;
|
||||
props.config.getStore().forceUpdate();
|
||||
}}
|
||||
></Button>
|
||||
</div>
|
||||
|
||||
<Modal
|
||||
title="弹窗配置"
|
||||
visible={configVisible}
|
||||
onOk={() => setConfigVisible(false)}
|
||||
onCancel={() => setConfigVisible(false)}
|
||||
footer={null}
|
||||
>
|
||||
<List>
|
||||
{props.config.getStoreChanger().getState().modalEditName !== '' && (
|
||||
<div>请退出编辑弹窗后再打开该配置</div>
|
||||
)}
|
||||
{props.config.getStoreChanger().getState().modalEditName === '' &&
|
||||
Object.keys(props.config.getStore().getData().modalMap).map((v) => {
|
||||
return (
|
||||
<List.Item
|
||||
key={v}
|
||||
actions={[
|
||||
<Popconfirm
|
||||
title="是否切换至该弹窗并进行编辑?"
|
||||
onConfirm={() => {
|
||||
props.config.getStoreChanger().updateModal(props.config.getStore(), v);
|
||||
setConfigVisible(false);
|
||||
}}
|
||||
okText={'是'}
|
||||
cancelText={'否'}
|
||||
>
|
||||
<Button type="link">修改</Button>
|
||||
</Popconfirm>,
|
||||
|
||||
<Popconfirm
|
||||
title="您确定要删除这个弹窗吗?"
|
||||
onConfirm={() => {
|
||||
props.config.getStoreChanger().removeModal(props.config.getStore(), v);
|
||||
setConfigVisible(false);
|
||||
}}
|
||||
okText={'是'}
|
||||
cancelText={'否'}
|
||||
>
|
||||
<Button type="link">删除</Button>
|
||||
</Popconfirm>,
|
||||
]}
|
||||
>
|
||||
{v}
|
||||
</List.Item>
|
||||
);
|
||||
})}
|
||||
{props.config.getStoreChanger().getState().modalEditName === '' &&
|
||||
Object.keys(props.config.getStore().getData().modalMap).length === 0 && (
|
||||
<div style={{ textAlign: 'center' }}>暂时没有弹窗</div>
|
||||
)}
|
||||
</List>
|
||||
</Modal>
|
||||
<Modal
|
||||
onOk={() => {
|
||||
form
|
||||
.validateFields()
|
||||
.then((values) => {
|
||||
form.resetFields();
|
||||
const modalName = values.modalName;
|
||||
props.config.getStoreChanger().newModalMap(props.config.getStore(), modalName);
|
||||
setVisible(false);
|
||||
})
|
||||
.catch((info) => {
|
||||
console.log('Validate Failed:', info);
|
||||
});
|
||||
}}
|
||||
title="新增弹窗"
|
||||
onCancel={() => setVisible(false)}
|
||||
visible={visible}
|
||||
>
|
||||
<Form layout="vertical" name="basic" form={form}>
|
||||
<Form.Item
|
||||
label="弹窗名称"
|
||||
name="modalName"
|
||||
rules={[{ required: true, message: '请输入弹窗名称!' }]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default Control;
|
||||
185
packages/dooringx-lib/src/components/leftConfig.tsx
Normal file
185
packages/dooringx-lib/src/components/leftConfig.tsx
Normal file
@@ -0,0 +1,185 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-02-04 10:32:45
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-07-06 23:55:37
|
||||
* @FilePath: \dooringv2\packages\dooring-v2-lib\src\components\leftConfig.tsx
|
||||
*/
|
||||
import React, { ReactNode, useEffect, useMemo, useState } from 'react';
|
||||
import { Input, Menu } from 'antd';
|
||||
import { dragEventResolve, LeftRegistComponentMapItem } from '../core/crossDrag';
|
||||
import UserConfig from '../config';
|
||||
import { DoubleLeftOutlined, DoubleRightOutlined, SearchOutlined } from '@ant-design/icons';
|
||||
|
||||
import styles from '../index.less';
|
||||
|
||||
interface LeftConfigProps {
|
||||
config: UserConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* 注册加载左侧组件方法,由于异步拉取,所以要异步加载
|
||||
* 不同tab页可以使用不同type区分
|
||||
* @param {*} props
|
||||
* @returns
|
||||
*/
|
||||
function LeftConfig(props: LeftConfigProps) {
|
||||
const [menuSelect, setMenuSelect] = useState('0');
|
||||
const [leftRender, setLeftRender] = useState<ReactNode | null>(null);
|
||||
const leftMapRenderListCategory = useMemo(() => {
|
||||
return props.config.getConfig().leftRenderListCategory;
|
||||
}, [props.config]);
|
||||
|
||||
const [search, setSearch] = useState('');
|
||||
|
||||
useEffect(() => {
|
||||
let cache: LeftRegistComponentMapItem[] = [];
|
||||
const type = leftMapRenderListCategory[parseInt(menuSelect, 10)]?.type;
|
||||
const isCustom = leftMapRenderListCategory[parseInt(menuSelect, 10)]?.custom;
|
||||
if (!isCustom) {
|
||||
const config = props.config.getConfig();
|
||||
cache = config.leftAllRegistMap.filter((k) => k.type === type);
|
||||
cache.forEach((v) => props.config.asyncRegistComponent(v.component));
|
||||
setLeftRender(
|
||||
<div className={styles.leftco}>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
padding: '10px',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
width: 100,
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
height: 32,
|
||||
lineHeight: '32px',
|
||||
marginRight: '10px',
|
||||
fontSize: '14px',
|
||||
fontFamily: 'PingFangSC-Medium, PingFang SC',
|
||||
fontWeight: 600,
|
||||
userSelect: 'none',
|
||||
}}
|
||||
>
|
||||
{leftMapRenderListCategory[parseInt(menuSelect, 10)].displayName}
|
||||
</div>
|
||||
<Input
|
||||
style={{
|
||||
borderRadius: '40px',
|
||||
}}
|
||||
allowClear
|
||||
value={search}
|
||||
onChange={(e) => {
|
||||
setSearch(e.target.value);
|
||||
}}
|
||||
prefix={<SearchOutlined />}
|
||||
/>
|
||||
</div>
|
||||
{search &&
|
||||
search !== '' &&
|
||||
cache
|
||||
.reduce<LeftRegistComponentMapItem[]>((prev, next) => {
|
||||
//筛选搜索条件,name或者displayName存在即显示
|
||||
if (next.displayName.includes(search) || next.component.includes(search)) {
|
||||
prev.push(next);
|
||||
}
|
||||
return prev;
|
||||
}, [])
|
||||
.map((v, index) => (
|
||||
<div className={styles.coitem} key={index} {...dragEventResolve(v)}>
|
||||
<div className={styles.redbox}>
|
||||
{v.imgCustom ? v.imgCustom : <img src={v.img}></img>}
|
||||
</div>
|
||||
|
||||
<div
|
||||
style={{
|
||||
textAlign: 'center',
|
||||
lineHeight: '20px',
|
||||
height: '20px',
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
>
|
||||
{v.displayName}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
{(!search || search === '') &&
|
||||
cache.map((v, index) => (
|
||||
<div className={styles.coitem} key={index} {...dragEventResolve(v)}>
|
||||
<div className={styles.redbox}>
|
||||
{v.imgCustom ? v.imgCustom : <img src={v.img}></img>}
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
textAlign: 'center',
|
||||
lineHeight: '20px',
|
||||
height: '20px',
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
>
|
||||
{v.displayName}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
const render = leftMapRenderListCategory[parseInt(menuSelect, 10)]?.customRender;
|
||||
setLeftRender(<div className={styles.leftco}>{render}</div>);
|
||||
}
|
||||
}, [menuSelect, props.config, leftMapRenderListCategory, search]);
|
||||
|
||||
const [isCollapse, setCollapse] = useState(false);
|
||||
const [renderCollapse, setRenderCollaspe] = useState(false);
|
||||
|
||||
return (
|
||||
<div style={{ display: 'flex', height: '100%' }}>
|
||||
<div style={{ display: 'flex', flexDirection: 'column' }}>
|
||||
<Menu style={{ flex: 1 }} defaultSelectedKeys={[menuSelect]} mode="vertical">
|
||||
{leftMapRenderListCategory.map((v, i) => {
|
||||
return (
|
||||
<Menu.Item key={i} onClick={() => setMenuSelect(i + '')} icon={v.icon}></Menu.Item>
|
||||
);
|
||||
})}
|
||||
</Menu>
|
||||
<Menu selectedKeys={[]}>
|
||||
<Menu.Item
|
||||
key="1"
|
||||
onClick={() =>
|
||||
setCollapse((pre) => {
|
||||
if (pre) {
|
||||
setTimeout(() => {
|
||||
setRenderCollaspe(false);
|
||||
}, 300);
|
||||
return !pre;
|
||||
} else {
|
||||
setRenderCollaspe(true);
|
||||
return !pre;
|
||||
}
|
||||
})
|
||||
}
|
||||
className={styles.menu_footer}
|
||||
icon={isCollapse ? <DoubleRightOutlined /> : <DoubleLeftOutlined />}
|
||||
></Menu.Item>
|
||||
</Menu>
|
||||
</div>
|
||||
<div
|
||||
className={`${styles.yhLeftrender} ant-menu scrollbar`}
|
||||
style={{
|
||||
width: isCollapse ? 0 : 270,
|
||||
paddingRight: isCollapse ? 0 : 7, // 这个是滚动条宽度
|
||||
overflowX: 'hidden',
|
||||
}}
|
||||
>
|
||||
{!renderCollapse && leftRender}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default LeftConfig;
|
||||
112
packages/dooringx-lib/src/components/modalRender.tsx
Normal file
112
packages/dooringx-lib/src/components/modalRender.tsx
Normal file
@@ -0,0 +1,112 @@
|
||||
import { IStoreData } from '../core/store/storetype';
|
||||
import React, { useMemo } from 'react';
|
||||
import Blocks from './blocks';
|
||||
import UserConfig from '../config';
|
||||
import * as ReactDOM from 'react-dom';
|
||||
import { deepCopy } from '../core/utils';
|
||||
|
||||
interface ModalRenderProps {
|
||||
data: IStoreData;
|
||||
name: string; //传递的modal名字
|
||||
config: UserConfig; //需要拿到componentRegister
|
||||
parentDom: HTMLDivElement;
|
||||
rootDom: HTMLDivElement;
|
||||
}
|
||||
|
||||
export const unmountMap: Map<string, Function> = new Map();
|
||||
|
||||
export function ModalRender(props: ModalRenderProps) {
|
||||
//先获取数据
|
||||
const storeData: IStoreData = useMemo(() => {
|
||||
const z = props.data.modalMap[props.name];
|
||||
if (z) {
|
||||
const data = deepCopy(z);
|
||||
//需要把第一个mask扔了手动写一个
|
||||
data.block.shift();
|
||||
return data;
|
||||
}
|
||||
return { block: [] };
|
||||
}, [props.data.modalMap, props.name]);
|
||||
const { parentDom, rootDom } = props;
|
||||
|
||||
//这里还要添加个关闭函数,
|
||||
const unmount = useMemo(() => {
|
||||
return () => {
|
||||
if (parentDom && rootDom) {
|
||||
ReactDOM.unmountComponentAtNode(parentDom);
|
||||
rootDom.removeChild(parentDom);
|
||||
rootDom.parentElement?.removeChild(rootDom);
|
||||
}
|
||||
};
|
||||
}, [parentDom, rootDom]);
|
||||
|
||||
unmountMap.set(props.name, unmount);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className="yh-container-modal"
|
||||
style={{
|
||||
height: `100%`,
|
||||
width: `100%`,
|
||||
position: 'fixed',
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
>
|
||||
{storeData.block.map((v) => {
|
||||
return <Blocks key={v.id} config={props.config} data={v} context={'preview'}></Blocks>;
|
||||
})}
|
||||
<div
|
||||
onClick={() => {
|
||||
unmount();
|
||||
}}
|
||||
style={{
|
||||
backgroundColor: '#716f6f9e',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
let wrap: HTMLDivElement | null;
|
||||
|
||||
export const createModal = (name: string, data: IStoreData, config: UserConfig) => {
|
||||
if (wrap) {
|
||||
wrap = null;
|
||||
}
|
||||
|
||||
if (!wrap) {
|
||||
wrap = document.createElement('div');
|
||||
wrap.style.cssText = `line-height:
|
||||
1.5;text-align:
|
||||
center;color: #333;
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
position: fixed;
|
||||
z-index: 100000;
|
||||
width: 100%;
|
||||
height:100%;
|
||||
top:0;
|
||||
left: 0;`;
|
||||
if (wrap) {
|
||||
document.body.appendChild(wrap);
|
||||
}
|
||||
}
|
||||
const divs = document.createElement('div');
|
||||
wrap.appendChild(divs);
|
||||
ReactDOM.render(
|
||||
<ModalRender
|
||||
name={name}
|
||||
data={data}
|
||||
config={config}
|
||||
parentDom={divs}
|
||||
rootDom={wrap}
|
||||
></ModalRender>,
|
||||
divs
|
||||
);
|
||||
};
|
||||
55
packages/dooringx-lib/src/components/preview.tsx
Normal file
55
packages/dooringx-lib/src/components/preview.tsx
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-03-14 05:40:37
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-07-08 20:44:45
|
||||
* @FilePath: \DooringV2\packages\dooringx-lib\src\components\preview.tsx
|
||||
*/
|
||||
import Container from './container';
|
||||
import React, { ReactElement, ReactNode, useEffect, useState } from 'react';
|
||||
import UserConfig from '../config';
|
||||
|
||||
function Preview(props: { config: UserConfig; loadText?: ReactNode }): ReactElement {
|
||||
const isEdit = props.config.getStoreChanger().isEdit();
|
||||
/// 这里需要在渲染组件之前必须把所有config加载完成,否则会导致先运行的函数无法运行
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
// 链接数据
|
||||
props.config
|
||||
.getDataCenter()
|
||||
.initAddToDataMap(props.config.getStore().getData(), props.config.getStoreChanger());
|
||||
// 链接事件
|
||||
props.config
|
||||
.getEventCenter()
|
||||
.syncEventMap(props.config.getStore().getData(), props.config.getStoreChanger());
|
||||
setTimeout(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
}, [props.config]);
|
||||
|
||||
if (isEdit) {
|
||||
// 正常情况不会走这
|
||||
const state = props.config.getStoreChanger().getOrigin()!.now;
|
||||
return (
|
||||
<>
|
||||
<Container config={props.config} context="preview" state={state}></Container>
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
if (loading) {
|
||||
return <div>{props.loadText ? props.loadText : 'loading'}</div>;
|
||||
} else {
|
||||
return (
|
||||
<>
|
||||
<Container
|
||||
config={props.config}
|
||||
context="preview"
|
||||
state={props.config.getStore().getData()}
|
||||
></Container>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
export default Preview;
|
||||
239
packages/dooringx-lib/src/components/rightConfig.tsx
Normal file
239
packages/dooringx-lib/src/components/rightConfig.tsx
Normal file
@@ -0,0 +1,239 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-03-14 05:42:13
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-07-05 23:35:05
|
||||
* @FilePath: \DooringV2\packages\dooring-v2-lib\src\components\rightConfig.tsx
|
||||
*/
|
||||
import { CreateOptionsRes } from '../core/components/formTypes';
|
||||
import { IBlockType, IStoreData } from '../core/store/storetype';
|
||||
import { store } from '../runtime/store';
|
||||
import { PropsWithChildren, useEffect, useMemo, useState } from 'react';
|
||||
import React from 'react';
|
||||
import { Tabs, Input, Row, Col } from 'antd';
|
||||
import UserConfig from '../config';
|
||||
import { RGBColor, SketchPicker } from 'react-color';
|
||||
import { rgba2Obj } from '../core/utils';
|
||||
import deepcopy from 'deepcopy';
|
||||
|
||||
interface RightConfigProps {
|
||||
state: IStoreData;
|
||||
config: UserConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* 这里一个需要异步拿取当前注册组件的配置项,另外需要异步加载所需的配置项。
|
||||
* @param {*} props
|
||||
* @returns
|
||||
*/
|
||||
function RightConfig(props: PropsWithChildren<RightConfigProps>) {
|
||||
const [menuSelect, setMenuSelect] = useState('0');
|
||||
const [current, setCurrent] = useState<IBlockType | null>(null);
|
||||
const rightMapRenderListCategory = useMemo(() => {
|
||||
return props.config.getConfig().rightRenderListCategory;
|
||||
}, [props.config]);
|
||||
useEffect(() => {
|
||||
const fn = () => {
|
||||
let item: IBlockType | undefined;
|
||||
store.getData().block.some((v) => {
|
||||
if (v.focus) {
|
||||
item = v;
|
||||
}
|
||||
return v.focus === true;
|
||||
});
|
||||
if (item) {
|
||||
setCurrent({ ...item });
|
||||
} else {
|
||||
setCurrent(null);
|
||||
}
|
||||
};
|
||||
const unregist = store.subscribe(fn);
|
||||
return () => {
|
||||
unregist();
|
||||
};
|
||||
}, []);
|
||||
const render = useMemo(() => {
|
||||
return (type: string, current: IBlockType) => {
|
||||
const fn = () => props.config.getComponentRegister().getComp(current.name);
|
||||
const data = fn();
|
||||
// 这里不可能拿不到组件,因为点击的那个组件已经渲染出来了
|
||||
if (data) {
|
||||
const renderList = data.props[type];
|
||||
if (renderList) {
|
||||
return renderList.map((v, i) => {
|
||||
const Component = props.config.getFormRegister().formMap[v.type];
|
||||
if (!Component) {
|
||||
console.error(`you might forgot to regist form component ${v.type}`);
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<Component
|
||||
key={i}
|
||||
data={v as CreateOptionsRes<any, any>}
|
||||
current={current}
|
||||
config={props.config}
|
||||
></Component>
|
||||
);
|
||||
});
|
||||
} else {
|
||||
return <div>还没有配置属性</div>;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
}, [props.config]);
|
||||
|
||||
const initColor = useMemo(() => {
|
||||
return props.config.getStoreChanger().isEdit()
|
||||
? rgba2Obj(props.config.getStoreChanger().getOrigin()?.now.globalState.containerColor)
|
||||
: rgba2Obj(props.config.getStore().getData().globalState.containerColor);
|
||||
}, [props.config]);
|
||||
const [color, setColor] = useState<RGBColor>(initColor);
|
||||
const [colorPickerVisible, setColorPickerVisible] = useState(false);
|
||||
const initTitle = useMemo(() => {
|
||||
const title = props.config.getStoreChanger().isEdit()
|
||||
? props.config.getStoreChanger().getOrigin()?.now.globalState.title
|
||||
: props.config.getStore().getData().globalState.title;
|
||||
return title;
|
||||
}, [props.config]);
|
||||
const [title, setTitle] = useState(initTitle);
|
||||
|
||||
const customGlobal = props.config.getConfig().rightGlobalCustom;
|
||||
return (
|
||||
<div
|
||||
className="ant-menu"
|
||||
style={{
|
||||
height: '100%',
|
||||
width: '400px',
|
||||
overflow: 'auto',
|
||||
padding: '0 10px',
|
||||
lineHeight: 1.5715,
|
||||
}}
|
||||
>
|
||||
{current && (
|
||||
<Tabs
|
||||
activeKey={menuSelect}
|
||||
style={{ width: '100%' }}
|
||||
onChange={(e) => {
|
||||
setMenuSelect(e);
|
||||
}}
|
||||
>
|
||||
{rightMapRenderListCategory.map((v, i) => {
|
||||
return (
|
||||
<Tabs.TabPane tab={v.icon} key={i + ''}>
|
||||
<div
|
||||
className="scrollbar"
|
||||
style={{
|
||||
height: 'calc(100vh - 110px)',
|
||||
overflow: 'auto',
|
||||
}}
|
||||
>
|
||||
{v.custom && v.customRender && v.customRender(v.type, current)}
|
||||
{!v.custom && render(v.type, current)}
|
||||
</div>
|
||||
</Tabs.TabPane>
|
||||
);
|
||||
})}
|
||||
</Tabs>
|
||||
)}
|
||||
{!current && !customGlobal && (
|
||||
<div style={{ padding: '20px' }}>
|
||||
<Row style={{ padding: '10 0 20px 0', fontWeight: 'bold' }}>全局设置</Row>
|
||||
<Row style={{ padding: '10px 0' }}>
|
||||
<Col span={6}>标题</Col>
|
||||
<Col span={18}>
|
||||
<Input
|
||||
value={title}
|
||||
onChange={(e) => {
|
||||
const val = e.target.value;
|
||||
setTitle(val);
|
||||
const isEdit = props.config.getStoreChanger().isEdit();
|
||||
if (isEdit) {
|
||||
const originData: IStoreData = deepcopy(
|
||||
props.config.getStoreChanger().getOrigin()!.now
|
||||
);
|
||||
originData.globalState.title = val;
|
||||
props.config.getStoreChanger().updateOrigin(originData);
|
||||
} else {
|
||||
const originData = deepcopy(props.config.getStore().getData());
|
||||
originData.globalState.title = val;
|
||||
props.config.getStore().setData(originData);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{ padding: '10px 0' }}>
|
||||
<Col span={6}>容器底色</Col>
|
||||
<Col span={18}>
|
||||
{
|
||||
<div style={{ position: 'relative' }}>
|
||||
<div
|
||||
onClick={() => {
|
||||
setColorPickerVisible((pre) => !pre);
|
||||
}}
|
||||
style={{
|
||||
background: '#fff',
|
||||
borderRadius: '1px',
|
||||
boxShadow: '0 0 0 1px rgba(0,0,0,.1)',
|
||||
cursor: 'pointer',
|
||||
display: 'inline-block',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
width: '20px',
|
||||
height: '20px',
|
||||
borderRadius: '2px',
|
||||
background: `rgba(${color.r}, ${color.g}, ${color.b}, ${color.a})`,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{colorPickerVisible && (
|
||||
<>
|
||||
<div style={{ position: 'absolute', zIndex: 2000 }}>
|
||||
<SketchPicker
|
||||
color={color}
|
||||
onChange={(c) => {
|
||||
const newcolor = c.rgb;
|
||||
setColor(newcolor);
|
||||
const isEdit = props.config.getStoreChanger().isEdit();
|
||||
if (isEdit) {
|
||||
const originData: IStoreData = deepcopy(
|
||||
props.config.getStoreChanger().getOrigin()!.now
|
||||
);
|
||||
originData.globalState.containerColor = `rgba(${newcolor.r}, ${newcolor.g}, ${newcolor.b}, ${newcolor.a})`;
|
||||
props.config.getStoreChanger().updateOrigin(originData);
|
||||
} else {
|
||||
const originData = deepcopy(props.config.getStore().getData());
|
||||
originData.globalState.containerColor = `rgba(${newcolor.r}, ${newcolor.g}, ${newcolor.b}, ${newcolor.a})`;
|
||||
props.config.getStore().setData(originData);
|
||||
}
|
||||
}}
|
||||
></SketchPicker>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
position: 'fixed',
|
||||
top: '0px',
|
||||
right: '0px',
|
||||
bottom: '0px',
|
||||
left: '0px',
|
||||
zIndex: 1000,
|
||||
}}
|
||||
onClick={() => setColorPickerVisible(false)}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
)}
|
||||
{!current && customGlobal && customGlobal}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
export default RightConfig;
|
||||
68
packages/dooringx-lib/src/components/wrapperMove/event.ts
Normal file
68
packages/dooringx-lib/src/components/wrapperMove/event.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-02-21 22:17:29
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-04-05 18:24:27
|
||||
* @FilePath: \dooringv2\src\components\wrapperMove\event.ts
|
||||
*/
|
||||
import { store } from '../../runtime/store';
|
||||
import { RefObject } from 'react';
|
||||
import { containerResizer } from '../../core/resizeHandler/containerResizer';
|
||||
import { contextMenuState } from '../../core/contextMenu';
|
||||
|
||||
export interface WrapperMoveStateProps {
|
||||
isDrag: boolean;
|
||||
startX: number;
|
||||
startY: number;
|
||||
needX: number;
|
||||
needY: number;
|
||||
ref: null | RefObject<HTMLDivElement>;
|
||||
}
|
||||
|
||||
export const wrapperMoveState: WrapperMoveStateProps = {
|
||||
isDrag: false,
|
||||
startX: 0,
|
||||
startY: 0,
|
||||
needX: 0,
|
||||
needY: 0,
|
||||
ref: null,
|
||||
};
|
||||
|
||||
export const wrapperEvent = (ref: RefObject<HTMLDivElement>) => {
|
||||
return {
|
||||
onMouseDown: (e: React.MouseEvent) => {
|
||||
// e.preventDefault();// 不能使用preventDefault 否则弹窗输入框焦点无法触发
|
||||
contextMenuState.unmountContextMenu();
|
||||
if (e.target !== ref.current) {
|
||||
} else {
|
||||
wrapperMoveState.isDrag = true;
|
||||
wrapperMoveState.startX = e.clientX;
|
||||
wrapperMoveState.startY = e.clientY;
|
||||
if (ref.current) {
|
||||
ref.current.style.cursor = 'grab';
|
||||
wrapperMoveState.ref = ref;
|
||||
}
|
||||
}
|
||||
},
|
||||
onMouseMove: (e: React.MouseEvent) => {
|
||||
e.preventDefault();
|
||||
if (wrapperMoveState.isDrag) {
|
||||
const diffX = e.clientX - wrapperMoveState.startX;
|
||||
const diffY = e.clientY - wrapperMoveState.startY;
|
||||
wrapperMoveState.needX = wrapperMoveState.needX + diffX;
|
||||
wrapperMoveState.needY = wrapperMoveState.needY + diffY;
|
||||
wrapperMoveState.startX = e.clientX;
|
||||
wrapperMoveState.startY = e.clientY;
|
||||
store.forceUpdate();
|
||||
}
|
||||
containerResizer.onMouseMove(e);
|
||||
},
|
||||
};
|
||||
};
|
||||
export const wrapperMoveMouseUp = () => {
|
||||
if (wrapperMoveState.ref && wrapperMoveState.ref.current) {
|
||||
wrapperMoveState.ref.current.style.cursor = 'default';
|
||||
}
|
||||
containerResizer.onMouseUp();
|
||||
wrapperMoveState.isDrag = false;
|
||||
};
|
||||
44
packages/dooringx-lib/src/components/wrapperMove/index.tsx
Normal file
44
packages/dooringx-lib/src/components/wrapperMove/index.tsx
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-03-14 04:29:09
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-03-14 04:58:51
|
||||
* @FilePath: \dooring-v2\src\core\wrapperMove\index.tsx
|
||||
*/
|
||||
import { AllHTMLAttributes, CSSProperties, PropsWithChildren, useRef } from 'react';
|
||||
import { wrapperEvent } from './event';
|
||||
import { onWheelEvent } from '../../core/scale';
|
||||
import React from 'react';
|
||||
|
||||
export interface ContainerWrapperProps extends AllHTMLAttributes<HTMLDivElement> {
|
||||
classNames?: string;
|
||||
style?: CSSProperties;
|
||||
}
|
||||
|
||||
function ContainerWrapper(props: PropsWithChildren<ContainerWrapperProps>) {
|
||||
const { children, style, classNames, ...rest } = props;
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
return (
|
||||
<div
|
||||
className={`ant-menu ${classNames}`}
|
||||
ref={ref}
|
||||
style={{
|
||||
backgroundColor: '#f0f0f0',
|
||||
height: '100%',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
flex: 1,
|
||||
position: 'relative',
|
||||
overflow: 'hidden',
|
||||
...style,
|
||||
}}
|
||||
{...wrapperEvent(ref)}
|
||||
{...onWheelEvent}
|
||||
{...rest}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
export default ContainerWrapper;
|
||||
531
packages/dooringx-lib/src/config/index.tsx
Normal file
531
packages/dooringx-lib/src/config/index.tsx
Normal file
@@ -0,0 +1,531 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-02-25 21:16:58
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-07-08 20:42:22
|
||||
* @FilePath: \DooringV2\packages\dooringx-lib\src\config\index.tsx
|
||||
*/
|
||||
import { IBlockType, IStoreData } from '../core/store/storetype';
|
||||
import { store } from '../runtime/store';
|
||||
import { formRegister, componentRegister, commander, storeChanger } from '../runtime';
|
||||
import { ComponentClass, FunctionComponent, ReactNode } from 'react';
|
||||
import { ComponentItemFactory } from '../core/components/abstract';
|
||||
import { marklineConfig } from '../core/markline/marklineConfig';
|
||||
import { CommanderItem } from '../core/command/commanderType';
|
||||
import { contextMenuState } from '../core/contextMenu';
|
||||
import { formComponentRegisterFn } from '../core/components/formComponentRegister';
|
||||
import { deepCopy } from '../core/utils';
|
||||
import { LeftRegistComponentMapItem } from '../core/crossDrag';
|
||||
import { FunctionCenterType } from '../core/functionCenter';
|
||||
import { EventCenter } from '../core/eventCenter';
|
||||
import { DataCenter } from '../core/dataCenter';
|
||||
import { createModal, unmountMap } from '../components/modalRender';
|
||||
import { scaleState } from '../core/scale/state';
|
||||
import { CommanderItemFactory } from '../core/command/abstract';
|
||||
import MmodalMask from '../core/components/defaultFormComponents/modalMask';
|
||||
import MmodalContainer from '../core/components/defaultFormComponents/modalContainer';
|
||||
|
||||
// 组件部分
|
||||
|
||||
/**
|
||||
*
|
||||
* @urlFn 组件异步加载函数
|
||||
* @component 组件默认导出
|
||||
* @export
|
||||
* @interface CacheComponentValueType
|
||||
*/
|
||||
export interface CacheComponentValueType {
|
||||
urlFn?: () => Promise<any>;
|
||||
component?: ComponentItemFactory;
|
||||
}
|
||||
export type CacheComponentType = Record<string, CacheComponentValueType> | {};
|
||||
export type AsyncCacheComponentType = Record<string, () => Promise<any>>;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @export 左侧的图标 custom 自定义渲染
|
||||
* @interface LeftMapRenderListPropsItemCategory
|
||||
*/
|
||||
export interface LeftMapRenderListPropsItemCategory {
|
||||
type: string;
|
||||
icon: ReactNode;
|
||||
custom?: boolean;
|
||||
customRender?: ReactNode;
|
||||
displayName?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @export 右侧的图标配置
|
||||
* @interface RightMapRenderListPropsItemCategory
|
||||
*/
|
||||
export interface RightMapRenderListPropsItemCategory {
|
||||
type: string;
|
||||
icon: ReactNode;
|
||||
custom?: boolean;
|
||||
customRender?: (type: string, current: IBlockType) => ReactNode;
|
||||
}
|
||||
|
||||
// 设置部分
|
||||
export interface InitConfig {
|
||||
/**
|
||||
* 初始化store
|
||||
* @type {IStoreData[]}
|
||||
* @memberof InitConfig
|
||||
*/
|
||||
initStoreData: IStoreData[];
|
||||
|
||||
/**
|
||||
* 左边tab页组件渲染包括异步路径 { type: 'basic', component: 'button', img: 'http://xxxx/1.jpg' ,url:'' },
|
||||
* @memberof InitConfig
|
||||
*/
|
||||
leftAllRegistMap: LeftRegistComponentMapItem[];
|
||||
/**
|
||||
* 左边tab页图标配置
|
||||
* type icon custom customRender
|
||||
* @memberof InitConfig
|
||||
*/
|
||||
leftRenderListCategory: LeftMapRenderListPropsItemCategory[];
|
||||
/**
|
||||
* 右边tab页图标配置
|
||||
* type icon custom customRender
|
||||
* @memberof InitConfig
|
||||
*/
|
||||
rightRenderListCategory: RightMapRenderListPropsItemCategory[];
|
||||
|
||||
/**
|
||||
*
|
||||
* 右侧全局自定义
|
||||
* @type {ReactNode}
|
||||
* @memberof InitConfig
|
||||
*/
|
||||
rightGlobalCustom: ReactNode;
|
||||
|
||||
/**
|
||||
* 组件加载缓存判定,用来设置不异步加载的组件
|
||||
* @memberof InitConfig
|
||||
*/
|
||||
initComponentCache: CacheComponentType;
|
||||
|
||||
/**
|
||||
*
|
||||
* 内置函数配置
|
||||
* @memberof InitConfig
|
||||
*/
|
||||
initFunctionMap: FunctionCenterType;
|
||||
|
||||
/**
|
||||
*
|
||||
* 内置数据中心配置数据
|
||||
* @memberof InitConfig
|
||||
*/
|
||||
initDataCenterMap: Record<string, any>;
|
||||
|
||||
/**
|
||||
*
|
||||
* commander 指令集合
|
||||
* @type {Array<CommanderItemFactory>}
|
||||
* @memberof InitConfig
|
||||
*/
|
||||
initCommandModule: Array<CommanderItemFactory>;
|
||||
|
||||
/**
|
||||
*
|
||||
* 右侧配置项
|
||||
* @type {(Record<
|
||||
* string,
|
||||
* FunctionComponent<any> | ComponentClass<any, any>
|
||||
* >)}
|
||||
* @memberof InitConfig
|
||||
*/
|
||||
initFormComponents: Record<string, FunctionComponent<any> | ComponentClass<any, any>>;
|
||||
}
|
||||
|
||||
export const defaultStore: IStoreData = {
|
||||
container: {
|
||||
width: 375,
|
||||
height: 667,
|
||||
},
|
||||
block: [],
|
||||
modalMap: {},
|
||||
dataSource: {
|
||||
defaultKey: 'defaultValue',
|
||||
},
|
||||
globalState: {
|
||||
containerColor: 'rgba(255,255,255,1)',
|
||||
title: 'dooring',
|
||||
},
|
||||
};
|
||||
|
||||
export const defaultConfig: InitConfig = {
|
||||
initStoreData: [defaultStore],
|
||||
leftAllRegistMap: [],
|
||||
leftRenderListCategory: [],
|
||||
rightGlobalCustom: null,
|
||||
rightRenderListCategory: [],
|
||||
initComponentCache: {
|
||||
modalMask: { component: MmodalMask }, // 这2个组件不配置显示
|
||||
modalContainer: { component: MmodalContainer },
|
||||
},
|
||||
initFunctionMap: {
|
||||
打开弹窗函数: {
|
||||
fn: (_ctx, next, config, args) => {
|
||||
const modalName = args['_modal'];
|
||||
const storeData = config.getStore().getData();
|
||||
createModal(modalName, storeData, config);
|
||||
next();
|
||||
},
|
||||
config: [
|
||||
{
|
||||
name: '弹窗名称',
|
||||
data: ['modal'],
|
||||
options: {
|
||||
receive: '_modal',
|
||||
multi: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
关闭弹窗函数: {
|
||||
fn: (_ctx, next, _config, args) => {
|
||||
const modalName = args['_modal'];
|
||||
const fn = unmountMap.get(modalName);
|
||||
fn ? fn() : '';
|
||||
next();
|
||||
},
|
||||
config: [
|
||||
{
|
||||
name: '弹窗名称',
|
||||
data: ['modal'],
|
||||
options: {
|
||||
receive: '_modal',
|
||||
multi: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
initDataCenterMap: {},
|
||||
initCommandModule: [],
|
||||
initFormComponents: {},
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* 部分无法合并属性如果b传了会以b为准
|
||||
* initstore不合并
|
||||
* leftallregistmap合并
|
||||
* leftRenderListCategory合并
|
||||
* rightRenderListCategory合并
|
||||
* rightGlobalCustom 不合并
|
||||
* initComponentCache合并
|
||||
* initFunctionMap合并
|
||||
* initDataCenterMap合并
|
||||
* initCommandModule合并
|
||||
* initFormComponents合并
|
||||
*
|
||||
* @export InitConfig
|
||||
*/
|
||||
export function userConfigMerge(a: Partial<InitConfig>, b?: Partial<InitConfig>): InitConfig {
|
||||
const mergeConfig: InitConfig = {
|
||||
initStoreData: [defaultStore],
|
||||
leftAllRegistMap: [],
|
||||
leftRenderListCategory: [],
|
||||
rightRenderListCategory: [],
|
||||
initComponentCache: {},
|
||||
initFunctionMap: {},
|
||||
initDataCenterMap: {},
|
||||
initCommandModule: [],
|
||||
rightGlobalCustom: null,
|
||||
initFormComponents: {},
|
||||
};
|
||||
if (!b) {
|
||||
return userConfigMerge(mergeConfig, a);
|
||||
}
|
||||
mergeConfig.initStoreData = b.initStoreData
|
||||
? [...b.initStoreData]
|
||||
: a.initStoreData
|
||||
? [...a.initStoreData]
|
||||
: [defaultStore];
|
||||
|
||||
mergeConfig.rightGlobalCustom = b.rightGlobalCustom ? b.rightGlobalCustom : a.rightGlobalCustom;
|
||||
|
||||
mergeConfig.leftAllRegistMap = b.leftAllRegistMap
|
||||
? a.leftAllRegistMap
|
||||
? [...a.leftAllRegistMap, ...b.leftAllRegistMap]
|
||||
: [...b.leftAllRegistMap]
|
||||
: a.leftAllRegistMap
|
||||
? [...a.leftAllRegistMap]
|
||||
: [];
|
||||
mergeConfig.leftRenderListCategory = b.leftRenderListCategory
|
||||
? a.leftRenderListCategory
|
||||
? [...a.leftRenderListCategory, ...b.leftRenderListCategory]
|
||||
: [...b.leftRenderListCategory]
|
||||
: a.leftRenderListCategory
|
||||
? [...a.leftRenderListCategory]
|
||||
: [...defaultConfig.leftRenderListCategory];
|
||||
mergeConfig.rightRenderListCategory = b.rightRenderListCategory
|
||||
? a.rightRenderListCategory
|
||||
? [...a.rightRenderListCategory, ...b.rightRenderListCategory]
|
||||
: [...b.rightRenderListCategory]
|
||||
: a.rightRenderListCategory
|
||||
? [...a.rightRenderListCategory]
|
||||
: [...defaultConfig.rightRenderListCategory];
|
||||
mergeConfig.initComponentCache = {
|
||||
...a.initComponentCache,
|
||||
...b.initComponentCache,
|
||||
};
|
||||
mergeConfig.initFunctionMap = {
|
||||
...a.initFunctionMap,
|
||||
...b.initFunctionMap,
|
||||
};
|
||||
mergeConfig.initFormComponents = {
|
||||
...a.initFormComponents,
|
||||
...b.initFormComponents,
|
||||
};
|
||||
mergeConfig.initDataCenterMap = {
|
||||
...a.initDataCenterMap,
|
||||
...b.initDataCenterMap,
|
||||
};
|
||||
mergeConfig.initCommandModule = b.initCommandModule
|
||||
? a.initCommandModule
|
||||
? [...a.initCommandModule, ...b.initCommandModule]
|
||||
: [...b.initCommandModule]
|
||||
: a.initCommandModule
|
||||
? [...a.initCommandModule]
|
||||
: [];
|
||||
return mergeConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @export 用户配置项
|
||||
* @class UserConfig
|
||||
*/
|
||||
export class UserConfig {
|
||||
public initConfig: InitConfig;
|
||||
public store = store;
|
||||
public componentRegister = componentRegister;
|
||||
public componentCache = {};
|
||||
public asyncComponentUrlMap = {} as AsyncCacheComponentType;
|
||||
public marklineConfig = marklineConfig;
|
||||
public commanderRegister = commander;
|
||||
public contextMenuState = contextMenuState;
|
||||
public formRegister = formRegister;
|
||||
public storeChanger = storeChanger;
|
||||
public eventCenter: EventCenter;
|
||||
public dataCenter: DataCenter;
|
||||
public scaleState = scaleState;
|
||||
constructor(initConfig?: Partial<InitConfig>) {
|
||||
const mergeConfig = userConfigMerge(defaultConfig, initConfig);
|
||||
this.initConfig = mergeConfig;
|
||||
this.eventCenter = new EventCenter({}, mergeConfig.initFunctionMap);
|
||||
this.dataCenter = new DataCenter(mergeConfig.initDataCenterMap);
|
||||
this.init();
|
||||
// 右侧配置项注册 初始注册组件暂时固定
|
||||
}
|
||||
|
||||
toRegist() {
|
||||
const modules = this.initConfig.initFormComponents;
|
||||
formComponentRegisterFn(formRegister, modules);
|
||||
|
||||
const cache = this.initConfig.initComponentCache;
|
||||
this.componentCache = cache;
|
||||
// 拿到组件缓存后,先同步加载map上组件
|
||||
Object.values(cache).forEach((v) => {
|
||||
if ((v as CacheComponentValueType).component) {
|
||||
this.registComponent((v as CacheComponentValueType).component!);
|
||||
}
|
||||
});
|
||||
// 异步组件注册地址
|
||||
this.initConfig.leftAllRegistMap.forEach((v) => {
|
||||
if (v.urlFn) {
|
||||
//@ts-ignore
|
||||
this.asyncComponentUrlMap[v.component] = v.urlFn;
|
||||
}
|
||||
});
|
||||
// 注册画布上组件
|
||||
this.store.getData().block.forEach((v) => {
|
||||
this.asyncRegistComponent(v.name);
|
||||
});
|
||||
|
||||
// 注册data
|
||||
this.dataCenter = new DataCenter(this.initConfig.initDataCenterMap);
|
||||
//数据需要加上store上的
|
||||
this.dataCenter.initAddToDataMap(this.store.getData(), this.storeChanger);
|
||||
// 修改事件与数据初始
|
||||
this.eventCenter = new EventCenter({}, this.initConfig.initFunctionMap);
|
||||
// 注册画布事件
|
||||
this.eventCenter.syncEventMap(this.store.getData(), this.storeChanger);
|
||||
}
|
||||
|
||||
init() {
|
||||
this.store.resetToInitData(deepCopy(this.initConfig.initStoreData), true);
|
||||
this.toRegist();
|
||||
}
|
||||
|
||||
getStoreJSON() {
|
||||
return JSON.stringify(this.store.getData());
|
||||
}
|
||||
|
||||
parseStoreJson(json: string) {
|
||||
return JSON.parse(json);
|
||||
}
|
||||
|
||||
resetData(data: IStoreData[]) {
|
||||
this.store.resetToInitData(data, true);
|
||||
this.toRegist();
|
||||
}
|
||||
|
||||
getScaleState() {
|
||||
return this.scaleState;
|
||||
}
|
||||
getDataCenter() {
|
||||
return this.dataCenter;
|
||||
}
|
||||
getEventCenter() {
|
||||
return this.eventCenter;
|
||||
}
|
||||
getStoreChanger() {
|
||||
return this.storeChanger;
|
||||
}
|
||||
getConfig() {
|
||||
return this.initConfig;
|
||||
}
|
||||
getStore() {
|
||||
return this.store;
|
||||
}
|
||||
getComponentRegister() {
|
||||
return this.componentRegister;
|
||||
}
|
||||
getContextMenuState() {
|
||||
return this.contextMenuState;
|
||||
}
|
||||
getFormRegister() {
|
||||
return this.formRegister;
|
||||
}
|
||||
getCommanderRegister() {
|
||||
return this.commanderRegister;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* 以默认设置重置配置项
|
||||
* @param {Partial<InitConfig>} v
|
||||
* @memberof UserConfig
|
||||
*/
|
||||
resetConfig(v: Partial<InitConfig>) {
|
||||
const mergeConfig = userConfigMerge(defaultConfig, v);
|
||||
this.initConfig = mergeConfig;
|
||||
this.init();
|
||||
store.forceUpdate();
|
||||
}
|
||||
/**
|
||||
* 会重置配置,请修改配置后增加
|
||||
* 异步增加左侧tab页
|
||||
* @memberof UserConfig
|
||||
*/
|
||||
addLeftCategory(v: LeftMapRenderListPropsItemCategory[]) {
|
||||
const obj = {} as InitConfig;
|
||||
obj.leftRenderListCategory = v;
|
||||
this.initConfig = userConfigMerge(this.initConfig, obj);
|
||||
this.init();
|
||||
store.forceUpdate();
|
||||
}
|
||||
|
||||
/**
|
||||
* 会重置配置,请修改配置后增加
|
||||
* 异步增加右侧tab页
|
||||
* @memberof UserConfig
|
||||
*/
|
||||
addRightCategory(v: RightMapRenderListPropsItemCategory[]) {
|
||||
const obj = {} as InitConfig;
|
||||
obj.rightRenderListCategory = v;
|
||||
this.initConfig = userConfigMerge(this.initConfig, obj);
|
||||
this.init();
|
||||
store.forceUpdate();
|
||||
}
|
||||
|
||||
/**
|
||||
* 会重置配置,请修改配置后增加
|
||||
* 异步增加组件map
|
||||
* @memberof UserConfig
|
||||
*/
|
||||
addCoRegistMap(v: LeftRegistComponentMapItem) {
|
||||
const obj = {} as InitConfig;
|
||||
obj.leftAllRegistMap = [v];
|
||||
this.initConfig = userConfigMerge(this.initConfig, obj);
|
||||
this.init();
|
||||
store.forceUpdate();
|
||||
}
|
||||
|
||||
/**
|
||||
*会重置配置,请修改配置后增加
|
||||
* 异步修改config 重置store
|
||||
* @memberof UserConfig
|
||||
*/
|
||||
setConfig(v: Partial<InitConfig>) {
|
||||
this.initConfig = userConfigMerge(this.initConfig, v);
|
||||
this.init();
|
||||
store.forceUpdate();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* 同步注册指令
|
||||
* @param {CommanderItem} command
|
||||
* @memberof UserConfig
|
||||
*/
|
||||
registCommander(command: CommanderItem) {
|
||||
this.commanderRegister.register(command);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* 用于修改markline配置
|
||||
* @returns
|
||||
* @memberof UserConfig
|
||||
*/
|
||||
getMarklineConfig() {
|
||||
return this.marklineConfig;
|
||||
}
|
||||
|
||||
getComponentCache() {
|
||||
return this.componentCache;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* 同步注册组件,不会检测缓存是否存在
|
||||
* @param {ComponentItemFactory} item
|
||||
* @memberof UserConfig
|
||||
*/
|
||||
registComponent(item: ComponentItemFactory) {
|
||||
this.componentRegister.register(item);
|
||||
}
|
||||
/**
|
||||
*
|
||||
* 异步注册组件,会判定缓存是否存在
|
||||
* @param {string} name
|
||||
* @memberof UserConfig
|
||||
*/
|
||||
async asyncRegistComponent(name: string) {
|
||||
//判定缓存
|
||||
if (
|
||||
!(this.componentCache as Record<string, CacheComponentValueType>)[name] &&
|
||||
this.asyncComponentUrlMap[name]
|
||||
) {
|
||||
const chunk = await this.asyncComponentUrlMap[name]();
|
||||
const chunkDefault = chunk.default;
|
||||
this.componentRegister.register(chunkDefault);
|
||||
(this.componentCache as Record<string, CacheComponentValueType>)[name] = {
|
||||
component: chunkDefault,
|
||||
};
|
||||
this.componentRegister.emitEvent(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default UserConfig;
|
||||
19
packages/dooringx-lib/src/core/command/abstract.ts
Normal file
19
packages/dooringx-lib/src/core/command/abstract.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-03-14 04:29:09
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-03-14 04:39:30
|
||||
* @FilePath: \dooring-v2\src\core\command\abstract.ts
|
||||
*/
|
||||
import { CommanderItem } from './commanderType';
|
||||
|
||||
export class CommanderItemFactory implements CommanderItem {
|
||||
constructor(
|
||||
public name: CommanderItem['name'],
|
||||
public keyboard: CommanderItem['keyboard'],
|
||||
public excute: CommanderItem['excute'],
|
||||
public display: CommanderItem['display'],
|
||||
public init: CommanderItem['init'] = () => {},
|
||||
public destroy: CommanderItem['destroy'] = () => {}
|
||||
) {}
|
||||
}
|
||||
17
packages/dooringx-lib/src/core/command/commanderType.ts
Normal file
17
packages/dooringx-lib/src/core/command/commanderType.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-03-14 04:29:09
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-03-14 04:39:43
|
||||
* @FilePath: \dooring-v2\src\core\command\commanderType.ts
|
||||
*/
|
||||
import Store from '../store';
|
||||
|
||||
export interface CommanderItem {
|
||||
init: () => void;
|
||||
display: string;
|
||||
name: string;
|
||||
keyboard: string;
|
||||
excute: (store: Store, options?: any) => void;
|
||||
destroy: () => void;
|
||||
}
|
||||
84
packages/dooringx-lib/src/core/command/index.ts
Normal file
84
packages/dooringx-lib/src/core/command/index.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-03-14 04:29:09
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-07-08 20:42:15
|
||||
* @FilePath: \DooringV2\packages\dooringx-lib\src\core\command\index.ts
|
||||
*/
|
||||
import Store from '../store';
|
||||
import { CommanderItem } from './commanderType';
|
||||
import { keycodeFilter } from './keycode';
|
||||
|
||||
class CommanderWrapper {
|
||||
constructor(public store: Store, public commandMap: Record<string, CommanderItem> = {}) {}
|
||||
getList() {
|
||||
return this.commandMap;
|
||||
}
|
||||
register(item: CommanderItem) {
|
||||
item.init();
|
||||
if (this.commandMap[item.name]) {
|
||||
// console.error(`${item.name} commander has registed`);
|
||||
return;
|
||||
}
|
||||
this.commandMap[item.name] = item;
|
||||
//注册快捷键,快捷键执行调用excute
|
||||
const remove = this.registerKeyBoard(item);
|
||||
//改写销毁方法
|
||||
const origindestroy = item.destroy;
|
||||
const newdestroy = () => {
|
||||
remove();
|
||||
origindestroy();
|
||||
};
|
||||
item.destroy = newdestroy;
|
||||
}
|
||||
|
||||
registerKeyBoard(current: CommanderItem) {
|
||||
if (current.keyboard.length === 0) {
|
||||
return () => {};
|
||||
}
|
||||
const onKeydown = (e: KeyboardEvent) => {
|
||||
if (document.activeElement !== document.body && e.target !== document.body) {
|
||||
return;
|
||||
}
|
||||
const { shiftKey, altKey, ctrlKey, metaKey, key } = e;
|
||||
const keyString: string[] = [];
|
||||
if (ctrlKey || metaKey) keyString.push('Control');
|
||||
if (shiftKey) keyString.push('Shift');
|
||||
if (altKey) keyString.push('Alt');
|
||||
if (key !== 'Control' && key !== 'Shift' && key !== 'Alt') {
|
||||
const res = keycodeFilter(key);
|
||||
if (res !== undefined) {
|
||||
keyString.push(res);
|
||||
} else {
|
||||
keyString.push(key);
|
||||
}
|
||||
}
|
||||
const keyname = keyString.join('+');
|
||||
if (current.keyboard === keyname) {
|
||||
current.excute(this.store);
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
}
|
||||
};
|
||||
window.addEventListener('keydown', onKeydown);
|
||||
return () => window.removeEventListener('keydown', onKeydown);
|
||||
}
|
||||
|
||||
unRegister(name: string) {
|
||||
if (!this.commandMap[name]) {
|
||||
console.error(`${name} commander not found`);
|
||||
return;
|
||||
}
|
||||
const item = this.commandMap[name];
|
||||
item.destroy();
|
||||
delete this.commandMap[item.name];
|
||||
}
|
||||
exec(name: string, options?: any) {
|
||||
if (!this.commandMap[name]) {
|
||||
console.error(`${name} commander not found`);
|
||||
return;
|
||||
}
|
||||
this.commandMap[name].excute(this.store, options);
|
||||
}
|
||||
}
|
||||
export default CommanderWrapper;
|
||||
225
packages/dooringx-lib/src/core/command/keycode.ts
Normal file
225
packages/dooringx-lib/src/core/command/keycode.ts
Normal file
@@ -0,0 +1,225 @@
|
||||
export default {
|
||||
Cancel: 3,
|
||||
Help: 6,
|
||||
Backspace: 8,
|
||||
Tab: 9,
|
||||
Clear: 12,
|
||||
Enter: 13,
|
||||
Shift: 16,
|
||||
Control: 17,
|
||||
Alt: 18,
|
||||
Pause: 19,
|
||||
CapsLock: 20,
|
||||
Escape: 27,
|
||||
Convert: 28,
|
||||
NonConvert: 29,
|
||||
Accept: 30,
|
||||
ModeChange: 31,
|
||||
' ': 32,
|
||||
PageUp: 33,
|
||||
PageDown: 34,
|
||||
End: 35,
|
||||
Home: 36,
|
||||
ArrowLeft: 37,
|
||||
ArrowUp: 38,
|
||||
ArrowRight: 39,
|
||||
ArrowDown: 40,
|
||||
Select: 41,
|
||||
Print: 42,
|
||||
Execute: 43,
|
||||
PrintScreen: 44,
|
||||
Insert: 45,
|
||||
Delete: 46,
|
||||
0: 48,
|
||||
')': 48,
|
||||
1: 49,
|
||||
'!': 49,
|
||||
2: 50,
|
||||
'@': 50,
|
||||
3: 51,
|
||||
'#': 51,
|
||||
4: 52,
|
||||
$: 52,
|
||||
5: 53,
|
||||
'%': 53,
|
||||
6: 54,
|
||||
'^': 54,
|
||||
7: 55,
|
||||
'&': 55,
|
||||
8: 56,
|
||||
'*': 56,
|
||||
9: 57,
|
||||
'(': 57,
|
||||
a: 65,
|
||||
A: 65,
|
||||
b: 66,
|
||||
B: 66,
|
||||
c: 67,
|
||||
C: 67,
|
||||
d: 68,
|
||||
D: 68,
|
||||
e: 69,
|
||||
E: 69,
|
||||
f: 70,
|
||||
F: 70,
|
||||
g: 71,
|
||||
G: 71,
|
||||
h: 72,
|
||||
H: 72,
|
||||
i: 73,
|
||||
I: 73,
|
||||
j: 74,
|
||||
J: 74,
|
||||
k: 75,
|
||||
K: 75,
|
||||
l: 76,
|
||||
L: 76,
|
||||
m: 77,
|
||||
M: 77,
|
||||
n: 78,
|
||||
N: 78,
|
||||
o: 79,
|
||||
O: 79,
|
||||
p: 80,
|
||||
P: 80,
|
||||
q: 81,
|
||||
Q: 81,
|
||||
r: 82,
|
||||
R: 82,
|
||||
s: 83,
|
||||
S: 83,
|
||||
t: 84,
|
||||
T: 84,
|
||||
u: 85,
|
||||
U: 85,
|
||||
v: 86,
|
||||
V: 86,
|
||||
w: 87,
|
||||
W: 87,
|
||||
x: 88,
|
||||
X: 88,
|
||||
y: 89,
|
||||
Y: 89,
|
||||
z: 90,
|
||||
Z: 90,
|
||||
OS: 91,
|
||||
ContextMenu: 93,
|
||||
F1: 112,
|
||||
F2: 113,
|
||||
F3: 114,
|
||||
F4: 115,
|
||||
F5: 116,
|
||||
F6: 117,
|
||||
F7: 118,
|
||||
F8: 119,
|
||||
F9: 120,
|
||||
F10: 121,
|
||||
F11: 122,
|
||||
F12: 123,
|
||||
F13: 124,
|
||||
F14: 125,
|
||||
F15: 126,
|
||||
F16: 127,
|
||||
F17: 128,
|
||||
F18: 129,
|
||||
F19: 130,
|
||||
F20: 131,
|
||||
F21: 132,
|
||||
F22: 133,
|
||||
F23: 134,
|
||||
F24: 135,
|
||||
NumLock: 144,
|
||||
ScrollLock: 145,
|
||||
VolumeMute: 181,
|
||||
VolumeDown: 182,
|
||||
VolumeUp: 183,
|
||||
';': 186,
|
||||
':': 186,
|
||||
'=': 187,
|
||||
'+': 187,
|
||||
',': 188,
|
||||
'<': 188,
|
||||
'-': 189,
|
||||
_: 189,
|
||||
'.': 190,
|
||||
'>': 190,
|
||||
'/': 191,
|
||||
'?': 191,
|
||||
'`': 192,
|
||||
'~': 192,
|
||||
'[': 219,
|
||||
'{': 219,
|
||||
'\\': 220,
|
||||
'|': 220,
|
||||
']': 221,
|
||||
'}': 221,
|
||||
"'": 222,
|
||||
'"': 222,
|
||||
Meta: 224,
|
||||
AltGraph: 225,
|
||||
Attn: 246,
|
||||
CrSel: 247,
|
||||
ExSel: 248,
|
||||
EraseEof: 249,
|
||||
Play: 250,
|
||||
ZoomOut: 251,
|
||||
};
|
||||
|
||||
const keymap: Record<string, string> = {
|
||||
a: 'a',
|
||||
A: 'a',
|
||||
b: 'b',
|
||||
B: 'b',
|
||||
c: 'c',
|
||||
C: 'c',
|
||||
d: 'd',
|
||||
D: 'd',
|
||||
e: 'e',
|
||||
E: 'e',
|
||||
f: 'f',
|
||||
F: 'f',
|
||||
g: 'g',
|
||||
G: 'g',
|
||||
h: 'h',
|
||||
H: 'h',
|
||||
i: 'i',
|
||||
I: 'i',
|
||||
j: 'j',
|
||||
J: 'j',
|
||||
k: 'k',
|
||||
K: 'k',
|
||||
l: 'l',
|
||||
L: 'l',
|
||||
m: 'm',
|
||||
M: 'm',
|
||||
n: 'n',
|
||||
N: 'n',
|
||||
o: 'o',
|
||||
O: 'o',
|
||||
p: 'p',
|
||||
P: 'p',
|
||||
q: 'q',
|
||||
Q: 'q',
|
||||
r: 'r',
|
||||
R: 'r',
|
||||
s: 's',
|
||||
S: 's',
|
||||
t: 't',
|
||||
T: 't',
|
||||
u: 'u',
|
||||
U: 'u',
|
||||
v: 'v',
|
||||
V: 'v',
|
||||
w: 'w',
|
||||
W: 'w',
|
||||
x: 'x',
|
||||
X: 'x',
|
||||
y: 'y',
|
||||
Y: 'y',
|
||||
z: 'z',
|
||||
Z: 'z',
|
||||
};
|
||||
|
||||
export const keycodeFilter = (key: string) => {
|
||||
return keymap[key];
|
||||
};
|
||||
20
packages/dooringx-lib/src/core/command/runtime.ts
Normal file
20
packages/dooringx-lib/src/core/command/runtime.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-07-04 14:18:18
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-07-04 14:21:33
|
||||
* @FilePath: \DooringV2\packages\dooring-v2-lib\src\core\command\runtime.ts
|
||||
*/
|
||||
import CommanderWrapper from '.';
|
||||
import { CommanderItemFactory } from './abstract';
|
||||
|
||||
export const registCommandFn = (module: CommanderItemFactory[], commander: CommanderWrapper) => {
|
||||
module.forEach((v) => {
|
||||
commander.register(v);
|
||||
});
|
||||
};
|
||||
export const unRegistCommandFn = (module: CommanderItemFactory[], commander: CommanderWrapper) => {
|
||||
module.forEach((v) => {
|
||||
commander.unRegister(v.name);
|
||||
});
|
||||
};
|
||||
22
packages/dooringx-lib/src/core/components/abstract.ts
Normal file
22
packages/dooringx-lib/src/core/components/abstract.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-03-14 04:29:09
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-04-04 23:14:00
|
||||
* @FilePath: \dooringv2\src\core\components\abstract.ts
|
||||
*/
|
||||
import { ComponentItem } from './componentItem';
|
||||
|
||||
export class ComponentItemFactory implements ComponentItem {
|
||||
constructor(
|
||||
public name: ComponentItem['name'],
|
||||
public display: ComponentItem['display'],
|
||||
public props: ComponentItem['props'],
|
||||
public initData: ComponentItem['initData'],
|
||||
public render: ComponentItem['render'],
|
||||
public resize: ComponentItem['resize'] = true,
|
||||
public needPosition: ComponentItem['needPosition'] = true,
|
||||
public init: ComponentItem['init'] = () => {},
|
||||
public destroy: ComponentItem['destroy'] = () => {}
|
||||
) {}
|
||||
}
|
||||
35
packages/dooringx-lib/src/core/components/componentItem.ts
Normal file
35
packages/dooringx-lib/src/core/components/componentItem.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-03-14 04:29:09
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-07-06 23:45:21
|
||||
* @FilePath: \dooringv2\packages\dooring-v2-lib\src\core\components\componentItem.ts
|
||||
*/
|
||||
import UserConfig from '../../config';
|
||||
import Store from '../store';
|
||||
import { IBlockType } from '../store/storetype';
|
||||
import { CreateOptionsResAll } from './formTypes';
|
||||
|
||||
/**
|
||||
*
|
||||
* 包装部分配置,渲染配置,条件渲染,属性
|
||||
* @export
|
||||
* @interface ComponentItem
|
||||
*/
|
||||
export interface ComponentItem {
|
||||
init: () => void;
|
||||
name: string; // map上key名
|
||||
display: string; //显示名称
|
||||
resize: boolean;
|
||||
needPosition: boolean; //是否要使用拖拽的点
|
||||
initData: Partial<IBlockType>; //初始值
|
||||
props: Record<string, CreateOptionsResAll[]>; // 配置属性
|
||||
render: (data: IBlockType, context: any, store: Store, config: UserConfig) => JSX.Element;
|
||||
destroy: () => void;
|
||||
}
|
||||
export type ComponentRenderConfigProps = {
|
||||
data: IBlockType;
|
||||
context: any;
|
||||
store: Store;
|
||||
config: UserConfig;
|
||||
};
|
||||
34
packages/dooringx-lib/src/core/components/createBlock.ts
Normal file
34
packages/dooringx-lib/src/core/components/createBlock.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-03-14 04:29:09
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-07-07 16:36:42
|
||||
* @FilePath: \DooringV2\packages\dooringx-lib\src\core\components\createBlock.ts
|
||||
*/
|
||||
import { IBlockType } from '../store/storetype';
|
||||
import { createUid } from '../utils';
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
import { ComponentItem } from './componentItem';
|
||||
|
||||
export function createBlock(top: number, left: number, ComponentItem: ComponentItem): IBlockType {
|
||||
return {
|
||||
id: createUid(ComponentItem.name),
|
||||
name: ComponentItem.name,
|
||||
top,
|
||||
left,
|
||||
zIndex: ComponentItem.initData.zIndex || 0,
|
||||
props: ComponentItem.initData.props || {},
|
||||
resize: ComponentItem.initData.resize || ComponentItem.resize,
|
||||
focus: false,
|
||||
position: ComponentItem.initData.position || 'absolute',
|
||||
display: ComponentItem.initData.display || 'block',
|
||||
width: ComponentItem.initData.width,
|
||||
height: ComponentItem.initData.height,
|
||||
syncList: ComponentItem.initData.syncList || [],
|
||||
canDrag: ComponentItem.initData.canDrag ?? true,
|
||||
eventMap: ComponentItem.initData.eventMap || {},
|
||||
functionList: ComponentItem.initData.functionList || [],
|
||||
animate: ComponentItem.initData.animate || {},
|
||||
fixed: ComponentItem.initData.fixed || false,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-04-05 19:21:36
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-07-05 23:56:07
|
||||
* @FilePath: \DooringV2\packages\dooring-v2-lib\src\core\components\defaultFormComponents\modalContainer.tsx
|
||||
*/
|
||||
import React from 'react';
|
||||
import { ComponentItemFactory } from '../abstract';
|
||||
|
||||
const MmodalContainer = new ComponentItemFactory(
|
||||
'modalContainer',
|
||||
'模态框容器',
|
||||
{},
|
||||
{
|
||||
props: {},
|
||||
width: 300,
|
||||
height: 300,
|
||||
},
|
||||
(data) => {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
zIndex: data.zIndex,
|
||||
width: data.width,
|
||||
height: data.height,
|
||||
backgroundColor: 'white',
|
||||
}}
|
||||
></div>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export default MmodalContainer;
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-04-04 20:35:11
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-07-05 23:55:53
|
||||
* @FilePath: \DooringV2\packages\dooring-v2-lib\src\core\components\defaultFormComponents\modalMask.tsx
|
||||
*/
|
||||
import { SaveOutlined } from '@ant-design/icons';
|
||||
import { Button } from 'antd';
|
||||
import React from 'react';
|
||||
import { ComponentItemFactory } from '../abstract';
|
||||
|
||||
const MmodalMask = new ComponentItemFactory(
|
||||
'modalMask',
|
||||
'模态框遮罩',
|
||||
{},
|
||||
{
|
||||
props: {},
|
||||
position: 'fixed',
|
||||
top: 0,
|
||||
left: 0,
|
||||
zIndex: 999,
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
canDrag: false,
|
||||
},
|
||||
(_, context, store, config) => {
|
||||
const container = store.getData().container;
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
width: context === 'preview' ? '100%' : container.width,
|
||||
height: context === 'preview' ? '100%' : container.height,
|
||||
backgroundColor: '#716f6f9e',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
{context === 'edit' && (
|
||||
<Button
|
||||
type="primary"
|
||||
shape="circle"
|
||||
title="save"
|
||||
style={{ position: 'absolute', right: '10px', top: '10px' }}
|
||||
icon={<SaveOutlined></SaveOutlined>}
|
||||
onClick={() => {
|
||||
config.getStoreChanger().closeModal(config.getStore());
|
||||
}}
|
||||
></Button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
true,
|
||||
false
|
||||
);
|
||||
|
||||
export default MmodalMask;
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-03-14 04:29:09
|
||||
* @LastEditors: chentianshang
|
||||
* @LastEditTime: 2021-07-06 09:53:29
|
||||
* @FilePath: /DooringV2/packages/dooring-v2-lib/src/core/components/formComponentRegister.ts
|
||||
*/
|
||||
|
||||
import { ComponentClass, FunctionComponent } from 'react';
|
||||
import { CreateOptionsRes } from './formTypes';
|
||||
export interface ContainerConfigItem {
|
||||
type: string;
|
||||
option: CreateOptionsRes<any, any>;
|
||||
}
|
||||
export const formComponentRegisterFn = (
|
||||
formComponent: FormComponentRegister,
|
||||
modules: Record<string, FunctionComponent<any> | ComponentClass<any, any>>
|
||||
) => {
|
||||
Object.keys(modules).forEach((v) => {
|
||||
formComponent.register(v, modules[v]);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* 拿到form组件地址和状态
|
||||
* 获取配置container配置项和普通组件配置项
|
||||
* @export
|
||||
* @class FormComponentRegister
|
||||
*/
|
||||
export class FormComponentRegister {
|
||||
constructor(
|
||||
public formMap: Record<string, FunctionComponent<any> | ComponentClass<any, any>> = {},
|
||||
public listener: Function[] = [],
|
||||
public eventMap: Record<string, Function[]> = {},
|
||||
public containerConfig: Array<ContainerConfigItem> = []
|
||||
) {}
|
||||
getMap() {
|
||||
return this.formMap;
|
||||
}
|
||||
getComp(name: string) {
|
||||
return this.formMap[name];
|
||||
}
|
||||
getConfig() {
|
||||
return this.containerConfig;
|
||||
}
|
||||
setConfig(config: Array<ContainerConfigItem>) {
|
||||
this.containerConfig = config;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* 同步注册方法
|
||||
* @memberof FormComponentRegister
|
||||
*/
|
||||
register(name: string, ele: FunctionComponent<any> | ComponentClass<any, any>) {
|
||||
this.formMap[name] = ele;
|
||||
}
|
||||
|
||||
emit() {
|
||||
this.listener.forEach((v) => v());
|
||||
}
|
||||
|
||||
on(event: string, fn: Function) {
|
||||
if (!this.eventMap[event]) {
|
||||
this.eventMap[event] = [];
|
||||
}
|
||||
this.eventMap[event].push(fn);
|
||||
return () => this.eventMap[event].filter((v) => v !== fn);
|
||||
}
|
||||
}
|
||||
27
packages/dooringx-lib/src/core/components/formTypes.ts
Normal file
27
packages/dooringx-lib/src/core/components/formTypes.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-03-14 04:29:09
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-07-06 23:45:02
|
||||
* @FilePath: \dooringv2\packages\dooring-v2-lib\src\core\components\formTypes.ts
|
||||
*/
|
||||
|
||||
export interface CreateOptionsResAll {
|
||||
type: string;
|
||||
option: any;
|
||||
}
|
||||
|
||||
export interface CreateOptionsRes<T, K extends keyof T> {
|
||||
type: keyof T;
|
||||
option: T[K];
|
||||
}
|
||||
|
||||
export function createPannelOptions<T, K extends keyof T>(
|
||||
type: K,
|
||||
option: T[K]
|
||||
): CreateOptionsRes<T, K> {
|
||||
return {
|
||||
type,
|
||||
option,
|
||||
};
|
||||
}
|
||||
79
packages/dooringx-lib/src/core/components/index.ts
Normal file
79
packages/dooringx-lib/src/core/components/index.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-03-14 04:29:09
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-03-14 04:43:53
|
||||
* @FilePath: \dooring-v2\src\core\components\index.ts
|
||||
*/
|
||||
import { ComponentItem } from './componentItem';
|
||||
|
||||
/**
|
||||
*
|
||||
* 注册组件需要异步的,由注册时效果决定。
|
||||
* 主要是存放所有已注册组件。可以在其render时提供对应context
|
||||
* @class ComponentRegister
|
||||
*/
|
||||
class ComponentRegister {
|
||||
constructor(
|
||||
public componentMap: Record<string, ComponentItem> = {},
|
||||
public componentList: ComponentItem[] = [],
|
||||
public listener: Function[] = [],
|
||||
public eventMap: Record<string, Function[]> = {}
|
||||
) {}
|
||||
getMap() {
|
||||
return this.componentMap;
|
||||
}
|
||||
getList() {
|
||||
return this.componentList;
|
||||
}
|
||||
getComp(name: string) {
|
||||
return this.componentMap[name];
|
||||
}
|
||||
|
||||
subscribe(fn: Function) {
|
||||
this.listener.push(fn);
|
||||
return () => this.listener.filter((v) => v !== fn);
|
||||
}
|
||||
|
||||
emit() {
|
||||
this.listener.forEach((v) => v());
|
||||
}
|
||||
|
||||
on(event: string, fn: Function) {
|
||||
if (!this.eventMap[event]) {
|
||||
this.eventMap[event] = [];
|
||||
}
|
||||
this.eventMap[event].push(fn);
|
||||
return () => this.eventMap[event].filter((v) => v !== fn);
|
||||
}
|
||||
emitEvent(event: string) {
|
||||
if (!this.eventMap[event]) {
|
||||
return;
|
||||
}
|
||||
this.eventMap[event].forEach((v) => v());
|
||||
}
|
||||
|
||||
register(item: ComponentItem) {
|
||||
if (this.componentMap[item.name]) {
|
||||
// console.error(`${item.name} component has registed`);
|
||||
return;
|
||||
}
|
||||
this.componentMap[item.name] = item;
|
||||
this.componentList.push(item);
|
||||
this.emit();
|
||||
item.init();
|
||||
}
|
||||
|
||||
unRegister(name: string) {
|
||||
if (!this.componentMap[name]) {
|
||||
console.error(`${name} component not found`);
|
||||
return;
|
||||
}
|
||||
const item = this.componentMap[name];
|
||||
item.destroy();
|
||||
this.emit();
|
||||
this.componentList = this.componentList.filter((v) => v !== item);
|
||||
delete this.componentMap[item.name];
|
||||
}
|
||||
}
|
||||
export default ComponentRegister;
|
||||
132
packages/dooringx-lib/src/core/contextMenu/index.tsx
Normal file
132
packages/dooringx-lib/src/core/contextMenu/index.tsx
Normal file
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-03-14 04:29:09
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-07-05 22:46:15
|
||||
* @FilePath: \DooringV2\packages\dooring-v2-lib\src\core\contextMenu\index.tsx
|
||||
*/
|
||||
import { Button } from 'antd';
|
||||
import React, { RefObject, useState } from 'react';
|
||||
import { ReactElement } from 'react';
|
||||
import ReactDOM, { unmountComponentAtNode } from 'react-dom';
|
||||
import { scaleState } from '../scale/state';
|
||||
import { isMac } from '../utils';
|
||||
|
||||
const ContextMenu = () => {
|
||||
const handleclick = () => {
|
||||
unmountContextMenu();
|
||||
};
|
||||
const forceUpdate = useState(0)[1];
|
||||
contextMenuState.forceUpdate = () => {
|
||||
forceUpdate((pre) => pre + 1);
|
||||
};
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
left: contextMenuState.left,
|
||||
top: contextMenuState.top,
|
||||
position: 'fixed',
|
||||
background: 'rgb(24, 23, 23)',
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
onClick={() => {
|
||||
handleclick();
|
||||
}}
|
||||
>
|
||||
请参考文档自定义右键菜单
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
export interface ContextMenuStateProps {
|
||||
left: number;
|
||||
top: number;
|
||||
menu: HTMLElement | null;
|
||||
parent: HTMLDivElement | null;
|
||||
contextMenu: ReactElement;
|
||||
unmountContextMenu: () => void;
|
||||
observer: null | MutationObserver;
|
||||
initLeft: number;
|
||||
initTop: number;
|
||||
forceUpdate: Function;
|
||||
state: boolean;
|
||||
}
|
||||
export const contextMenuState: ContextMenuStateProps = {
|
||||
left: 0,
|
||||
top: 0,
|
||||
menu: null,
|
||||
parent: null,
|
||||
contextMenu: <ContextMenu />,
|
||||
unmountContextMenu: unmountContextMenu,
|
||||
observer: null,
|
||||
initLeft: 0,
|
||||
initTop: 0,
|
||||
forceUpdate: () => {},
|
||||
state: false,
|
||||
};
|
||||
|
||||
export function contextMenuEvent(
|
||||
e: React.MouseEvent<HTMLDivElement, MouseEvent>,
|
||||
ref: RefObject<HTMLDivElement>
|
||||
) {
|
||||
e.preventDefault();
|
||||
contextMenuState.unmountContextMenu();
|
||||
const config: MutationObserverInit = {
|
||||
attributes: true,
|
||||
};
|
||||
const callback: MutationCallback = (mutationsList) => {
|
||||
if (isMac()) {
|
||||
//mac 有bug
|
||||
contextMenuState.unmountContextMenu();
|
||||
} else {
|
||||
for (let mutation of mutationsList) {
|
||||
if (mutation.type === 'attributes') {
|
||||
const scale = scaleState.value;
|
||||
const curLeft = parseFloat((mutation.target as HTMLDivElement).style.left);
|
||||
const curTop = parseFloat((mutation.target as HTMLDivElement).style.top);
|
||||
const diffL = (curLeft - contextMenuState.initLeft) * scale;
|
||||
const diffT = (curTop - contextMenuState.initTop) * scale;
|
||||
contextMenuState.initLeft = curLeft;
|
||||
contextMenuState.initTop = curTop;
|
||||
contextMenuState.left = contextMenuState.left + diffL;
|
||||
contextMenuState.top = contextMenuState.top + diffT;
|
||||
contextMenuState.forceUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
contextMenuState.state = true;
|
||||
contextMenuState.observer = new MutationObserver(callback);
|
||||
if (ref.current) {
|
||||
//记录初始值
|
||||
contextMenuState.initTop = parseFloat(ref.current.style.top);
|
||||
contextMenuState.initLeft = parseFloat(ref.current.style.left);
|
||||
contextMenuState.observer.observe(ref.current, config);
|
||||
}
|
||||
contextMenuState.left = e.clientX;
|
||||
contextMenuState.top = e.clientY;
|
||||
if (!contextMenuState.menu) {
|
||||
contextMenuState.menu = document.createElement('div');
|
||||
document.body && document.body.appendChild(contextMenuState.menu);
|
||||
}
|
||||
if (!contextMenuState.parent) {
|
||||
contextMenuState.parent = document.createElement('div');
|
||||
}
|
||||
contextMenuState.menu.appendChild(contextMenuState.parent);
|
||||
ReactDOM.render(contextMenuState.contextMenu, contextMenuState.parent);
|
||||
}
|
||||
|
||||
export function unmountContextMenu() {
|
||||
contextMenuState.state = false;
|
||||
if (contextMenuState.observer) {
|
||||
contextMenuState.observer.disconnect();
|
||||
}
|
||||
if (contextMenuState.menu && contextMenuState.parent) {
|
||||
unmountComponentAtNode(contextMenuState.parent);
|
||||
contextMenuState.menu.removeChild(contextMenuState.parent);
|
||||
contextMenuState.parent = null;
|
||||
}
|
||||
}
|
||||
|
||||
export default ContextMenu;
|
||||
84
packages/dooringx-lib/src/core/crossDrag/index.ts
Normal file
84
packages/dooringx-lib/src/core/crossDrag/index.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-03-14 04:29:09
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-07-04 15:21:54
|
||||
* @FilePath: \DooringV2\packages\dooring-v2-lib\src\core\crossDrag\index.ts
|
||||
*/
|
||||
import { store } from '../../runtime/store';
|
||||
import { componentRegister } from '../../runtime';
|
||||
import { DragEvent, ReactNode } from 'react';
|
||||
import { createBlock } from '../components/createBlock';
|
||||
import { IBlockType } from '../store/storetype';
|
||||
import { deepCopy } from '../utils';
|
||||
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
* @interface LeftRegistComponentMapItem
|
||||
* @img 图片地址
|
||||
* @urlFn 组件异步加载函数
|
||||
*/
|
||||
export interface LeftRegistComponentMapItem {
|
||||
type: string;
|
||||
component: string;
|
||||
img: string;
|
||||
imgCustom?: ReactNode;
|
||||
displayName: string;
|
||||
urlFn?: () => Promise<any>;
|
||||
}
|
||||
|
||||
let currentDrag: LeftRegistComponentMapItem | null = null;
|
||||
export const dragEventResolve = function (item: LeftRegistComponentMapItem) {
|
||||
return {
|
||||
draggable: true,
|
||||
onDragStart: () => {
|
||||
currentDrag = item;
|
||||
},
|
||||
onDragOver: (e: DragEvent<HTMLDivElement>) => {
|
||||
e.preventDefault();
|
||||
},
|
||||
onDrop: () => {},
|
||||
onDragEnd: () => {},
|
||||
};
|
||||
};
|
||||
|
||||
export const containerDragResolve = {
|
||||
onDragStart: () => {},
|
||||
onDragOver: (e: DragEvent<HTMLDivElement>) => {
|
||||
e.preventDefault();
|
||||
},
|
||||
onDrop: (e: DragEvent<HTMLDivElement>) => {
|
||||
const offsetX = e.nativeEvent.offsetX;
|
||||
const offestY = e.nativeEvent.offsetY;
|
||||
//drop后修改store,
|
||||
if (currentDrag) {
|
||||
// 还需要拿到注册的组件状态
|
||||
const origin = componentRegister.getComp(currentDrag.component);
|
||||
if (!origin) {
|
||||
console.log(currentDrag.component, 'wait the chunk pull compeletely and retry');
|
||||
return;
|
||||
}
|
||||
const target = e.target as HTMLElement;
|
||||
let newblock: IBlockType;
|
||||
if (!origin.needPosition) {
|
||||
newblock = createBlock(
|
||||
origin.initData.top ?? offestY,
|
||||
origin.initData.left ?? offsetX,
|
||||
origin
|
||||
);
|
||||
} else {
|
||||
if (target.id !== 'yh-container') {
|
||||
newblock = createBlock(offestY + target.offsetTop, offsetX + target.offsetLeft, origin);
|
||||
} else {
|
||||
newblock = createBlock(offestY, offsetX, origin);
|
||||
}
|
||||
}
|
||||
const data = deepCopy(store.getData());
|
||||
data.block.push(newblock);
|
||||
store.setData({ ...data });
|
||||
}
|
||||
currentDrag = null;
|
||||
},
|
||||
onDragEnd: () => {},
|
||||
};
|
||||
123
packages/dooringx-lib/src/core/dataCenter/index.ts
Normal file
123
packages/dooringx-lib/src/core/dataCenter/index.ts
Normal file
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-04-13 11:20:55
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-05-04 20:34:36
|
||||
* @FilePath: \dooringv2\packages\dooring-v2-lib\src\core\dataCenter\index.ts
|
||||
*/
|
||||
|
||||
import UserConfig from '../../config';
|
||||
import { IStoreData } from '../store/storetype';
|
||||
import { StoreChanger } from '../storeChanger';
|
||||
|
||||
/**
|
||||
*
|
||||
* 用来管理页面数据,包括全局数据,做全局设置变量时可以加上
|
||||
* 使用Record<string,any>结构,每个组件的数据需要抛出并设定键进行通信。
|
||||
* @export
|
||||
* @class DataCenter
|
||||
*/
|
||||
export class DataCenter {
|
||||
public asyncMap: Record<string, Function> = {};
|
||||
constructor(public dataMap: Record<string, any> = {}) {}
|
||||
|
||||
/**
|
||||
*
|
||||
* 拿到map
|
||||
* @return {*}
|
||||
* @memberof DataCenter
|
||||
*/
|
||||
getDataMap() {
|
||||
return this.dataMap;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* 用于设置map数据
|
||||
* 在异步注册时会触发get的回调,动态不需要持久化
|
||||
* @memberof DataCenter
|
||||
*/
|
||||
setToMap(data: Record<string, any>) {
|
||||
this.dataMap = Object.assign(this.dataMap, data);
|
||||
Object.keys(data).forEach((v) => {
|
||||
if (this.asyncMap[v]) {
|
||||
this.asyncMap[v]();
|
||||
delete this.asyncMap[v];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* 静态设置map 和异步无关 静态需要持久化,datacenter存入store
|
||||
* 该更新不放在redo undo处
|
||||
* @param {Record<string, any>} data
|
||||
* @memberof DataCenter
|
||||
*/
|
||||
staticSetToMap(data: Record<string, any>, config: UserConfig) {
|
||||
this.dataMap = data;
|
||||
const storeChanger = config.getStoreChanger();
|
||||
const store = config.getStore();
|
||||
const storeCurrentData = store.getData();
|
||||
const sign = storeChanger.isEdit();
|
||||
if (sign) {
|
||||
const originData = storeChanger.getOrigin();
|
||||
if (originData) {
|
||||
const currentData = originData.data[originData.current];
|
||||
currentData.dataSource = data;
|
||||
}
|
||||
} else {
|
||||
storeCurrentData.dataSource = data;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* 初始收集使用 -> to datacenter
|
||||
* @param {IStoreData} data
|
||||
* @memberof DataCenter
|
||||
*/
|
||||
initAddToDataMap(data: IStoreData, storeChanger: StoreChanger) {
|
||||
const sign = storeChanger.isEdit();
|
||||
//这里只能初始触发,一般不会走编辑状态,否则逻辑可能会有问题
|
||||
if (sign) {
|
||||
// 编辑状态收集orgin
|
||||
const originData = storeChanger.getOrigin();
|
||||
if (originData) {
|
||||
const currentData = originData.data[originData.current];
|
||||
this.dataMap = currentData.dataSource;
|
||||
}
|
||||
} else {
|
||||
this.dataMap = data.dataSource;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* 获取值可异步
|
||||
* @param {string} name
|
||||
* @memberof DataCenter
|
||||
*/
|
||||
getValue(name: string) {
|
||||
const value = this.dataMap[name];
|
||||
if (value) {
|
||||
return Promise.resolve(value);
|
||||
}
|
||||
return new Promise((resolve) => {
|
||||
this.asyncMap[name] = () => {
|
||||
resolve(this.getValue(name));
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* 获取值不可异步
|
||||
* @param {string} name
|
||||
* @memberof DataCenter
|
||||
*/
|
||||
get(name: string) {
|
||||
const value = this.dataMap[name];
|
||||
return value;
|
||||
}
|
||||
}
|
||||
72
packages/dooringx-lib/src/core/eventCenter/eventQuene.ts
Normal file
72
packages/dooringx-lib/src/core/eventCenter/eventQuene.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-04-08 20:22:43
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-06-28 16:08:56
|
||||
* @FilePath: \DooringV2\packages\dooring-v2-lib\src\core\eventCenter\eventQuene.ts
|
||||
*/
|
||||
import { EventCenterMapItem, EventCenterUserSelect } from '.';
|
||||
import UserConfig from '../../config';
|
||||
import { FunctionCenterFunction } from '../functionCenter';
|
||||
|
||||
export class EventQuene {
|
||||
available: number;
|
||||
waiters: Array<{
|
||||
fn: FunctionCenterFunction;
|
||||
args: any;
|
||||
eventList: {
|
||||
arr: Array<EventCenterMapItem>;
|
||||
displayName: string;
|
||||
userSelect: Array<EventCenterUserSelect>;
|
||||
};
|
||||
iname: EventCenterMapItem;
|
||||
}>;
|
||||
context: Record<string, any>;
|
||||
config: UserConfig;
|
||||
constructor(available: number = 1, config: UserConfig, context = {}) {
|
||||
this.available = available;
|
||||
this.waiters = [];
|
||||
this._continue = this._continue.bind(this);
|
||||
this.context = context;
|
||||
this.config = config;
|
||||
}
|
||||
take(
|
||||
task: FunctionCenterFunction,
|
||||
args: Record<string, any>,
|
||||
eventList: {
|
||||
arr: Array<EventCenterMapItem>;
|
||||
displayName: string;
|
||||
userSelect: Array<EventCenterUserSelect>;
|
||||
},
|
||||
iname: EventCenterMapItem
|
||||
) {
|
||||
if (this.available > 0) {
|
||||
this.available--;
|
||||
task(this.context, this.leave.bind(this), this.config, args, eventList, iname);
|
||||
} else {
|
||||
this.waiters.push({ fn: task, args: args, eventList, iname });
|
||||
}
|
||||
}
|
||||
leave() {
|
||||
this.available++;
|
||||
if (this.waiters.length > 0) {
|
||||
this._continue();
|
||||
}
|
||||
}
|
||||
_continue() {
|
||||
if (this.available > 0) {
|
||||
this.available--;
|
||||
let task = this.waiters.shift();
|
||||
if (task?.fn) {
|
||||
task.fn(
|
||||
this.context,
|
||||
this.leave.bind(this),
|
||||
this.config,
|
||||
task.args,
|
||||
task.eventList,
|
||||
task.iname
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
149
packages/dooringx-lib/src/core/eventCenter/index.ts
Normal file
149
packages/dooringx-lib/src/core/eventCenter/index.ts
Normal file
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-04-06 19:33:17
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-07-04 14:41:48
|
||||
* @FilePath: \DooringV2\packages\dooring-v2-lib\src\core\eventCenter\index.ts
|
||||
*/
|
||||
import UserConfig from '../../config';
|
||||
import { FunctionCenter, FunctionCenterType } from '../functionCenter';
|
||||
import { FunctionDataType } from '../functionCenter/config';
|
||||
import { IStoreData } from '../store/storetype';
|
||||
import { StoreChanger } from '../storeChanger';
|
||||
import { EventQuene } from './eventQuene';
|
||||
|
||||
// 每个组件制作时可以抛出多个事件,事件名为id+自定义name,
|
||||
// 每个组件可以抛出多个函数,存在函数中心
|
||||
|
||||
export interface EventCenterMapItem {
|
||||
name: string; // 函数名
|
||||
args: Record<string, any>; // 输入参数都会变成对象传来,
|
||||
data: Record<string, FunctionDataType>; // 用户选的种类 键是每个配置项名
|
||||
}
|
||||
export interface EventCenterUserSelect {
|
||||
uid: string;
|
||||
value: string;
|
||||
detail: Record<string, any>;
|
||||
}
|
||||
|
||||
export type EventCenterMapType = Record<
|
||||
string,
|
||||
{
|
||||
arr: Array<EventCenterMapItem>;
|
||||
displayName: string;
|
||||
userSelect: Array<EventCenterUserSelect>;
|
||||
}
|
||||
>;
|
||||
|
||||
export class EventCenter {
|
||||
/**
|
||||
* 该map需要存入store,值为函数的key的数组
|
||||
* @param {Record<string, Array<string>>} [eventMap={}]
|
||||
* @memberof EventCenter
|
||||
*/
|
||||
public functionCenter: FunctionCenter;
|
||||
constructor(public eventMap: EventCenterMapType = {}, configFunction?: FunctionCenterType) {
|
||||
this.functionCenter = new FunctionCenter(configFunction);
|
||||
}
|
||||
|
||||
getFunctionCenter() {
|
||||
return this.functionCenter;
|
||||
}
|
||||
|
||||
getEventMap() {
|
||||
return this.eventMap;
|
||||
}
|
||||
resetEventMap() {
|
||||
this.eventMap = {};
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* 重置map进行收集事件 主要就是收集eventMap字段
|
||||
* 这个应该优化在换store情况下。
|
||||
* @param {IStoreData} data
|
||||
* @memberof EventCenter
|
||||
*/
|
||||
syncEventMap(data: IStoreData, storeChanger: StoreChanger) {
|
||||
// 需要判断是否在弹窗状态。如果在弹窗状态,数据以storeChanger为准,否则就以store为准
|
||||
const sign = storeChanger.isEdit();
|
||||
this.eventMap = {};
|
||||
if (sign) {
|
||||
const originData = storeChanger.getOrigin();
|
||||
if (originData) {
|
||||
const currentData = originData.data[originData.current];
|
||||
// 收集源block数据
|
||||
currentData.block.forEach((v) => {
|
||||
this.eventMap = Object.assign(this.eventMap, v.eventMap);
|
||||
});
|
||||
//收集源modal数据
|
||||
Object.keys(currentData.modalMap).forEach((v) => {
|
||||
currentData.modalMap[v].block.forEach((k) => {
|
||||
this.eventMap = Object.assign(this.eventMap, k.eventMap);
|
||||
});
|
||||
});
|
||||
//收集当前modal数据
|
||||
data.block.forEach((v) => {
|
||||
this.eventMap = Object.assign(this.eventMap, v.eventMap);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
data.block.forEach((v) => {
|
||||
this.eventMap = Object.assign(this.eventMap, v.eventMap);
|
||||
});
|
||||
Object.keys(data.modalMap).forEach((v) => {
|
||||
data.modalMap[v].block.forEach((k) => {
|
||||
this.eventMap = Object.assign(this.eventMap, k.eventMap);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* 手动更新状态eventMap
|
||||
* @param {string} name
|
||||
* @memberof EventCenter
|
||||
*/
|
||||
manualUpdateMap(name: string, displayName: string, arr?: Array<EventCenterMapItem>) {
|
||||
if (!this.eventMap[name]) {
|
||||
this.eventMap[name] = {
|
||||
arr: [],
|
||||
displayName: displayName,
|
||||
userSelect: [],
|
||||
};
|
||||
}
|
||||
if (arr && this.eventMap[name].displayName) {
|
||||
this.eventMap[name].arr = arr;
|
||||
} else if (arr && this.eventMap[name]) {
|
||||
this.eventMap[name] = {
|
||||
displayName,
|
||||
arr,
|
||||
userSelect: [],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* 执行事件链
|
||||
* @param {string} name
|
||||
* @memberof EventCenter
|
||||
*/
|
||||
async runEventQueue(name: string, config: UserConfig) {
|
||||
const eventList = this.eventMap[name];
|
||||
if (!eventList) {
|
||||
console.error(`未查询到该事件${name}`);
|
||||
return;
|
||||
}
|
||||
const arr = new EventQuene(1, config);
|
||||
//如果组件异步加载,那么函数会过段时间载入,等同于异步函数
|
||||
// 函数中心需要处理未找到时的异步处理情况
|
||||
if (Array.isArray(eventList.arr)) {
|
||||
for (let i of eventList.arr) {
|
||||
const fn = await this.functionCenter.getFunction(i.name);
|
||||
arr.take(fn, i.args, eventList, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
60
packages/dooringx-lib/src/core/focusHandler/index.tsx
Normal file
60
packages/dooringx-lib/src/core/focusHandler/index.tsx
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-03-14 04:29:09
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-03-14 12:10:16
|
||||
* @FilePath: \dooring-v2\src\core\focusHandler\index.tsx
|
||||
*/
|
||||
import { store } from '../../runtime/store';
|
||||
import { innerDragState } from '../innerDrag/state';
|
||||
import { IBlockType } from '../store/storetype';
|
||||
import { deepCopy } from '../utils';
|
||||
import { selectRangeMouseDown } from '../selectRange';
|
||||
import { unmountContextMenu } from '../contextMenu';
|
||||
import { focusState } from './state';
|
||||
|
||||
export function containerFocusRemove() {
|
||||
const onMouseDown = (e: React.MouseEvent) => {
|
||||
const clonedata = deepCopy(store.getData());
|
||||
const newBlock = clonedata.block.map((v: IBlockType) => {
|
||||
v.focus = false;
|
||||
return v;
|
||||
});
|
||||
focusState.blocks = [];
|
||||
store.setData({ ...clonedata, block: newBlock });
|
||||
if (!innerDragState.item) {
|
||||
selectRangeMouseDown(e);
|
||||
}
|
||||
unmountContextMenu();
|
||||
};
|
||||
return {
|
||||
onMouseDown,
|
||||
};
|
||||
}
|
||||
|
||||
export function blockFocus(e: React.MouseEvent, item: IBlockType) {
|
||||
const clonedata = deepCopy(store.getData());
|
||||
if (e.shiftKey) {
|
||||
const newBlock = clonedata.block.map((v: IBlockType) => {
|
||||
if (v.id === item.id) {
|
||||
v.focus = true;
|
||||
focusState.blocks.push(item);
|
||||
}
|
||||
return v;
|
||||
});
|
||||
store.setData({ ...clonedata, block: newBlock });
|
||||
} else {
|
||||
let blocks: IBlockType[] = [];
|
||||
const newBlock = clonedata.block.map((v: IBlockType) => {
|
||||
if (v.id === item.id) {
|
||||
blocks.push(item);
|
||||
v.focus = true;
|
||||
} else {
|
||||
v.focus = false;
|
||||
}
|
||||
return v;
|
||||
});
|
||||
focusState.blocks = blocks;
|
||||
store.setData({ ...clonedata, block: newBlock });
|
||||
}
|
||||
}
|
||||
14
packages/dooringx-lib/src/core/focusHandler/state.ts
Normal file
14
packages/dooringx-lib/src/core/focusHandler/state.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-03-14 12:06:20
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-03-14 12:06:50
|
||||
* @FilePath: \dooring-v2\src\core\focusHandler\state.ts
|
||||
*/
|
||||
import { IBlockType } from '../store/storetype';
|
||||
export interface FocusStateType {
|
||||
blocks: IBlockType[];
|
||||
}
|
||||
export const focusState: FocusStateType = {
|
||||
blocks: [],
|
||||
};
|
||||
27
packages/dooringx-lib/src/core/functionCenter/config.ts
Normal file
27
packages/dooringx-lib/src/core/functionCenter/config.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-06-25 10:03:21
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-06-28 19:29:21
|
||||
* @FilePath: \DooringV2\packages\dooring-v2-lib\src\core\functionCenter\config.ts
|
||||
*/
|
||||
// 设定函数配置项格式,
|
||||
|
||||
export type FunctionDataType = keyof FunctionDataMap;
|
||||
export type FunctionNameType = string;
|
||||
export type FuncitonOptionConfigType = {
|
||||
receive: string;
|
||||
multi: boolean;
|
||||
};
|
||||
export interface FunctionDataMap {
|
||||
dataSource: FuncitonOptionConfigType;
|
||||
modal: FuncitonOptionConfigType;
|
||||
input: FuncitonOptionConfigType;
|
||||
ctx: FuncitonOptionConfigType;
|
||||
}
|
||||
// data 如果是''则在datasource,input,ctx选择
|
||||
export type FunctionConfigType = {
|
||||
name: FunctionNameType; // 会放到左侧展示 唯一!
|
||||
data: FunctionDataType[];
|
||||
options: FuncitonOptionConfigType;
|
||||
}[];
|
||||
128
packages/dooringx-lib/src/core/functionCenter/index.ts
Normal file
128
packages/dooringx-lib/src/core/functionCenter/index.ts
Normal file
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-04-08 19:59:01
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-07-07 01:46:17
|
||||
* @FilePath: \dooringv2\packages\dooringx-lib\src\core\functionCenter\index.ts
|
||||
*/
|
||||
|
||||
import UserConfig from '../../config';
|
||||
import { EventCenterMapItem, EventCenterUserSelect } from '../eventCenter';
|
||||
import { FunctionConfigType } from './config';
|
||||
|
||||
/**
|
||||
*
|
||||
* ctx可在事件链中传递,函数完成后调用next执行下一个函数
|
||||
* @export
|
||||
*/
|
||||
export type FunctionCenterFunction = (
|
||||
ctx: Record<string, any>,
|
||||
next: Function,
|
||||
config: UserConfig,
|
||||
args: Record<string, any>,
|
||||
eventList: {
|
||||
arr: Array<EventCenterMapItem>;
|
||||
displayName: string;
|
||||
userSelect: Array<EventCenterUserSelect>;
|
||||
},
|
||||
iname: EventCenterMapItem
|
||||
) => void;
|
||||
|
||||
export type FunctionCenterType = Record<
|
||||
string,
|
||||
{ fn: FunctionCenterFunction; config: FunctionConfigType }
|
||||
>;
|
||||
|
||||
/**
|
||||
*
|
||||
* 初始化时可以加载初始已配好的函数
|
||||
* @export
|
||||
* @class FunctionCenter
|
||||
*/
|
||||
export class FunctionCenter {
|
||||
/**
|
||||
*
|
||||
* 该map 用于获取函数未获取到时,异步拉取
|
||||
* @memberof FunctionCenter
|
||||
*/
|
||||
public asyncMap: Record<string, Function> = {};
|
||||
public configMap: Record<string, FunctionConfigType> = {};
|
||||
public funcitonMap: Record<string, FunctionCenterFunction> = {};
|
||||
constructor(public initConfig: FunctionCenterType = {}) {
|
||||
this.init(initConfig);
|
||||
}
|
||||
|
||||
init(initConfig: FunctionCenterType) {
|
||||
this.reset();
|
||||
this.funcitonMap = Object.keys(initConfig).reduce<Record<string, FunctionCenterFunction>>(
|
||||
(prev, next) => {
|
||||
prev[next] = initConfig[next].fn;
|
||||
return prev;
|
||||
},
|
||||
{}
|
||||
);
|
||||
this.configMap = Object.keys(initConfig).reduce<Record<string, FunctionConfigType>>(
|
||||
(prev, next) => {
|
||||
prev[next] = initConfig[next].config;
|
||||
return prev;
|
||||
},
|
||||
{}
|
||||
);
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.funcitonMap = {};
|
||||
this.configMap = {};
|
||||
}
|
||||
|
||||
getFunctionMap() {
|
||||
return this.funcitonMap;
|
||||
}
|
||||
|
||||
getConfigMap() {
|
||||
return this.configMap;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* 注册函数,同名覆盖,返回删除函数
|
||||
* @param {string} name
|
||||
* @param {FunctionCenterFunction} fn
|
||||
* @return {*}
|
||||
* @memberof FunctionCenter
|
||||
*/
|
||||
register(name: string, fn: FunctionCenterFunction, config: FunctionConfigType) {
|
||||
// 注册时,需要通知asyncmap已经拿到
|
||||
this.funcitonMap[name] = fn;
|
||||
if (this.asyncMap[name]) {
|
||||
this.asyncMap[name]();
|
||||
}
|
||||
this.configMap[name] = config;
|
||||
return () => {
|
||||
delete this.funcitonMap[name];
|
||||
delete this.configMap[name];
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* 获取函数,包含异步获取函数
|
||||
* @param {string} name
|
||||
* @return {*} {Promise<FunctionCenterFunction>}
|
||||
* @memberof FunctionCenter
|
||||
*/
|
||||
getFunction(name: string): Promise<FunctionCenterFunction> {
|
||||
//如果拿不到,可能是异步,进行监听回调
|
||||
const fn = this.funcitonMap[name];
|
||||
if (fn) {
|
||||
return Promise.resolve(fn);
|
||||
}
|
||||
return new Promise((resolve) => {
|
||||
console.warn(`waiting the function now ${name} `);
|
||||
this.asyncMap[name] = () => {
|
||||
delete this.asyncMap[name];
|
||||
resolve(this.getFunction(name));
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
124
packages/dooringx-lib/src/core/innerDrag/index.ts
Normal file
124
packages/dooringx-lib/src/core/innerDrag/index.ts
Normal file
@@ -0,0 +1,124 @@
|
||||
import { store } from '../../runtime/store';
|
||||
import { RefObject } from 'react';
|
||||
import { blockFocus, containerFocusRemove } from '../focusHandler';
|
||||
import { marklineConfig } from '../markline/marklineConfig';
|
||||
import { resizerMouseMove, resizerMouseUp } from '../resizeHandler';
|
||||
import { scaleState } from '../scale/state';
|
||||
import { selectRangeMouseMove, selectData, selectRangeMouseUp } from '../selectRange';
|
||||
import { IBlockType } from '../store/storetype';
|
||||
import { deepCopy, isMac } from '../utils';
|
||||
import { wrapperMoveMouseUp } from '../../components/wrapperMove/event';
|
||||
import { contextMenuState } from '../contextMenu';
|
||||
import { innerDragState } from './state';
|
||||
|
||||
export const innerDrag = function (item: IBlockType, ref: RefObject<HTMLDivElement>) {
|
||||
return {
|
||||
onMouseDown: (e: React.MouseEvent) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
if (!item.canDrag) {
|
||||
containerFocusRemove().onMouseDown(e);
|
||||
return;
|
||||
}
|
||||
blockFocus(e, item);
|
||||
if (item.id && innerDragState.lastClick && item.id !== innerDragState.lastClick.id) {
|
||||
contextMenuState.unmountContextMenu();
|
||||
}
|
||||
innerDragState.lastClick = item;
|
||||
|
||||
if (item.position === 'static') {
|
||||
return;
|
||||
}
|
||||
if (ref.current) {
|
||||
ref.current.style.cursor = 'move';
|
||||
ref.current.style.willChange = 'left,right,width,height';
|
||||
}
|
||||
innerDragState.startX = e.clientX;
|
||||
innerDragState.startY = e.clientY;
|
||||
innerDragState.item = item;
|
||||
innerDragState.isDrag = true;
|
||||
innerDragState.ref = ref;
|
||||
innerDragState.current = store.getIndex();
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const innerContainerDrag = function () {
|
||||
let lastblock: null | IBlockType;
|
||||
const onMouseMove = (e: React.MouseEvent) => {
|
||||
e.preventDefault();
|
||||
if (isMac() && contextMenuState.state) {
|
||||
//mac有bug
|
||||
return;
|
||||
}
|
||||
|
||||
const id = innerDragState.item?.id;
|
||||
if (id && innerDragState.isDrag) {
|
||||
const current = store.getData().block.find((v) => v.id === id);
|
||||
if (current?.position === 'static') {
|
||||
return;
|
||||
}
|
||||
let { clientX: moveX, clientY: moveY } = e;
|
||||
const { startX, startY } = innerDragState;
|
||||
const scale = scaleState.value;
|
||||
let durX = (moveX - startX) / scale;
|
||||
let durY = (moveY - startY) / scale;
|
||||
let newblock: IBlockType[];
|
||||
if (lastblock !== innerDragState.item) {
|
||||
const cloneblock: IBlockType[] = deepCopy(store.getData().block);
|
||||
lastblock = innerDragState.item;
|
||||
newblock = cloneblock.map((v) => {
|
||||
if (v.focus && v.position !== 'static') {
|
||||
v.left = v.left + durX;
|
||||
v.top = v.top + durY;
|
||||
}
|
||||
return v;
|
||||
});
|
||||
} else {
|
||||
newblock = store.getData().block.map((v) => {
|
||||
if (v.focus && v.position !== 'static') {
|
||||
v.left = v.left + durX;
|
||||
v.top = v.top + durY;
|
||||
}
|
||||
return v;
|
||||
});
|
||||
}
|
||||
innerDragState.startX = moveX;
|
||||
innerDragState.startY = moveY;
|
||||
store.setData({ ...store.getData(), block: newblock });
|
||||
}
|
||||
resizerMouseMove(e);
|
||||
if (selectData.selectDiv) {
|
||||
selectRangeMouseMove(e);
|
||||
}
|
||||
};
|
||||
return {
|
||||
onMouseMove,
|
||||
};
|
||||
};
|
||||
export const innerContainerDragUp = function () {
|
||||
const onMouseUp = (e: React.MouseEvent) => {
|
||||
e.preventDefault();
|
||||
wrapperMoveMouseUp();
|
||||
selectRangeMouseUp(e);
|
||||
if (innerDragState.ref && innerDragState.ref.current) {
|
||||
innerDragState.ref.current.style.cursor = 'default';
|
||||
innerDragState.ref.current.style.willChange = 'auto';
|
||||
}
|
||||
resizerMouseUp();
|
||||
if (innerDragState.current) {
|
||||
const endindex = store.getIndex();
|
||||
store.getStoreList().splice(innerDragState.current, endindex - innerDragState.current);
|
||||
store.setIndex(innerDragState.current);
|
||||
}
|
||||
innerDragState.ref = null;
|
||||
innerDragState.isDrag = false;
|
||||
innerDragState.item = null;
|
||||
innerDragState.current = 0;
|
||||
marklineConfig.marklineUnfocus = null;
|
||||
store.forceupdate();
|
||||
};
|
||||
return {
|
||||
onMouseUp,
|
||||
};
|
||||
};
|
||||
30
packages/dooringx-lib/src/core/innerDrag/state.ts
Normal file
30
packages/dooringx-lib/src/core/innerDrag/state.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-03-14 12:09:11
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-03-14 12:09:46
|
||||
* @FilePath: \dooring-v2\src\core\innerDrag\state.ts
|
||||
*/
|
||||
import { RefObject } from 'react';
|
||||
|
||||
import { IBlockType } from '../store/storetype';
|
||||
|
||||
export interface innerDragStateType {
|
||||
startX: number;
|
||||
startY: number;
|
||||
item: null | IBlockType;
|
||||
isDrag: boolean;
|
||||
ref: RefObject<HTMLDivElement> | null;
|
||||
current: number;
|
||||
lastClick: null | IBlockType;
|
||||
}
|
||||
|
||||
export const innerDragState: innerDragStateType = {
|
||||
startX: 0,
|
||||
startY: 0,
|
||||
item: null,
|
||||
isDrag: false,
|
||||
ref: null,
|
||||
current: 0,
|
||||
lastClick: null,
|
||||
};
|
||||
79
packages/dooringx-lib/src/core/markline/calcRender.ts
Normal file
79
packages/dooringx-lib/src/core/markline/calcRender.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-03-14 04:29:09
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-03-14 11:49:50
|
||||
* @FilePath: \dooring-v2\src\core\markline\calcRender.ts
|
||||
*/
|
||||
import { store } from '../../runtime/store';
|
||||
import { innerDragState } from '../innerDrag/state';
|
||||
import { scaleState } from '../scale/state';
|
||||
import { grideModeRender, gridModeDisplay } from './gridMode';
|
||||
import { switchMarklineDisplay } from './normalMode';
|
||||
import { resizeCurrentCalculate } from './resizeMarkline';
|
||||
import { marklineConfig } from './marklineConfig';
|
||||
export interface LinesTypes {
|
||||
x: number[];
|
||||
y: number[];
|
||||
}
|
||||
|
||||
export function marklineCalRender() {
|
||||
//focus可能好几个,做对比的是拖拽那个
|
||||
const lines: LinesTypes = { x: [], y: [] };
|
||||
if (innerDragState.item?.position === 'static' || innerDragState.item?.position === 'relative') {
|
||||
return lines;
|
||||
}
|
||||
|
||||
const item = innerDragState.item;
|
||||
const ref = innerDragState.ref;
|
||||
|
||||
if (item && ref && ref.current && innerDragState.isDrag) {
|
||||
const focus = store.getData().block.find((v) => v.id === item.id)!;
|
||||
if (!marklineConfig.marklineUnfocus) {
|
||||
marklineConfig.marklineUnfocus = store
|
||||
.getData()
|
||||
.block.filter(
|
||||
(v) => v.focus === false && v.position !== 'static' && v.position !== 'relative'
|
||||
);
|
||||
}
|
||||
const { width, height } = ref.current.getBoundingClientRect();
|
||||
|
||||
// left 和top 被深拷贝过,最新的值需要即时获取
|
||||
const left = focus?.left;
|
||||
const top = focus?.top;
|
||||
if (typeof left !== 'number' || typeof top !== 'number') {
|
||||
return lines; //莫名可能没有这2值
|
||||
}
|
||||
const scale = scaleState.value;
|
||||
const wwidth = width / scale;
|
||||
const wheight = height / scale;
|
||||
|
||||
marklineConfig.marklineUnfocus.forEach((v) => {
|
||||
let l = v?.left;
|
||||
let t = v?.top;
|
||||
if (typeof l !== 'number' || typeof t !== 'number') {
|
||||
console.warn(`${v} component miss top or left`);
|
||||
} else {
|
||||
// 如果不是由外层容器决定的则没有这2属性
|
||||
const w = v.width;
|
||||
const h = v.height;
|
||||
|
||||
// 只有满足要求的才进行push
|
||||
if (marklineConfig.mode === 'normal') {
|
||||
switchMarklineDisplay(l, t, w, h, left, top, wwidth, wheight, lines, focus);
|
||||
}
|
||||
}
|
||||
});
|
||||
if (marklineConfig.mode === 'grid' && marklineConfig.isAbsorb) {
|
||||
gridModeDisplay(left, top, focus);
|
||||
}
|
||||
}
|
||||
|
||||
if (marklineConfig.mode === 'grid') {
|
||||
grideModeRender(lines);
|
||||
}
|
||||
|
||||
resizeCurrentCalculate(lines);
|
||||
|
||||
return lines;
|
||||
}
|
||||
82
packages/dooringx-lib/src/core/markline/gridMode.ts
Normal file
82
packages/dooringx-lib/src/core/markline/gridMode.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-03-14 04:29:09
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-03-14 11:49:43
|
||||
* @FilePath: \dooring-v2\src\core\markline\gridMode.ts
|
||||
*/
|
||||
import { store } from '../../runtime/store';
|
||||
import { IBlockType } from '../store/storetype';
|
||||
import { deepCopy } from '../utils';
|
||||
import { LinesTypes } from './calcRender';
|
||||
import { marklineConfig } from './marklineConfig';
|
||||
export function gridModeDisplay(left: number, top: number, focus: IBlockType) {
|
||||
// 有吸附走吸附,只吸top和left,宽高不需要
|
||||
// 无吸附拖拽时显示所有网格。
|
||||
const container = store.getData().container;
|
||||
const containerWidth = container.width;
|
||||
const containerHeight = container.height;
|
||||
const indent = marklineConfig.gridIndent;
|
||||
const diff = marklineConfig.indent;
|
||||
// 网格按照宽高除以下,每间隔一定距离给个线
|
||||
// 横线
|
||||
for (let i = 0; i < containerHeight; i++) {
|
||||
const tmp = i * indent;
|
||||
if (Math.abs(top - tmp) < diff) {
|
||||
focus.top = tmp;
|
||||
break;
|
||||
} else if (tmp + diff > top) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// 竖线
|
||||
for (let i = 0; i < containerWidth; i++) {
|
||||
const tmp = i * indent;
|
||||
if (Math.abs(left - tmp) < diff) {
|
||||
focus.left = tmp;
|
||||
break;
|
||||
} else if (tmp + diff > left) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface lastGridStatusProps {
|
||||
lastWidth: number;
|
||||
lastHeight: number;
|
||||
lastIndent: number;
|
||||
lastLine: LinesTypes;
|
||||
}
|
||||
|
||||
export const lastGridStatus: lastGridStatusProps = {
|
||||
lastWidth: 0,
|
||||
lastHeight: 0,
|
||||
lastIndent: 0,
|
||||
lastLine: { x: [], y: [] },
|
||||
};
|
||||
|
||||
export function grideModeRender(lines: LinesTypes) {
|
||||
const container = store.getData().container;
|
||||
const containerWidth = container.width;
|
||||
const containerHeight = container.height;
|
||||
const indent = marklineConfig.gridIndent;
|
||||
if (
|
||||
lastGridStatus.lastWidth === containerWidth &&
|
||||
lastGridStatus.lastHeight === containerHeight &&
|
||||
lastGridStatus.lastIndent === indent
|
||||
) {
|
||||
lines.x = lastGridStatus.lastLine.x;
|
||||
lines.y = lastGridStatus.lastLine.y;
|
||||
} else {
|
||||
for (let i = 0; i < containerWidth; i++) {
|
||||
lines.x.push(i * indent);
|
||||
}
|
||||
for (let i = 0; i < containerHeight; i++) {
|
||||
lines.y.push(i * indent);
|
||||
}
|
||||
lastGridStatus.lastLine = deepCopy(lines);
|
||||
lastGridStatus.lastWidth = containerWidth;
|
||||
lastGridStatus.lastHeight = containerHeight;
|
||||
lastGridStatus.lastIndent = indent;
|
||||
}
|
||||
}
|
||||
80
packages/dooringx-lib/src/core/markline/index.tsx
Normal file
80
packages/dooringx-lib/src/core/markline/index.tsx
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-03-14 04:29:09
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-03-14 11:45:22
|
||||
* @FilePath: \dooring-v2\src\core\markline\index.tsx
|
||||
*/
|
||||
import React from 'react';
|
||||
import { useMemo } from 'react';
|
||||
import { IBlockType } from '../store/storetype';
|
||||
import { marklineCalRender } from './calcRender';
|
||||
|
||||
// 主要逻辑需要注入组件内拖拽
|
||||
|
||||
export interface MarklineConfigType {
|
||||
indent: number;
|
||||
isAbsorb: boolean;
|
||||
mode: 'normal' | 'grid';
|
||||
gridIndent: number;
|
||||
resizeIndent: number;
|
||||
marklineUnfocus: null | IBlockType[];
|
||||
}
|
||||
|
||||
// 间隔距离执行吸附
|
||||
export const marklineConfig: MarklineConfigType = {
|
||||
indent: 2,
|
||||
isAbsorb: true,
|
||||
mode: 'normal',
|
||||
gridIndent: 50,
|
||||
resizeIndent: 0,
|
||||
marklineUnfocus: null,
|
||||
};
|
||||
|
||||
export function MarklineX(props: any) {
|
||||
return (
|
||||
<div
|
||||
className="yh-markline"
|
||||
style={{
|
||||
borderTop: '1px dashed black',
|
||||
position: 'absolute',
|
||||
width: '100%',
|
||||
top: props.top,
|
||||
display: props.display,
|
||||
zIndex: 9999,
|
||||
}}
|
||||
></div>
|
||||
);
|
||||
}
|
||||
export function MarklineY(props: any) {
|
||||
return (
|
||||
<div
|
||||
className="yh-markline"
|
||||
style={{
|
||||
borderLeft: '1px dashed black',
|
||||
position: 'absolute',
|
||||
height: '100%',
|
||||
left: props.left,
|
||||
display: props.display,
|
||||
zIndex: 9999,
|
||||
}}
|
||||
></div>
|
||||
);
|
||||
}
|
||||
|
||||
export function NormalMarkLineRender() {
|
||||
const lines = marklineCalRender();
|
||||
const render = useMemo(() => {
|
||||
return (
|
||||
<>
|
||||
{lines.x.map((v, i) => {
|
||||
return <MarklineX key={i} top={v}></MarklineX>;
|
||||
})}
|
||||
{lines.y.map((v, i) => {
|
||||
return <MarklineY key={i} left={v}></MarklineY>;
|
||||
})}
|
||||
</>
|
||||
);
|
||||
}, [lines]);
|
||||
return <>{render}</>;
|
||||
}
|
||||
27
packages/dooringx-lib/src/core/markline/marklineConfig.ts
Normal file
27
packages/dooringx-lib/src/core/markline/marklineConfig.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-03-14 11:49:13
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-03-14 11:49:31
|
||||
* @FilePath: \dooring-v2\src\core\markline\marklineConfig.ts
|
||||
*/
|
||||
import { IBlockType } from '../store/storetype';
|
||||
|
||||
export interface MarklineConfigType {
|
||||
indent: number;
|
||||
isAbsorb: boolean;
|
||||
mode: 'normal' | 'grid';
|
||||
gridIndent: number;
|
||||
resizeIndent: number;
|
||||
marklineUnfocus: null | IBlockType[];
|
||||
}
|
||||
|
||||
// 间隔距离执行吸附
|
||||
export const marklineConfig: MarklineConfigType = {
|
||||
indent: 2,
|
||||
isAbsorb: true,
|
||||
mode: 'normal',
|
||||
gridIndent: 50,
|
||||
resizeIndent: 0,
|
||||
marklineUnfocus: null,
|
||||
};
|
||||
279
packages/dooringx-lib/src/core/markline/normalMode.ts
Normal file
279
packages/dooringx-lib/src/core/markline/normalMode.ts
Normal file
@@ -0,0 +1,279 @@
|
||||
import { IBlockType } from '../store/storetype';
|
||||
import { LinesTypes } from './calcRender';
|
||||
import { marklineConfig } from './marklineConfig';
|
||||
export function switchMarklineDisplay(
|
||||
l: number,
|
||||
t: number,
|
||||
w: number | string | undefined,
|
||||
h: number | string | undefined,
|
||||
left: number,
|
||||
top: number,
|
||||
width: number,
|
||||
height: number,
|
||||
lines: LinesTypes,
|
||||
focus: IBlockType
|
||||
) {
|
||||
// 做吸附只能选择一个接近的吸,所以只匹配一个即可。
|
||||
// 头对头
|
||||
if (marklineConfig.isAbsorb) {
|
||||
if (Math.abs(top - t) < marklineConfig.indent) {
|
||||
lines.x.push(t);
|
||||
focus.top = t;
|
||||
}
|
||||
// 中对头
|
||||
else if (Math.abs(top + height / 2 - t) < marklineConfig.indent) {
|
||||
lines.x.push(t);
|
||||
focus.top = t - height / 2;
|
||||
}
|
||||
// 尾对头
|
||||
else if (Math.abs(top + height - t) < marklineConfig.indent) {
|
||||
lines.x.push(t);
|
||||
focus.top = t - height;
|
||||
} else if (h && typeof h === 'number') {
|
||||
// 头对中
|
||||
if (Math.abs(t + h / 2 - top) < marklineConfig.indent) {
|
||||
lines.x.push(t + h / 2);
|
||||
focus.top = t + h / 2;
|
||||
}
|
||||
// 中对中
|
||||
else if (Math.abs(t + h / 2 - top - height / 2) < marklineConfig.indent) {
|
||||
lines.x.push(t + h / 2);
|
||||
focus.top = t + h / 2 - height / 2;
|
||||
}
|
||||
// 尾对中
|
||||
else if (Math.abs(t + h / 2 - top - height) < marklineConfig.indent) {
|
||||
lines.x.push(t + h / 2);
|
||||
focus.top = t + h / 2 - height;
|
||||
}
|
||||
// 头对尾
|
||||
else if (Math.abs((t + h - top) / 2) < marklineConfig.indent) {
|
||||
lines.x.push(t + h);
|
||||
focus.top = t + h;
|
||||
}
|
||||
// 中对尾
|
||||
else if (Math.abs(t + h - top - height / 2) < marklineConfig.indent) {
|
||||
lines.x.push(t + h);
|
||||
focus.top = t + h - height / 2;
|
||||
}
|
||||
// 尾对尾
|
||||
else if (Math.abs((t + h - top - height) / 2) < marklineConfig.indent) {
|
||||
lines.x.push(t + h);
|
||||
focus.top = t + h - height;
|
||||
}
|
||||
}
|
||||
// x轴方向
|
||||
// 头对头
|
||||
if (Math.abs(left - l) < marklineConfig.indent) {
|
||||
lines.y.push(l);
|
||||
focus.left = l;
|
||||
}
|
||||
// 中对头
|
||||
else if (Math.abs(left + width / 2 - l) < marklineConfig.indent) {
|
||||
lines.y.push(l);
|
||||
focus.left = l - width / 2;
|
||||
}
|
||||
// 尾对头
|
||||
else if (Math.abs(left + width - l) < marklineConfig.indent) {
|
||||
lines.y.push(l);
|
||||
focus.left = l - width;
|
||||
} else if (w && typeof w === 'number') {
|
||||
// 头对中
|
||||
if (Math.abs(l + w / 2 - left) < marklineConfig.indent) {
|
||||
lines.y.push(l + w / 2);
|
||||
focus.left = l + w / 2;
|
||||
}
|
||||
// 中对中
|
||||
else if (Math.abs(l + w / 2 - left - width / 2) < marklineConfig.indent) {
|
||||
lines.y.push(l + w / 2);
|
||||
focus.left = l + w / 2 - width / 2;
|
||||
}
|
||||
// 尾对中
|
||||
else if (Math.abs(l + w / 2 - left - width) < marklineConfig.indent) {
|
||||
lines.y.push(l + w / 2);
|
||||
focus.left = l + w / 2 - width;
|
||||
}
|
||||
// 头对尾
|
||||
else if (Math.abs((l + w - left) / 2) < marklineConfig.indent) {
|
||||
lines.y.push(l + w);
|
||||
focus.left = l + w;
|
||||
}
|
||||
// 中对尾
|
||||
else if (Math.abs(l + w - left - width / 2) < marklineConfig.indent) {
|
||||
lines.y.push(l + w);
|
||||
focus.left = l + w - width / 2;
|
||||
}
|
||||
// 尾对尾
|
||||
else if (Math.abs((l + w - left - width) / 2) < marklineConfig.indent) {
|
||||
lines.y.push(l + w);
|
||||
focus.left = l + w - width;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (Math.abs(top - t) < marklineConfig.indent) {
|
||||
lines.x.push(t);
|
||||
}
|
||||
// 中对头
|
||||
if (Math.abs(top + height / 2 - t) < marklineConfig.indent) {
|
||||
lines.x.push(t);
|
||||
}
|
||||
// 尾对头
|
||||
if (Math.abs(top + height - t) < marklineConfig.indent) {
|
||||
lines.x.push(t);
|
||||
}
|
||||
if (h && typeof h === 'number') {
|
||||
// 头对中
|
||||
if (Math.abs(t + h / 2 - top) < marklineConfig.indent) {
|
||||
lines.x.push(t + h / 2);
|
||||
}
|
||||
// 中对中
|
||||
if (Math.abs(t + h / 2 - top - height / 2) < marklineConfig.indent) {
|
||||
lines.x.push(t + h / 2);
|
||||
}
|
||||
// 尾对中
|
||||
if (Math.abs(t + h / 2 - top - height) < marklineConfig.indent) {
|
||||
lines.x.push(t + h / 2);
|
||||
}
|
||||
// 头对尾
|
||||
if (Math.abs((t + h - top) / 2) < marklineConfig.indent) {
|
||||
lines.x.push(t + h);
|
||||
}
|
||||
// 中对尾
|
||||
if (Math.abs(t + h - top - height / 2) < marklineConfig.indent) {
|
||||
lines.x.push(t + h);
|
||||
}
|
||||
// 尾对尾
|
||||
if (Math.abs((t + h - top - height) / 2) < marklineConfig.indent) {
|
||||
lines.x.push(t + h);
|
||||
}
|
||||
}
|
||||
// x轴方向
|
||||
// 头对头
|
||||
if (Math.abs(left - l) < marklineConfig.indent) {
|
||||
lines.y.push(l);
|
||||
}
|
||||
// 中对头
|
||||
if (Math.abs(left + width / 2 - l) < marklineConfig.indent) {
|
||||
lines.y.push(l);
|
||||
}
|
||||
// 尾对头
|
||||
if (Math.abs(left + width - l) < marklineConfig.indent) {
|
||||
lines.y.push(l);
|
||||
}
|
||||
if (w && typeof w === 'number') {
|
||||
// 头对中
|
||||
if (Math.abs(l + w / 2 - left) < marklineConfig.indent) {
|
||||
lines.y.push(l + w / 2);
|
||||
}
|
||||
// 中对中
|
||||
if (Math.abs(l + w / 2 - left - width / 2) < marklineConfig.indent) {
|
||||
lines.y.push(l + w / 2);
|
||||
}
|
||||
// 尾对中
|
||||
if (Math.abs(l + w / 2 - left - width) < marklineConfig.indent) {
|
||||
lines.y.push(l + w / 2);
|
||||
}
|
||||
// 头对尾
|
||||
if (Math.abs((l + w - left) / 2) < marklineConfig.indent) {
|
||||
lines.y.push(l + w);
|
||||
}
|
||||
// 中对尾
|
||||
if (Math.abs(l + w - left - width / 2) < marklineConfig.indent) {
|
||||
lines.y.push(l + w);
|
||||
}
|
||||
// 尾对尾
|
||||
if (Math.abs((l + w - left - width) / 2) < marklineConfig.indent) {
|
||||
lines.y.push(l + w);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function switchMarklineResizeDisplay(
|
||||
l: number,
|
||||
t: number,
|
||||
w: number | string | undefined,
|
||||
h: number | string | undefined,
|
||||
left: number,
|
||||
top: number,
|
||||
width: number,
|
||||
height: number,
|
||||
lines: LinesTypes
|
||||
) {
|
||||
// 头对头
|
||||
if (Math.abs(top - t) <= marklineConfig.resizeIndent) {
|
||||
lines.x.push(t);
|
||||
}
|
||||
// 中对头
|
||||
if (Math.abs(top + height / 2 - t) <= marklineConfig.resizeIndent) {
|
||||
lines.x.push(t);
|
||||
}
|
||||
// 尾对头
|
||||
if (Math.abs(top + height - t) <= marklineConfig.resizeIndent) {
|
||||
lines.x.push(t);
|
||||
}
|
||||
if (h && typeof h === 'number') {
|
||||
// 头对中
|
||||
if (Math.abs(t + h / 2 - top) <= marklineConfig.resizeIndent) {
|
||||
lines.x.push(t + h / 2);
|
||||
}
|
||||
// 中对中
|
||||
if (Math.abs(t + h / 2 - top - height / 2) <= marklineConfig.resizeIndent) {
|
||||
lines.x.push(t + h / 2);
|
||||
}
|
||||
// 尾对中
|
||||
if (Math.abs(t + h / 2 - top - height) <= marklineConfig.resizeIndent) {
|
||||
lines.x.push(t + h / 2);
|
||||
}
|
||||
// 头对尾
|
||||
if (Math.abs((t + h - top) / 2) <= marklineConfig.resizeIndent) {
|
||||
lines.x.push(t + h);
|
||||
}
|
||||
// 中对尾
|
||||
if (Math.abs(t + h - top - height / 2) <= marklineConfig.resizeIndent) {
|
||||
lines.x.push(t + h);
|
||||
}
|
||||
// 尾对尾
|
||||
if (Math.abs((t + h - top - height) / 2) <= marklineConfig.resizeIndent) {
|
||||
lines.x.push(t + h);
|
||||
}
|
||||
}
|
||||
// x轴方向
|
||||
// 头对头
|
||||
if (Math.abs(left - l) <= marklineConfig.resizeIndent) {
|
||||
lines.y.push(l);
|
||||
}
|
||||
// 中对头
|
||||
if (Math.abs(left + width / 2 - l) <= marklineConfig.resizeIndent) {
|
||||
lines.y.push(l);
|
||||
}
|
||||
// 尾对头
|
||||
if (Math.abs(left + width - l) <= marklineConfig.resizeIndent) {
|
||||
lines.y.push(l);
|
||||
}
|
||||
if (w && typeof w === 'number') {
|
||||
// 头对中
|
||||
if (Math.abs(l + w / 2 - left) <= marklineConfig.resizeIndent) {
|
||||
lines.y.push(l + w / 2);
|
||||
}
|
||||
// 中对中
|
||||
if (Math.abs(l + w / 2 - left - width / 2) <= marklineConfig.resizeIndent) {
|
||||
lines.y.push(l + w / 2);
|
||||
}
|
||||
// 尾对中
|
||||
if (Math.abs(l + w / 2 - left - width) <= marklineConfig.resizeIndent) {
|
||||
lines.y.push(l + w / 2);
|
||||
}
|
||||
// 头对尾
|
||||
if (Math.abs((l + w - left) / 2) <= marklineConfig.resizeIndent) {
|
||||
lines.y.push(l + w);
|
||||
}
|
||||
// 中对尾
|
||||
if (Math.abs(l + w - left - width / 2) <= marklineConfig.resizeIndent) {
|
||||
lines.y.push(l + w);
|
||||
}
|
||||
// 尾对尾
|
||||
if (Math.abs((l + w - left - width) / 2) <= marklineConfig.resizeIndent) {
|
||||
lines.y.push(l + w);
|
||||
}
|
||||
}
|
||||
}
|
||||
41
packages/dooringx-lib/src/core/markline/resizeMarkline.ts
Normal file
41
packages/dooringx-lib/src/core/markline/resizeMarkline.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-02-18 11:52:38
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-03-14 11:49:58
|
||||
* @FilePath: \dooring-v2\src\core\markline\resizeMarkline.ts
|
||||
*/
|
||||
|
||||
import { store } from '../../runtime/store';
|
||||
import { resizeState } from '../resizeHandler';
|
||||
import { scaleState } from '../scale/state';
|
||||
import { LinesTypes } from './calcRender';
|
||||
import { switchMarklineResizeDisplay } from './normalMode';
|
||||
import { marklineConfig } from './marklineConfig';
|
||||
|
||||
export function resizeCurrentCalculate(lines: LinesTypes) {
|
||||
const id = resizeState.item?.id;
|
||||
|
||||
if (resizeState.ref?.current && id) {
|
||||
const newblock = store.getData().block;
|
||||
const unfocus = newblock.filter((v) => v.id !== id);
|
||||
const { width, height } = resizeState.ref.current.getBoundingClientRect();
|
||||
const focus = store.getData().block.find((v) => v.id === id)!;
|
||||
const { left, top } = focus;
|
||||
const scale = scaleState.value;
|
||||
const wwidth = width / scale;
|
||||
const wheight = height / scale;
|
||||
unfocus.forEach((v) => {
|
||||
const { left: l, top: t } = v;
|
||||
// 如果不是由外层容器决定的则没有这2属性
|
||||
const w = v.width;
|
||||
const h = v.height;
|
||||
const ww = w && typeof w === 'number' ? w / scale : w;
|
||||
const wh = h && typeof h === 'number' ? h / scale : h;
|
||||
// 只有满足要求的才进行push
|
||||
if (marklineConfig.mode === 'normal') {
|
||||
switchMarklineResizeDisplay(l, t, ww, wh, left, top, wwidth, wheight, lines);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-03-09 15:19:36
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-03-14 04:52:57
|
||||
* @FilePath: \dooring-v2\src\core\resizeHandler\containerResizer.ts
|
||||
*/
|
||||
|
||||
import { store } from '../../runtime/store';
|
||||
import { scaleState } from '../scale/state';
|
||||
import { IStoreData } from '../store/storetype';
|
||||
import { deepCopy } from '../utils';
|
||||
|
||||
export const containerState = {
|
||||
isDrag: false,
|
||||
startY: 0,
|
||||
startIndex: 0,
|
||||
};
|
||||
|
||||
export const containerResizer = {
|
||||
onMousedown: (e: React.MouseEvent) => {
|
||||
containerState.isDrag = true;
|
||||
containerState.startY = e.clientY;
|
||||
containerState.startIndex = store.getIndex();
|
||||
},
|
||||
onMouseMove: (e: React.MouseEvent) => {
|
||||
if (containerState.isDrag) {
|
||||
const scale = scaleState.value;
|
||||
const diff = ((e.clientY - containerState.startY) / scale) * 2;
|
||||
const clonedata: IStoreData = deepCopy(store.getData());
|
||||
const height = clonedata.container.height;
|
||||
let tmpHeight = height + diff < 600 ? 600 : height + diff;
|
||||
clonedata.container.height = tmpHeight;
|
||||
store.setData(clonedata);
|
||||
containerState.startY = e.clientY;
|
||||
}
|
||||
},
|
||||
onMouseUp: () => {
|
||||
if (containerState.isDrag) {
|
||||
containerState.isDrag = false;
|
||||
const endIndex = store.getIndex();
|
||||
store.getStoreList().splice(containerState.startIndex, endIndex - containerState.startIndex);
|
||||
store.setIndex(containerState.startIndex);
|
||||
}
|
||||
},
|
||||
};
|
||||
204
packages/dooringx-lib/src/core/resizeHandler/index.tsx
Normal file
204
packages/dooringx-lib/src/core/resizeHandler/index.tsx
Normal file
@@ -0,0 +1,204 @@
|
||||
import { store } from '../../runtime/store';
|
||||
import { RefObject, useMemo } from 'react';
|
||||
import { scaleState } from '../scale/state';
|
||||
import { IBlockType } from '../store/storetype';
|
||||
import { deepCopy } from '../utils';
|
||||
import React from 'react';
|
||||
import classnames from 'classnames';
|
||||
import styles from '../../index.less';
|
||||
interface BlockResizerProps {
|
||||
data: IBlockType;
|
||||
rect: RefObject<HTMLDivElement>;
|
||||
}
|
||||
interface resizeStateType {
|
||||
startX: number;
|
||||
startY: number;
|
||||
item: null | IBlockType;
|
||||
isResize: boolean;
|
||||
direction: directionType;
|
||||
ref: RefObject<HTMLDivElement> | null;
|
||||
current: number;
|
||||
}
|
||||
type directionType =
|
||||
| 'top'
|
||||
| 'topleft'
|
||||
| 'topright'
|
||||
| 'left'
|
||||
| 'bottomleft'
|
||||
| 'bottom'
|
||||
| 'bottomright'
|
||||
| 'right';
|
||||
export const resizeState: resizeStateType = {
|
||||
startX: 0,
|
||||
startY: 0,
|
||||
item: null,
|
||||
isResize: false,
|
||||
direction: 'bottom',
|
||||
ref: null,
|
||||
current: 0,
|
||||
};
|
||||
|
||||
const onMouseDown = (
|
||||
e: React.MouseEvent,
|
||||
direction: directionType,
|
||||
item: IBlockType,
|
||||
ref: RefObject<HTMLDivElement>
|
||||
) => {
|
||||
e.stopPropagation();
|
||||
resizeState.isResize = true;
|
||||
resizeState.item = item;
|
||||
resizeState.startX = e.clientX;
|
||||
resizeState.startY = e.clientY;
|
||||
resizeState.direction = direction;
|
||||
resizeState.ref = ref;
|
||||
resizeState.current = store.getIndex();
|
||||
};
|
||||
|
||||
export const resizerMouseUp = () => {
|
||||
resizeState.isResize = false;
|
||||
resizeState.item = null;
|
||||
if (resizeState.current) {
|
||||
const endindex = store.getIndex();
|
||||
store.getStoreList().splice(resizeState.current, endindex - resizeState.current);
|
||||
store.setIndex(resizeState.current);
|
||||
}
|
||||
resizeState.current = 0;
|
||||
};
|
||||
const changePosition = (v: IBlockType, durX: number, durY: number) => {
|
||||
const direction = resizeState.direction;
|
||||
const { width, height } = resizeState.ref!.current!.getBoundingClientRect();
|
||||
const scale = scaleState.value;
|
||||
let tmpy = height / scale - durY;
|
||||
let tmpx = width / scale - durX;
|
||||
switch (direction) {
|
||||
case 'right':
|
||||
v.width = width / scale + durX;
|
||||
break;
|
||||
case 'bottom':
|
||||
v.height = height / scale + durY;
|
||||
break;
|
||||
case 'left':
|
||||
v.left = width / scale > 0 ? v.left + durX : v.left;
|
||||
v.width = tmpx > 0 ? tmpx : 0;
|
||||
break;
|
||||
case 'top':
|
||||
v.top = height / scale > 0 ? v.top + durY : v.top;
|
||||
v.height = tmpy > 0 ? tmpy : 0;
|
||||
break;
|
||||
case 'bottomright':
|
||||
v.width = width / scale + durX;
|
||||
v.height = height / scale + durY;
|
||||
break;
|
||||
case 'topright':
|
||||
v.width = width / scale + durX;
|
||||
v.top = height / scale > 0 ? v.top + durY : v.top;
|
||||
v.height = tmpy > 0 ? tmpy : 0;
|
||||
break;
|
||||
case 'topleft':
|
||||
v.top = height / scale > 0 ? v.top + durY : v.top;
|
||||
v.height = tmpy > 0 ? tmpy : 0;
|
||||
v.left = width / scale > 0 ? v.left + durX : v.left;
|
||||
v.width = tmpx > 0 ? tmpx : 0;
|
||||
break;
|
||||
case 'bottomleft':
|
||||
v.left = width / scale > 0 ? v.left + durX : v.left;
|
||||
v.width = tmpx > 0 ? tmpx : 0;
|
||||
v.height = height / scale + durY;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
export const resizerMouseMove = (e: React.MouseEvent) => {
|
||||
//根据direction修改位置
|
||||
if (resizeState.isResize && resizeState.item) {
|
||||
let { clientX: moveX, clientY: moveY } = e;
|
||||
const { startX, startY } = resizeState;
|
||||
const scale = scaleState.value;
|
||||
let durX = (moveX - startX) / scale;
|
||||
let durY = (moveY - startY) / scale;
|
||||
const clonedata = deepCopy(store.getData());
|
||||
const newblock: IBlockType[] = clonedata.block.map((v: IBlockType) => {
|
||||
if (v.id === resizeState.item!.id) {
|
||||
changePosition(v, durX, durY);
|
||||
}
|
||||
return v;
|
||||
});
|
||||
resizeState.startX = moveX;
|
||||
resizeState.startY = moveY;
|
||||
store.setData({ ...clonedata, block: newblock });
|
||||
}
|
||||
};
|
||||
|
||||
export function BlockResizer(props: BlockResizerProps) {
|
||||
const render = useMemo(() => {
|
||||
if (props.data.focus && props.data.resize) {
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={classnames(styles.resizepoint, styles.top)}
|
||||
onMouseDown={(e) => {
|
||||
onMouseDown(e, 'top', props.data, props.rect);
|
||||
}}
|
||||
onMouseUp={resizerMouseUp}
|
||||
></div>
|
||||
<div
|
||||
className={classnames(styles.resizepoint, styles.topleft)}
|
||||
onMouseDown={(e) => {
|
||||
onMouseDown(e, 'topleft', props.data, props.rect);
|
||||
}}
|
||||
onMouseUp={resizerMouseUp}
|
||||
></div>
|
||||
<div
|
||||
className={classnames(styles.resizepoint, styles.left)}
|
||||
onMouseDown={(e) => {
|
||||
onMouseDown(e, 'left', props.data, props.rect);
|
||||
}}
|
||||
onMouseUp={resizerMouseUp}
|
||||
></div>
|
||||
<div
|
||||
className={classnames(styles.resizepoint, styles.topright)}
|
||||
onMouseDown={(e) => {
|
||||
onMouseDown(e, 'topright', props.data, props.rect);
|
||||
}}
|
||||
onMouseUp={resizerMouseUp}
|
||||
></div>
|
||||
<div
|
||||
className={classnames(styles.resizepoint, styles.bottomleft)}
|
||||
onMouseDown={(e) => {
|
||||
onMouseDown(e, 'bottomleft', props.data, props.rect);
|
||||
}}
|
||||
onMouseUp={resizerMouseUp}
|
||||
></div>
|
||||
<div
|
||||
className={classnames(styles.resizepoint, styles.bottom)}
|
||||
onMouseDown={(e) => {
|
||||
onMouseDown(e, 'bottom', props.data, props.rect);
|
||||
}}
|
||||
onMouseUp={resizerMouseUp}
|
||||
></div>
|
||||
<div
|
||||
className={classnames(styles.resizepoint, styles.right)}
|
||||
onMouseDown={(e) => {
|
||||
onMouseDown(e, 'right', props.data, props.rect);
|
||||
}}
|
||||
onMouseUp={resizerMouseUp}
|
||||
></div>
|
||||
<div
|
||||
className={classnames(styles.resizepoint, styles.bottomright)}
|
||||
onMouseDown={(e) => {
|
||||
onMouseDown(e, 'bottomright', props.data, props.rect);
|
||||
}}
|
||||
onMouseUp={resizerMouseUp}
|
||||
></div>
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [props.data.focus, props.data.resize]);
|
||||
|
||||
return <>{render}</>;
|
||||
}
|
||||
5
packages/dooringx-lib/src/core/scale/cancel.ts
Normal file
5
packages/dooringx-lib/src/core/scale/cancel.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { unmountContextMenu } from '../contextMenu';
|
||||
|
||||
export const scaleCancelFn = () => {
|
||||
unmountContextMenu();
|
||||
};
|
||||
54
packages/dooringx-lib/src/core/scale/index.ts
Normal file
54
packages/dooringx-lib/src/core/scale/index.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-03-14 04:29:09
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-04-05 22:18:43
|
||||
* @FilePath: \dooringv2\src\core\scale\index.ts
|
||||
*/
|
||||
import { store } from '../../runtime/store';
|
||||
import Store from '../store';
|
||||
import { scaleCancelFn } from './cancel';
|
||||
import { scaleState } from './state';
|
||||
|
||||
export const onWheelEvent = {
|
||||
onWheel: (e: React.WheelEvent<HTMLDivElement>) => {
|
||||
const dom = document.querySelector('.ant-modal-mask');
|
||||
if (dom) {
|
||||
//出现弹窗禁止滚动
|
||||
return;
|
||||
}
|
||||
if (e.deltaY > 0) {
|
||||
scaleCancelFn();
|
||||
if (scaleState.value < scaleState.maxValue) {
|
||||
scaleState.value = scaleState.value + 0.1;
|
||||
store.forceUpdate();
|
||||
}
|
||||
} else {
|
||||
scaleCancelFn();
|
||||
//往上滚缩小
|
||||
if (scaleState.value > scaleState.minValue) {
|
||||
scaleState.value = scaleState.value - 0.1;
|
||||
store.forceUpdate();
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export const scaleFn = {
|
||||
increase(number: number = 0.1, store: Store) {
|
||||
if (scaleState.value < scaleState.maxValue) {
|
||||
scaleCancelFn();
|
||||
scaleState.value = scaleState.value + number;
|
||||
store.forceUpdate();
|
||||
}
|
||||
return scaleState.value;
|
||||
},
|
||||
decrease(number: number = 0.1, store: Store) {
|
||||
scaleCancelFn();
|
||||
if (scaleState.value > scaleState.minValue) {
|
||||
scaleState.value = scaleState.value - number;
|
||||
store.forceUpdate();
|
||||
}
|
||||
return scaleState.value;
|
||||
},
|
||||
};
|
||||
12
packages/dooringx-lib/src/core/scale/state.ts
Normal file
12
packages/dooringx-lib/src/core/scale/state.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-07-07 10:28:09
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-07-07 23:16:31
|
||||
* @FilePath: \DooringV2\packages\dooringx-lib\src\core\scale\state.ts
|
||||
*/
|
||||
export const scaleState = {
|
||||
value: 0.8,
|
||||
maxValue: 1.3,
|
||||
minValue: 0.4,
|
||||
};
|
||||
100
packages/dooringx-lib/src/core/selectRange/index.ts
Normal file
100
packages/dooringx-lib/src/core/selectRange/index.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
import { store } from '../../runtime/store';
|
||||
import { IStoreData } from '../store/storetype';
|
||||
import { deepCopy } from '../utils';
|
||||
import { focusState } from '../focusHandler/state';
|
||||
import { scaleState } from '../scale/state';
|
||||
import style from '../../index.less';
|
||||
export interface SelectDataProps {
|
||||
selectDiv: HTMLDivElement | null;
|
||||
posx: number;
|
||||
posy: number;
|
||||
startX: number;
|
||||
startY: number;
|
||||
}
|
||||
|
||||
export const selectData: SelectDataProps = {
|
||||
selectDiv: null,
|
||||
posx: 0,
|
||||
posy: 0,
|
||||
startX: 0,
|
||||
startY: 0,
|
||||
};
|
||||
|
||||
export function selectRangeMouseDown(e: React.MouseEvent) {
|
||||
if (!selectData.selectDiv) {
|
||||
selectData.selectDiv = document.createElement('div');
|
||||
}
|
||||
if (selectData.selectDiv) {
|
||||
selectData.startX = e.nativeEvent.offsetX;
|
||||
selectData.startY = e.nativeEvent.offsetY;
|
||||
selectData.posx = e.clientX;
|
||||
selectData.posy = e.clientY;
|
||||
selectData.selectDiv.className = style.yhTempDiv;
|
||||
selectData.selectDiv.style.left = e.clientX + 'px';
|
||||
selectData.selectDiv.style.top = e.clientY + 'px';
|
||||
selectData.selectDiv.style.position = 'fixed';
|
||||
document.body.appendChild(selectData.selectDiv);
|
||||
selectData.selectDiv.onmouseup = (e) => selectRangeMouseUp(e);
|
||||
selectData.selectDiv.onmousemove = (e) => selectRangeMouseMove(e);
|
||||
}
|
||||
}
|
||||
|
||||
export function selectRangeMouseMove(ev: React.MouseEvent | MouseEvent) {
|
||||
if (selectData.selectDiv) {
|
||||
selectData.selectDiv.style.left = Math.min(ev.clientX, selectData.posx) + 'px';
|
||||
selectData.selectDiv.style.top = Math.min(ev.clientY, selectData.posy) + 'px';
|
||||
selectData.selectDiv.style.width = Math.abs(selectData.posx - ev.clientX) + 'px';
|
||||
selectData.selectDiv.style.height = Math.abs(selectData.posy - ev.clientY) + 'px';
|
||||
}
|
||||
}
|
||||
|
||||
function typeGuard(e: React.MouseEvent | MouseEvent): e is React.MouseEvent {
|
||||
return !(e instanceof Event);
|
||||
}
|
||||
|
||||
function selectFocus(left: number, top: number, width: number, height: number) {
|
||||
if (width === 0 || height === 0) {
|
||||
return;
|
||||
}
|
||||
const clonedata: IStoreData = deepCopy(store.getData());
|
||||
const blocks = clonedata.block;
|
||||
let change = false;
|
||||
const maxleft = left + width;
|
||||
const maxtop = top + height;
|
||||
blocks.forEach((v) => {
|
||||
const l = v.left;
|
||||
const t = v.top;
|
||||
if (l >= left && l <= maxleft && t >= top && t <= maxtop) {
|
||||
change = true;
|
||||
v.focus = true;
|
||||
focusState.blocks.push(v);
|
||||
}
|
||||
});
|
||||
if (change) {
|
||||
store.setData(clonedata);
|
||||
}
|
||||
}
|
||||
|
||||
export function selectRangeMouseUp(e: React.MouseEvent | MouseEvent) {
|
||||
if (selectData.selectDiv) {
|
||||
// 这里需要判定区域
|
||||
// 如果是react触发 ,left和top就是起始值和终止值的最小值
|
||||
// 如果是原生触发,left和top是起始点减去其宽高
|
||||
let left = 0;
|
||||
let top = 0;
|
||||
const { width, height } = selectData.selectDiv.getBoundingClientRect();
|
||||
const scale = scaleState.value;
|
||||
const wwidth = width / scale;
|
||||
const wheight = height / scale;
|
||||
if (typeGuard(e)) {
|
||||
left = Math.min(e.nativeEvent.offsetX, selectData.startX);
|
||||
top = Math.min(e.nativeEvent.offsetY, selectData.startY);
|
||||
} else {
|
||||
left = selectData.startX - wwidth;
|
||||
top = selectData.startY - wheight;
|
||||
}
|
||||
selectFocus(left, top, wwidth, wheight);
|
||||
selectData.selectDiv.parentNode!.removeChild(selectData.selectDiv);
|
||||
selectData.selectDiv = null;
|
||||
}
|
||||
}
|
||||
143
packages/dooringx-lib/src/core/store/index.ts
Normal file
143
packages/dooringx-lib/src/core/store/index.ts
Normal file
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-03-14 04:29:09
|
||||
* @LastEditors: Please set LastEditors
|
||||
* @LastEditTime: 2021-06-19 17:07:10
|
||||
* @FilePath: \dooringv2\packages\dooring-v2-lib\src\core\store\index.ts
|
||||
*/
|
||||
import { IStoreData } from './storetype';
|
||||
import { storeChangerState } from '../storeChanger/state';
|
||||
|
||||
export const initialData: IStoreData = {
|
||||
container: {
|
||||
width: 375,
|
||||
height: 600,
|
||||
},
|
||||
block: [],
|
||||
modalMap: {},
|
||||
dataSource: {},
|
||||
globalState: {},
|
||||
};
|
||||
|
||||
class Store {
|
||||
constructor(
|
||||
public storeDataList: IStoreData[] = [initialData],
|
||||
public listeners: Array<Function> = [],
|
||||
public current: number = 0,
|
||||
public forceupdate: Function = () => {}
|
||||
) {}
|
||||
|
||||
getData() {
|
||||
return this.storeDataList[this.current];
|
||||
}
|
||||
getStoreList() {
|
||||
return this.storeDataList;
|
||||
}
|
||||
|
||||
getListeners() {
|
||||
return this.listeners;
|
||||
}
|
||||
|
||||
getIndex() {
|
||||
return this.current;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* 注意重置需要注册事件
|
||||
* @param {IStoreData[]} initData
|
||||
* @param {boolean} [check=false]
|
||||
* @memberof Store
|
||||
*/
|
||||
resetToInitData(initData: IStoreData[], check = false) {
|
||||
this.storeDataList = initData;
|
||||
this.current = 0;
|
||||
//如果是编辑模式,需要修改
|
||||
if (storeChangerState.modalEditName !== '' && check) {
|
||||
storeChangerState.modalEditName = '';
|
||||
}
|
||||
this.emit();
|
||||
}
|
||||
/**
|
||||
*
|
||||
* 注意重置需要注册事件
|
||||
* @param {IStoreData[]} initData
|
||||
* @param {number} current
|
||||
* @param {boolean} [check=false]
|
||||
* @memberof Store
|
||||
*/
|
||||
resetToCustomData(initData: IStoreData[], current: number, check = false) {
|
||||
this.storeDataList = initData;
|
||||
this.current = current;
|
||||
//如果是编辑模式,需要修改
|
||||
if (storeChangerState.modalEditName !== '' && check) {
|
||||
storeChangerState.modalEditName = '';
|
||||
}
|
||||
this.emit();
|
||||
}
|
||||
resetListeners() {
|
||||
this.listeners = [];
|
||||
}
|
||||
|
||||
replaceList(list: IStoreData[]) {
|
||||
this.storeDataList = list;
|
||||
}
|
||||
|
||||
setForceUpdate(fn: Function) {
|
||||
this.forceupdate = fn;
|
||||
}
|
||||
forceUpdate() {
|
||||
this.forceupdate();
|
||||
}
|
||||
|
||||
setIndex(num: number) {
|
||||
this.current = num;
|
||||
}
|
||||
|
||||
redo() {
|
||||
const maxLength = this.storeDataList.length;
|
||||
if (this.current + 1 < maxLength) {
|
||||
this.current = this.current + 1;
|
||||
this.emit();
|
||||
}
|
||||
}
|
||||
|
||||
undo() {
|
||||
if (this.current > 0) {
|
||||
this.current = this.current - 1;
|
||||
this.emit();
|
||||
}
|
||||
}
|
||||
|
||||
cleanRedundant(index: number) {
|
||||
this.storeDataList = this.storeDataList.slice(0, index + 1);
|
||||
}
|
||||
|
||||
setData(data: IStoreData) {
|
||||
// 如果current不是最后那个,说明后面的被undo过的,如果要新增,那么需要清除之前的
|
||||
let flag = true;
|
||||
if (this.current + 1 !== this.storeDataList.length) {
|
||||
this.cleanRedundant(this.current);
|
||||
flag = false;
|
||||
}
|
||||
this.current = this.current + 1;
|
||||
this.storeDataList[this.current] = data;
|
||||
if (flag && this.current + 1 !== this.storeDataList.length) {
|
||||
this.storeDataList.length = this.current + 1;
|
||||
}
|
||||
|
||||
this.emit();
|
||||
}
|
||||
|
||||
emit() {
|
||||
this.listeners.forEach((fn) => {
|
||||
fn(this.getData());
|
||||
});
|
||||
}
|
||||
|
||||
subscribe(listener: Function) {
|
||||
this.listeners.push(listener);
|
||||
return () => (this.listeners = this.listeners.filter((v) => v !== listener));
|
||||
}
|
||||
}
|
||||
|
||||
export default Store;
|
||||
48
packages/dooringx-lib/src/core/store/storetype.ts
Normal file
48
packages/dooringx-lib/src/core/store/storetype.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-03-14 04:29:09
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-07-07 17:00:07
|
||||
* @FilePath: \DooringV2\packages\dooringx-lib\src\core\store\storetype.ts
|
||||
*/
|
||||
|
||||
import { EventCenterMapType } from '../eventCenter';
|
||||
|
||||
export interface IStoreData {
|
||||
container: {
|
||||
width: number;
|
||||
height: number;
|
||||
};
|
||||
block: Array<IBlockType>;
|
||||
modalMap: Record<string, IStoreData>;
|
||||
dataSource: Record<string, any>;
|
||||
globalState: Record<string, any>;
|
||||
}
|
||||
|
||||
export interface IBlockType {
|
||||
id: string;
|
||||
name: string;
|
||||
top: number;
|
||||
left: number;
|
||||
zIndex: number;
|
||||
position: 'absolute' | 'relative' | 'fixed' | 'static' | 'sticky';
|
||||
width?: number | string;
|
||||
height?: number | string;
|
||||
display?: 'inline-block' | 'block' | 'inline';
|
||||
focus: boolean;
|
||||
resize: boolean;
|
||||
canDrag: boolean;
|
||||
props: Record<string, any>;
|
||||
syncList: Array<string>;
|
||||
eventMap: EventCenterMapType; //调用的event 与对应的函数名 如果要增加参数,则类型不能是Array<string>,需要[{name:string,...args}]
|
||||
functionList: Array<string>; //抛出的函数名
|
||||
animate: {
|
||||
animate?: string; //动画名
|
||||
animationIterationCount?: any;
|
||||
speed?: //动画速度
|
||||
'animate__slow' | 'animate__slower' | 'animate__fast' | 'animate__faster' | '';
|
||||
delay?: //首次延迟
|
||||
'animate__delay-2s' | 'animate__delay-3s' | 'animate__delay-4s' | 'animate__delay-5s' | '';
|
||||
};
|
||||
fixed: boolean; // 用于制作fixed组件
|
||||
}
|
||||
239
packages/dooringx-lib/src/core/storeChanger/index.ts
Normal file
239
packages/dooringx-lib/src/core/storeChanger/index.ts
Normal file
@@ -0,0 +1,239 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-04-05 14:55:31
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-07-07 16:59:54
|
||||
* @FilePath: \DooringV2\packages\dooringx-lib\src\core\storeChanger\index.ts
|
||||
*/
|
||||
|
||||
import { message } from 'antd';
|
||||
import Store from '../store';
|
||||
import { IStoreData } from '../store/storetype';
|
||||
import { createUid, deepCopy } from '../utils';
|
||||
import { storeChangerState } from './state';
|
||||
|
||||
export type StoreChangerMap = Record<
|
||||
'ORIGIN',
|
||||
{
|
||||
data: Array<IStoreData>;
|
||||
current: number;
|
||||
now: IStoreData;
|
||||
} | null
|
||||
>;
|
||||
|
||||
function createDefaultModalBlock(): IStoreData['block'] {
|
||||
return [
|
||||
{
|
||||
id: createUid('modal-mask'),
|
||||
name: 'modalMask',
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
zIndex: 0,
|
||||
props: {},
|
||||
resize: true,
|
||||
focus: false,
|
||||
position: 'fixed',
|
||||
display: 'block',
|
||||
syncList: [],
|
||||
canDrag: false,
|
||||
eventMap: {},
|
||||
functionList: [],
|
||||
animate: {},
|
||||
fixed: false,
|
||||
},
|
||||
{
|
||||
id: createUid('modal-container'),
|
||||
name: 'modalContainer',
|
||||
top: 100,
|
||||
left: 35,
|
||||
zIndex: 0,
|
||||
props: {},
|
||||
resize: true,
|
||||
focus: true,
|
||||
position: 'absolute',
|
||||
display: 'block',
|
||||
width: 300,
|
||||
height: 300,
|
||||
syncList: [],
|
||||
canDrag: true,
|
||||
eventMap: {},
|
||||
functionList: [],
|
||||
animate: {},
|
||||
fixed: false,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
// 用来存储主store
|
||||
const ORIGIN = 'ORIGIN';
|
||||
|
||||
const defaultModalStore: () => IStoreData = () => {
|
||||
const newblock = createDefaultModalBlock();
|
||||
return {
|
||||
container: {
|
||||
width: 375,
|
||||
height: 600,
|
||||
},
|
||||
block: newblock,
|
||||
modalMap: {},
|
||||
dataSource: {},
|
||||
globalState: {},
|
||||
};
|
||||
};
|
||||
|
||||
export class StoreChanger {
|
||||
public map: StoreChangerMap;
|
||||
constructor() {
|
||||
this.map = { ORIGIN: null };
|
||||
}
|
||||
|
||||
getState() {
|
||||
return storeChangerState;
|
||||
}
|
||||
|
||||
getOrigin() {
|
||||
return this.map[ORIGIN];
|
||||
}
|
||||
|
||||
isEdit() {
|
||||
if (storeChangerState.modalEditName !== '') {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
isInModalMap(store: Store, name: string) {
|
||||
const modalNameList = Object.keys(store.getData().modalMap);
|
||||
if (modalNameList.includes(name)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
initStoreChanger() {
|
||||
storeChangerState.modalEditName = '';
|
||||
this.map = { ORIGIN: null };
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* 更新origin内容,用于编辑模式下更新全局属性
|
||||
* 需要判断是否在编辑模式,否则会报错
|
||||
* @memberof StoreChanger
|
||||
*/
|
||||
updateOrigin(data: IStoreData) {
|
||||
const origin = this.getOrigin();
|
||||
if (origin!.data.length === origin!.current + 1) {
|
||||
//说明为末尾,
|
||||
origin!.data.push(data);
|
||||
} else {
|
||||
//替换下一个索引
|
||||
origin!.data[origin!.current + 1] = data;
|
||||
}
|
||||
origin!.now = data;
|
||||
origin!.current = origin!.current + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* 保存现阶段store,将store替换为新modal数据
|
||||
* @memberof StoreChanger
|
||||
*/
|
||||
newModalMap(store: Store, name: string) {
|
||||
const sign = this.isEdit();
|
||||
if (sign) {
|
||||
message.error('请保存弹窗后编辑其他弹窗');
|
||||
return;
|
||||
}
|
||||
//新建modal name不能重名,否则直接报错
|
||||
const sign2 = this.isInModalMap(store, name);
|
||||
if (sign2) {
|
||||
message.error(`已有重名弹窗:${name}`);
|
||||
return;
|
||||
}
|
||||
storeChangerState.modalEditName = name;
|
||||
this.map[ORIGIN] = {
|
||||
data: store.getStoreList(),
|
||||
current: store.getIndex(),
|
||||
now: store.getStoreList()[store.getIndex()],
|
||||
};
|
||||
store.resetToInitData([defaultModalStore()]);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* 存储modal到主store的map中,切换主store
|
||||
* @param {Store} store
|
||||
* @memberof StoreChanger
|
||||
*/
|
||||
closeModal(store: Store) {
|
||||
const sign = this.isEdit();
|
||||
if (!sign) {
|
||||
message.error('您并没有正在编辑弹窗');
|
||||
return;
|
||||
}
|
||||
const main = this.map[ORIGIN];
|
||||
const tmpModalData = deepCopy(store.getData());
|
||||
if (main) {
|
||||
store.resetToCustomData(main.data, main.current);
|
||||
const cloneData: IStoreData = deepCopy(store.getData());
|
||||
cloneData.modalMap[storeChangerState.modalEditName] = tmpModalData;
|
||||
store.setData(cloneData);
|
||||
storeChangerState.modalEditName = '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* 在已经保存的map中获取,如果正在编辑别的弹窗,则报错。
|
||||
* @param {Store} store store必须为主store
|
||||
* @param {string} name
|
||||
* @memberof StoreChanger
|
||||
*/
|
||||
updateModal(store: Store, name: string) {
|
||||
const sign = this.isEdit();
|
||||
if (sign) {
|
||||
message.error('请保存弹窗后编辑其他弹窗');
|
||||
return;
|
||||
}
|
||||
const sign2 = this.isInModalMap(store, name);
|
||||
if (!sign2) {
|
||||
message.error(`未找到该弹窗:${name}`);
|
||||
return;
|
||||
}
|
||||
storeChangerState.modalEditName = name;
|
||||
const modalData = store.getData().modalMap[name];
|
||||
this.map[ORIGIN] = {
|
||||
data: store.getStoreList(),
|
||||
current: store.getIndex(),
|
||||
now: store.getStoreList()[store.getIndex()],
|
||||
};
|
||||
store.resetToInitData([modalData]);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* 删除弹窗,不能处于编辑弹窗状态
|
||||
* @param {Store} store
|
||||
* @param {string} name
|
||||
* @returns
|
||||
* @memberof StoreChanger
|
||||
*/
|
||||
removeModal(store: Store, name: string) {
|
||||
const sign = this.isEdit();
|
||||
if (sign) {
|
||||
message.error('请保存弹窗后删除其他弹窗');
|
||||
return;
|
||||
}
|
||||
const sign2 = this.isInModalMap(store, name);
|
||||
if (!sign2) {
|
||||
message.error(`未找到该弹窗:${name}`);
|
||||
return;
|
||||
}
|
||||
const cloneData: IStoreData = deepCopy(store.getData());
|
||||
delete cloneData.modalMap[name];
|
||||
store.setData(cloneData);
|
||||
}
|
||||
}
|
||||
11
packages/dooringx-lib/src/core/storeChanger/state.ts
Normal file
11
packages/dooringx-lib/src/core/storeChanger/state.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-04-05 15:40:04
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-04-05 15:40:47
|
||||
* @FilePath: \dooringv2\src\core\storeChanger\state.ts
|
||||
*/
|
||||
|
||||
export const storeChangerState = {
|
||||
modalEditName: '',
|
||||
};
|
||||
117
packages/dooringx-lib/src/core/transfer/index.ts
Normal file
117
packages/dooringx-lib/src/core/transfer/index.ts
Normal file
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-04-21 22:59:57
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-07-07 19:50:12
|
||||
* @FilePath: \DooringV2\packages\dooringx-lib\src\core\transfer\index.ts
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @export 这里的转换可能有问题,需要都使用mobileWidth去转换
|
||||
* @param {number} top
|
||||
* @param {number} left
|
||||
* @param {(string | number | undefined)} height
|
||||
* @param {(string | number | undefined)} width
|
||||
* @param {boolean} isFixed
|
||||
* @returns
|
||||
*/
|
||||
export function transfer(
|
||||
top: number,
|
||||
left: number,
|
||||
height: string | number | undefined,
|
||||
width: string | number | undefined,
|
||||
isFixed: boolean
|
||||
) {
|
||||
if (isFixed) {
|
||||
// 由于是375x667基准,所以top大于667的,那么top为底部高度
|
||||
let newtop = 0;
|
||||
|
||||
const newleft = getRealWidth(left);
|
||||
let newheight: string | number | undefined;
|
||||
let newwidth: string | number | undefined;
|
||||
if (typeof height === 'string' || typeof height === 'undefined') {
|
||||
newheight = height;
|
||||
} else {
|
||||
newheight = getRealHeight(height);
|
||||
}
|
||||
if (typeof width === 'string' || typeof width === 'undefined') {
|
||||
newwidth = width;
|
||||
} else {
|
||||
newwidth = getRealWidth(width);
|
||||
}
|
||||
|
||||
if (top >= 667) {
|
||||
if (typeof newheight === 'number') {
|
||||
newtop = getRealHeight() - newheight;
|
||||
} else {
|
||||
// 如果没有高度或者高度是百分比,则定位会有问题
|
||||
newtop = getRealHeight();
|
||||
}
|
||||
} else {
|
||||
if (typeof height === 'number' && top >= 667 - height && typeof newheight === 'number') {
|
||||
// 这种是距离底部比高多 按底部计算
|
||||
newtop = getRealHeight() - newheight;
|
||||
} else {
|
||||
newtop = getRealHeight(top);
|
||||
}
|
||||
}
|
||||
return {
|
||||
top: newtop,
|
||||
left: newleft,
|
||||
height: newheight,
|
||||
width: newwidth,
|
||||
};
|
||||
} else {
|
||||
const newtop = getRealHeight(top);
|
||||
const newleft = getRealWidth(left);
|
||||
let newheight: string | number | undefined;
|
||||
let newwidth: string | number | undefined;
|
||||
if (typeof height === 'string' || typeof height === 'undefined') {
|
||||
newheight = height;
|
||||
} else {
|
||||
newheight = getRealHeight(height);
|
||||
}
|
||||
if (typeof width === 'string' || typeof width === 'undefined') {
|
||||
newwidth = width;
|
||||
} else {
|
||||
newwidth = getRealWidth(width);
|
||||
}
|
||||
|
||||
return {
|
||||
top: newtop,
|
||||
left: newleft,
|
||||
height: newheight,
|
||||
width: newwidth,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export function getCurrentMobileInfo() {
|
||||
let userAgentMatched = window.navigator.userAgent.match(
|
||||
/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i
|
||||
);
|
||||
const width = userAgentMatched
|
||||
? window.innerWidth
|
||||
: window.innerWidth < 500
|
||||
? window.innerWidth
|
||||
: 375;
|
||||
|
||||
const height = userAgentMatched
|
||||
? window.screen.availHeight
|
||||
: window.screen.availHeight < 667
|
||||
? window.screen.availHeight
|
||||
: 667;
|
||||
return [width, height];
|
||||
}
|
||||
|
||||
export function getRealWidth(w: number | string = 375) {
|
||||
const width = typeof w === 'string' ? parseFloat(w) : w;
|
||||
return (getCurrentMobileInfo()[0] / 375) * width;
|
||||
}
|
||||
|
||||
export function getRealHeight(H: number | string = 667) {
|
||||
const height = typeof H === 'string' ? parseFloat(H) : H;
|
||||
return (getCurrentMobileInfo()[0] / 375) * height;
|
||||
}
|
||||
9
packages/dooringx-lib/src/core/utils/icon.ts
Normal file
9
packages/dooringx-lib/src/core/utils/icon.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { createFromIconfontCN } from '@ant-design/icons';
|
||||
|
||||
export const IconFont = createFromIconfontCN({
|
||||
scriptUrl: '//at.alicdn.com/t/font_2607370_myr2zkz3ku.js', // 在 iconfont.cn 上生成
|
||||
extraCommonProps: {
|
||||
fill: 'currentColor',
|
||||
stroke: 'currentColor',
|
||||
},
|
||||
});
|
||||
281
packages/dooringx-lib/src/core/utils/index.ts
Normal file
281
packages/dooringx-lib/src/core/utils/index.ts
Normal file
@@ -0,0 +1,281 @@
|
||||
import { message } from 'antd';
|
||||
import { RGBColor } from 'react-color';
|
||||
import * as uuid from 'uuid';
|
||||
import Store from '../store';
|
||||
import { IBlockType, IStoreData } from '../store/storetype';
|
||||
import { specialCoList } from './special';
|
||||
import deepCopys from 'deepcopy';
|
||||
import { FunctionDataMap } from '../functionCenter/config';
|
||||
import UserConfig from '../../config';
|
||||
|
||||
export function deepCopy(obj: any) {
|
||||
return deepCopys(obj);
|
||||
}
|
||||
|
||||
export function swap(indexa: number, indexb: number, arr: Array<any>) {
|
||||
arr[indexa] = arr.splice(indexb, 1, arr[indexa])[0];
|
||||
return arr;
|
||||
}
|
||||
|
||||
// 将rgba字符串对象转化为rgba对象
|
||||
export function rgba2Obj(rgba = '') {
|
||||
let reg = /rgba\(\s*?(\d+)\s*?,\s*?(\d+)\s*?,\s*?(\d+)\s*?,\s*?(\d+)\s*?\)/g;
|
||||
let rgbaObj: RGBColor = { r: 0, g: 0, b: 0, a: 0 };
|
||||
|
||||
rgba.replace(reg, (_m, r, g, b, a) => {
|
||||
rgbaObj = { r, g, b, a };
|
||||
return rgba;
|
||||
});
|
||||
return rgbaObj;
|
||||
}
|
||||
|
||||
export function createUid(name?: string) {
|
||||
if (name) {
|
||||
return name + '-' + uuid.v4();
|
||||
} else {
|
||||
return uuid.v4();
|
||||
}
|
||||
}
|
||||
|
||||
export const isMac = () => {
|
||||
const isMac = /macintosh|mac os x/i.test(navigator.userAgent);
|
||||
if (isMac) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
export const changeItem = (
|
||||
store: Store,
|
||||
id: string,
|
||||
property: keyof IBlockType,
|
||||
value: IBlockType[keyof IBlockType]
|
||||
) => {
|
||||
const clonedata: IStoreData = deepCopy(store.getData());
|
||||
let canchange = true;
|
||||
clonedata.block.forEach((v) => {
|
||||
if (v.id === id) {
|
||||
if (specialCoList.includes(v.name)) {
|
||||
message.error('该组件不可调整');
|
||||
canchange = false;
|
||||
}
|
||||
v[property] = value as never;
|
||||
}
|
||||
});
|
||||
if (canchange) {
|
||||
store.setData(clonedata);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* 清除所有聚焦,选中某个元素
|
||||
* @param {Store} store
|
||||
* @param {string} id
|
||||
*/
|
||||
export const focusEle = (store: Store, id: string) => {
|
||||
const clonedata: IStoreData = deepCopy(store.getData());
|
||||
clonedata.block.forEach((v) => {
|
||||
if (v.id === id) {
|
||||
v.focus = true;
|
||||
} else {
|
||||
v.focus = false;
|
||||
}
|
||||
});
|
||||
store.setData(clonedata);
|
||||
};
|
||||
|
||||
export const changeLayer = (store: Store, id: string, action: 'up' | 'down' | 'delete') => {
|
||||
const clonedata: IStoreData = deepCopy(store.getData());
|
||||
let index = -1;
|
||||
switch (action) {
|
||||
case 'up':
|
||||
clonedata.block.forEach((v, i) => {
|
||||
if (v.id === id) {
|
||||
if (specialCoList.includes(v.name)) {
|
||||
message.error('该组件不可调整');
|
||||
return;
|
||||
} else {
|
||||
index = i;
|
||||
}
|
||||
}
|
||||
});
|
||||
if (index > 0) {
|
||||
// 查看上一个元素
|
||||
const item = clonedata.block[index - 1];
|
||||
if (specialCoList.includes(item.name)) {
|
||||
return;
|
||||
}
|
||||
swap(index, index - 1, clonedata.block);
|
||||
store.setData(clonedata);
|
||||
}
|
||||
return;
|
||||
case 'down':
|
||||
clonedata.block.forEach((v, i) => {
|
||||
if (v.id === id) {
|
||||
if (specialCoList.includes(v.name)) {
|
||||
message.error('该组件不可调整');
|
||||
return;
|
||||
} else {
|
||||
index = i;
|
||||
}
|
||||
}
|
||||
});
|
||||
if (index > -1 && index + 1 < clonedata.block.length) {
|
||||
const item = clonedata.block[index + 1];
|
||||
if (specialCoList.includes(item.name)) {
|
||||
return;
|
||||
}
|
||||
swap(index, index + 1, clonedata.block);
|
||||
store.setData(clonedata);
|
||||
}
|
||||
return;
|
||||
case 'delete':
|
||||
let candelete = true;
|
||||
clonedata.block = clonedata.block.filter((v) => {
|
||||
if (v.id === id) {
|
||||
if (specialCoList.includes(v.name)) {
|
||||
candelete = false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
if (candelete) {
|
||||
store.setData(clonedata);
|
||||
} else {
|
||||
message.error('该组件无法删除');
|
||||
}
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} array
|
||||
* @param {*} from
|
||||
* @param {*} to
|
||||
*/
|
||||
export const arrayMove = (array: any, from: number, to: number) => {
|
||||
array = [...array];
|
||||
arrayMoveMutate(array, from, to);
|
||||
return array;
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} length
|
||||
* @param {*} index
|
||||
*/
|
||||
const indexSub = (arrLength: number, toIndex: number) => {
|
||||
return toIndex < 0 ? arrLength + toIndex : toIndex;
|
||||
// return resIndex;
|
||||
};
|
||||
|
||||
/**
|
||||
* 数组换位
|
||||
* @param {Array} array The array with the item to move. / [1,2,3]
|
||||
* @param {Number} from Index of item to move. If negative, it will begin that many elements from the end / 0 / -1 / 2
|
||||
* @param {Number} to Index of where to move the item. If negative, it will begin that many elements from the end / 0 / -1 / 2
|
||||
* returns A new array with the item moved to the new position [1,2,3] -> [1,3,2]
|
||||
*/
|
||||
const arrayMoveMutate = (array: [], from: number, to: number) => {
|
||||
const arrLength = array.length;
|
||||
const startIndex = indexSub(arrLength, from);
|
||||
if (startIndex >= 0 && startIndex < arrLength) {
|
||||
const endIndex = indexSub(arrLength, to);
|
||||
const [item] = array.splice(from, 1);
|
||||
array.splice(endIndex, 0, item);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* 这个函数将返回值全部统一成数组// modal的不走此方法
|
||||
* @param {keyof FunctionDataMap} v
|
||||
* @param {Record<string, any>} args
|
||||
* @param {string} name
|
||||
* @param {UserConfig} config
|
||||
* @param {Record<string, any>} ctx
|
||||
* @return {Array<string, any>}
|
||||
*/
|
||||
export const changeUserValue = (
|
||||
v: keyof FunctionDataMap,
|
||||
args: Record<string, any>,
|
||||
name: string,
|
||||
config: UserConfig,
|
||||
ctx: Record<string, any>
|
||||
) => {
|
||||
const userChoose = args[name];
|
||||
switch (v) {
|
||||
case 'ctx':
|
||||
if (Array.isArray(userChoose)) {
|
||||
return userChoose.reduce((pr: Array<string>, ne: string) => {
|
||||
const val = ctx[ne];
|
||||
pr.push(val);
|
||||
return pr;
|
||||
}, []);
|
||||
}
|
||||
return [];
|
||||
case 'dataSource':
|
||||
const dataCenter = config.getDataCenter().getDataMap();
|
||||
if (Array.isArray(userChoose)) {
|
||||
return userChoose.reduce((pr: Array<string>, ne: string) => {
|
||||
const val = dataCenter[ne];
|
||||
pr.push(val);
|
||||
return pr;
|
||||
}, []);
|
||||
}
|
||||
return [];
|
||||
default:
|
||||
if (Array.isArray(userChoose)) {
|
||||
return userChoose;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
};
|
||||
/**
|
||||
*
|
||||
* 这个函数将返回值全部统一成对象 modal的不走此方法
|
||||
* @param {keyof FunctionDataMap} v
|
||||
* @param {Record<string, any>} args
|
||||
* @param {string} name
|
||||
* @param {UserConfig} config
|
||||
* @param {Record<string, any>} ctx
|
||||
* @return {Record<string, any>}
|
||||
*/
|
||||
export const changeUserValueRecord = (
|
||||
v: keyof FunctionDataMap,
|
||||
args: Record<string, any>,
|
||||
name: string,
|
||||
config: UserConfig,
|
||||
ctx: Record<string, any>
|
||||
) => {
|
||||
const userChoose = args[name];
|
||||
switch (v) {
|
||||
case 'ctx':
|
||||
if (Array.isArray(userChoose)) {
|
||||
return userChoose.reduce((pr: Record<string, any>, ne: string) => {
|
||||
const val = ctx[ne];
|
||||
return Object.assign(pr, { [ne]: val });
|
||||
}, {});
|
||||
}
|
||||
return {};
|
||||
case 'dataSource':
|
||||
const dataCenter = config.getDataCenter().getDataMap();
|
||||
if (Array.isArray(userChoose)) {
|
||||
return userChoose.reduce((pr: Record<string, any>, ne: string) => {
|
||||
const val = dataCenter[ne];
|
||||
return Object.assign(pr, { [ne]: val });
|
||||
}, {});
|
||||
}
|
||||
return {};
|
||||
default:
|
||||
if (Array.isArray(userChoose)) {
|
||||
return userChoose.reduce((pr: Record<string, any>, ne: string) => {
|
||||
return Object.assign(pr, { [ne]: ne });
|
||||
}, {});
|
||||
}
|
||||
return {};
|
||||
}
|
||||
};
|
||||
1
packages/dooringx-lib/src/core/utils/special.ts
Normal file
1
packages/dooringx-lib/src/core/utils/special.ts
Normal file
@@ -0,0 +1 @@
|
||||
export const specialCoList = ['modalMask'];
|
||||
98
packages/dooringx-lib/src/hooks/index.ts
Normal file
98
packages/dooringx-lib/src/hooks/index.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-03-14 05:35:15
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-07-04 17:52:02
|
||||
* @FilePath: \DooringV2\packages\dooring-v2-lib\src\hooks\index.ts
|
||||
*/
|
||||
import { store } from '../runtime/store';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import UserConfig from '../config';
|
||||
import { ComponentRenderConfigProps } from '../core/components/componentItem';
|
||||
import { registCommandFn, unRegistCommandFn } from '../core/command/runtime';
|
||||
|
||||
export function useStoreState(
|
||||
config: UserConfig,
|
||||
extraFn: Function = () => {},
|
||||
everyFn: Function = () => {}
|
||||
) {
|
||||
const [state, setState] = useState(store.getData());
|
||||
const forceUpdate = useState(0)[1];
|
||||
useEffect(() => {
|
||||
const unRegister = store.subscribe(() => {
|
||||
setState(store.getData());
|
||||
config.getEventCenter().syncEventMap(store.getData(), config.getStoreChanger());
|
||||
extraFn();
|
||||
});
|
||||
store.setForceUpdate(() => forceUpdate((v) => v + 1));
|
||||
const commandModules = config.getConfig().initCommandModule;
|
||||
const commander = config.getCommanderRegister();
|
||||
registCommandFn(commandModules, commander);
|
||||
return () => {
|
||||
unRegister();
|
||||
unRegistCommandFn(commandModules, commander);
|
||||
};
|
||||
}, [config, extraFn]);
|
||||
useEffect(() => {
|
||||
everyFn();
|
||||
}, [everyFn]);
|
||||
|
||||
// 去除默认滚动
|
||||
useEffect(() => {
|
||||
const fn1 = function (event: Event) {
|
||||
if ((event as MouseEvent).ctrlKey === true || (event as MouseEvent).metaKey) {
|
||||
event.preventDefault();
|
||||
}
|
||||
};
|
||||
const fn2 = function (event: Event) {
|
||||
if ((event as MouseEvent).ctrlKey === true || (event as MouseEvent).metaKey) {
|
||||
event.preventDefault();
|
||||
}
|
||||
};
|
||||
window.addEventListener('mousewheel', fn1, { passive: false });
|
||||
//firefox
|
||||
window.addEventListener('DOMMouseScroll', fn2, { passive: false });
|
||||
return () => {
|
||||
window.removeEventListener('mousewheel', fn1);
|
||||
window.removeEventListener('mousewheel', fn2);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return [state];
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* 组件动态注册eventMap与eventCenter
|
||||
* @export
|
||||
* @param {ComponentRenderConfigProps} props render参数传来的
|
||||
* @param {string} eventName 同一个组件名称不能重复
|
||||
* @returns
|
||||
*/
|
||||
export function useDynamicAddEventCenter(
|
||||
props: ComponentRenderConfigProps,
|
||||
eventName: string,
|
||||
displayName: string
|
||||
) {
|
||||
const eventCenter = useMemo(() => {
|
||||
return props.config.getEventCenter();
|
||||
}, [props.config]);
|
||||
|
||||
useEffect(() => {
|
||||
const data = props.store.getData();
|
||||
const map = props.data.eventMap;
|
||||
const storeItem = data.block.find((v) => v.id === props.data.id);
|
||||
if (storeItem) {
|
||||
if (!map[eventName]) {
|
||||
//动态store加属性需要通过hook
|
||||
storeItem.eventMap[eventName] = {
|
||||
arr: [],
|
||||
displayName,
|
||||
userSelect: [],
|
||||
};
|
||||
eventCenter.manualUpdateMap(eventName, displayName);
|
||||
}
|
||||
}
|
||||
}, [eventCenter, props.data.eventMap, props.data.id, props.store]);
|
||||
return;
|
||||
}
|
||||
173
packages/dooringx-lib/src/index.less
Normal file
173
packages/dooringx-lib/src/index.less
Normal file
@@ -0,0 +1,173 @@
|
||||
.yhLeftrender {
|
||||
overflow: auto;
|
||||
|
||||
.leftco {
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.coitem {
|
||||
margin: 5px;
|
||||
|
||||
.redbox {
|
||||
height: 68px;
|
||||
width: 68px;
|
||||
background: var(--redbox-color);
|
||||
// line-height: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
#icon-checkbox,
|
||||
#icon-tabs,
|
||||
#icon-jiantou path {
|
||||
fill: currentColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.yh_container {
|
||||
// ::selection {
|
||||
// color: inherit;
|
||||
// background-color: inherit;
|
||||
// }
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
|
||||
:global(.am-list-item) {
|
||||
background-color: rgba(255, 255, 255, 0);
|
||||
}
|
||||
|
||||
:global(.am-list-body) {
|
||||
background-color: rgba(255, 255, 255, 0);
|
||||
}
|
||||
|
||||
:global(.ant-input) {
|
||||
background-color: rgba(255, 255, 255, 0);
|
||||
}
|
||||
}
|
||||
|
||||
.yh_container_preview {
|
||||
:global(.am-list-item) {
|
||||
background-color: rgba(255, 255, 255, 0);
|
||||
}
|
||||
|
||||
:global(.am-list-body) {
|
||||
background-color: rgba(255, 255, 255, 0);
|
||||
}
|
||||
|
||||
:global(.ant-input) {
|
||||
background-color: rgba(255, 255, 255, 0);
|
||||
}
|
||||
}
|
||||
|
||||
.yh_block_focus {
|
||||
&::before {
|
||||
position: absolute;
|
||||
top: -3px;
|
||||
left: -3px;
|
||||
right: -3px;
|
||||
bottom: -3px;
|
||||
content: '';
|
||||
border: 2px dashed #2196f3;
|
||||
}
|
||||
}
|
||||
|
||||
.yhTempDiv {
|
||||
background-color: #7165fa2b;
|
||||
}
|
||||
|
||||
.resizepoint {
|
||||
position: absolute;
|
||||
height: 6px;
|
||||
width: 6px;
|
||||
background-color: #2196f3;
|
||||
cursor: pointer;
|
||||
border-radius: 50%;
|
||||
|
||||
&.left {
|
||||
left: -6px;
|
||||
top: calc(50% - 3px);
|
||||
|
||||
&:hover {
|
||||
cursor: e-resize;
|
||||
}
|
||||
}
|
||||
|
||||
&.right {
|
||||
right: -6px;
|
||||
top: calc(50% - 3px);
|
||||
|
||||
&:hover {
|
||||
cursor: e-resize;
|
||||
}
|
||||
}
|
||||
|
||||
&.top {
|
||||
top: -6px;
|
||||
left: calc(50% - 3px);
|
||||
|
||||
&:hover {
|
||||
cursor: s-resize;
|
||||
}
|
||||
}
|
||||
|
||||
&.bottom {
|
||||
bottom: -6px;
|
||||
left: calc(50% - 3px);
|
||||
|
||||
&:hover {
|
||||
cursor: s-resize;
|
||||
}
|
||||
}
|
||||
|
||||
&.topleft {
|
||||
top: -6px;
|
||||
left: -6px;
|
||||
|
||||
&:hover {
|
||||
cursor: nw-resize;
|
||||
}
|
||||
}
|
||||
|
||||
&.bottomleft {
|
||||
bottom: -6px;
|
||||
left: -6px;
|
||||
|
||||
&:hover {
|
||||
cursor: ne-resize;
|
||||
}
|
||||
}
|
||||
|
||||
&.bottomright {
|
||||
bottom: -6px;
|
||||
right: -6px;
|
||||
|
||||
&:hover {
|
||||
cursor: nw-resize;
|
||||
}
|
||||
}
|
||||
|
||||
&.topright {
|
||||
top: -6px;
|
||||
right: -6px;
|
||||
|
||||
&:hover {
|
||||
cursor: ne-resize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.menu_footer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
bottom: 0;
|
||||
}
|
||||
60
packages/dooringx-lib/src/index.tsx
Normal file
60
packages/dooringx-lib/src/index.tsx
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-03-14 04:22:18
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-07-07 03:38:44
|
||||
* @FilePath: \dooringv2\packages\dooringx-lib\src\index.tsx
|
||||
*/
|
||||
|
||||
// 初始化store用
|
||||
export { useStoreState } from './hooks';
|
||||
// 动态添加事件使用
|
||||
export { useDynamicAddEventCenter } from './hooks';
|
||||
// 画布组件
|
||||
export { default as Container } from './components/container';
|
||||
// 左边组件
|
||||
export { default as LeftConfig } from './components/leftConfig';
|
||||
// 预览组件
|
||||
export { default as Preview } from './components/preview';
|
||||
// 右侧配置组件
|
||||
export { default as RightConfig } from './components/rightConfig';
|
||||
// 画布外层
|
||||
export { default as ContainerWrapper } from './components/wrapperMove';
|
||||
// 控制全局 弹窗等
|
||||
export { default as Control } from './components/control';
|
||||
// 这个放到外层容器属性里 ...innerContainerDragUp()
|
||||
export { innerContainerDragUp } from './core/innerDrag';
|
||||
// 用于修改store
|
||||
export { store } from './runtime/store';
|
||||
// 用于获取运行中的实例
|
||||
export { commander, componentRegister, formRegister, storeChanger } from './runtime';
|
||||
|
||||
export { unmountContextMenu } from './core/contextMenu';
|
||||
|
||||
// 用户的设置 包括可以获取store commander
|
||||
export { default as UserConfig } from './config';
|
||||
// 合并配置项
|
||||
export { userConfigMerge } from './config';
|
||||
|
||||
//制作放大缩小的函数
|
||||
export { scaleFn } from './core/scale/index';
|
||||
|
||||
// 以下导出用于制作插件
|
||||
// 用于制作组件的函数
|
||||
export { ComponentItemFactory } from './core/components/abstract';
|
||||
// 用于制作组件配置项的函数
|
||||
export { createPannelOptions } from './core/components/formTypes';
|
||||
// 用于config类型定义
|
||||
export { InitConfig } from './config';
|
||||
// 用于制作函数转换
|
||||
export { changeUserValueRecord } from './core/utils/index';
|
||||
export { changeUserValue } from './core/utils/index';
|
||||
// 用于制作快捷键
|
||||
export { CommanderItemFactory } from './core/command/abstract';
|
||||
|
||||
export { defaultStore } from './config';
|
||||
//state
|
||||
export { focusState } from './core/focusHandler/state';
|
||||
//utils
|
||||
export { deepCopy, rgba2Obj, swap, createUid, arrayMove } from './core/utils';
|
||||
export { specialCoList } from './core/utils/special';
|
||||
17
packages/dooringx-lib/src/runtime/index.ts
Normal file
17
packages/dooringx-lib/src/runtime/index.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-03-14 04:33:52
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-05-30 15:51:50
|
||||
* @FilePath: \dooringv2\packages\dooring-v2-lib\src\runtime\index.ts
|
||||
*/
|
||||
import CommanderWrapper from '../core/command';
|
||||
import ComponentRegister from '../core/components';
|
||||
import { FormComponentRegister } from '../core/components/formComponentRegister';
|
||||
import { StoreChanger } from '../core/storeChanger';
|
||||
import { store } from './store';
|
||||
|
||||
export const commander = new CommanderWrapper(store);
|
||||
export const componentRegister = new ComponentRegister();
|
||||
export const formRegister = new FormComponentRegister();
|
||||
export const storeChanger = new StoreChanger();
|
||||
11
packages/dooringx-lib/src/runtime/store.ts
Normal file
11
packages/dooringx-lib/src/runtime/store.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* @Author: yehuozhili
|
||||
* @Date: 2021-03-14 11:32:30
|
||||
* @LastEditors: yehuozhili
|
||||
* @LastEditTime: 2021-03-14 11:32:48
|
||||
* @FilePath: \dooring-v2\src\runtime\store.ts
|
||||
*/
|
||||
// 单独提出来为了避免循环引用
|
||||
import Store from '../core/store';
|
||||
|
||||
export const store = new Store();
|
||||
Reference in New Issue
Block a user