change pkg

This commit is contained in:
yehuozhili
2021-07-09 01:41:03 +08:00
commit 968a072537
115 changed files with 19556 additions and 0 deletions

View 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;

View 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;

View 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;

View 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;

View 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
);
};

View 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;

View 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;

View 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;
};

View 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;