improve code

This commit is contained in:
chenos 2021-07-03 17:26:08 +08:00
parent 8b34b84a5e
commit 699726d5bb
8 changed files with 464 additions and 218 deletions

View File

@ -20,7 +20,7 @@ import { observable } from '@formily/reactive';
import { uid, clone } from '@formily/shared'; import { uid, clone } from '@formily/shared';
import { ArrayCollapse, ArrayTable, FormLayout } from '@formily/antd'; import { ArrayCollapse, ArrayTable, FormLayout } from '@formily/antd';
import { Space, Card } from 'antd'; import { Space, Card, Modal } from 'antd';
import { Action, useLogin, useRegister, useSubmit } from '../action'; import { Action, useLogin, useRegister, useSubmit } from '../action';
import { AddNew } from '../add-new'; import { AddNew } from '../add-new';
import { Cascader } from '../cascader'; import { Cascader } from '../cascader';
@ -46,6 +46,9 @@ import { TimePicker } from '../time-picker';
import { Upload } from '../upload'; import { Upload } from '../upload';
import { FormItem } from '../form-item'; import { FormItem } from '../form-item';
import { CodeOutlined } from '@ant-design/icons';
import Editor from '@monaco-editor/react';
export const BlockContext = createContext({ dragRef: null }); export const BlockContext = createContext({ dragRef: null });
const Div = (props) => <div {...props} />; const Div = (props) => <div {...props} />;
@ -170,6 +173,30 @@ export function useDesignable(path?: any) {
return { return {
schema: currentSchema, schema: currentSchema,
refresh, refresh,
prepend: (property: ISchema, targetPath?: any): Schema => {
let target = currentSchema;
if (targetPath) {
target = findPropertyByPath(schema, targetPath);
}
if (!target) {
console.error('target schema does not exist.');
return;
}
if (!property.name) {
property.name = uid();
}
const properties = {};
properties[property.name] = property;
Object.keys(target.properties).forEach((name, index) => {
const current = target.properties[name];
current.parent.removeProperty(current.name);
properties[current.name] = current.toJSON();
});
console.log({ properties }, target.properties)
target.setProperties(properties);
refresh();
return target.properties[property.name];
},
appendChild: (property: ISchema, targetPath?: any): Schema => { appendChild: (property: ISchema, targetPath?: any): Schema => {
let target = currentSchema; let target = currentSchema;
if (targetPath) { if (targetPath) {
@ -222,6 +249,27 @@ export function useDesignable(path?: any) {
refresh(); refresh();
return target.parent.properties[property.name]; return target.parent.properties[property.name];
}, },
deepRemove(targetPath?: any) {
let target = currentSchema;
if (targetPath) {
target = findPropertyByPath(schema, targetPath);
}
if (!target) {
console.error('target schema does not exist.');
return;
}
const remove = (s: Schema) => {
if (!s.parent) {
return;
}
s.parent.removeProperty(s.name);
if (Object.keys(s.parent.properties || {}).length === 0) {
remove(s.parent);
}
};
remove(target);
refresh();
},
remove(targetPath?: any) { remove(targetPath?: any) {
let target = currentSchema; let target = currentSchema;
if (targetPath) { if (targetPath) {
@ -274,6 +322,7 @@ export const createDesignableSchemaField = (options) => {
}} }}
> >
<SchemaField schema={schema} /> <SchemaField schema={schema} />
<CodePreview schema={schema} />
</DesignableContext.Provider> </DesignableContext.Provider>
); );
}; };
@ -281,6 +330,23 @@ export const createDesignableSchemaField = (options) => {
return DesignableSchemaField; return DesignableSchemaField;
}; };
const CodePreview = ({ schema }) => {
const [visible, setVisible] = useState(false);
return (
<>
<CodeOutlined onClick={() => setVisible(true)} />
<Modal width={'80%'} onCancel={() => setVisible(false)} visible={visible}>
<Editor
height="60vh"
defaultLanguage="json"
value={JSON.stringify(schema.toJSON(), null, 2)}
/>
{/* <pre>{JSON.stringify(schema.toJSON(), null, 2)}</pre> */}
</Modal>
</>
);
};
export const DesignableSchemaField = createDesignableSchemaField({ export const DesignableSchemaField = createDesignableSchemaField({
scope, scope,
components, components,

View File

@ -13,82 +13,205 @@ import { FormItem } from '../../form-item';
import { createDesignableSchemaField } from '../../DesignableSchemaField'; import { createDesignableSchemaField } from '../../DesignableSchemaField';
import { createForm } from '@formily/core'; import { createForm } from '@formily/core';
import { Grid } from '../'; import { Grid } from '../';
import { Card } from 'antd';
const schema: ISchema = { const schema: ISchema = {
type: 'object', type: 'void',
name: uid(),
'x-component': 'Grid',
properties: { properties: {
[uid()]: { [`row_${uid()}`]: {
type: 'void', type: 'void',
'x-component': 'Grid', 'x-component': 'Grid.Row',
properties: { properties: {
[`row_${uid()}`]: { [`col_${uid()}`]: {
type: 'void', type: 'void',
'x-component': 'Grid.Row', 'x-component': 'Grid.Col',
'x-component-props': {
width: 30,
},
properties: { properties: {
[`col_${uid()}`]: { [`block_${uid()}`]: {
type: 'void', type: 'void',
'x-component': 'Grid.Col', 'x-component': 'Grid.Block',
"x-component-props": {
width: 30,
},
properties: { properties: {
[`block_${uid()}`]: { [uid()]: {
type: 'void', type: 'string',
'x-component': 'Grid.Block', title: uid(),
properties: { 'x-designable-bar': 'FormItem.DesignableBar',
[uid()]: { 'x-decorator': 'FormItem',
type: 'string', 'x-component': 'Input',
title: uid(),
'x-designable-bar': 'FormItem.DesignableBar',
'x-decorator': 'FormItem',
'x-component': 'Input',
},
},
},
},
},
[`col_${uid()}`]: {
type: 'void',
'x-component': 'Grid.Col',
"x-component-props": {
width: 70,
},
properties: {
[`block_${uid()}`]: {
type: 'void',
'x-component': 'Grid.Block',
properties: {
[uid()]: {
type: 'string',
title: uid(),
'x-designable-bar': 'FormItem.DesignableBar',
'x-decorator': 'FormItem',
'x-component': 'Input',
},
},
}, },
}, },
}, },
}, },
}, },
[`row_${uid()}`]: { [`col_${uid()}`]: {
type: 'void', type: 'void',
'x-component': 'Grid.Row', 'x-component': 'Grid.Col',
'x-component-props': {
width: 70,
},
properties: { properties: {
[`col_${uid()}`]: { [`block_${uid()}`]: {
type: 'void', type: 'void',
'x-component': 'Grid.Col', 'x-component': 'Grid.Block',
properties: { properties: {
[`block_${uid()}`]: { [uid()]: {
type: 'string',
title: uid(),
'x-designable-bar': 'FormItem.DesignableBar',
'x-decorator': 'FormItem',
'x-component': 'Input',
},
},
},
},
},
},
},
[`row_${uid()}`]: {
type: 'void',
'x-component': 'Grid.Row',
properties: {
[`col_${uid()}`]: {
type: 'void',
'x-component': 'Grid.Col',
properties: {
[`block_${uid()}`]: {
type: 'void',
'x-component': 'Grid.Block',
properties: {
[uid()]: {
type: 'string',
title: uid(),
'x-designable-bar': 'FormItem.DesignableBar',
'x-decorator': 'FormItem',
'x-component': 'Input',
},
},
},
},
},
},
},
[`row_${uid()}`]: {
type: 'void',
'x-component': 'Grid.Row',
properties: {
[`col_${uid()}`]: {
type: 'void',
'x-component': 'Grid.Col',
properties: {
[`block_${uid()}`]: {
type: 'void',
'x-component': 'Grid.Block',
properties: {
[uid()]: {
type: 'void', type: 'void',
'x-component': 'Grid.Block', name: uid(),
'x-decorator': 'Card',
'x-component': 'Grid',
properties: { properties: {
[uid()]: { [`row_${uid()}`]: {
type: 'string', type: 'void',
title: uid(), 'x-component': 'Grid.Row',
'x-designable-bar': 'FormItem.DesignableBar', properties: {
'x-decorator': 'FormItem', [`col_${uid()}`]: {
'x-component': 'Input', type: 'void',
'x-component': 'Grid.Col',
'x-component-props': {
width: 30,
},
properties: {
[`block_${uid()}`]: {
type: 'void',
'x-component': 'Grid.Block',
properties: {
[uid()]: {
type: 'string',
title: uid(),
'x-designable-bar': 'FormItem.DesignableBar',
'x-decorator': 'FormItem',
'x-component': 'Input',
},
},
},
},
},
[`col_${uid()}`]: {
type: 'void',
'x-component': 'Grid.Col',
'x-component-props': {
width: 70,
},
properties: {
[`block_${uid()}`]: {
type: 'void',
'x-component': 'Grid.Block',
properties: {
[uid()]: {
type: 'string',
title: uid(),
'x-designable-bar': 'FormItem.DesignableBar',
'x-decorator': 'FormItem',
'x-component': 'Input',
},
},
},
},
},
},
},
[`row_${uid()}`]: {
type: 'void',
'x-component': 'Grid.Row',
properties: {
[`col_${uid()}`]: {
type: 'void',
'x-component': 'Grid.Col',
properties: {
[`block_${uid()}`]: {
type: 'void',
'x-component': 'Grid.Block',
properties: {
[uid()]: {
type: 'string',
title: uid(),
'x-designable-bar': 'FormItem.DesignableBar',
'x-decorator': 'FormItem',
'x-component': 'Input',
},
},
},
},
},
},
},
[`row_${uid()}`]: {
type: 'void',
'x-component': 'Grid.Row',
properties: {
[`col_${uid()}`]: {
type: 'void',
'x-component': 'Grid.Col',
properties: {
[`block_${uid()}`]: {
type: 'void',
'x-component': 'Grid.Block',
properties: {
[uid()]: {
type: 'string',
title: uid(),
'x-designable-bar': 'FormItem.DesignableBar',
'x-decorator': 'FormItem',
'x-component': 'Input',
},
},
},
},
},
},
}, },
}, },
}, },
@ -106,6 +229,7 @@ const DesignableSchemaField = createDesignableSchemaField({
Grid, Grid,
Input, Input,
FormItem, FormItem,
Card,
}, },
}); });
@ -114,7 +238,12 @@ const form = createForm();
export default () => { export default () => {
return ( return (
<FormProvider form={form}> <FormProvider form={form}>
<DesignableSchemaField schema={schema} /> <DesignableSchemaField schema={{
type: 'object',
properties: {
[schema.name]: schema,
}
}} />
</FormProvider> </FormProvider>
); );
}; };

View File

@ -2,30 +2,35 @@ import React, { createContext, useContext, useEffect, useRef } from 'react';
import { useState } from 'react'; import { useState } from 'react';
import { useMouseEvents, useWillUnmount } from 'beautiful-react-hooks'; import { useMouseEvents, useWillUnmount } from 'beautiful-react-hooks';
import { useField } from '@formily/react'; import { useField } from '@formily/react';
import { useMount } from 'ahooks';
import constate from 'constate';
export const DragDropManagerContext = createContext<any>({ export function useDragDropManager() {
drag: null, const [drag, setDrag] = useState(null);
drops: {}, const [drop, setDrop] = useState(null);
name: `${Math.random()}`, const [drops, setDrops] = useState({});
}); const addDrop = (dropId, data) =>
setDrops((prevDrops) => {
export function DragDropProvider(props) { prevDrops[dropId] = data;
const { gridRef, onDrop, children } = props; return prevDrops;
return ( });
<DragDropManagerContext.Provider const getDrop = (dropId) => {
value={{ return drops[dropId];
drag: null, };
drops: {}, return { drag, drop, drops, setDrag, addDrop, setDrop, getDrop };
onDrop,
gridRef,
name: `${Math.random()}`,
}}
>
{children}
</DragDropManagerContext.Provider>
);
} }
const [DragDropManagerProvider, useDragDropManagerContext] =
constate(useDragDropManager);
export { DragDropManagerProvider, useDragDropManagerContext };
// export const DragDropManagerContext = createContext<any>({
// drag: null,
// drops: {},
// name: `${Math.random()}`,
// });
export function mergeRefs<T = any>( export function mergeRefs<T = any>(
refs: Array<React.MutableRefObject<T> | React.LegacyRef<T>>, refs: Array<React.MutableRefObject<T> | React.LegacyRef<T>>,
): React.RefCallback<T> { ): React.RefCallback<T> {
@ -52,7 +57,8 @@ export function useDrag(options?: any) {
left: 0, left: 0,
top: 0, top: 0,
}); });
const dragDropManager = useContext(DragDropManagerContext); const { setDrag, setDrop, getDrop } = useDragDropManagerContext();
// const dragDropManager = useContext(DragDropManagerContext);
const field = useField(); const field = useField();
console.log( console.log(
'init', 'init',
@ -62,28 +68,26 @@ export function useDrag(options?: any) {
useWillUnmount(() => { useWillUnmount(() => {
setIsDragging(false); setIsDragging(false);
console.log(
'useWillUnmount',
{ isDragging, previewElement },
dragDropManager.previewElement,
field ? field.address.segments : null,
);
// @ts-ignore // @ts-ignore
window.__previewElement && window.__previewElement.remove(); window.__previewElement && window.__previewElement.remove();
// @ts-ignore // @ts-ignore
window.__previewElement = undefined; window.__previewElement = undefined;
dragDropManager.drag = null; setDrag(null);
// dragDropManager.drag = null;
document.body.style.cursor = null; document.body.style.cursor = null;
document.body.style.userSelect = null; document.body.style.userSelect = null;
}) });
onMouseDown((event: React.MouseEvent) => { onMouseDown((event: React.MouseEvent) => {
if (event.button !== 0) { if (event.button !== 0) {
return; return;
} }
dragDropManager.drag = { type, item }; setDrop({ type, item });
dragDropManager.drop = { type, item }; setDrag({ type, item });
// dragDropManager.drag = { type, item };
// dragDropManager.drop = { type, item };
setIsDragging(true); setIsDragging(true);
const postion = { const postion = {
@ -112,7 +116,10 @@ export function useDrag(options?: any) {
setPreviewElement(wrap); setPreviewElement(wrap);
// @ts-ignore // @ts-ignore
window.__previewElement = wrap; window.__previewElement = wrap;
console.log('dragDropManager.previewElement', dragDropManager.previewElement) // console.log(
// 'dragDropManager.previewElement',
// dragDropManager.previewElement,
// );
document.body.appendChild(wrap); document.body.appendChild(wrap);
const el = document.createElement('div'); const el = document.createElement('div');
wrap.appendChild(el); wrap.appendChild(el);
@ -122,12 +129,12 @@ export function useDrag(options?: any) {
document.body.style.userSelect = 'none'; document.body.style.userSelect = 'none';
document.body.className = 'dragging'; document.body.className = 'dragging';
console.log( // console.log(
'onMouseDown', // 'onMouseDown',
{ isDragging, previewElement }, // { isDragging, previewElement },
dragDropManager.previewElement, // dragDropManager.previewElement,
field ? field.address.segments : null, // field ? field.address.segments : null,
); // );
// console.log('onMouseDown', event); // console.log('onMouseDown', event);
}); });
@ -136,17 +143,18 @@ export function useDrag(options?: any) {
return; return;
} }
console.log( // console.log(
'onMouseUp', // 'onMouseUp',
{ isDragging, previewElement }, // { isDragging, previewElement },
field ? field.address.segments : null, // field ? field.address.segments : null,
); // );
// @ts-ignore // @ts-ignore
event.dragItem = item; event.dragItem = item;
setIsDragging(false); setIsDragging(false);
dragDropManager.drag = null; setDrag(null);
// dragDropManager.drag = null;
previewElement.remove(); previewElement.remove();
setPreviewElement(undefined); setPreviewElement(undefined);
document.body.style.cursor = null; document.body.style.cursor = null;
@ -168,7 +176,8 @@ export function useDrag(options?: any) {
continue; continue;
} }
const dropId = dropElement.getAttribute('data-drop-id'); const dropId = dropElement.getAttribute('data-drop-id');
const dropContext = dropId ? dragDropManager.drops[dropId] : null;
const dropContext = getDrop(dropId);
if (dropContext && dropContext.accept === type) { if (dropContext && dropContext.accept === type) {
if ( if (
!dropContext.shallow || !dropContext.shallow ||
@ -188,12 +197,12 @@ export function useDrag(options?: any) {
if (!isDragging || !previewElement) { if (!isDragging || !previewElement) {
return; return;
} }
console.log( // console.log(
'onMouseMove', // 'onMouseMove',
{ isDragging, previewElement }, // { isDragging, previewElement },
dragDropManager.previewElement, // dragDropManager.previewElement,
field ? field.address.segments : null, // field ? field.address.segments : null,
); // );
// console.log({previewElement}) // console.log({previewElement})
const offset = { const offset = {
@ -205,6 +214,7 @@ export function useDrag(options?: any) {
if (type) { if (type) {
let dropElement = document.elementFromPoint(event.clientX, event.clientY); let dropElement = document.elementFromPoint(event.clientX, event.clientY);
console.log({ dropElement });
const dropIds = []; const dropIds = [];
while (dropElement) { while (dropElement) {
if (!dropElement.getAttribute) { if (!dropElement.getAttribute) {
@ -212,8 +222,9 @@ export function useDrag(options?: any) {
continue; continue;
} }
const dropId = dropElement.getAttribute('data-drop-id'); const dropId = dropElement.getAttribute('data-drop-id');
const dropContext = dropId ? dragDropManager.drops[dropId] : null; const dropContext = getDrop(dropId);
if (dropContext && dropContext.accept === type) { if (dropContext && dropContext.accept === type) {
console.log({ dropId });
if ( if (
!dropContext.shallow || !dropContext.shallow ||
(dropContext.shallow && dropIds.length === 0) (dropContext.shallow && dropIds.length === 0)
@ -225,7 +236,7 @@ export function useDrag(options?: any) {
} }
dropElement = dropElement.parentNode as HTMLElement; dropElement = dropElement.parentNode as HTMLElement;
} }
dragDropManager.drag = { type, dropIds }; setDrag({ type, dropIds });
} }
onDrag && onDrag(event); onDrag && onDrag(event);
@ -235,30 +246,44 @@ export function useDrag(options?: any) {
} }
export function useDrop(options) { export function useDrop(options) {
const { accept, data, shallow, onDrop, onHover, canDrop = true } = options; const {
uid: dropId,
accept,
data,
shallow,
onDrop,
onHover,
canDrop = true,
} = options;
const dropRef = useRef<HTMLDivElement>(); const dropRef = useRef<HTMLDivElement>();
const { onMouseEnter, onMouseLeave, onMouseMove, onMouseUp } = const { onMouseEnter, onMouseLeave, onMouseMove, onMouseUp } =
useMouseEvents(dropRef); useMouseEvents(dropRef);
const [isOver, setIsOver] = useState(false); const [isOver, setIsOver] = useState(false);
const [onTopHalf, setOnTopHalf] = useState(null); const [onTopHalf, setOnTopHalf] = useState(null);
const [dropId] = useState<string>(`d${Math.random()}`); // const dragDropManager = useContext(DragDropManagerContext);
const dragDropManager = useContext(DragDropManagerContext); const { drag, drop, addDrop, setDrop } = useDragDropManagerContext();
useEffect(() => { useEffect(() => {
dragDropManager.drops[dropId] = { addDrop(dropId, {
accept, accept,
data, data,
shallow, shallow,
}; });
// console.log('dragDropManager.drops', dragDropManager.drops);
// dragDropManager.drops[dropId] = {
// accept,
// data,
// shallow,
// };
dropRef.current.setAttribute('data-drop-id', dropId); dropRef.current.setAttribute('data-drop-id', dropId);
}, [accept, data, shallow]); }, []);
onMouseEnter((event) => { onMouseEnter((event) => {
if (!canDrop) { if (!canDrop) {
return; return;
} }
// console.log({ dragDropManager }); // console.log({ dragDropManager });
if (!dragDropManager.drag || dragDropManager.drag.type !== accept) { if (!drag || drag.type !== accept) {
return; return;
} }
setIsOver(true); setIsOver(true);
@ -268,13 +293,10 @@ export function useDrop(options) {
if (!canDrop) { if (!canDrop) {
return; return;
} }
if (!dragDropManager.drag || dragDropManager.drag.type !== accept) { if (!drag || drag.type !== accept) {
return; return;
} }
if ( if (drag.dropIds && drag.dropIds.includes(dropId)) {
dragDropManager.drag.dropIds &&
dragDropManager.drag.dropIds.includes(dropId)
) {
const top = event.clientY - dropRef.current.getBoundingClientRect().top; const top = event.clientY - dropRef.current.getBoundingClientRect().top;
const onTop = top < dropRef.current.clientHeight / 2; const onTop = top < dropRef.current.clientHeight / 2;
setOnTopHalf(onTop); setOnTopHalf(onTop);
@ -303,12 +325,12 @@ export function useDrop(options) {
// @ts-ignore // @ts-ignore
event.data = data; event.data = data;
// @ts-ignore // @ts-ignore
event.dragItem = dragDropManager.drop.item; event.dragItem = drop.item;
// @ts-ignore // @ts-ignore
event.dropElement = dropRef.current; event.dropElement = dropRef.current;
onDrop && onDrop(event); onDrop && onDrop(event);
dragDropManager.onDrop && dragDropManager.onDrop(event); // dragDropManager.onDrop && dragDropManager.onDrop(event);
dragDropManager.drop = null; setDrop(null);
} }
setIsOver(false); setIsOver(false);
}); });
@ -363,10 +385,12 @@ export function useColResizer(options?: any) {
const parent = dragRef.current.parentElement; const parent = dragRef.current.parentElement;
const els = parent.querySelectorAll(':scope > .nb-grid-col'); const els = parent.querySelectorAll(':scope > .nb-grid-col');
const size = []; const size = [];
const gap = dragRef.current.clientWidth;
console.log('parent.clientWidth', parent.clientWidth, dragRef.current.clientWidth );
els.forEach((el: HTMLDivElement) => { els.forEach((el: HTMLDivElement) => {
const w = (100 * el.clientWidth) / parent.clientWidth; const w = (100 * el.clientWidth) / parent.clientWidth;
const w2 = const w2 =
(100 * (el.clientWidth + 24 + 24 / els.length)) / parent.clientWidth; (100 * (el.clientWidth + gap + gap / els.length)) / parent.clientWidth;
size.push(w2); size.push(w2);
el.style.width = `${w}%`; el.style.width = `${w}%`;
}); });

View File

@ -5,6 +5,7 @@ import React, {
createContext, createContext,
useContext, useContext,
useEffect, useEffect,
useState,
} from 'react'; } from 'react';
// import { DndProvider, useDrag, useDragDropManager } from 'react-dnd'; // import { DndProvider, useDrag, useDragDropManager } from 'react-dnd';
// import { HTML5Backend } from 'react-dnd-html5-backend'; // import { HTML5Backend } from 'react-dnd-html5-backend';
@ -21,11 +22,12 @@ import './style.less';
import cls from 'classnames'; import cls from 'classnames';
import { useDesignable, useSchemaPath } from '../DesignableSchemaField'; import { useDesignable, useSchemaPath } from '../DesignableSchemaField';
import { useColResizer } from './hooks'; import { DragDropManagerProvider, useColResizer } from './hooks';
import { useDrag, useDrop, DragDropProvider, mergeRefs } from './hooks'; import { useDrag, useDrop, mergeRefs } from './hooks';
export const GridContext = createContext({ export const GridContext = createContext({
ref: null, ref: null,
gridName: null,
}); });
const ColumnSizeContext = createContext(null); const ColumnSizeContext = createContext(null);
@ -34,9 +36,12 @@ export const GridBlockContext = createContext({
dragRef: null, dragRef: null,
}); });
const RowDivider = ({ onDrop }) => { const RowDivider = ({ name, onDrop }) => {
const { gridName } = useContext(GridContext);
const { isOver, dropRef } = useDrop({ const { isOver, dropRef } = useDrop({
accept: 'grid', uid: `row_divider_${name}`,
accept: gridName,
shallow: true,
onDrop, onDrop,
}); });
return ( return (
@ -48,11 +53,14 @@ const RowDivider = ({ onDrop }) => {
}; };
const ColDivider = (props: any) => { const ColDivider = (props: any) => {
const { onDragEnd, resizable } = props; const { name, onDragEnd, resizable, onDrop } = props;
const { gridName } = useContext(GridContext);
const { isDragging, dragRef } = useColResizer({ onDragEnd }); const { isDragging, dragRef } = useColResizer({ onDragEnd });
const { isOver, dropRef } = useDrop({ const { isOver, dropRef } = useDrop({
accept: 'grid', uid: `col_divider_${name}`,
data: {}, accept: gridName,
shallow: true,
onDrop,
}); });
return ( return (
<div <div
@ -69,59 +77,72 @@ const ColDivider = (props: any) => {
}; };
export const Grid: any = observer((props) => { export const Grid: any = observer((props) => {
const schema = useFieldSchema();
const { insertBefore, insertAfter, remove } = useDesignable();
const ref = useRef(); const ref = useRef();
const schema = useFieldSchema();
const gridPath = useSchemaPath();
const {
schema: designableSchema,
insertAfter,
prepend,
deepRemove,
} = useDesignable();
return ( return (
<DragDropProvider> <DragDropManagerProvider>
<GridContext.Provider value={{ ref }}> <GridContext.Provider value={{ ref, gridName: schema.name }}>
<div ref={ref} className={'nb-grid'}> <div ref={ref} className={'nb-grid'}>
<RowDivider <RowDivider
key={`${schema.name}_0`}
name={`${schema.name}_0`}
onDrop={(e) => { onDrop={(e) => {
const blockSchema = e.dragItem.schema; const blockSchema = e.dragItem.schema;
const path = [...e.dragItem.path]; const path = [...e.dragItem.path];
path.pop(); path.pop();
remove(path); prepend({
insertBefore({
type: 'void', type: 'void',
"x-component": 'Grid.Row', 'x-component': 'Grid.Row',
properties: { properties: {
[uid()]: { [uid()]: {
type: 'void', type: 'void',
"x-component": 'Grid.Col', 'x-component': 'Grid.Col',
properties: { properties: {
[blockSchema.name]: blockSchema, [blockSchema.name]: blockSchema,
}, },
}, },
}, },
}); });
deepRemove(path);
}} }}
/> />
{schema.mapProperties((property) => { {schema.mapProperties((property, key, index) => {
return ( return (
<> <>
<div style={{ display: 'flex' }} className={'nb-grid-row'}> <div style={{ display: 'flex' }} className={'nb-grid-row'}>
<RecursionField name={property.name} schema={property} /> <RecursionField name={property.name} schema={property} />
</div> </div>
<RowDivider <RowDivider
key={`${schema.name}_${index + 1}`}
name={`${schema.name}_${index + 1}`}
onDrop={(e) => { onDrop={(e) => {
const blockSchema = e.dragItem.schema; const blockSchema = e.dragItem.schema;
const path = [...e.dragItem.path]; const path = [...e.dragItem.path];
path.pop(); path.pop();
remove(path); insertAfter(
insertAfter({ {
type: 'void', type: 'void',
"x-component": 'Grid.Row', 'x-component': 'Grid.Row',
properties: { properties: {
[uid()]: { [uid()]: {
type: 'void', type: 'void',
"x-component": 'Grid.Col', 'x-component': 'Grid.Col',
properties: { properties: {
[blockSchema.name]: blockSchema, [blockSchema.name]: blockSchema,
},
}, },
}, },
}, },
}); [...gridPath, key],
);
deepRemove(path);
}} }}
/> />
</> </>
@ -129,46 +150,86 @@ export const Grid: any = observer((props) => {
})} })}
</div> </div>
</GridContext.Provider> </GridContext.Provider>
</DragDropProvider> </DragDropManagerProvider>
); );
}); });
Grid.Row = observer((props) => { Grid.Row = observer((props) => {
const field = useField(); const field = useField();
const schema = useFieldSchema(); const schema = useFieldSchema();
const { schema: designableSchema, refresh } = useDesignable(); const rowPath = useSchemaPath();
const len = Object.keys(schema.properties || {}).length; const {
schema: designableSchema,
refresh,
insertAfter,
appendChild,
prepend,
remove,
deepRemove,
} = useDesignable();
const len = Object.keys(designableSchema.properties || {}).length;
console.log({ len });
return ( return (
<ColumnSizeContext.Provider value={len}> <ColumnSizeContext.Provider value={len}>
<ColDivider
name={`${schema.name}_0`}
onDrop={(e) => {
const blockSchema = e.dragItem.schema;
prepend({
type: 'void',
'x-component': 'Grid.Col',
properties: {
[blockSchema.name]: blockSchema,
},
});
const path = [...e.dragItem.path];
path.pop();
deepRemove(path);
}}
/>
{schema.mapProperties((property, key, index) => { {schema.mapProperties((property, key, index) => {
return ( return (
<> <>
<RecursionField name={property.name} schema={property} />
<ColDivider <ColDivider
resizable={index > 0} name={`${schema.name}_${index + 1}`}
resizable={index < len - 1}
onDrop={(e) => {
const blockSchema = e.dragItem.schema;
insertAfter(
{
type: 'void',
'x-component': 'Grid.Col',
properties: {
[blockSchema.name]: blockSchema,
},
},
[...rowPath, key],
);
const path = [...e.dragItem.path];
path.pop();
deepRemove(path);
}}
onDragEnd={(e) => { onDragEnd={(e) => {
schema.mapProperties((s, key, index) => {
field.query(`.${schema.name}.${key}`).take((f) => {
f.componentProps['width'] = e.data.size[index];
});
s['x-component-props'] = s['x-component-props'] || {};
s['x-component-props']['width'] = e.data.size[index];
return s;
});
designableSchema.mapProperties((s, key, index) => { designableSchema.mapProperties((s, key, index) => {
s['x-component-props'] = s['x-component-props'] || {}; s['x-component-props'] = s['x-component-props'] || {};
s['x-component-props']['width'] = e.data.size[index]; s['x-component-props']['width'] = e.data.size[index];
return s; return s;
}); });
schema.mapProperties((s, key, index) => {
refresh(); s['x-component-props'] = s['x-component-props'] || {};
console.log('e.data', designableSchema); s['x-component-props']['width'] = e.data.size[index];
field.query(`.${schema.name}.${key}`).take((f) => {
f.componentProps['width'] = e.data.size[index];
});
return s;
});
// refresh();
}} }}
/> />
<RecursionField name={property.name} schema={property} />
</> </>
); );
})} })}
<ColDivider />
</ColumnSizeContext.Provider> </ColumnSizeContext.Provider>
); );
}); });
@ -179,7 +240,7 @@ Grid.Col = observer((props) => {
const size = useContext(ColumnSizeContext); const size = useContext(ColumnSizeContext);
return ( return (
<div <div
style={{ width: `calc(${width || 100 / size}% - 24px / ${size})` }} style={{ width: `calc(${width || 100 / size}% - 24px - 24px / ${size})` }}
className={'nb-grid-col'} className={'nb-grid-col'}
> >
{props.children} {props.children}
@ -192,7 +253,7 @@ Grid.Block = observer((props) => {
const ctx = useContext(GridContext); const ctx = useContext(GridContext);
const path = useSchemaPath(); const path = useSchemaPath();
const { isDragging, dragRef, previewRef } = useDrag({ const { isDragging, dragRef, previewRef } = useDrag({
type: 'grid', type: ctx.gridName,
onDragStart() { onDragStart() {
console.log('onDragStart'); console.log('onDragStart');
}, },
@ -208,7 +269,8 @@ Grid.Block = observer((props) => {
}, },
}); });
const { isOver, onTopHalf, dropRef } = useDrop({ const { isOver, onTopHalf, dropRef } = useDrop({
accept: 'grid', uid: schema.name,
accept: ctx.gridName,
data: {}, data: {},
canDrop: !isDragging, canDrop: !isDragging,
}); });

View File

@ -9,6 +9,9 @@
.nb-grid-col-divider { .nb-grid-col-divider {
&.resizable { &.resizable {
cursor: col-resize; cursor: col-resize;
&:hover {
background: #e6f7ff;
}
} }
} }
} }
@ -16,11 +19,6 @@
.nb-grid.dragging { .nb-grid.dragging {
position: relative; position: relative;
.nb-grid-col-divider { .nb-grid-col-divider {
// background-color: #ddd;
// opacity: 0.5;
&.resizable {
cursor: col-resize;
}
&.hover { &.hover {
background: #e6f7ff; background: #e6f7ff;
} }
@ -38,7 +36,7 @@
} }
} }
.designable-bar { .designable-bar {
display: none; display: none !important;
} }
.nb-grid-block { .nb-grid-block {
position: relative; position: relative;

View File

@ -2,7 +2,6 @@ import React, { useState } from 'react';
import classNames from 'classnames'; import classNames from 'classnames';
import { useField, observer, RecursionField, Schema } from '@formily/react'; import { useField, observer, RecursionField, Schema } from '@formily/react';
import { Dropdown, Menu, Switch } from 'antd'; import { Dropdown, Menu, Switch } from 'antd';
import { useSchemaQuery } from '../grid';
import { import {
MenuOutlined, MenuOutlined,
ArrowUpOutlined, ArrowUpOutlined,
@ -11,19 +10,12 @@ import {
} from '@ant-design/icons'; } from '@ant-design/icons';
export const DesignableBar = (props) => { export const DesignableBar = (props) => {
const { schema, addBlock, removeBlock, refresh } = useSchemaQuery();
const [active, setActive] = useState(false); const [active, setActive] = useState(false);
const field = useField(); const field = useField();
return ( return (
<> <>
<Menu.Item <Menu.Item
onClick={() => { onClick={() => {
addBlock(
{},
{
insertBefore: true,
},
);
setActive(false); setActive(false);
}} }}
icon={<ArrowUpOutlined />} icon={<ArrowUpOutlined />}
@ -32,7 +24,6 @@ export const DesignableBar = (props) => {
</Menu.Item> </Menu.Item>
<Menu.Item <Menu.Item
onClick={() => { onClick={() => {
addBlock();
setActive(false); setActive(false);
}} }}
icon={<ArrowDownOutlined />} icon={<ArrowDownOutlined />}
@ -43,12 +34,9 @@ export const DesignableBar = (props) => {
<Menu.Item onClick={() => {}}> <Menu.Item onClick={() => {}}>
<Switch <Switch
onChange={(checked) => { onChange={(checked) => {
const key = Object.keys(schema.properties).shift(); // field.query(key).take((target: Formily.Core.Models.Field) => {
schema.properties[key].required = checked; // target.required = checked;
refresh(); // });
field.query(key).take((target: Formily.Core.Models.Field) => {
target.required = checked;
});
}} }}
checkedChildren="必填" checkedChildren="必填"
unCheckedChildren="非必填" unCheckedChildren="非必填"
@ -57,7 +45,6 @@ export const DesignableBar = (props) => {
<Menu.Divider /> <Menu.Divider />
<Menu.Item <Menu.Item
onClick={() => { onClick={() => {
removeBlock();
setActive(false); setActive(false);
}} }}
icon={<DeleteOutlined />} icon={<DeleteOutlined />}

View File

@ -2,7 +2,6 @@ import React, { useState } from 'react';
import classNames from 'classnames'; import classNames from 'classnames';
import { useField, observer, RecursionField, Schema } from '@formily/react'; import { useField, observer, RecursionField, Schema } from '@formily/react';
import { Dropdown, Menu } from 'antd'; import { Dropdown, Menu } from 'antd';
import { useSchemaQuery } from '../grid';
import { import {
MenuOutlined, MenuOutlined,
ArrowUpOutlined, ArrowUpOutlined,
@ -11,18 +10,11 @@ import {
} from '@ant-design/icons'; } from '@ant-design/icons';
export const ColumnDesignableBar = (props) => { export const ColumnDesignableBar = (props) => {
const { addBlock, removeBlock } = useSchemaQuery();
const [active, setActive] = useState(false); const [active, setActive] = useState(false);
return ( return (
<> <>
<Menu.Item <Menu.Item
onClick={() => { onClick={() => {
addBlock(
{},
{
insertBefore: true,
},
);
setActive(false); setActive(false);
}} }}
icon={<ArrowUpOutlined />} icon={<ArrowUpOutlined />}
@ -31,7 +23,6 @@ export const ColumnDesignableBar = (props) => {
</Menu.Item> </Menu.Item>
<Menu.Item <Menu.Item
onClick={() => { onClick={() => {
addBlock();
setActive(false); setActive(false);
}} }}
icon={<ArrowDownOutlined />} icon={<ArrowDownOutlined />}
@ -41,7 +32,6 @@ export const ColumnDesignableBar = (props) => {
<Menu.Divider /> <Menu.Divider />
<Menu.Item <Menu.Item
onClick={() => { onClick={() => {
removeBlock();
setActive(false); setActive(false);
}} }}
icon={<DeleteOutlined />} icon={<DeleteOutlined />}

View File

@ -2,7 +2,6 @@ import React, { useState } from 'react';
import classNames from 'classnames'; import classNames from 'classnames';
import { useField, observer, RecursionField, Schema } from '@formily/react'; import { useField, observer, RecursionField, Schema } from '@formily/react';
import { Dropdown, Menu } from 'antd'; import { Dropdown, Menu } from 'antd';
import { useSchemaQuery } from '../grid';
import { import {
MenuOutlined, MenuOutlined,
ArrowUpOutlined, ArrowUpOutlined,
@ -11,18 +10,11 @@ import {
} from '@ant-design/icons'; } from '@ant-design/icons';
export const DesignableBar = (props) => { export const DesignableBar = (props) => {
const { addBlock, removeBlock } = useSchemaQuery();
const [active, setActive] = useState(false); const [active, setActive] = useState(false);
return ( return (
<> <>
<Menu.Item <Menu.Item
onClick={() => { onClick={() => {
addBlock(
{},
{
insertBefore: true,
},
);
setActive(false); setActive(false);
}} }}
icon={<ArrowUpOutlined />} icon={<ArrowUpOutlined />}
@ -31,7 +23,6 @@ export const DesignableBar = (props) => {
</Menu.Item> </Menu.Item>
<Menu.Item <Menu.Item
onClick={() => { onClick={() => {
addBlock();
setActive(false); setActive(false);
}} }}
icon={<ArrowDownOutlined />} icon={<ArrowDownOutlined />}
@ -41,7 +32,6 @@ export const DesignableBar = (props) => {
<Menu.Divider /> <Menu.Divider />
<Menu.Item <Menu.Item
onClick={() => { onClick={() => {
removeBlock();
setActive(false); setActive(false);
}} }}
icon={<DeleteOutlined />} icon={<DeleteOutlined />}