improve a lot of details
This commit is contained in:
parent
a7fd94affd
commit
5f752d28d7
102
packages/client/src/blocks/grid/Block.tsx
Normal file
102
packages/client/src/blocks/grid/Block.tsx
Normal file
@ -0,0 +1,102 @@
|
||||
import React, { useState } from 'react';
|
||||
import { useDrag, mergeRefs, useDrop } from './DND';
|
||||
import classNames from 'classnames';
|
||||
import { useField } from '@formily/react';
|
||||
import { Dropdown, Menu } from 'antd';
|
||||
import { useSchemaQuery } from '.';
|
||||
import { MenuOutlined, ArrowUpOutlined, ArrowDownOutlined, DeleteOutlined } from '@ant-design/icons';
|
||||
|
||||
export const Block = (props) => {
|
||||
const { children, title } = props;
|
||||
const field = useField<Formily.Core.Models.Field>();
|
||||
const { isDragging, dragRef, previewRef } = useDrag({
|
||||
type: 'grid',
|
||||
onDragStart() {
|
||||
// console.log('onDragStart');
|
||||
},
|
||||
onDragEnd(event) {
|
||||
console.log('onDragEnd', event);
|
||||
},
|
||||
onDrag(event) {
|
||||
// console.log('onDrag');
|
||||
},
|
||||
item: {
|
||||
path: field.address.segments,
|
||||
},
|
||||
});
|
||||
console.log('useDrag', 'previewElement', field.address.segments);
|
||||
const { isOver, onTopHalf, dropRef } = useDrop({
|
||||
accept: 'grid',
|
||||
data: {},
|
||||
canDrop: !isDragging,
|
||||
});
|
||||
const { addBlock } = useSchemaQuery();
|
||||
return (
|
||||
<div
|
||||
data-type={'block'}
|
||||
ref={mergeRefs([dropRef, previewRef])}
|
||||
className={classNames('block', { 'top-half': onTopHalf, hover: isOver })}
|
||||
// style={{ textAlign: 'center', lineHeight: '60px', background: '#f1f1f1' }}
|
||||
>
|
||||
<ActionBar dragRef={dragRef}/>
|
||||
<div
|
||||
style={{ textAlign: 'center', lineHeight: '60px', background: '#f1f1f1' }}
|
||||
>
|
||||
{children} {field.path.entire}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const ActionBar = ({ dragRef }) => {
|
||||
const { addBlock, removeBlock } = useSchemaQuery();
|
||||
const [active, setActive] = useState(false);
|
||||
return (
|
||||
<div className={classNames('action-bar', { active })}>
|
||||
<Dropdown
|
||||
overlayStyle={{ minWidth: 200 }}
|
||||
trigger={['click']}
|
||||
visible={active}
|
||||
onVisibleChange={setActive}
|
||||
overlay={
|
||||
<Menu>
|
||||
<Menu.Item
|
||||
onClick={() => {
|
||||
addBlock({}, {
|
||||
insertBefore: true,
|
||||
});
|
||||
setActive(false);
|
||||
}}
|
||||
icon={<ArrowUpOutlined />}
|
||||
>
|
||||
在上方插入区块
|
||||
</Menu.Item>
|
||||
<Menu.Item
|
||||
onClick={() => {
|
||||
addBlock();
|
||||
setActive(false);
|
||||
}}
|
||||
icon={<ArrowDownOutlined />}
|
||||
>
|
||||
在下方插入区块
|
||||
</Menu.Item>
|
||||
<Menu.Divider />
|
||||
<Menu.Item
|
||||
onClick={() => {
|
||||
removeBlock();
|
||||
setActive(false);
|
||||
}}
|
||||
icon={<DeleteOutlined />}
|
||||
>
|
||||
删除区块
|
||||
</Menu.Item>
|
||||
</Menu>
|
||||
}
|
||||
>
|
||||
<MenuOutlined className={'draggable'} ref={dragRef} />
|
||||
</Dropdown>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Block;
|
@ -1,5 +1,7 @@
|
||||
import React, { useRef, useState } from 'react';
|
||||
import React, { cloneElement, useRef, useState } from 'react';
|
||||
import { useMouseEvents } from 'beautiful-react-hooks';
|
||||
import { mergeRefs, useDrop } from './DND';
|
||||
import classNames from 'classnames';
|
||||
|
||||
export function useColResizer(options?: any) {
|
||||
const { onDragStart, onDrag, onDragEnd } = options || {};
|
||||
@ -12,9 +14,15 @@ export function useColResizer(options?: any) {
|
||||
const [initial, setInitial] = useState<any>(null);
|
||||
|
||||
onMouseDown((event: React.MouseEvent) => {
|
||||
setIsDragging(true);
|
||||
if (event.button !== 0) {
|
||||
return;
|
||||
}
|
||||
const prev = dragRef.current.previousElementSibling as HTMLDivElement;
|
||||
const next = dragRef.current.nextElementSibling as HTMLDivElement;
|
||||
if (!prev || !next) {
|
||||
return;
|
||||
}
|
||||
setIsDragging(true);
|
||||
if (!initial) {
|
||||
setInitial({
|
||||
offset: event.clientX,
|
||||
@ -41,7 +49,7 @@ export function useColResizer(options?: any) {
|
||||
setInitial(null);
|
||||
// @ts-ignore
|
||||
event.data = { size };
|
||||
onDragEnd(event);
|
||||
onDragEnd && onDragEnd(event);
|
||||
});
|
||||
|
||||
onMouseMove((event: React.MouseEvent) => {
|
||||
@ -57,31 +65,36 @@ export function useColResizer(options?: any) {
|
||||
// console.log('dragRef.current.nextSibling', prev.style.width);
|
||||
});
|
||||
|
||||
return { dragOffset, dragRef, columns };
|
||||
return { isDragging, dragOffset, dragRef, columns };
|
||||
}
|
||||
|
||||
export const Col: any = (props) => {
|
||||
const { size, children } = props;
|
||||
const { size, children, position = {}, isLast } = props;
|
||||
return (
|
||||
<div
|
||||
className={'col'}
|
||||
style={{ width: `${size * 100}%` }}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
<>
|
||||
<div data-type={'col'} className={'col'} style={{ width: `${size * 100}%` }}>
|
||||
{children}
|
||||
</div>
|
||||
<Col.Divider />
|
||||
</>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
Col.Divider = (props) => {
|
||||
const { onDragEnd } = props;
|
||||
const { dragRef } = useColResizer({ onDragEnd });
|
||||
const { onDragEnd, resizable = true } = props;
|
||||
const { isDragging, dragRef } = useColResizer({ onDragEnd });
|
||||
const { isOver, dropRef } = useDrop({
|
||||
accept: 'grid',
|
||||
data: { },
|
||||
});
|
||||
return (
|
||||
<div
|
||||
className={'col-divider'}
|
||||
style={{ width: '24px', cursor: 'col-resize' }}
|
||||
ref={dragRef}
|
||||
data-type={'col-divider'}
|
||||
className={classNames('col-divider', { hover: isOver, resizable })}
|
||||
style={{ width: '24px' }}
|
||||
ref={mergeRefs(resizable ? [dropRef, dragRef] : [dropRef])}
|
||||
></div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default Col;
|
||||
|
@ -1,15 +1,24 @@
|
||||
import React, { createContext, useContext, useEffect, useRef } from 'react';
|
||||
import { useState } from 'react';
|
||||
import { useMouseEvents } from 'beautiful-react-hooks';
|
||||
import { useMouseEvents, useWillUnmount } from 'beautiful-react-hooks';
|
||||
import { useField } from '@formily/react';
|
||||
|
||||
export const DragDropManagerContext = createContext({ drag: null, drops: {} });
|
||||
export const DragDropManagerContext = createContext<any>({
|
||||
drag: null,
|
||||
drops: {},
|
||||
name: `${Math.random()}`,
|
||||
});
|
||||
|
||||
export function DragDropProvider({ children }) {
|
||||
export function DragDropProvider(props) {
|
||||
const { gridRef, onDrop, children } = props;
|
||||
return (
|
||||
<DragDropManagerContext.Provider
|
||||
value={{
|
||||
drag: null,
|
||||
drops: {},
|
||||
onDrop,
|
||||
gridRef,
|
||||
name: `${Math.random()}`,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
@ -18,11 +27,11 @@ export function DragDropProvider({ children }) {
|
||||
}
|
||||
|
||||
export function mergeRefs<T = any>(
|
||||
refs: Array<React.MutableRefObject<T> | React.LegacyRef<T>>
|
||||
refs: Array<React.MutableRefObject<T> | React.LegacyRef<T>>,
|
||||
): React.RefCallback<T> {
|
||||
return (value) => {
|
||||
refs.forEach((ref) => {
|
||||
if (typeof ref === "function") {
|
||||
if (typeof ref === 'function') {
|
||||
ref(value);
|
||||
} else if (ref != null) {
|
||||
(ref as React.MutableRefObject<T | null>).current = value;
|
||||
@ -32,34 +41,62 @@ export function mergeRefs<T = any>(
|
||||
}
|
||||
|
||||
export function useDrag(options?: any) {
|
||||
const { type, onDragStart, onDrag, onDragEnd } = options;
|
||||
const dragRef = useRef<HTMLButtonElement>();
|
||||
const { type, onDragStart, onDrag, onDragEnd, item = {} } = options;
|
||||
const dragRef = useRef<HTMLDivElement>();
|
||||
const previewRef = useRef<HTMLDivElement>();
|
||||
const [dragOffset, setDragOffset] = useState({ left: 0, top: 0 });
|
||||
const [previewOffset, setPreviewOffset] = useState({ left: 0, top: 0 });
|
||||
const { onMouseDown } = useMouseEvents(dragRef);
|
||||
const { onMouseMove, onMouseUp } = useMouseEvents();
|
||||
const [previewElement, setPreviewElement] = useState<HTMLDivElement>();
|
||||
const [isDragging, setIsDragging] = useState(false);
|
||||
const [mousePostionWithinPreview, setMousePostionWithinPreview] = useState({
|
||||
left: 0,
|
||||
top: 0,
|
||||
});
|
||||
const dragDropManager = useContext(DragDropManagerContext);
|
||||
const field = useField();
|
||||
console.log(
|
||||
'init',
|
||||
{ isDragging, previewElement },
|
||||
field ? field.address.segments : null,
|
||||
);
|
||||
|
||||
useWillUnmount(() => {
|
||||
setIsDragging(false);
|
||||
console.log(
|
||||
'useWillUnmount',
|
||||
{ isDragging, previewElement },
|
||||
dragDropManager.previewElement,
|
||||
field ? field.address.segments : null,
|
||||
);
|
||||
// @ts-ignore
|
||||
window.__previewElement && window.__previewElement.remove();
|
||||
// @ts-ignore
|
||||
window.__previewElement = undefined;
|
||||
|
||||
dragDropManager.drag = null;
|
||||
document.body.style.cursor = null;
|
||||
document.body.style.userSelect = null;
|
||||
})
|
||||
|
||||
onMouseDown((event: React.MouseEvent) => {
|
||||
dragDropManager.drag = { type };
|
||||
if (event.button !== 0) {
|
||||
return;
|
||||
}
|
||||
dragDropManager.drag = { type, item };
|
||||
dragDropManager.drop = { type, item };
|
||||
setIsDragging(true);
|
||||
|
||||
const postion = {
|
||||
left: event.clientX - previewRef.current.getBoundingClientRect().left,
|
||||
top: event.clientY - previewRef.current.getBoundingClientRect().top,
|
||||
};
|
||||
|
||||
setMousePostionWithinPreview(postion);
|
||||
|
||||
const offset = {
|
||||
left: event.clientX - previewRef.current.offsetLeft,
|
||||
top: event.clientY - previewRef.current.offsetTop,
|
||||
left: event.pageX - postion.left,
|
||||
top: event.pageY - postion.top,
|
||||
};
|
||||
setDragOffset(offset);
|
||||
|
||||
const offset2 = {
|
||||
left: event.clientX - offset.left,
|
||||
top: event.clientY - offset.top,
|
||||
};
|
||||
setPreviewOffset(offset2);
|
||||
|
||||
console.log('previewRef.current.clientWidth', previewRef.current.clientWidth);
|
||||
|
||||
const wrap = document.createElement('div');
|
||||
wrap.style.position = 'absolute';
|
||||
@ -69,73 +106,100 @@ export function useDrag(options?: any) {
|
||||
wrap.style.top = `0px`;
|
||||
wrap.style.zIndex = '9999';
|
||||
wrap.style.width = `${previewRef.current.clientWidth}px`;
|
||||
wrap.style.transform = `translate(${offset2.left}px, ${offset2.top}px)`;
|
||||
wrap.style.transform = `translate(${offset.left}px, ${offset.top}px)`;
|
||||
|
||||
setPreviewElement(wrap);
|
||||
// @ts-ignore
|
||||
window.__previewElement = wrap;
|
||||
console.log('dragDropManager.previewElement', dragDropManager.previewElement)
|
||||
document.body.appendChild(wrap);
|
||||
const el = document.createElement('div');
|
||||
wrap.appendChild(el);
|
||||
el.outerHTML = previewRef.current.outerHTML;
|
||||
onDragStart && onDragStart(event);
|
||||
document.body.style.cursor = 'grab';
|
||||
document.body.style.userSelect = 'none';
|
||||
document.body.className = 'dragging';
|
||||
|
||||
console.log('onMouseDown', dragDropManager);
|
||||
console.log(
|
||||
'onMouseDown',
|
||||
{ isDragging, previewElement },
|
||||
dragDropManager.previewElement,
|
||||
field ? field.address.segments : null,
|
||||
);
|
||||
// console.log('onMouseDown', event);
|
||||
});
|
||||
|
||||
onMouseUp((event: React.MouseEvent) => {
|
||||
setIsDragging(false);
|
||||
dragDropManager.drag = null;
|
||||
if (!previewElement) {
|
||||
if (!isDragging || !previewElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
previewElement.remove();
|
||||
document.body.style.cursor = null;
|
||||
console.log(
|
||||
'onMouseUp',
|
||||
{ isDragging, previewElement },
|
||||
field ? field.address.segments : null,
|
||||
);
|
||||
|
||||
if (type) {
|
||||
let dropElement = document.elementFromPoint(event.clientX, event.clientY);
|
||||
const dropIds = [];
|
||||
while (dropElement) {
|
||||
if (!dropElement.getAttribute) {
|
||||
dropElement = dropElement.parentNode as Element;
|
||||
continue;
|
||||
}
|
||||
const dropId = dropElement.getAttribute('data-drop-id');
|
||||
const dropContext = dropId ? dragDropManager.drops[dropId] : null;
|
||||
if (dropContext && dropContext.accept === type) {
|
||||
if (
|
||||
!dropContext.shallow ||
|
||||
(dropContext.shallow && dropIds.length === 0)
|
||||
) {
|
||||
// @ts-ignore
|
||||
event.data = dropContext.data;
|
||||
onDragEnd && onDragEnd(event);
|
||||
dropIds.push(dropId);
|
||||
}
|
||||
}
|
||||
dropElement = dropElement.parentNode as Element;
|
||||
}
|
||||
} else {
|
||||
// @ts-ignore
|
||||
event.dragItem = item;
|
||||
|
||||
setIsDragging(false);
|
||||
dragDropManager.drag = null;
|
||||
previewElement.remove();
|
||||
setPreviewElement(undefined);
|
||||
document.body.style.cursor = null;
|
||||
document.body.style.userSelect = null;
|
||||
// @ts-ignore
|
||||
window.__previewElement && window.__previewElement.remove();
|
||||
// @ts-ignore
|
||||
window.__previewElement = undefined;
|
||||
|
||||
if (!type) {
|
||||
onDragEnd && onDragEnd(event);
|
||||
}
|
||||
|
||||
let dropElement = document.elementFromPoint(event.clientX, event.clientY);
|
||||
const dropIds = [];
|
||||
while (dropElement) {
|
||||
if (!dropElement.getAttribute) {
|
||||
dropElement = dropElement.parentNode as HTMLElement;
|
||||
continue;
|
||||
}
|
||||
const dropId = dropElement.getAttribute('data-drop-id');
|
||||
const dropContext = dropId ? dragDropManager.drops[dropId] : null;
|
||||
if (dropContext && dropContext.accept === type) {
|
||||
if (
|
||||
!dropContext.shallow ||
|
||||
(dropContext.shallow && dropIds.length === 0)
|
||||
) {
|
||||
// @ts-ignore
|
||||
event.data = dropContext.data;
|
||||
onDragEnd && onDragEnd(event);
|
||||
dropIds.push(dropId);
|
||||
}
|
||||
}
|
||||
dropElement = dropElement.parentNode as HTMLElement;
|
||||
}
|
||||
});
|
||||
|
||||
onMouseMove((event: React.MouseEvent) => {
|
||||
if (!isDragging) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!previewElement) {
|
||||
if (!isDragging || !previewElement) {
|
||||
return;
|
||||
}
|
||||
console.log(
|
||||
'onMouseMove',
|
||||
{ isDragging, previewElement },
|
||||
dragDropManager.previewElement,
|
||||
field ? field.address.segments : null,
|
||||
);
|
||||
// console.log({previewElement})
|
||||
|
||||
const offset = {
|
||||
left: event.clientX - dragOffset.left,
|
||||
top: event.clientY - dragOffset.top,
|
||||
left: event.pageX - mousePostionWithinPreview.left,
|
||||
top: event.pageY - mousePostionWithinPreview.top,
|
||||
};
|
||||
|
||||
setPreviewOffset(offset);
|
||||
|
||||
previewElement.style.transform = `translate(${offset.left}px, ${offset.top}px)`;
|
||||
|
||||
if (type) {
|
||||
@ -143,7 +207,7 @@ export function useDrag(options?: any) {
|
||||
const dropIds = [];
|
||||
while (dropElement) {
|
||||
if (!dropElement.getAttribute) {
|
||||
dropElement = dropElement.parentNode as Element;
|
||||
dropElement = dropElement.parentNode as HTMLElement;
|
||||
continue;
|
||||
}
|
||||
const dropId = dropElement.getAttribute('data-drop-id');
|
||||
@ -158,7 +222,7 @@ export function useDrag(options?: any) {
|
||||
// @ts-ignore
|
||||
// event.data = dropContext.data;
|
||||
}
|
||||
dropElement = dropElement.parentNode as Element;
|
||||
dropElement = dropElement.parentNode as HTMLElement;
|
||||
}
|
||||
dragDropManager.drag = { type, dropIds };
|
||||
}
|
||||
@ -166,15 +230,16 @@ export function useDrag(options?: any) {
|
||||
onDrag && onDrag(event);
|
||||
});
|
||||
|
||||
return { isDragging, previewOffset, dragOffset, dragRef, previewRef };
|
||||
return { isDragging, dragRef, previewRef };
|
||||
}
|
||||
|
||||
export function useDrop(options) {
|
||||
const { accept, data, shallow } = options;
|
||||
const { accept, data, shallow, onDrop, onHover, canDrop = true } = options;
|
||||
const dropRef = useRef<HTMLDivElement>();
|
||||
const { onMouseEnter, onMouseLeave, onMouseMove, onMouseUp } =
|
||||
useMouseEvents(dropRef);
|
||||
const [isOver, setIsOver] = useState(false);
|
||||
const [onTopHalf, setOnTopHalf] = useState(null);
|
||||
const [dropId] = useState<string>(`d${Math.random()}`);
|
||||
const dragDropManager = useContext(DragDropManagerContext);
|
||||
|
||||
@ -188,14 +253,20 @@ export function useDrop(options) {
|
||||
}, [accept, data, shallow]);
|
||||
|
||||
onMouseEnter((event) => {
|
||||
console.log({ dragDropManager });
|
||||
if (!canDrop) {
|
||||
return;
|
||||
}
|
||||
// console.log({ dragDropManager });
|
||||
if (!dragDropManager.drag || dragDropManager.drag.type !== accept) {
|
||||
return;
|
||||
}
|
||||
setIsOver(true);
|
||||
});
|
||||
|
||||
onMouseMove(() => {
|
||||
onMouseMove((event: React.MouseEvent) => {
|
||||
if (!canDrop) {
|
||||
return;
|
||||
}
|
||||
if (!dragDropManager.drag || dragDropManager.drag.type !== accept) {
|
||||
return;
|
||||
}
|
||||
@ -203,21 +274,53 @@ export function useDrop(options) {
|
||||
dragDropManager.drag.dropIds &&
|
||||
dragDropManager.drag.dropIds.includes(dropId)
|
||||
) {
|
||||
const top = event.clientY - dropRef.current.getBoundingClientRect().top;
|
||||
const onTop = top < dropRef.current.clientHeight / 2;
|
||||
setOnTopHalf(onTop);
|
||||
// @ts-ignore
|
||||
event.onTopHalf = onTop;
|
||||
setIsOver(true);
|
||||
onHover && onHover(event);
|
||||
} else {
|
||||
setIsOver(false);
|
||||
}
|
||||
});
|
||||
|
||||
onMouseUp((event) => {
|
||||
onMouseUp((event: React.MouseEvent) => {
|
||||
if (!canDrop) {
|
||||
return;
|
||||
}
|
||||
if (event.button !== 0) {
|
||||
return;
|
||||
}
|
||||
if (isOver) {
|
||||
const top = event.clientY - dropRef.current.getBoundingClientRect().top;
|
||||
const onTop = top < dropRef.current.clientHeight / 2;
|
||||
setOnTopHalf(onTop);
|
||||
// @ts-ignore
|
||||
event.onTopHalf = onTop;
|
||||
// @ts-ignore
|
||||
event.data = data;
|
||||
// @ts-ignore
|
||||
event.dragItem = dragDropManager.drop.item;
|
||||
// @ts-ignore
|
||||
event.dropElement = dropRef.current;
|
||||
onDrop && onDrop(event);
|
||||
dragDropManager.onDrop && dragDropManager.onDrop(event);
|
||||
dragDropManager.drop = null;
|
||||
}
|
||||
setIsOver(false);
|
||||
});
|
||||
|
||||
onMouseLeave(() => {
|
||||
if (!canDrop) {
|
||||
return;
|
||||
}
|
||||
setIsOver(false);
|
||||
});
|
||||
|
||||
return {
|
||||
onTopHalf: isOver ? onTopHalf : null,
|
||||
isOver,
|
||||
dropRef,
|
||||
};
|
||||
|
@ -1,150 +0,0 @@
|
||||
import React, { useContext } from 'react';
|
||||
import { DndProvider, useDrag, useDrop } from 'react-dnd';
|
||||
import classNames from 'classnames';
|
||||
import { useSchema } from '../../fields';
|
||||
import { AcceptContext } from './Grid';
|
||||
import { useField } from '@formily/react';
|
||||
|
||||
export function DropFirstRow() {
|
||||
const field = useField();
|
||||
const { schema } = useSchema();
|
||||
const accept = useContext(AcceptContext);
|
||||
const [{ canDrop, isOver }, drop] = useDrop(
|
||||
() => ({
|
||||
accept,
|
||||
drop: () => ({
|
||||
gridType: 'first-row',
|
||||
schema,
|
||||
segments: field.address.segments,
|
||||
}),
|
||||
collect: (monitor) => ({
|
||||
isOver: monitor.isOver(),
|
||||
canDrop: monitor.canDrop(),
|
||||
}),
|
||||
}),
|
||||
[schema],
|
||||
);
|
||||
|
||||
const active = canDrop && isOver;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames('drop-row', 'first', { active })}
|
||||
ref={drop}
|
||||
></div>
|
||||
);
|
||||
}
|
||||
|
||||
export function DropRow() {
|
||||
const { schema } = useSchema();
|
||||
const field = useField();
|
||||
const accept = useContext(AcceptContext);
|
||||
const [{ canDrop, isOver }, drop] = useDrop(
|
||||
() => ({
|
||||
accept,
|
||||
drop: () => ({
|
||||
gridType: 'row',
|
||||
schema,
|
||||
segments: field.address.segments,
|
||||
}),
|
||||
collect: (monitor) => ({
|
||||
isOver: monitor.isOver(),
|
||||
canDrop: monitor.canDrop(),
|
||||
}),
|
||||
}),
|
||||
[schema],
|
||||
);
|
||||
|
||||
const active = canDrop && isOver;
|
||||
|
||||
return <div className={classNames('drop-row', { active })} ref={drop}></div>;
|
||||
}
|
||||
|
||||
export function DropColumn() {
|
||||
const field = useField();
|
||||
const { schema } = useSchema();
|
||||
const accept = useContext(AcceptContext);
|
||||
const [{ canDrop, isOver }, drop] = useDrop(
|
||||
() => ({
|
||||
accept,
|
||||
drop: () => ({
|
||||
gridType: 'column',
|
||||
schema,
|
||||
segments: field.address.segments,
|
||||
}),
|
||||
collect: (monitor) => ({
|
||||
isOver: monitor.isOver(),
|
||||
canDrop: monitor.canDrop(),
|
||||
}),
|
||||
}),
|
||||
[schema],
|
||||
);
|
||||
|
||||
const active = canDrop && isOver;
|
||||
|
||||
return (
|
||||
<div className={classNames('drop-column', { active })} ref={drop}></div>
|
||||
);
|
||||
}
|
||||
|
||||
export function DropLastColumn() {
|
||||
const field = useField();
|
||||
const { schema } = useSchema();
|
||||
const accept = useContext(AcceptContext);
|
||||
const [{ canDrop, isOver }, drop] = useDrop(
|
||||
() => ({
|
||||
accept,
|
||||
drop: () => ({
|
||||
gridType: 'last-column',
|
||||
schema,
|
||||
segments: field.address.segments,
|
||||
}),
|
||||
collect: (monitor) => ({
|
||||
isOver: monitor.isOver(),
|
||||
canDrop: monitor.canDrop(),
|
||||
}),
|
||||
}),
|
||||
[schema],
|
||||
);
|
||||
|
||||
const active = canDrop && isOver;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames('drop-column', 'last', { active })}
|
||||
ref={drop}
|
||||
></div>
|
||||
);
|
||||
}
|
||||
|
||||
export function DropBlock({ canDrop }) {
|
||||
const { schema } = useSchema();
|
||||
const accept = useContext(AcceptContext);
|
||||
const [{ isOver }, drop] = useDrop(
|
||||
() => ({
|
||||
accept,
|
||||
drop: () => ({ schema }),
|
||||
collect: (monitor) => ({
|
||||
isOver: monitor.isOver(),
|
||||
canDrop: monitor.canDrop(),
|
||||
}),
|
||||
canDrop: () => canDrop,
|
||||
}),
|
||||
[canDrop],
|
||||
);
|
||||
|
||||
console.log({ canDrop });
|
||||
|
||||
const active = canDrop && isOver;
|
||||
|
||||
return (
|
||||
<div className={classNames('drop-block', { active })} ref={drop}></div>
|
||||
);
|
||||
}
|
||||
|
||||
export default {
|
||||
DropRow,
|
||||
DropColumn,
|
||||
DropLastColumn,
|
||||
DropBlock,
|
||||
};
|
@ -1,319 +1,88 @@
|
||||
import React, { createContext, useContext, useState } from 'react';
|
||||
import { Row, Col, Dropdown, Menu } from 'antd';
|
||||
import { DndProvider, useDrag, useDrop } from 'react-dnd';
|
||||
import { HTML5Backend } from 'react-dnd-html5-backend';
|
||||
import {
|
||||
DropFirstRow,
|
||||
DropRow,
|
||||
DropColumn,
|
||||
DropBlock,
|
||||
DropLastColumn,
|
||||
} from './Drop';
|
||||
import {
|
||||
CloseOutlined,
|
||||
DeleteOutlined,
|
||||
MenuOutlined,
|
||||
PlusOutlined,
|
||||
ArrowDownOutlined,
|
||||
ArrowUpOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import {
|
||||
getFullPaths,
|
||||
SchemaDesignerContext,
|
||||
useSchemaQuery,
|
||||
useSchema,
|
||||
} from '../../fields';
|
||||
import { TouchBackend } from 'react-dnd-touch-backend';
|
||||
import { usePreview } from 'react-dnd-preview';
|
||||
import classNames from 'classnames';
|
||||
import './style.less';
|
||||
import { useField, useFieldSchema } from '@formily/react';
|
||||
import React, { cloneElement, useRef } from 'react';
|
||||
import { Col } from './Col';
|
||||
import { Row } from './Row';
|
||||
import { Block } from './Block';
|
||||
import { DragDropProvider } from './DND';
|
||||
import { useSchemaQuery } from './';
|
||||
|
||||
const Preview = () => {
|
||||
const accept = useContext(AcceptContext);
|
||||
const { item, style, display, itemType } = usePreview();
|
||||
if (itemType !== accept) {
|
||||
return null;
|
||||
}
|
||||
if (!display) {
|
||||
return null;
|
||||
}
|
||||
if (!item.ref) {
|
||||
return null;
|
||||
}
|
||||
if (!item.ref.current) {
|
||||
return null;
|
||||
}
|
||||
const el = item.ref.current as HTMLDivElement;
|
||||
console.log({ itemType });
|
||||
type Event = React.MouseEvent & {
|
||||
dropElement: HTMLElement;
|
||||
onTopHalf?: boolean;
|
||||
dragItem?: any;
|
||||
};
|
||||
|
||||
export const Grid = (props) => {
|
||||
const { children, onDrop } = props;
|
||||
const ref = useRef();
|
||||
const { schema, moveTo, refresh } = useSchemaQuery();
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
...style,
|
||||
height: el.clientHeight,
|
||||
width: el.clientWidth,
|
||||
zIndex: 9999,
|
||||
opacity: 0.8,
|
||||
// left: `-${el.clientWidth}px`,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
transform: 'translate(-90%, -5%)',
|
||||
}}
|
||||
dangerouslySetInnerHTML={{ __html: el.outerHTML }}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export interface GridPorps {
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
export interface GridRowPorps {
|
||||
children?: React.ReactNode;
|
||||
rowOrder?: number;
|
||||
}
|
||||
|
||||
export interface GridColumnPorps {
|
||||
children?: React.ReactNode;
|
||||
span?: any;
|
||||
}
|
||||
|
||||
export interface GridBlockProps {
|
||||
children?: React.ReactNode;
|
||||
lastComponentType?: string;
|
||||
}
|
||||
|
||||
export type GridComponent = React.FC<GridPorps> & {
|
||||
Row?: React.FC<GridRowPorps>;
|
||||
Column?: React.FC<GridColumnPorps>;
|
||||
Block?: React.FC<GridBlockProps>;
|
||||
};
|
||||
|
||||
export const AcceptContext = createContext(null);
|
||||
|
||||
export const Grid: GridComponent = (props) => {
|
||||
const { children } = props;
|
||||
const schema = useFieldSchema();
|
||||
const field = useField();
|
||||
console.log({ accept: schema.name, schema: schema.toJSON() });
|
||||
return (
|
||||
<div className={'grid'} style={{ marginTop: 24 }}>
|
||||
<AcceptContext.Provider value={schema.name}>
|
||||
<DndProvider
|
||||
backend={TouchBackend}
|
||||
options={{ enableMouseEvents: true }}
|
||||
>
|
||||
{children}
|
||||
<Preview />
|
||||
</DndProvider>
|
||||
</AcceptContext.Provider>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Grid.Row = (props) => {
|
||||
const { children, rowOrder } = props;
|
||||
const { schema } = useSchema();
|
||||
const field = useField();
|
||||
const accept = useContext(AcceptContext);
|
||||
console.log({ accept });
|
||||
const [{ canDrop, isOverCurrent }, drop] = useDrop(() => ({
|
||||
accept,
|
||||
drop: (item, monitor) => {
|
||||
const didDrop = monitor.didDrop();
|
||||
if (didDrop) {
|
||||
return;
|
||||
}
|
||||
return { gridType: 'row', schema, segments: field.address.segments };
|
||||
},
|
||||
collect: (monitor) => ({
|
||||
isOver: monitor.isOver(),
|
||||
canDrop: monitor.canDrop(),
|
||||
isOverCurrent: monitor.isOver({ shallow: true }),
|
||||
}),
|
||||
}));
|
||||
|
||||
const active = canDrop && isOverCurrent;
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={drop}
|
||||
className={classNames('grid-row', `grid-row-order-${rowOrder}`, {
|
||||
active,
|
||||
})}
|
||||
>
|
||||
{rowOrder === 0 && <DropFirstRow />}
|
||||
<Row gutter={24}>{children}</Row>
|
||||
<DropLastColumn />
|
||||
{/* <DropRow /> */}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Grid.Column = (props) => {
|
||||
const { children, span } = props;
|
||||
return (
|
||||
<Col span={span}>
|
||||
<DropColumn />
|
||||
{children}
|
||||
</Col>
|
||||
);
|
||||
};
|
||||
|
||||
interface DropResult {
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
Grid.Block = (props) => {
|
||||
const { children, lastComponentType } = props;
|
||||
const { schema, refresh } = useSchema();
|
||||
const fieldSchema = useFieldSchema();
|
||||
const field = useField();
|
||||
const accept = useContext(AcceptContext);
|
||||
const context = useContext(SchemaDesignerContext);
|
||||
const {
|
||||
insertAfter,
|
||||
insertAfterWithAddRow,
|
||||
insertBeforeWithAddRow,
|
||||
insertBeforeWithAddColumn,
|
||||
appendToRowWithAddColumn,
|
||||
} = useSchemaQuery();
|
||||
const ref = React.useRef();
|
||||
console.log({ accept });
|
||||
const [{ opacity, isDragging }, drag, preview] = useDrag(
|
||||
() => ({
|
||||
type: accept,
|
||||
item: {
|
||||
ref,
|
||||
preview,
|
||||
schema,
|
||||
},
|
||||
collect: (monitor) => ({
|
||||
isDragging: monitor.isDragging(),
|
||||
opacity: monitor.isDragging() ? 0.9 : 1,
|
||||
}),
|
||||
end: (item, monitor) => {
|
||||
const dropResult = monitor.getDropResult<DropResult>();
|
||||
if (item && dropResult) {
|
||||
if (dropResult.gridType === 'block') {
|
||||
insertAfter(field.address.segments, dropResult.segments);
|
||||
} else if (dropResult.gridType === 'row') {
|
||||
insertAfterWithAddRow(field.address.segments, dropResult.segments);
|
||||
} else if (dropResult.gridType === 'column') {
|
||||
insertBeforeWithAddColumn(
|
||||
field.address.segments,
|
||||
dropResult.segments,
|
||||
);
|
||||
} else if (dropResult.gridType === 'last-column') {
|
||||
appendToRowWithAddColumn(
|
||||
field.address.segments,
|
||||
dropResult.segments,
|
||||
);
|
||||
} else if (dropResult.gridType === 'first-row') {
|
||||
insertBeforeWithAddRow(
|
||||
field.address.segments,
|
||||
dropResult.segments,
|
||||
<div className={'grid'}>
|
||||
<DragDropProvider
|
||||
gridRef={ref}
|
||||
onDrop={(event: Event) => {
|
||||
const el = event.dropElement;
|
||||
const type = el.getAttribute('data-type');
|
||||
const getIndex = (el) => {
|
||||
const type = el.getAttribute('data-type');
|
||||
return Array.prototype.indexOf.call(
|
||||
el.parentNode.querySelectorAll(`.${type}`),
|
||||
el,
|
||||
);
|
||||
};
|
||||
let position: any = { type };
|
||||
if (type === 'row') {
|
||||
// position.rowIndex = getIndex(el);
|
||||
position = {
|
||||
type: 'row-divider',
|
||||
rowDividerIndex: getIndex(
|
||||
event.onTopHalf ? el.previousSibling : el.nextSibling,
|
||||
),
|
||||
};
|
||||
}
|
||||
refresh();
|
||||
}
|
||||
},
|
||||
}),
|
||||
[field, schema],
|
||||
);
|
||||
|
||||
const segments = field.address.segments;
|
||||
|
||||
const [{ canDrop, isOver }, drop] = useDrop(
|
||||
() => ({
|
||||
accept,
|
||||
drop: () => {
|
||||
console.log('source.segments', segments);
|
||||
return { gridType: 'block', segments, schema };
|
||||
},
|
||||
collect: (monitor) => ({
|
||||
isOver: monitor.isOver(),
|
||||
canDrop: monitor.canDrop(),
|
||||
}),
|
||||
// hover: (item, monitor) => {
|
||||
// console.log(monitor.getSourceClientOffset());
|
||||
// },
|
||||
canDrop: () => !isDragging,
|
||||
}),
|
||||
[isDragging, schema],
|
||||
);
|
||||
|
||||
const active = canDrop && isOver;
|
||||
|
||||
drop(ref);
|
||||
|
||||
console.log({ isDragging });
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
style={{ opacity }}
|
||||
className={classNames(`grid-block`, `grid-block--${lastComponentType}`, {
|
||||
active,
|
||||
})}
|
||||
>
|
||||
<ActionBar dragRef={drag} />
|
||||
{children}
|
||||
{/* <DropBlock canDrop={!isDragging} /> */}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const ActionBar = ({ dragRef }) => {
|
||||
const { addBlock, removeBlock } = useSchemaQuery();
|
||||
const [active, setActive] = useState(false);
|
||||
return (
|
||||
<div className={classNames('action-bar', { active })}>
|
||||
<Dropdown
|
||||
overlayStyle={{ minWidth: 200 }}
|
||||
trigger={['click']}
|
||||
visible={active}
|
||||
onVisibleChange={setActive}
|
||||
overlay={
|
||||
<Menu>
|
||||
<Menu.Item
|
||||
onClick={() => {
|
||||
addBlock({}, true);
|
||||
setActive(false);
|
||||
}}
|
||||
icon={<ArrowUpOutlined />}
|
||||
>
|
||||
在上方插入区块
|
||||
</Menu.Item>
|
||||
<Menu.Item
|
||||
onClick={() => {
|
||||
addBlock({});
|
||||
setActive(false);
|
||||
}}
|
||||
icon={<ArrowDownOutlined />}
|
||||
>
|
||||
在下方插入区块
|
||||
</Menu.Item>
|
||||
<Menu.Divider />
|
||||
<Menu.Item
|
||||
onClick={() => {
|
||||
removeBlock();
|
||||
setActive(false);
|
||||
}}
|
||||
icon={<DeleteOutlined />}
|
||||
>
|
||||
删除区块
|
||||
</Menu.Item>
|
||||
</Menu>
|
||||
}
|
||||
if (type === 'row-divider') {
|
||||
position.rowDividerIndex = getIndex(el);
|
||||
}
|
||||
if (type === 'col-divider') {
|
||||
position.colDividerIndex = getIndex(el);
|
||||
position.rowIndex = getIndex(el.parentNode);
|
||||
}
|
||||
if (type === 'block') {
|
||||
const rowNode = el.parentNode.parentNode;
|
||||
position.blockIndex = getIndex(el);
|
||||
position.colIndex = getIndex(el.parentNode);
|
||||
position.rowIndex = getIndex(rowNode);
|
||||
const colsize = rowNode.querySelectorAll('.col').length;
|
||||
if (colsize === 1) {
|
||||
position = {
|
||||
type: 'row-divider',
|
||||
rowDividerIndex: getIndex(
|
||||
event.onTopHalf
|
||||
? rowNode.previousSibling
|
||||
: rowNode.nextSibling,
|
||||
),
|
||||
};
|
||||
} else {
|
||||
position.type = 'block-divider';
|
||||
position.blockDividerIndex = getIndex(el);
|
||||
if (!event.onTopHalf) {
|
||||
position.blockDividerIndex += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
onDrop && onDrop(event);
|
||||
moveTo(event.dragItem.path, position);
|
||||
console.log('onDrop', position, event.dragItem);
|
||||
}}
|
||||
>
|
||||
<MenuOutlined className={'draggable'} ref={dragRef} />
|
||||
</Dropdown>
|
||||
<Row.Divider style={{ marginTop: -24 }} />
|
||||
{children}
|
||||
</DragDropProvider>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Grid.Row = Row;
|
||||
Grid.Col = Col;
|
||||
Grid.Block = Block;
|
||||
|
||||
export default Grid;
|
||||
|
@ -1,21 +1,46 @@
|
||||
import React from 'react';
|
||||
import React, { cloneElement, useRef } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { Col } from './Col';
|
||||
import { DragDropProvider, useDrop } from './DND';
|
||||
import { useField } from '@formily/react';
|
||||
|
||||
export const Row = (props) => {
|
||||
const { children, onColResize } = props;
|
||||
const len = children.length;
|
||||
const { children, onColResize, position = {}, isLast } = props;
|
||||
const { isOver, onTopHalf, dropRef } = useDrop({
|
||||
accept: 'grid',
|
||||
data: {},
|
||||
shallow: true,
|
||||
});
|
||||
return (
|
||||
<div style={{ display: 'flex' }}>
|
||||
{children.map((child, index) => {
|
||||
return (
|
||||
<>
|
||||
{child}
|
||||
{len > index + 1 && <Col.Divider onDragEnd={onColResize} />}
|
||||
</>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<>
|
||||
<div
|
||||
data-type={'row'}
|
||||
ref={dropRef}
|
||||
className={classNames('row', { hover: isOver, 'top-half': onTopHalf })}
|
||||
style={{ margin: '0 -24px', display: 'flex' }}
|
||||
>
|
||||
<Col.Divider resizable={false} />
|
||||
{children}
|
||||
</div>
|
||||
<Row.Divider />
|
||||
</>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
Row.Divider = (props) => {
|
||||
const { style = {}, position } = props;
|
||||
const { isOver, dropRef } = useDrop({
|
||||
accept: 'grid',
|
||||
data: { position },
|
||||
});
|
||||
return (
|
||||
<div
|
||||
data-type={'row-divider'}
|
||||
ref={dropRef}
|
||||
className={classNames('row-divider', { hover: isOver })}
|
||||
style={{ ...style, height: '24px' }}
|
||||
></div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Row;
|
||||
|
@ -1,133 +0,0 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import { FormProvider, FormConsumer, useField, useFieldSchema } from '@formily/react';
|
||||
import { createForm } from '@formily/core';
|
||||
import {
|
||||
SchemaFieldWithDesigner,
|
||||
registerFieldComponents,
|
||||
useSchema,
|
||||
} from '../../../fields';
|
||||
import { grid, row, column, block } from '../utils';
|
||||
import { blocks2properties } from '../utils';
|
||||
|
||||
function Designer(props) {
|
||||
const form = useMemo(() => createForm({}), []);
|
||||
const { schema } = props;
|
||||
return (
|
||||
<div>
|
||||
<FormProvider form={form}>
|
||||
<SchemaFieldWithDesigner schema={schema} />
|
||||
{/* <FormConsumer>
|
||||
{(form) => {
|
||||
return <div>{JSON.stringify(form.values, null, 2)}</div>;
|
||||
}}
|
||||
</FormConsumer> */}
|
||||
</FormProvider>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Hello(props) {
|
||||
const schema = useFieldSchema();
|
||||
return (
|
||||
<div style={{ marginBottom: 24, padding: '1rem', background: '#f9f9f9', minHeight: 50, lineHeight: '50px' }}>
|
||||
Hello {schema.title}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
registerFieldComponents({ Hello });
|
||||
const blocks = [
|
||||
{
|
||||
type: 'string',
|
||||
title: `Block 1`,
|
||||
required: true,
|
||||
'x-read-pretty': false,
|
||||
// 'x-decorator': 'FormItem',
|
||||
'x-component': 'Hello',
|
||||
rowOrder: 1,
|
||||
columnOrder: 1,
|
||||
blockOrder: 1,
|
||||
},
|
||||
{
|
||||
type: 'string',
|
||||
title: `Block 2`,
|
||||
required: true,
|
||||
'x-read-pretty': false,
|
||||
// 'x-decorator': 'FormItem',
|
||||
'x-component': 'Hello',
|
||||
rowOrder: 1,
|
||||
columnOrder: 2,
|
||||
blockOrder: 1,
|
||||
},
|
||||
{
|
||||
type: 'string',
|
||||
title: `Block 3`,
|
||||
required: true,
|
||||
'x-read-pretty': false,
|
||||
// 'x-decorator': 'FormItem',
|
||||
'x-component': 'Hello',
|
||||
rowOrder: 2,
|
||||
columnOrder: 1,
|
||||
blockOrder: 1,
|
||||
},
|
||||
{
|
||||
type: 'string',
|
||||
title: `Block 4`,
|
||||
required: true,
|
||||
'x-read-pretty': false,
|
||||
// 'x-decorator': 'FormItem',
|
||||
'x-component': 'Hello',
|
||||
rowOrder: 1,
|
||||
columnOrder: 1,
|
||||
blockOrder: 2,
|
||||
},
|
||||
{
|
||||
type: 'string',
|
||||
title: `Block 5`,
|
||||
required: true,
|
||||
'x-read-pretty': false,
|
||||
// 'x-decorator': 'FormItem',
|
||||
'x-component': 'Hello',
|
||||
rowOrder: 3,
|
||||
columnOrder: 1,
|
||||
blockOrder: 1,
|
||||
},
|
||||
{
|
||||
type: 'string',
|
||||
title: `Block 6`,
|
||||
required: true,
|
||||
'x-read-pretty': false,
|
||||
// 'x-decorator': 'FormItem',
|
||||
'x-component': 'Hello',
|
||||
rowOrder: 3,
|
||||
columnOrder: 2,
|
||||
blockOrder: 1,
|
||||
},
|
||||
];
|
||||
const schema = blocks2properties(blocks);
|
||||
|
||||
export default () => {
|
||||
console.log({schema});
|
||||
return (
|
||||
<div>
|
||||
<Designer
|
||||
schema={{
|
||||
type: 'object',
|
||||
properties: {
|
||||
layout: {
|
||||
type: 'void',
|
||||
'x-component': 'FormLayout',
|
||||
'x-component-props': {
|
||||
layout: 'vertical',
|
||||
},
|
||||
properties: {
|
||||
[schema.name]: schema,
|
||||
},
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
{/* <pre>{JSON.stringify(schema, null, 2)}</pre> */}
|
||||
</div>
|
||||
);
|
||||
};
|
@ -1,200 +0,0 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import { FormProvider, FormConsumer, useField, useFieldSchema } from '@formily/react';
|
||||
import { createForm } from '@formily/core';
|
||||
import {
|
||||
SchemaFieldWithDesigner,
|
||||
registerFieldComponents,
|
||||
} from '../../../fields';
|
||||
import { grid, row, column, block } from '../utils';
|
||||
import { blocks2properties } from '../utils';
|
||||
import { Card } from 'antd';
|
||||
|
||||
function Designer(props) {
|
||||
const form = useMemo(() => createForm({}), []);
|
||||
const { schema } = props;
|
||||
return (
|
||||
<div>
|
||||
<FormProvider form={form}>
|
||||
<SchemaFieldWithDesigner schema={schema} />
|
||||
{/* <FormConsumer>
|
||||
{(form) => {
|
||||
return <div>{JSON.stringify(form.values, null, 2)}</div>;
|
||||
}}
|
||||
</FormConsumer> */}
|
||||
</FormProvider>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Hello(props) {
|
||||
const schema = useFieldSchema();
|
||||
return (
|
||||
<div style={{ marginBottom: 24, padding: '1rem', background: '#f9f9f9', minHeight: 50, lineHeight: '50px' }}>
|
||||
Hello {schema.title}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
registerFieldComponents({ Hello, Designer, Card });
|
||||
|
||||
const nested = blocks2properties([
|
||||
{
|
||||
type: 'string',
|
||||
title: `Nested Block 1`,
|
||||
required: true,
|
||||
'x-read-pretty': false,
|
||||
// 'x-decorator': 'FormItem',
|
||||
'x-component': 'Hello',
|
||||
rowOrder: 1,
|
||||
columnOrder: 1,
|
||||
blockOrder: 1,
|
||||
},
|
||||
{
|
||||
type: 'string',
|
||||
title: `Nested Block 2`,
|
||||
required: true,
|
||||
'x-read-pretty': false,
|
||||
// 'x-decorator': 'FormItem',
|
||||
'x-component': 'Hello',
|
||||
rowOrder: 1,
|
||||
columnOrder: 2,
|
||||
blockOrder: 1,
|
||||
},
|
||||
{
|
||||
type: 'string',
|
||||
title: `Nested Block 3`,
|
||||
required: true,
|
||||
'x-read-pretty': false,
|
||||
// 'x-decorator': 'FormItem',
|
||||
'x-component': 'Hello',
|
||||
rowOrder: 2,
|
||||
columnOrder: 1,
|
||||
blockOrder: 1,
|
||||
},
|
||||
]);
|
||||
|
||||
const blocks = [
|
||||
{
|
||||
type: 'string',
|
||||
title: `Block 1`,
|
||||
required: true,
|
||||
'x-read-pretty': false,
|
||||
// 'x-decorator': 'FormItem',
|
||||
'x-component': 'Hello',
|
||||
rowOrder: 1,
|
||||
columnOrder: 1,
|
||||
blockOrder: 1,
|
||||
},
|
||||
{
|
||||
type: 'string',
|
||||
title: `Block 2`,
|
||||
required: true,
|
||||
'x-read-pretty': false,
|
||||
// 'x-decorator': 'FormItem',
|
||||
'x-component': 'Hello',
|
||||
rowOrder: 1,
|
||||
columnOrder: 2,
|
||||
blockOrder: 1,
|
||||
},
|
||||
{
|
||||
type: 'string',
|
||||
title: `Block 3`,
|
||||
required: true,
|
||||
'x-read-pretty': false,
|
||||
// 'x-decorator': 'FormItem',
|
||||
'x-component': 'Hello',
|
||||
rowOrder: 2,
|
||||
columnOrder: 1,
|
||||
blockOrder: 1,
|
||||
},
|
||||
{
|
||||
type: 'string',
|
||||
title: `Block 4`,
|
||||
required: true,
|
||||
'x-read-pretty': false,
|
||||
// 'x-decorator': 'FormItem',
|
||||
'x-component': 'Hello',
|
||||
rowOrder: 1,
|
||||
columnOrder: 1,
|
||||
blockOrder: 2,
|
||||
},
|
||||
{
|
||||
type: 'string',
|
||||
title: `Block 5`,
|
||||
required: true,
|
||||
'x-read-pretty': false,
|
||||
// 'x-decorator': 'FormItem',
|
||||
'x-component': 'Hello',
|
||||
rowOrder: 3,
|
||||
columnOrder: 1,
|
||||
blockOrder: 1,
|
||||
},
|
||||
{
|
||||
type: 'string',
|
||||
title: `Block 6`,
|
||||
required: true,
|
||||
'x-read-pretty': false,
|
||||
// 'x-decorator': 'FormItem',
|
||||
'x-component': 'Hello',
|
||||
rowOrder: 3,
|
||||
columnOrder: 2,
|
||||
blockOrder: 1,
|
||||
},
|
||||
{
|
||||
type: 'string',
|
||||
title: `Block 7`,
|
||||
required: true,
|
||||
'x-read-pretty': false,
|
||||
'x-decorator': 'Card',
|
||||
'x-decorator-props': {
|
||||
title: '内嵌区块(只能在当前区域内部拖拽)',
|
||||
},
|
||||
'x-component': 'Designer',
|
||||
'x-component-props': {
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
layout: {
|
||||
type: 'void',
|
||||
'x-component': 'FormLayout',
|
||||
'x-component-props': {
|
||||
layout: 'vertical',
|
||||
},
|
||||
properties: {
|
||||
[nested.name]: nested,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
rowOrder: 4,
|
||||
columnOrder: 1,
|
||||
blockOrder: 1,
|
||||
},
|
||||
];
|
||||
const schema = blocks2properties(blocks);
|
||||
export default () => {
|
||||
console.log({schema});
|
||||
return (
|
||||
<div>
|
||||
<Designer
|
||||
schema={{
|
||||
type: 'object',
|
||||
properties: {
|
||||
layout: {
|
||||
type: 'void',
|
||||
'x-component': 'FormLayout',
|
||||
'x-component-props': {
|
||||
layout: 'vertical',
|
||||
},
|
||||
properties: {
|
||||
[schema.name]: schema,
|
||||
},
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
{/* <pre>{JSON.stringify(schema, null, 2)}</pre> */}
|
||||
</div>
|
||||
);
|
||||
};
|
@ -1,12 +1,12 @@
|
||||
import React, { createContext, useContext, useEffect, useRef } from 'react';
|
||||
import { useDrag, useDrop, DragDropProvider } from '../';
|
||||
import { useDrag, useDrop, DragDropProvider, mergeRefs } from '../';
|
||||
import { Button, Space } from 'antd';
|
||||
|
||||
function DropZone({ options, children }) {
|
||||
const { isOver, dropRef } = useDrop(options);
|
||||
return (
|
||||
<div
|
||||
ref={dropRef}
|
||||
ref={dropRef as any}
|
||||
style={{
|
||||
textAlign: 'center',
|
||||
lineHeight: '100px',
|
||||
@ -32,23 +32,7 @@ function Dragable() {
|
||||
// console.log('onDrag');
|
||||
},
|
||||
});
|
||||
return (
|
||||
<Button ref={mergeRefs<any>([dragRef, previewRef])}>拖拽1</Button>
|
||||
);
|
||||
}
|
||||
|
||||
function mergeRefs<T = any>(
|
||||
refs: Array<React.MutableRefObject<T> | React.LegacyRef<T>>
|
||||
): React.RefCallback<T> {
|
||||
return (value) => {
|
||||
refs.forEach((ref) => {
|
||||
if (typeof ref === "function") {
|
||||
ref(value);
|
||||
} else if (ref != null) {
|
||||
(ref as React.MutableRefObject<T | null>).current = value;
|
||||
}
|
||||
});
|
||||
};
|
||||
return <Button ref={mergeRefs<any>([dragRef, previewRef])}>拖拽1</Button>;
|
||||
}
|
||||
|
||||
function Dragable2() {
|
||||
@ -64,17 +48,15 @@ function Dragable2() {
|
||||
// console.log('onDrag');
|
||||
},
|
||||
});
|
||||
return (
|
||||
<Button ref={mergeRefs<any>([dragRef, previewRef])}>拖拽2</Button>
|
||||
);
|
||||
return <Button ref={mergeRefs<any>([dragRef, previewRef])}>拖拽2</Button>;
|
||||
}
|
||||
|
||||
export default () => {
|
||||
return (
|
||||
<DragDropProvider>
|
||||
<Space style={{marginBottom: 12}}>
|
||||
<Dragable />
|
||||
<Dragable2 />
|
||||
<Space style={{ marginBottom: 12 }}>
|
||||
<Dragable />
|
||||
<Dragable2 />
|
||||
</Space>
|
||||
<DropZone
|
||||
options={{
|
||||
|
@ -1,6 +1,26 @@
|
||||
.col-divider {
|
||||
position: relative;
|
||||
&:hover {
|
||||
&:last-child {
|
||||
&.resizable:hover {
|
||||
cursor: auto;
|
||||
&::after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
&.resizable.hover {
|
||||
&::after {
|
||||
display: block !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
&.resizable {
|
||||
cursor: col-resize;
|
||||
}
|
||||
&.hover {
|
||||
cursor: grab !important;
|
||||
}
|
||||
&.resizable:hover,
|
||||
&.hover {
|
||||
&::after {
|
||||
content: '';
|
||||
display: block;
|
||||
@ -14,3 +34,80 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.row-divider {
|
||||
position: relative;
|
||||
&.hover {
|
||||
&::after {
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
height: 12px;
|
||||
width: 100%;
|
||||
background: #e6f7ff;
|
||||
}
|
||||
}
|
||||
&:last-child {
|
||||
margin-bottom: -24px;
|
||||
}
|
||||
}
|
||||
|
||||
.row {
|
||||
position: relative;
|
||||
&.hover {
|
||||
&::after {
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: 24px;
|
||||
bottom: -18px;
|
||||
height: 12px;
|
||||
right: 24px;
|
||||
background: #e6f7ff;
|
||||
}
|
||||
&.top-half {
|
||||
&::after {
|
||||
top: -18px;
|
||||
bottom: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.block {
|
||||
position: relative;
|
||||
margin-bottom: 24px;
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
&.hover {
|
||||
&::after {
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: -18px;
|
||||
height: 12px;
|
||||
width: 100%;
|
||||
background: #e6f7ff;
|
||||
}
|
||||
&.top-half {
|
||||
&::after {
|
||||
top: -18px;
|
||||
bottom: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
> .action-bar {
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
right: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.draggable {
|
||||
cursor: grab !important;
|
||||
}
|
208
packages/client/src/blocks/grid/demos/demo6.tsx
Normal file
208
packages/client/src/blocks/grid/demos/demo6.tsx
Normal file
@ -0,0 +1,208 @@
|
||||
import { Grid, Row, Col, Block, SchemaFieldWithDesigner } from '../';
|
||||
import './demo5.less';
|
||||
import { Card } from 'antd';
|
||||
import classNames from 'classnames';
|
||||
import React, { useMemo } from 'react';
|
||||
import {
|
||||
FormProvider,
|
||||
FormConsumer,
|
||||
useField,
|
||||
useFieldSchema,
|
||||
ISchema,
|
||||
Schema,
|
||||
} from '@formily/react';
|
||||
import { createForm } from '@formily/core';
|
||||
|
||||
function Designer(props: { schema?: ISchema }) {
|
||||
const form = useMemo(() => createForm({}), []);
|
||||
const { schema } = props;
|
||||
return (
|
||||
<div>
|
||||
<FormProvider form={form}>
|
||||
<SchemaFieldWithDesigner schema={schema} />
|
||||
{/* <FormConsumer>
|
||||
{(form) => {
|
||||
return <div>{JSON.stringify(form.values, null, 2)}</div>;
|
||||
}}
|
||||
</FormConsumer> */}
|
||||
</FormProvider>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const schema = new Schema({
|
||||
type: 'object',
|
||||
properties: {
|
||||
grid: {
|
||||
type: 'void',
|
||||
title: 'aa',
|
||||
'x-component': 'Grid',
|
||||
properties: {
|
||||
row1: {
|
||||
type: 'void',
|
||||
'x-component': 'Grid.Row',
|
||||
properties: {
|
||||
col1: {
|
||||
type: 'void',
|
||||
'x-component': 'Grid.Col',
|
||||
'x-component-props': {
|
||||
size: 1 / 2,
|
||||
},
|
||||
properties: {
|
||||
block11: {
|
||||
type: 'void',
|
||||
'x-component': 'Grid.Block',
|
||||
'x-component-props': {
|
||||
title: 'block11',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
col2: {
|
||||
type: 'void',
|
||||
'x-component': 'Grid.Col',
|
||||
'x-component-props': {
|
||||
size: 1 / 2,
|
||||
isLast: true,
|
||||
},
|
||||
properties: {
|
||||
block21: {
|
||||
type: 'void',
|
||||
'x-component': 'Grid.Block',
|
||||
'x-component-props': {
|
||||
title: 'block21',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
row2: {
|
||||
type: 'void',
|
||||
'x-component': 'Grid.Row',
|
||||
properties: {
|
||||
col21: {
|
||||
type: 'void',
|
||||
'x-component': 'Grid.Col',
|
||||
'x-component-props': {
|
||||
size: 1 / 3,
|
||||
},
|
||||
properties: {
|
||||
block211: {
|
||||
type: 'void',
|
||||
'x-component': 'Grid.Block',
|
||||
'x-component-props': {
|
||||
title: 'block211',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
col22: {
|
||||
type: 'void',
|
||||
'x-component': 'Grid.Col',
|
||||
'x-component-props': {
|
||||
size: 2 / 3,
|
||||
isLast: true,
|
||||
},
|
||||
properties: {
|
||||
block221: {
|
||||
type: 'void',
|
||||
'x-component': 'Grid.Block',
|
||||
'x-component-props': {
|
||||
title: 'block221',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
row3: {
|
||||
type: 'void',
|
||||
'x-component': 'Grid.Row',
|
||||
"x-component-props": {
|
||||
isLast: true,
|
||||
},
|
||||
properties: {
|
||||
col31: {
|
||||
type: 'void',
|
||||
'x-component': 'Grid.Col',
|
||||
'x-component-props': {
|
||||
size: 1,
|
||||
isLast: true,
|
||||
},
|
||||
properties: {
|
||||
block311: {
|
||||
type: 'void',
|
||||
'x-component': 'Grid.Block',
|
||||
'x-component-props': {
|
||||
title: 'block311',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
export default () => {
|
||||
return (
|
||||
<Card>
|
||||
<Designer
|
||||
schema={schema}
|
||||
/>
|
||||
{/* <Grid
|
||||
onDrop={(e) => {
|
||||
console.log('onDrop', e, e.data);
|
||||
}}
|
||||
>
|
||||
<Row
|
||||
onColResize={(e) => {
|
||||
console.log(e.data);
|
||||
}}
|
||||
>
|
||||
{[1, 2, 3].map((index) => (
|
||||
<Col size={1 / 3}>
|
||||
<Block>col {index}</Block>
|
||||
</Col>
|
||||
))}
|
||||
</Row>
|
||||
<Row
|
||||
onColResize={(e) => {
|
||||
console.log(e.data);
|
||||
}}
|
||||
>
|
||||
{[4, 5, 6].map((index) => (
|
||||
<Col size={1 / 3}>
|
||||
<Block>col {index}</Block>
|
||||
</Col>
|
||||
))}
|
||||
</Row>
|
||||
<Row
|
||||
onColResize={(e) => {
|
||||
console.log(e.data);
|
||||
}}
|
||||
>
|
||||
{[7, 8].map((index) => (
|
||||
<Col size={1 / 3}>
|
||||
<Block>col {index}</Block>
|
||||
</Col>
|
||||
))}
|
||||
</Row>
|
||||
<Row
|
||||
onColResize={(e) => {
|
||||
console.log(e.data);
|
||||
}}
|
||||
>
|
||||
{[9].map((index) => (
|
||||
<Col size={1}>
|
||||
<Block>col {index}</Block>
|
||||
</Col>
|
||||
))}
|
||||
</Row>
|
||||
</Grid> */}
|
||||
</Card>
|
||||
);
|
||||
};
|
@ -14,14 +14,14 @@ group:
|
||||
基于行(Row)和列(Col)来定义区块(Block)的外部框架。
|
||||
|
||||
## 代码演示
|
||||
|
||||
<!--
|
||||
### 基本用法
|
||||
|
||||
<code src="./demos/demo2.tsx"/>
|
||||
|
||||
### 内嵌区块
|
||||
|
||||
<code src="./demos/demo3.tsx"/>
|
||||
<code src="./demos/demo3.tsx"/> -->
|
||||
|
||||
### useDrag & useDrop
|
||||
|
||||
@ -31,6 +31,10 @@ group:
|
||||
|
||||
<code src="./demos/demo5.tsx"/>
|
||||
|
||||
### Grid
|
||||
|
||||
<code src="./demos/demo6.tsx"/>
|
||||
|
||||
## API 说明
|
||||
|
||||
### Grid
|
||||
@ -67,7 +71,11 @@ interface BlockOptions {
|
||||
|
||||
原始 schema 需要至少 grid->row->col->block->custom 五层嵌套,写起来非常繁琐,`blocks2properties` 方法可以简化配置。
|
||||
|
||||
### useDrag
|
||||
### useDrag & useDrop
|
||||
|
||||
拖拽 hooks
|
||||
|
||||
原生态的
|
||||
|
||||
### useDrop
|
||||
|
||||
|
@ -1,3 +1,271 @@
|
||||
import React, { useContext, createContext, useState } from 'react';
|
||||
import {
|
||||
Schema,
|
||||
ISchema,
|
||||
useFieldSchema,
|
||||
useForm,
|
||||
useField,
|
||||
} from '@formily/react';
|
||||
import { uid } from '@formily/shared';
|
||||
import { SchemaField } from '../../fields';
|
||||
import set from 'lodash/set';
|
||||
|
||||
export const SchemaDesignerContext = createContext<Schema>(new Schema({}));
|
||||
export const SchemaRefreshContext = createContext(null);
|
||||
|
||||
export function SchemaFieldWithDesigner(props: { schema?: ISchema }) {
|
||||
function Container(props) {
|
||||
const { schema } = props;
|
||||
const [, refresh] = useState(0);
|
||||
return (
|
||||
<SchemaRefreshContext.Provider
|
||||
value={() => {
|
||||
refresh(Math.random());
|
||||
}}
|
||||
>
|
||||
<SchemaDesignerContext.Provider value={schema}>
|
||||
<SchemaField schema={schema} />
|
||||
</SchemaDesignerContext.Provider>
|
||||
<pre>{JSON.stringify(schema.toJSON(), null, 2)}</pre>
|
||||
</SchemaRefreshContext.Provider>
|
||||
);
|
||||
}
|
||||
return <Container schema={new Schema(props.schema)} />;
|
||||
}
|
||||
|
||||
export function removeProperty(property: Schema) {
|
||||
property.parent.removeProperty(property.name);
|
||||
}
|
||||
|
||||
export function addPropertyBefore(target, prop) {
|
||||
Object.keys(target.parent.properties).forEach((name) => {
|
||||
if (name === target.name) {
|
||||
target.parent.addProperty(prop.name, prop);
|
||||
}
|
||||
const property = target.parent.properties[name];
|
||||
property.parent.removeProperty(property.name);
|
||||
target.parent.addProperty(property.name, property.toJSON());
|
||||
});
|
||||
}
|
||||
|
||||
export function addPropertyAfter(target, prop) {
|
||||
Object.keys(target.parent.properties).forEach((name) => {
|
||||
const property = target.parent.properties[name];
|
||||
property.parent.removeProperty(property.name);
|
||||
target.parent.addProperty(property.name, property.toJSON());
|
||||
if (name === target.name) {
|
||||
target.parent.addProperty(prop.name, prop);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export const getSchemaAddressSegments = (schema: Schema) => {
|
||||
if (!schema) {
|
||||
return [];
|
||||
}
|
||||
const segments = [schema.name];
|
||||
if (schema.parent && schema.parent.name) {
|
||||
segments.unshift(...getSchemaAddressSegments(schema.parent));
|
||||
}
|
||||
return segments;
|
||||
};
|
||||
|
||||
export function useSchemaQuery() {
|
||||
const context = useContext(SchemaDesignerContext);
|
||||
const refresh = useContext(SchemaRefreshContext);
|
||||
const fieldSchema = useFieldSchema();
|
||||
const field = useField();
|
||||
const form = useForm();
|
||||
|
||||
const getSchemaByPath = (path) => {
|
||||
let s: Schema = context;
|
||||
const names = [...path];
|
||||
while (names.length) {
|
||||
s = s.properties[names.shift()];
|
||||
}
|
||||
return s;
|
||||
};
|
||||
|
||||
const schema = getSchemaByPath(field.address.segments);
|
||||
|
||||
const getPropertyByPosition = (position) => {
|
||||
if (position.type === 'row-divider') {
|
||||
const names = Object.keys(schema.properties);
|
||||
const isOver = position.rowDividerIndex > names.length - 1;
|
||||
const index = isOver ? names.length - 1 : position.rowDividerIndex;
|
||||
const name = names[index];
|
||||
const property = schema.properties[name];
|
||||
const addProperty = isOver ? addPropertyAfter : addPropertyBefore;
|
||||
return (data) => {
|
||||
return addProperty(property, {
|
||||
type: 'void',
|
||||
name: `r_${uid()}`,
|
||||
'x-component': 'Grid.Row',
|
||||
properties: {
|
||||
[`c_${uid()}`]: {
|
||||
type: 'void',
|
||||
'x-component': 'Grid.Col',
|
||||
'x-component-props': {
|
||||
size: 1,
|
||||
},
|
||||
properties: {
|
||||
[data.name]: data,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
}
|
||||
const rowNames = Object.keys(schema.properties);
|
||||
const rowName = rowNames[position.rowIndex];
|
||||
const row = schema.properties[rowName];
|
||||
if (position.type === 'col-divider') {
|
||||
const names = Object.keys(row.properties);
|
||||
const isOver = position.colDividerIndex > names.length - 1;
|
||||
const index = isOver ? names.length - 1 : position.colDividerIndex;
|
||||
const name = names[index];
|
||||
const property = row.properties[name];
|
||||
const addProperty = isOver ? addPropertyAfter : addPropertyBefore;
|
||||
const count = Object.keys(row.properties).length+1;
|
||||
return (data) => {
|
||||
const other = 1-(1/count);
|
||||
Object.keys(row.properties).forEach(name => {
|
||||
const prop = row.properties[name];
|
||||
const segments = getSchemaAddressSegments(prop);
|
||||
form.setFieldState(segments.join('.'), state => {
|
||||
state.componentProps.size = other * state.componentProps.size;
|
||||
console.log({state}, other * state.componentProps.size);
|
||||
});
|
||||
});
|
||||
addProperty(property, {
|
||||
type: 'void',
|
||||
name: `c_${uid()}`,
|
||||
'x-component': 'Grid.Col',
|
||||
'x-component-props': {
|
||||
size: 1/count,
|
||||
},
|
||||
properties: {
|
||||
[data.name]: data,
|
||||
},
|
||||
});
|
||||
};
|
||||
}
|
||||
const colNames = Object.keys(row.properties);
|
||||
const colName = colNames[position.colIndex];
|
||||
const col = row.properties[colName];
|
||||
if (position.type === 'block-divider') {
|
||||
const names = Object.keys(col.properties);
|
||||
const isOver = position.blockDividerIndex > names.length - 1;
|
||||
const index = isOver ? names.length - 1 : position.blockDividerIndex;
|
||||
const name = names[index];
|
||||
const property = col.properties[name];
|
||||
const addProperty = isOver ? addPropertyAfter : addPropertyBefore;
|
||||
return (data) => {
|
||||
return addProperty(property, data);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
schema,
|
||||
fieldSchema,
|
||||
refresh,
|
||||
removeBlock: () => {
|
||||
if (Object.keys(schema.parent.parent.properties).length === 1) {
|
||||
removeProperty(schema.parent.parent);
|
||||
} else if (Object.keys(schema.parent.properties).length === 1) {
|
||||
removeProperty(schema.parent);
|
||||
const cols = [];
|
||||
let allSize = 0;
|
||||
Object.keys(schema.parent.parent.properties).forEach(name => {
|
||||
const prop = schema.parent.parent.properties[name];
|
||||
const segments = getSchemaAddressSegments(prop);
|
||||
cols.push(segments);
|
||||
form.setFieldState(segments.join('.'), state => {
|
||||
allSize += state.componentProps.size;
|
||||
});
|
||||
return;
|
||||
});
|
||||
for (const segments of cols) {
|
||||
form.setFieldState(segments.join('.'), state => {
|
||||
state.componentProps.size = state.componentProps.size/allSize;
|
||||
});
|
||||
}
|
||||
}
|
||||
refresh();
|
||||
},
|
||||
addBlock: (data?: any, options?: any) => {
|
||||
const { insertBefore = false } = options || {};
|
||||
data = {
|
||||
type: 'void',
|
||||
name: `b_${uid()}`,
|
||||
'x-component': 'Grid.Block',
|
||||
};
|
||||
const addProperty = insertBefore ? addPropertyBefore : addPropertyAfter;
|
||||
if (Object.keys(schema.parent.parent.properties).length === 1) {
|
||||
addProperty(schema.parent.parent, {
|
||||
type: 'void',
|
||||
name: `r_${uid()}`,
|
||||
'x-component': 'Grid.Row',
|
||||
properties: {
|
||||
[`c_${uid()}`]: {
|
||||
type: 'void',
|
||||
'x-component': 'Grid.Col',
|
||||
'x-component-props': {
|
||||
size: 1,
|
||||
},
|
||||
properties: {
|
||||
[data.name]: data,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
} else {
|
||||
addProperty(schema, data);
|
||||
}
|
||||
refresh();
|
||||
},
|
||||
moveTo: (path, position) => {
|
||||
const source = getSchemaByPath(path);
|
||||
const insert = getPropertyByPosition(position);
|
||||
if (!insert) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 只有一列时,删除当前行
|
||||
if (Object.keys(source.parent.parent.properties).length === 1) {
|
||||
source.parent.parent.parent.removeProperty(source.parent.parent.name);
|
||||
}
|
||||
// 某列只有一个区块时删除当前列
|
||||
else if (Object.keys(source.parent.properties).length === 1) {
|
||||
source.parent.parent.removeProperty(source.parent.name);
|
||||
const cols = [];
|
||||
let allSize = 0;
|
||||
Object.keys(source.parent.parent.properties).forEach(name => {
|
||||
const prop = source.parent.parent.properties[name];
|
||||
const segments = getSchemaAddressSegments(prop);
|
||||
cols.push(segments);
|
||||
form.setFieldState(segments.join('.'), state => {
|
||||
allSize += state.componentProps.size;
|
||||
});
|
||||
return;
|
||||
});
|
||||
for (const segments of cols) {
|
||||
form.setFieldState(segments.join('.'), state => {
|
||||
state.componentProps.size = state.componentProps.size/allSize;
|
||||
});
|
||||
}
|
||||
} else {
|
||||
source.parent.removeProperty(source.name);
|
||||
}
|
||||
insert(source.toJSON());
|
||||
refresh();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export * from './DND';
|
||||
export * from './Row';
|
||||
export * from './Col';
|
||||
export * from './DND';
|
||||
export * from './Grid';
|
||||
export * from './Block';
|
||||
|
@ -1,5 +1,267 @@
|
||||
import set from 'lodash/set';
|
||||
import React, { useContext, createContext, useState } from 'react';
|
||||
import { Schema, useFieldSchema, useForm } from '@formily/react';
|
||||
import { uid } from '@formily/shared';
|
||||
import { SchemaField } from '../../fields';
|
||||
import set from 'lodash/set';
|
||||
|
||||
export const SchemaDesignerContext = createContext<Schema>(new Schema({}));
|
||||
export const SchemaRefreshContext = createContext(null);
|
||||
|
||||
export function SchemaFieldWithDesigner(props) {
|
||||
function Container(props) {
|
||||
const { schema } = props;
|
||||
const [, refresh] = useState(0);
|
||||
return (
|
||||
<SchemaRefreshContext.Provider
|
||||
value={() => {
|
||||
refresh(Math.random());
|
||||
}}
|
||||
>
|
||||
<SchemaDesignerContext.Provider value={schema}>
|
||||
<SchemaField schema={schema} />
|
||||
</SchemaDesignerContext.Provider>
|
||||
</SchemaRefreshContext.Provider>
|
||||
);
|
||||
}
|
||||
return <Container schema={new Schema(props.schema)} />;
|
||||
}
|
||||
|
||||
export const getFullPaths = (schema: Schema) => {
|
||||
if (!schema) {
|
||||
return [];
|
||||
}
|
||||
const paths = [schema.name];
|
||||
if (schema.parent && schema.parent.name) {
|
||||
paths.unshift(...getFullPaths(schema.parent));
|
||||
}
|
||||
return paths;
|
||||
};
|
||||
|
||||
export function useSchema() {
|
||||
const context = useContext(SchemaDesignerContext);
|
||||
const refresh = useContext(SchemaRefreshContext);
|
||||
const fieldSchema = useFieldSchema();
|
||||
const paths = getFullPaths(fieldSchema);
|
||||
let schema: Schema = context;
|
||||
const names = [...paths];
|
||||
while (names.length) {
|
||||
schema = schema.properties[names.shift()];
|
||||
}
|
||||
return { schema, fieldSchema, refresh };
|
||||
}
|
||||
|
||||
export function getSchema(context) {
|
||||
return (paths) => {
|
||||
const fullPaths = Array.isArray(paths) ? paths : getFullPaths(paths);
|
||||
let s: Schema = context;
|
||||
const names = [...fullPaths];
|
||||
while (names.length) {
|
||||
s = s.properties[names.shift()];
|
||||
}
|
||||
return s;
|
||||
};
|
||||
}
|
||||
|
||||
export function addPropertyBefore(target, prop) {
|
||||
Object.keys(target.parent.properties).forEach((name) => {
|
||||
if (name === target.name) {
|
||||
target.parent.addProperty(prop.name, prop);
|
||||
}
|
||||
const property = target.parent.properties[name];
|
||||
property.parent.removeProperty(property.name);
|
||||
target.parent.addProperty(property.name, property.toJSON());
|
||||
});
|
||||
}
|
||||
|
||||
export function addPropertyAfter(target, prop) {
|
||||
Object.keys(target.parent.properties).forEach((name) => {
|
||||
const property = target.parent.properties[name];
|
||||
property.parent.removeProperty(property.name);
|
||||
target.parent.addProperty(property.name, property.toJSON());
|
||||
if (name === target.name) {
|
||||
target.parent.addProperty(prop.name, prop);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function useSchemaQuery() {
|
||||
const context = useContext(SchemaDesignerContext);
|
||||
const form = useForm();
|
||||
const { schema, refresh } = useSchema();
|
||||
|
||||
return {
|
||||
resizeColumn() {
|
||||
function getPrevProperty() {
|
||||
const names = Object.keys(schema.parent.properties);
|
||||
const index = names.indexOf(schema.name as string);
|
||||
return schema.parent.properties[names[index-1]];
|
||||
}
|
||||
console.log('onMouseDown', schema.name);
|
||||
console.log('onMouseDown', schema.parent.properties);
|
||||
console.log('onMouseDown', getFullPaths(getPrevProperty()));
|
||||
form.setFieldState(getFullPaths(getPrevProperty()).join('.'), (state) => {
|
||||
const span = state.componentProps.span;
|
||||
let width = state.componentProps.width||50;
|
||||
width -= 1;
|
||||
state.componentProps = {
|
||||
span: 1*span - 1,
|
||||
width,
|
||||
style: {
|
||||
flex: `0 0 ${width}%`,
|
||||
maxWidth: `${width}%`,
|
||||
},
|
||||
};
|
||||
});
|
||||
console.log('onMouseDown', getFullPaths(schema));
|
||||
form.setFieldState(getFullPaths(schema).join('.'), (state) => {
|
||||
const span = state.componentProps.span;
|
||||
console.log({span})
|
||||
let width = state.componentProps.width||50;
|
||||
width += 1;
|
||||
state.componentProps = {
|
||||
span: 1*span + 1,
|
||||
style: {
|
||||
flex: `0 0 ${width}%`,
|
||||
maxWidth: `${width}%`,
|
||||
},
|
||||
};
|
||||
});
|
||||
refresh();
|
||||
},
|
||||
removeBlock() {
|
||||
schema.parent.removeProperty(schema.name);
|
||||
refresh();
|
||||
},
|
||||
addBlock: (data, up = false) => {
|
||||
const blockSchema = block({
|
||||
type: 'string',
|
||||
title: `Block ${uid()}`,
|
||||
required: true,
|
||||
'x-read-pretty': false,
|
||||
// 'x-decorator': 'FormItem',
|
||||
'x-component': 'Hello',
|
||||
rowOrder: 2,
|
||||
columnOrder: 1,
|
||||
blockOrder: 1,
|
||||
});
|
||||
if ('Grid.Column' === schema.parent['x-component']) {
|
||||
if (Object.keys(schema.parent.parent.properties).length > 1) {
|
||||
up
|
||||
? addPropertyBefore(schema, blockSchema)
|
||||
: addPropertyAfter(schema, blockSchema);
|
||||
} else {
|
||||
const rowSchema = row(column(blockSchema));
|
||||
up
|
||||
? addPropertyAfter(schema.parent.parent, rowSchema)
|
||||
: addPropertyAfter(schema.parent.parent, rowSchema);
|
||||
}
|
||||
}
|
||||
console.log('x-component', schema.parent['x-component']);
|
||||
refresh();
|
||||
// schema.parent['x-component']
|
||||
},
|
||||
insertAfter: (sourcePath, targetPath) => {
|
||||
const source = getSchema(context)(sourcePath);
|
||||
const target = getSchema(context)(targetPath);
|
||||
if (!source || !target) {
|
||||
return;
|
||||
}
|
||||
console.log({
|
||||
sourcePath,
|
||||
source,
|
||||
target,
|
||||
targetPath,
|
||||
sourceParentproperties: source.parent.properties,
|
||||
});
|
||||
const names = [];
|
||||
Object.keys(target.parent.properties).forEach((name) => {
|
||||
// if (names.includes(source.name)) {
|
||||
// return;
|
||||
// }
|
||||
// names.push(name);
|
||||
const property = target.parent.properties[name];
|
||||
property.parent.removeProperty(property.name);
|
||||
target.parent.addProperty(property.name, property.toJSON());
|
||||
if (name === target.name) {
|
||||
// names.push(source.name);
|
||||
source.parent.removeProperty(source.name);
|
||||
console.log('source.parent.properties', source.parent.properties);
|
||||
target.parent.addProperty(source.name, source.toJSON());
|
||||
}
|
||||
});
|
||||
},
|
||||
insertAfterWithAddRow: (sourcePath, targetPath) => {
|
||||
const source = getSchema(context)(sourcePath);
|
||||
const target = getSchema(context)(targetPath);
|
||||
const rowSchema = row(column(source.toJSON()));
|
||||
source.parent.removeProperty(source.name);
|
||||
Object.keys(target.parent.properties).forEach((name) => {
|
||||
const property = target.parent.properties[name];
|
||||
property.parent.removeProperty(property.name);
|
||||
target.parent.addProperty(property.name, property.toJSON());
|
||||
if (name === target.name) {
|
||||
target.parent.addProperty(rowSchema.name, rowSchema);
|
||||
}
|
||||
});
|
||||
},
|
||||
insertBeforeWithAddRow: (sourcePath, targetPath) => {
|
||||
const source = getSchema(context)(sourcePath);
|
||||
const target = getSchema(context)(targetPath);
|
||||
const rowSchema = row(column(source.toJSON()));
|
||||
source.parent.removeProperty(source.name);
|
||||
Object.keys(target.parent.properties).forEach((name) => {
|
||||
if (name === target.name) {
|
||||
target.parent.addProperty(rowSchema.name, rowSchema);
|
||||
}
|
||||
const property = target.parent.properties[name];
|
||||
property.parent.removeProperty(property.name);
|
||||
target.parent.addProperty(property.name, property.toJSON());
|
||||
});
|
||||
},
|
||||
appendToRowWithAddColumn: (sourcePath, targetPath) => {
|
||||
const source = getSchema(context)(sourcePath);
|
||||
const target = getSchema(context)(targetPath);
|
||||
const colSchema = column(source.toJSON());
|
||||
source.parent.removeProperty(source.name);
|
||||
const len = Object.keys(target.properties).length + 1;
|
||||
target.addProperty(colSchema.name, colSchema);
|
||||
console.log('target.properties', target.properties);
|
||||
Object.keys(target.properties).forEach((name) => {
|
||||
const prop = target.properties[name];
|
||||
form.setFieldState(getFullPaths(prop).join('.'), (state) => {
|
||||
state.componentProps = {
|
||||
span: 24 / len,
|
||||
};
|
||||
});
|
||||
});
|
||||
},
|
||||
insertBeforeWithAddColumn: (sourcePath, targetPath) => {
|
||||
const source = getSchema(context)(sourcePath);
|
||||
const target = getSchema(context)(targetPath);
|
||||
const colSchema = column(source.toJSON());
|
||||
source.parent.removeProperty(source.name);
|
||||
const len = Object.keys(target.parent.properties).length + 1;
|
||||
console.log('x-component-props.span', 24 / len);
|
||||
Object.keys(target.parent.properties).forEach((name) => {
|
||||
if (name === target.name) {
|
||||
target.parent.addProperty(colSchema.name, colSchema);
|
||||
}
|
||||
const property = target.parent.properties[name];
|
||||
property.parent.removeProperty(property.name);
|
||||
const json = property.toJSON();
|
||||
target.parent.addProperty(property.name, json);
|
||||
});
|
||||
Object.keys(target.parent.properties).forEach((name) => {
|
||||
const prop = target.parent.properties[name];
|
||||
form.setFieldState(getFullPaths(prop).join('.'), (state) => {
|
||||
state.componentProps = {
|
||||
span: 24 / len,
|
||||
};
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export const grid = (...rows: any[]) => {
|
||||
const rowProperties = {};
|
||||
@ -59,6 +321,7 @@ export const block = (...fields: any[]) => {
|
||||
const properties = {};
|
||||
fields.forEach((item) => {
|
||||
const name = item.name || `f_${uid()}`;
|
||||
// item.title = `${item.title} ${name}`;
|
||||
properties[name] = item;
|
||||
});
|
||||
const lastComponentType = fields[fields.length - 1]['x-component'];
|
||||
@ -73,22 +336,3 @@ export const block = (...fields: any[]) => {
|
||||
properties,
|
||||
};
|
||||
};
|
||||
|
||||
export function blocks2properties(blocks: any[]) {
|
||||
const obj = {
|
||||
rows: [],
|
||||
};
|
||||
blocks.forEach(block => {
|
||||
const path = ['rows', block['rowOrder'], block['columnOrder'], block['blockOrder']];
|
||||
console.log(path);
|
||||
set(obj, path, block);
|
||||
});
|
||||
return grid(...obj.rows.filter(Boolean).map((cols) => {
|
||||
return row(
|
||||
...cols.filter(Boolean).map((items: any[]) => {
|
||||
console.log({items: items.filter(Boolean)})
|
||||
return column(...items.filter(Boolean).map(item => block(item)));
|
||||
}),
|
||||
);
|
||||
}));
|
||||
}
|
||||
|
@ -90,300 +90,6 @@ export const SchemaField = createSchemaField({
|
||||
scope: fieldScope,
|
||||
});
|
||||
|
||||
export const SchemaDesignerContext = createContext<Schema>(new Schema({}));
|
||||
export const SchemaRefreshContext = createContext(null);
|
||||
|
||||
export function SchemaFieldWithDesigner(props) {
|
||||
function Container(props) {
|
||||
const { schema } = props;
|
||||
const [, refresh] = useState(0);
|
||||
return (
|
||||
<SchemaRefreshContext.Provider
|
||||
value={() => {
|
||||
refresh(Math.random());
|
||||
}}
|
||||
>
|
||||
<SchemaDesignerContext.Provider value={schema}>
|
||||
<SchemaField schema={schema} />
|
||||
</SchemaDesignerContext.Provider>
|
||||
</SchemaRefreshContext.Provider>
|
||||
);
|
||||
}
|
||||
return <Container schema={new Schema(props.schema)} />;
|
||||
}
|
||||
|
||||
export const getFullPaths = (schema: Schema) => {
|
||||
if (!schema) {
|
||||
return [];
|
||||
}
|
||||
const paths = [schema.name];
|
||||
if (schema.parent && schema.parent.name) {
|
||||
paths.unshift(...getFullPaths(schema.parent));
|
||||
}
|
||||
return paths;
|
||||
};
|
||||
|
||||
export function useSchema() {
|
||||
const context = useContext(SchemaDesignerContext);
|
||||
const refresh = useContext(SchemaRefreshContext);
|
||||
const fieldSchema = useFieldSchema();
|
||||
const paths = getFullPaths(fieldSchema);
|
||||
let schema: Schema = context;
|
||||
const names = [...paths];
|
||||
while (names.length) {
|
||||
schema = schema.properties[names.shift()];
|
||||
}
|
||||
return { schema, fieldSchema, refresh };
|
||||
}
|
||||
|
||||
export function getSchema(context) {
|
||||
return (paths) => {
|
||||
const fullPaths = Array.isArray(paths) ? paths : getFullPaths(paths);
|
||||
let s: Schema = context;
|
||||
const names = [...fullPaths];
|
||||
while (names.length) {
|
||||
s = s.properties[names.shift()];
|
||||
}
|
||||
return s;
|
||||
};
|
||||
}
|
||||
|
||||
export function addPropertyBefore(target, prop) {
|
||||
Object.keys(target.parent.properties).forEach((name) => {
|
||||
if (name === target.name) {
|
||||
target.parent.addProperty(prop.name, prop);
|
||||
}
|
||||
const property = target.parent.properties[name];
|
||||
property.parent.removeProperty(property.name);
|
||||
target.parent.addProperty(property.name, property.toJSON());
|
||||
});
|
||||
}
|
||||
|
||||
export function addPropertyAfter(target, prop) {
|
||||
Object.keys(target.parent.properties).forEach((name) => {
|
||||
const property = target.parent.properties[name];
|
||||
property.parent.removeProperty(property.name);
|
||||
target.parent.addProperty(property.name, property.toJSON());
|
||||
if (name === target.name) {
|
||||
target.parent.addProperty(prop.name, prop);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function useSchemaQuery() {
|
||||
const context = useContext(SchemaDesignerContext);
|
||||
const form = useForm();
|
||||
const { schema, refresh } = useSchema();
|
||||
return {
|
||||
removeBlock() {
|
||||
schema.parent.removeProperty(schema.name);
|
||||
refresh();
|
||||
},
|
||||
addBlock: (data, up = false) => {
|
||||
const blockSchema = block({
|
||||
type: 'string',
|
||||
title: `Block ${uid()}`,
|
||||
required: true,
|
||||
'x-read-pretty': false,
|
||||
// 'x-decorator': 'FormItem',
|
||||
'x-component': 'Hello',
|
||||
rowOrder: 2,
|
||||
columnOrder: 1,
|
||||
blockOrder: 1,
|
||||
});
|
||||
if ('Grid.Column' === schema.parent['x-component']) {
|
||||
if (Object.keys(schema.parent.parent.properties).length > 1) {
|
||||
up
|
||||
? addPropertyBefore(schema, blockSchema)
|
||||
: addPropertyAfter(schema, blockSchema);
|
||||
} else {
|
||||
const rowSchema = row(column(blockSchema));
|
||||
up
|
||||
? addPropertyAfter(schema.parent.parent, rowSchema)
|
||||
: addPropertyAfter(schema.parent.parent, rowSchema);
|
||||
}
|
||||
}
|
||||
console.log('x-component', schema.parent['x-component']);
|
||||
refresh();
|
||||
// schema.parent['x-component']
|
||||
},
|
||||
insertAfter: (sourcePath, targetPath) => {
|
||||
const source = getSchema(context)(sourcePath);
|
||||
const target = getSchema(context)(targetPath);
|
||||
if (!source || !target) {
|
||||
return;
|
||||
}
|
||||
console.log({
|
||||
sourcePath,
|
||||
source,
|
||||
target,
|
||||
targetPath,
|
||||
sourceParentproperties: source.parent.properties,
|
||||
});
|
||||
const names = [];
|
||||
Object.keys(target.parent.properties).forEach((name) => {
|
||||
// if (names.includes(source.name)) {
|
||||
// return;
|
||||
// }
|
||||
// names.push(name);
|
||||
const property = target.parent.properties[name];
|
||||
property.parent.removeProperty(property.name);
|
||||
target.parent.addProperty(property.name, property.toJSON());
|
||||
if (name === target.name) {
|
||||
// names.push(source.name);
|
||||
source.parent.removeProperty(source.name);
|
||||
console.log('source.parent.properties', source.parent.properties);
|
||||
target.parent.addProperty(source.name, source.toJSON());
|
||||
}
|
||||
});
|
||||
},
|
||||
insertAfterWithAddRow: (sourcePath, targetPath) => {
|
||||
const source = getSchema(context)(sourcePath);
|
||||
const target = getSchema(context)(targetPath);
|
||||
const rowSchema = row(column(source.toJSON()));
|
||||
source.parent.removeProperty(source.name);
|
||||
Object.keys(target.parent.properties).forEach((name) => {
|
||||
const property = target.parent.properties[name];
|
||||
property.parent.removeProperty(property.name);
|
||||
target.parent.addProperty(property.name, property.toJSON());
|
||||
if (name === target.name) {
|
||||
target.parent.addProperty(rowSchema.name, rowSchema);
|
||||
}
|
||||
});
|
||||
},
|
||||
insertBeforeWithAddRow: (sourcePath, targetPath) => {
|
||||
const source = getSchema(context)(sourcePath);
|
||||
const target = getSchema(context)(targetPath);
|
||||
const rowSchema = row(column(source.toJSON()));
|
||||
source.parent.removeProperty(source.name);
|
||||
Object.keys(target.parent.properties).forEach((name) => {
|
||||
if (name === target.name) {
|
||||
target.parent.addProperty(rowSchema.name, rowSchema);
|
||||
}
|
||||
const property = target.parent.properties[name];
|
||||
property.parent.removeProperty(property.name);
|
||||
target.parent.addProperty(property.name, property.toJSON());
|
||||
});
|
||||
},
|
||||
appendToRowWithAddColumn: (sourcePath, targetPath) => {
|
||||
const source = getSchema(context)(sourcePath);
|
||||
const target = getSchema(context)(targetPath);
|
||||
const colSchema = column(source.toJSON());
|
||||
source.parent.removeProperty(source.name);
|
||||
const len = Object.keys(target.properties).length + 1;
|
||||
target.addProperty(colSchema.name, colSchema);
|
||||
console.log('target.properties', target.properties);
|
||||
Object.keys(target.properties).forEach((name) => {
|
||||
const prop = target.properties[name];
|
||||
form.setFieldState(getFullPaths(prop).join('.'), (state) => {
|
||||
state.componentProps = {
|
||||
span: 24 / len,
|
||||
};
|
||||
});
|
||||
});
|
||||
},
|
||||
insertBeforeWithAddColumn: (sourcePath, targetPath) => {
|
||||
const source = getSchema(context)(sourcePath);
|
||||
const target = getSchema(context)(targetPath);
|
||||
const colSchema = column(source.toJSON());
|
||||
source.parent.removeProperty(source.name);
|
||||
const len = Object.keys(target.parent.properties).length + 1;
|
||||
console.log('x-component-props.span', 24 / len);
|
||||
Object.keys(target.parent.properties).forEach((name) => {
|
||||
if (name === target.name) {
|
||||
target.parent.addProperty(colSchema.name, colSchema);
|
||||
}
|
||||
const property = target.parent.properties[name];
|
||||
property.parent.removeProperty(property.name);
|
||||
const json = property.toJSON();
|
||||
target.parent.addProperty(property.name, json);
|
||||
});
|
||||
Object.keys(target.parent.properties).forEach((name) => {
|
||||
const prop = target.parent.properties[name];
|
||||
form.setFieldState(getFullPaths(prop).join('.'), (state) => {
|
||||
state.componentProps = {
|
||||
span: 24 / len,
|
||||
};
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export const grid = (...rows: any[]) => {
|
||||
const rowProperties = {};
|
||||
rows.forEach((row, index) => {
|
||||
set(row, 'x-component-props.rowOrder', index);
|
||||
rowProperties[row.name] = row;
|
||||
});
|
||||
const name = `g_${uid()}`;
|
||||
return {
|
||||
type: 'void',
|
||||
name,
|
||||
'x-component': 'Grid',
|
||||
'x-component-props': {},
|
||||
properties: rowProperties,
|
||||
};
|
||||
};
|
||||
|
||||
export const row = (...cols: any[]) => {
|
||||
const rowName = `r_${uid()}`;
|
||||
const colsProperties = {};
|
||||
cols.forEach((col, index) => {
|
||||
set(col, 'x-component-props.columnOrder', index);
|
||||
set(col, 'x-component-props.span', 24 / cols.length);
|
||||
colsProperties[col.name] = col;
|
||||
});
|
||||
return {
|
||||
type: 'void',
|
||||
name: rowName,
|
||||
'x-component': 'Grid.Row',
|
||||
'x-component-props': {},
|
||||
properties: colsProperties,
|
||||
};
|
||||
};
|
||||
|
||||
export const column = (...blocks: any[]) => {
|
||||
const colName = `c_${uid()}`;
|
||||
const properties = {};
|
||||
blocks.forEach((item) => {
|
||||
properties[item.name] = item;
|
||||
});
|
||||
return {
|
||||
name: colName,
|
||||
type: 'void',
|
||||
'x-component': 'Grid.Column',
|
||||
'x-read-pretty': true,
|
||||
'x-component-props': {
|
||||
labelCol: 6,
|
||||
wrapperCol: 10,
|
||||
span: 24,
|
||||
},
|
||||
properties,
|
||||
};
|
||||
};
|
||||
|
||||
export const block = (...fields: any[]) => {
|
||||
const blockName = `b_${uid()}`;
|
||||
const properties = {};
|
||||
fields.forEach((item) => {
|
||||
const name = item.name || `f_${uid()}`;
|
||||
// item.title = `${item.title} ${name}`;
|
||||
properties[name] = item;
|
||||
});
|
||||
const lastComponentType = fields[fields.length - 1]['x-component'];
|
||||
return {
|
||||
name: blockName,
|
||||
type: 'void',
|
||||
'x-component': 'Grid.Block',
|
||||
'x-component-props': {
|
||||
lastComponentType,
|
||||
},
|
||||
'x-read-pretty': false,
|
||||
properties,
|
||||
};
|
||||
};
|
||||
|
||||
export function parseEffects(effects: any, form?: any) {
|
||||
if (!effects) {
|
||||
return;
|
||||
|
Loading…
Reference in New Issue
Block a user