feat: improve draggable api
This commit is contained in:
parent
5b4dd1800e
commit
de25f56a79
@ -1,12 +1,13 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useDesignable } from '../..';
|
import { useDesignable } from '../..';
|
||||||
|
import { SortableItem } from '../sortable-item';
|
||||||
|
|
||||||
export const BlockItem: React.FC<any> = (props) => {
|
export const BlockItem: React.FC<any> = (props) => {
|
||||||
const { DesignableBar } = useDesignable();
|
const { DesignableBar } = useDesignable();
|
||||||
return (
|
return (
|
||||||
<div className="nb-block-item">
|
<SortableItem>
|
||||||
{props.children}
|
{props.children}
|
||||||
<DesignableBar />
|
<DesignableBar />
|
||||||
</div>
|
</SortableItem>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,55 +1,15 @@
|
|||||||
import React from 'react';
|
import { observer, useFieldSchema } from '@formily/react';
|
||||||
import { uid } from '@formily/shared';
|
import { uid } from '@formily/shared';
|
||||||
import { observer, useField, useFieldSchema } from '@formily/react';
|
import { BlockItem, DndContext, DragHandler, SchemaComponent, SchemaComponentProvider } from '@nocobase/client';
|
||||||
import { useDraggable, useDroppable } from '@dnd-kit/core';
|
import React from 'react';
|
||||||
import { SchemaComponent, SchemaComponentProvider, BlockItem, DndContext } from '@nocobase/client';
|
|
||||||
|
|
||||||
function Draggable(props) {
|
|
||||||
const { attributes, listeners, setNodeRef, transform } = useDraggable({
|
|
||||||
id: props.id,
|
|
||||||
data: props.data,
|
|
||||||
});
|
|
||||||
const style = transform
|
|
||||||
? {
|
|
||||||
transform: `translate3d(${transform.x}px, ${transform.y}px, 0)`,
|
|
||||||
}
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<button ref={setNodeRef} style={style} {...listeners} {...attributes}>
|
|
||||||
{props.children}
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function Droppable(props) {
|
|
||||||
const { isOver, setNodeRef } = useDroppable({
|
|
||||||
id: props.id,
|
|
||||||
data: props.data,
|
|
||||||
});
|
|
||||||
const style = {
|
|
||||||
color: isOver ? 'green' : undefined,
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div ref={setNodeRef} style={style}>
|
|
||||||
{props.children}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const Block = observer((props) => {
|
const Block = observer((props) => {
|
||||||
const fieldSchema = useFieldSchema();
|
const fieldSchema = useFieldSchema();
|
||||||
const field = useField();
|
|
||||||
return (
|
return (
|
||||||
<Droppable id={field.address.toString()} data={{ schema: fieldSchema }}>
|
|
||||||
<div style={{ marginBottom: 20, padding: '0 20px', height: 50, lineHeight: '50px', background: '#f1f1f1' }}>
|
<div style={{ marginBottom: 20, padding: '0 20px', height: 50, lineHeight: '50px', background: '#f1f1f1' }}>
|
||||||
Block {fieldSchema.name}
|
Block {fieldSchema.name}
|
||||||
<Draggable id={field.address.toString()} data={{ schema: fieldSchema }}>
|
<DragHandler />
|
||||||
Drag
|
|
||||||
</Draggable>
|
|
||||||
</div>
|
</div>
|
||||||
</Droppable>
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1 +1,2 @@
|
|||||||
export * from './BlockItem';
|
export * from './BlockItem';
|
||||||
|
|
||||||
|
@ -10,6 +10,9 @@ const useDragEnd = () => {
|
|||||||
console.log({ active, over });
|
console.log({ active, over });
|
||||||
const activeSchema = active?.data?.current?.schema;
|
const activeSchema = active?.data?.current?.schema;
|
||||||
const overSchema = over?.data?.current?.schema;
|
const overSchema = over?.data?.current?.schema;
|
||||||
|
const insertAdjacent = over?.data?.current?.insertAdjacent;
|
||||||
|
const wrapSchema = over?.data?.current?.wrapSchema;
|
||||||
|
const onInsertAdjacent = over?.data?.current?.onInsertAdjacent;
|
||||||
|
|
||||||
if (!activeSchema || !overSchema) {
|
if (!activeSchema || !overSchema) {
|
||||||
return;
|
return;
|
||||||
@ -20,7 +23,25 @@ const useDragEnd = () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
dn.on('afterInsertAdjacent', refresh);
|
dn.on('afterInsertAdjacent', refresh);
|
||||||
dn.insertBeforeBeginOrAfterEnd(activeSchema);
|
dn.on('afterRemove', refresh);
|
||||||
|
|
||||||
|
if (activeSchema.parent === overSchema.parent) {
|
||||||
|
return dn.insertBeforeBeginOrAfterEnd(activeSchema);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (insertAdjacent) {
|
||||||
|
console.log('removeIfChildrenEmpty', activeSchema)
|
||||||
|
dn.insertAdjacent(insertAdjacent, activeSchema, {
|
||||||
|
wrap: wrapSchema,
|
||||||
|
removeEmptyParents: true,
|
||||||
|
});
|
||||||
|
// onInsertAdjacent && onInsertAdjacent({
|
||||||
|
// dn,
|
||||||
|
// orginDraggedParentSchema,
|
||||||
|
// draggedSchema: activeSchema,
|
||||||
|
// });
|
||||||
|
return;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { FormLayout } from '@formily/antd';
|
||||||
import { createForm, FormPath } from '@formily/core';
|
import { createForm, FormPath } from '@formily/core';
|
||||||
import {
|
import {
|
||||||
FieldContext,
|
FieldContext,
|
||||||
@ -72,7 +73,7 @@ const useDefaultValues = (props: any = {}, opts: any = {}) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const Form: ComposedForm = observer((props) => {
|
export const Form: ComposedForm = observer((props) => {
|
||||||
const { useValues = useDefaultValues } = props;
|
const { request, initialValue, useValues = useDefaultValues, ...others } = props;
|
||||||
const decorator = useFormDecorator();
|
const decorator = useFormDecorator();
|
||||||
const fieldSchema = useFieldSchema();
|
const fieldSchema = useFieldSchema();
|
||||||
const form = useMemo(() => createForm(), []);
|
const form = useMemo(() => createForm(), []);
|
||||||
@ -83,7 +84,13 @@ export const Form: ComposedForm = observer((props) => {
|
|||||||
form.setValues(data?.data);
|
form.setValues(data?.data);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
return <Spin spinning={loading}>{decorator ? <FormDecorator form={form} /> : <FormComponent form={form} />}</Spin>;
|
return (
|
||||||
|
<Spin spinning={loading}>
|
||||||
|
<FormLayout layout={'vertical'} {...others}>
|
||||||
|
{decorator ? <FormDecorator form={form} /> : <FormComponent form={form} />}
|
||||||
|
</FormLayout>
|
||||||
|
</Spin>
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
Form.__NOCOBASE_FORM = true;
|
Form.__NOCOBASE_FORM = true;
|
||||||
|
@ -1,115 +1,90 @@
|
|||||||
import React from 'react';
|
|
||||||
import { uid } from '@formily/shared';
|
|
||||||
import { observer, useFieldSchema } from '@formily/react';
|
import { observer, useFieldSchema } from '@formily/react';
|
||||||
import { useDraggable, useDroppable } from '@dnd-kit/core';
|
import { uid } from '@formily/shared';
|
||||||
import { SchemaComponent, SchemaComponentProvider, Grid } from '@nocobase/client';
|
import { BlockItem, DragHandler, Grid, SchemaComponent, SchemaComponentProvider } from '@nocobase/client';
|
||||||
|
import React from 'react';
|
||||||
function Draggable(props) {
|
|
||||||
const { attributes, listeners, setNodeRef, transform } = useDraggable({
|
|
||||||
id: props.id,
|
|
||||||
data: props.data,
|
|
||||||
});
|
|
||||||
const style = transform
|
|
||||||
? {
|
|
||||||
transform: `translate3d(${transform.x}px, ${transform.y}px, 0)`,
|
|
||||||
}
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<button ref={setNodeRef} style={style} {...listeners} {...attributes}>
|
|
||||||
{props.children}
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function Droppable(props) {
|
|
||||||
const { isOver, setNodeRef } = useDroppable({
|
|
||||||
id: props.id,
|
|
||||||
data: props.data,
|
|
||||||
});
|
|
||||||
const style = {
|
|
||||||
color: isOver ? 'green' : undefined,
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div ref={setNodeRef} style={style}>
|
|
||||||
{props.children}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const Block = observer((props) => {
|
const Block = observer((props) => {
|
||||||
const fieldSchema = useFieldSchema();
|
const fieldSchema = useFieldSchema();
|
||||||
return (
|
return (
|
||||||
<Droppable id={fieldSchema.name} data={{ schema: fieldSchema }}>
|
|
||||||
<div style={{ marginBottom: 20, padding: '0 20px', height: 50, lineHeight: '50px', background: '#f1f1f1' }}>
|
<div style={{ marginBottom: 20, padding: '0 20px', height: 50, lineHeight: '50px', background: '#f1f1f1' }}>
|
||||||
Block {fieldSchema.name}
|
Block {fieldSchema.title}
|
||||||
<Draggable id={fieldSchema.name} data={{ schema: fieldSchema }}>
|
<DragHandler />
|
||||||
Drag
|
|
||||||
</Draggable>
|
|
||||||
</div>
|
</div>
|
||||||
</Droppable>
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
return (
|
return (
|
||||||
<SchemaComponentProvider components={{ Grid, Block }}>
|
<SchemaComponentProvider components={{ Grid, Block, BlockItem }}>
|
||||||
<SchemaComponent
|
<SchemaComponent
|
||||||
schema={{
|
schema={{
|
||||||
type: 'void',
|
type: 'void',
|
||||||
name: 'page',
|
name: 'grid1',
|
||||||
'x-component': 'Grid',
|
'x-component': 'Grid',
|
||||||
'x-uid': uid(),
|
'x-uid': uid(),
|
||||||
properties: {
|
properties: {
|
||||||
[uid()]: {
|
row1: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
'x-component': 'Grid.Row',
|
'x-component': 'Grid.Row',
|
||||||
'x-uid': uid(),
|
'x-uid': uid(),
|
||||||
properties: {
|
properties: {
|
||||||
[uid()]: {
|
col11: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
'x-component': 'Grid.Col',
|
'x-component': 'Grid.Col',
|
||||||
properties: {
|
properties: {
|
||||||
[uid()]: {
|
block1: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
|
title: '1',
|
||||||
|
'x-decorator': 'BlockItem',
|
||||||
|
'x-component': 'Block',
|
||||||
|
},
|
||||||
|
block2: {
|
||||||
|
type: 'void',
|
||||||
|
title: '2',
|
||||||
|
'x-decorator': 'BlockItem',
|
||||||
'x-component': 'Block',
|
'x-component': 'Block',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
[uid()]: {
|
col12: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
'x-component': 'Grid.Col',
|
'x-component': 'Grid.Col',
|
||||||
properties: {
|
properties: {
|
||||||
[uid()]: {
|
block3: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
|
title: '3',
|
||||||
|
'x-decorator': 'BlockItem',
|
||||||
'x-component': 'Block',
|
'x-component': 'Block',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
[uid()]: {
|
row2: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
'x-component': 'Grid.Row',
|
'x-component': 'Grid.Row',
|
||||||
'x-uid': uid(),
|
'x-uid': uid(),
|
||||||
properties: {
|
properties: {
|
||||||
[uid()]: {
|
col21: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
'x-component': 'Grid.Col',
|
'x-component': 'Grid.Col',
|
||||||
properties: {
|
properties: {
|
||||||
[uid()]: {
|
block4: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
|
title: '4',
|
||||||
|
'x-decorator': 'BlockItem',
|
||||||
'x-component': 'Block',
|
'x-component': 'Block',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
[uid()]: {
|
col22: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
'x-component': 'Grid.Col',
|
'x-component': 'Grid.Col',
|
||||||
properties: {
|
properties: {
|
||||||
[uid()]: {
|
block5: {
|
||||||
type: 'void',
|
type: 'void',
|
||||||
|
title: '5',
|
||||||
|
'x-decorator': 'BlockItem',
|
||||||
'x-component': 'Block',
|
'x-component': 'Block',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
|
import { useDroppable } from '@dnd-kit/core';
|
||||||
|
import { css } from '@emotion/css';
|
||||||
|
import { observer, RecursionField, Schema, useField, useFieldSchema } from '@formily/react';
|
||||||
|
import { uid } from '@formily/shared';
|
||||||
|
import cls from 'classnames';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { observer } from '@formily/react';
|
|
||||||
import { DndContext } from '../dnd-context';
|
import { DndContext } from '../dnd-context';
|
||||||
|
|
||||||
export const Grid: any = observer((props) => {
|
export const Grid: any = observer((props) => {
|
||||||
@ -10,10 +14,72 @@ export const Grid: any = observer((props) => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const ColDivider = (props) => {
|
||||||
|
const { isOver, setNodeRef } = useDroppable({
|
||||||
|
id: props.id,
|
||||||
|
data: props.data,
|
||||||
|
});
|
||||||
|
|
||||||
|
const droppableStyle = {
|
||||||
|
backgroundColor: isOver ? 'green' : undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
return <div ref={setNodeRef} style={{ width: 24, ...droppableStyle }}></div>;
|
||||||
|
};
|
||||||
|
|
||||||
Grid.Row = observer((props) => {
|
Grid.Row = observer((props) => {
|
||||||
return <div>{props.children}</div>;
|
const field = useField();
|
||||||
|
const fieldSchema = useFieldSchema();
|
||||||
|
const addr = field.address.toString();
|
||||||
|
const wrapSchema = (schema: Schema) => {
|
||||||
|
const s = new Schema({
|
||||||
|
type: 'void',
|
||||||
|
name: `col_${uid()}`,
|
||||||
|
'x-uid': uid(),
|
||||||
|
'x-component': 'Grid.Col',
|
||||||
|
});
|
||||||
|
// parent 更新了,需要重新指定
|
||||||
|
if (Schema.isSchemaInstance(schema)) {
|
||||||
|
schema.parent = s;
|
||||||
|
}
|
||||||
|
s.addProperty(schema.name, schema);
|
||||||
|
return s;
|
||||||
|
};
|
||||||
|
const onInsertAdjacent = ({ dn, draggedSchema }) => {
|
||||||
|
dn.remove(draggedSchema, {
|
||||||
|
removeEmptyParents: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
console.log('fieldSchema', fieldSchema.toJSON());
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cls(
|
||||||
|
'nb-grid-row',
|
||||||
|
css`
|
||||||
|
margin: 0 -24px;
|
||||||
|
display: flex;
|
||||||
|
`,
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<ColDivider
|
||||||
|
id={`${addr}_0`}
|
||||||
|
data={{ wrapSchema, insertAdjacent: 'afterBegin', schema: fieldSchema, onInsertAdjacent }}
|
||||||
|
/>
|
||||||
|
{fieldSchema.mapProperties((schema, key, index) => {
|
||||||
|
return (
|
||||||
|
<React.Fragment key={key}>
|
||||||
|
<RecursionField name={key} schema={schema} />
|
||||||
|
<ColDivider
|
||||||
|
id={`${addr}_${index + 1}`}
|
||||||
|
data={{ wrapSchema, insertAdjacent: 'afterEnd', schema, onInsertAdjacent }}
|
||||||
|
/>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
Grid.Col = observer((props) => {
|
Grid.Col = observer((props) => {
|
||||||
return <div>{props.children}</div>;
|
return <div className={cls('nb-grid-col')}>{props.children}</div>;
|
||||||
});
|
});
|
||||||
|
@ -25,8 +25,10 @@ export * from './radio';
|
|||||||
export * from './record-picker';
|
export * from './record-picker';
|
||||||
export * from './row-selection';
|
export * from './row-selection';
|
||||||
export * from './select';
|
export * from './select';
|
||||||
|
export * from './sortable-item';
|
||||||
export * from './tabs';
|
export * from './tabs';
|
||||||
export * from './time-picker';
|
export * from './time-picker';
|
||||||
export * from './tree-select';
|
export * from './tree-select';
|
||||||
export * from './upload';
|
export * from './upload';
|
||||||
export * from './void-table';
|
export * from './void-table';
|
||||||
|
|
||||||
|
@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
nav:
|
||||||
|
path: /client
|
||||||
|
group:
|
||||||
|
path: /schema-components
|
||||||
|
---
|
||||||
|
|
||||||
|
# SortableItem
|
||||||
|
|
@ -0,0 +1,61 @@
|
|||||||
|
import { useDraggable, useDroppable } from '@dnd-kit/core';
|
||||||
|
import { observer, useField, useFieldSchema } from '@formily/react';
|
||||||
|
import React, { createContext, useContext } from 'react';
|
||||||
|
|
||||||
|
export const DraggableContext = createContext(null);
|
||||||
|
|
||||||
|
export const Sortable = (props: any) => {
|
||||||
|
const draggable = useDraggable({
|
||||||
|
id: props.id,
|
||||||
|
data: props.data,
|
||||||
|
});
|
||||||
|
|
||||||
|
const { isOver, setNodeRef } = useDroppable({
|
||||||
|
id: props.id,
|
||||||
|
data: props.data,
|
||||||
|
});
|
||||||
|
|
||||||
|
const droppableStyle = {
|
||||||
|
color: isOver ? 'green' : undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DraggableContext.Provider value={draggable}>
|
||||||
|
<div ref={setNodeRef} style={droppableStyle}>
|
||||||
|
{props.children}
|
||||||
|
</div>
|
||||||
|
</DraggableContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const SortableItem = observer((props) => {
|
||||||
|
const field = useField();
|
||||||
|
const fieldSchema = useFieldSchema();
|
||||||
|
const onInsertAdjacent = ({ dn, orginDraggedParentSchema }) => {
|
||||||
|
dn.removeIfChildrenEmpty(orginDraggedParentSchema, {
|
||||||
|
removeEmptyParents: true,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<Sortable
|
||||||
|
id={field.address.toString()}
|
||||||
|
data={{ insertAdjacent: 'afterEnd', onInsertAdjacent, schema: fieldSchema }}
|
||||||
|
>
|
||||||
|
{props.children}
|
||||||
|
</Sortable>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export const DragHandler = () => {
|
||||||
|
const { attributes, listeners, setNodeRef, transform } = useContext(DraggableContext);
|
||||||
|
const style = transform
|
||||||
|
? {
|
||||||
|
transform: `translate3d(${transform.x}px, ${transform.y}px, 0)`,
|
||||||
|
}
|
||||||
|
: undefined;
|
||||||
|
return (
|
||||||
|
<div ref={setNodeRef} style={{ ...style, display: 'inline-block' }} {...listeners} {...attributes}>
|
||||||
|
Drag
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
@ -1,8 +1,8 @@
|
|||||||
import React, { useContext } from 'react';
|
|
||||||
import { ISchema, Schema, useField, useFieldSchema } from '@formily/react';
|
import { ISchema, Schema, useField, useFieldSchema } from '@formily/react';
|
||||||
import { SchemaComponentContext } from '../context';
|
|
||||||
import set from 'lodash/set';
|
|
||||||
import { uid } from '@formily/shared';
|
import { uid } from '@formily/shared';
|
||||||
|
import set from 'lodash/set';
|
||||||
|
import React, { useContext } from 'react';
|
||||||
|
import { SchemaComponentContext } from '../context';
|
||||||
|
|
||||||
interface CreateDesignableProps {
|
interface CreateDesignableProps {
|
||||||
current: Schema;
|
current: Schema;
|
||||||
@ -17,6 +17,11 @@ export function createDesignable(options: CreateDesignableProps) {
|
|||||||
*/
|
*/
|
||||||
type Position = 'beforeBegin' | 'afterBegin' | 'beforeEnd' | 'afterEnd';
|
type Position = 'beforeBegin' | 'afterBegin' | 'beforeEnd' | 'afterEnd';
|
||||||
|
|
||||||
|
interface InsertAdjacentOptions {
|
||||||
|
wrap?: (s: ISchema) => ISchema;
|
||||||
|
removeEmptyParents?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
const generateUid = (s: ISchema) => {
|
const generateUid = (s: ISchema) => {
|
||||||
if (!s['x-uid']) {
|
if (!s['x-uid']) {
|
||||||
s['x-uid'] = uid();
|
s['x-uid'] = uid();
|
||||||
@ -26,6 +31,8 @@ const generateUid = (s: ISchema) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const defaultWrap = (s: ISchema) => s;
|
||||||
|
|
||||||
export class Designable {
|
export class Designable {
|
||||||
current: Schema;
|
current: Schema;
|
||||||
|
|
||||||
@ -66,25 +73,63 @@ export class Designable {
|
|||||||
this.events[name].forEach((fn) => fn.bind(this)(...args));
|
this.events[name].forEach((fn) => fn.bind(this)(...args));
|
||||||
}
|
}
|
||||||
|
|
||||||
insertAdjacent(position: Position, schema: ISchema) {
|
insertAdjacent(position: Position, schema: ISchema, options: InsertAdjacentOptions = {}) {
|
||||||
switch (position) {
|
switch (position) {
|
||||||
case 'beforeBegin':
|
case 'beforeBegin':
|
||||||
return this.insertBeforeBegin(schema);
|
return this.insertBeforeBegin(schema, options);
|
||||||
case 'afterBegin':
|
case 'afterBegin':
|
||||||
return this.insertAfterBegin(schema);
|
return this.insertAfterBegin(schema, options);
|
||||||
case 'beforeEnd':
|
case 'beforeEnd':
|
||||||
return this.insertBeforeEnd(schema);
|
return this.insertBeforeEnd(schema, options);
|
||||||
case 'afterEnd':
|
case 'afterEnd':
|
||||||
return this.insertAfterEnd(schema);
|
return this.insertAfterEnd(schema, options);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
remove() {
|
removeIfChildrenEmpty(schema?: Schema, options: { recursive?: boolean } = {}) {
|
||||||
const s = this.current.parent.removeProperty(this.current.name);
|
if (!schema) {
|
||||||
this.emit('afterRemove', s);
|
return;
|
||||||
|
}
|
||||||
|
const { recursive } = options;
|
||||||
|
let s = schema;
|
||||||
|
const count = Object.keys(s.properties || {}).length;
|
||||||
|
console.log('removeIfChildrenEmpty', count, s.properties);
|
||||||
|
if (count > 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let removed;
|
||||||
|
while (s.parent) {
|
||||||
|
removed = s.parent.removeProperty(s.name);
|
||||||
|
if (!recursive) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const count = Object.keys(s.parent.properties || {}).length;
|
||||||
|
if (count > 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
s = s.parent;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
insertBeforeBeginOrAfterEnd(schema: ISchema) {
|
remove(schema?: Schema, options: { removeEmptyParents?: boolean } = {}) {
|
||||||
|
const { removeEmptyParents } = options;
|
||||||
|
let s = schema || this.current;
|
||||||
|
let removed;
|
||||||
|
while (s.parent) {
|
||||||
|
removed = s.parent.removeProperty(s.name);
|
||||||
|
if (!removeEmptyParents) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const count = Object.keys(s.parent.properties || {}).length;
|
||||||
|
if (count > 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
s = s.parent;
|
||||||
|
}
|
||||||
|
this.emit('afterRemove', removed);
|
||||||
|
}
|
||||||
|
|
||||||
|
insertBeforeBeginOrAfterEnd(schema: ISchema, options: InsertAdjacentOptions = {}) {
|
||||||
if (!Schema.isSchemaInstance(this.current)) {
|
if (!Schema.isSchemaInstance(this.current)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -104,22 +149,25 @@ export class Designable {
|
|||||||
fromIndex = index;
|
fromIndex = index;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return fromIndex > toIndex ? this.insertBeforeBegin(schema) : this.insertAfterEnd(schema);
|
return fromIndex > toIndex ? this.insertBeforeBegin(schema, options) : this.insertAfterEnd(schema, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Before the current schema itself.
|
* Before the current schema itself.
|
||||||
*/
|
*/
|
||||||
insertBeforeBegin(schema: ISchema) {
|
insertBeforeBegin(schema: ISchema, options: InsertAdjacentOptions = {}) {
|
||||||
if (!Schema.isSchemaInstance(this.current)) {
|
if (!Schema.isSchemaInstance(this.current)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const { wrap = defaultWrap, removeEmptyParents } = options;
|
||||||
const properties = {};
|
const properties = {};
|
||||||
let start = false;
|
let start = false;
|
||||||
|
|
||||||
if (Schema.isSchemaInstance(schema)) {
|
if (Schema.isSchemaInstance(schema)) {
|
||||||
schema.parent.removeProperty(schema.name);
|
schema.parent.removeProperty(schema.name);
|
||||||
schema.parent = this.current.parent;
|
if (removeEmptyParents) {
|
||||||
|
this.removeIfChildrenEmpty(schema.parent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.current.parent.mapProperties((property, key) => {
|
this.current.parent.mapProperties((property, key) => {
|
||||||
@ -133,7 +181,9 @@ export class Designable {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.prepareProperty(schema);
|
this.prepareProperty(schema);
|
||||||
const s = this.current.parent.addProperty(schema.name, schema);
|
const wrapped = wrap(schema);
|
||||||
|
const s = this.current.parent.addProperty(wrapped.name, wrapped);
|
||||||
|
s.parent = this.current.parent;
|
||||||
this.current.parent.setProperties(properties);
|
this.current.parent.setProperties(properties);
|
||||||
this.emit('afterInsertAdjacent', 'beforeBegin', s);
|
this.emit('afterInsertAdjacent', 'beforeBegin', s);
|
||||||
}
|
}
|
||||||
@ -144,13 +194,16 @@ export class Designable {
|
|||||||
* @param schema
|
* @param schema
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
insertAfterBegin(schema: ISchema) {
|
insertAfterBegin(schema: ISchema, options: InsertAdjacentOptions = {}) {
|
||||||
if (!Schema.isSchemaInstance(this.current)) {
|
if (!Schema.isSchemaInstance(this.current)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const { wrap = defaultWrap, removeEmptyParents } = options;
|
||||||
if (Schema.isSchemaInstance(schema)) {
|
if (Schema.isSchemaInstance(schema)) {
|
||||||
schema.parent.removeProperty(schema.name);
|
schema.parent.removeProperty(schema.name);
|
||||||
schema.parent = this.current;
|
if (removeEmptyParents) {
|
||||||
|
this.removeIfChildrenEmpty(schema.parent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const properties = {};
|
const properties = {};
|
||||||
this.current.mapProperties((schema, key) => {
|
this.current.mapProperties((schema, key) => {
|
||||||
@ -158,7 +211,9 @@ export class Designable {
|
|||||||
});
|
});
|
||||||
this.current.properties = {};
|
this.current.properties = {};
|
||||||
this.prepareProperty(schema);
|
this.prepareProperty(schema);
|
||||||
const s = this.current.addProperty(schema.name, schema);
|
const wrapped = wrap(schema);
|
||||||
|
const s = this.current.addProperty(wrapped.name, wrapped);
|
||||||
|
s.parent = this.current;
|
||||||
this.current.setProperties(properties);
|
this.current.setProperties(properties);
|
||||||
this.emit('afterInsertAdjacent', 'afterBegin', s);
|
this.emit('afterInsertAdjacent', 'afterBegin', s);
|
||||||
}
|
}
|
||||||
@ -169,32 +224,40 @@ export class Designable {
|
|||||||
* @param schema
|
* @param schema
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
insertBeforeEnd(schema: ISchema) {
|
insertBeforeEnd(schema: ISchema, options: InsertAdjacentOptions = {}) {
|
||||||
if (!Schema.isSchemaInstance(this.current)) {
|
if (!Schema.isSchemaInstance(this.current)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const { wrap = defaultWrap, removeEmptyParents } = options;
|
||||||
if (Schema.isSchemaInstance(schema)) {
|
if (Schema.isSchemaInstance(schema)) {
|
||||||
schema.parent.removeProperty(schema.name);
|
schema.parent.removeProperty(schema.name);
|
||||||
schema.parent = this.current;
|
if (removeEmptyParents) {
|
||||||
|
this.removeIfChildrenEmpty(schema.parent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.prepareProperty(schema);
|
this.prepareProperty(schema);
|
||||||
const s = this.current.addProperty(schema.name || uid(), schema);
|
const wrapped = wrap(schema);
|
||||||
|
const s = this.current.addProperty(wrapped.name || uid(), wrapped);
|
||||||
|
s.parent = this.current;
|
||||||
this.emit('afterInsertAdjacent', 'beforeEnd', s);
|
this.emit('afterInsertAdjacent', 'beforeEnd', s);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* After the current schema itself.
|
* After the current schema itself.
|
||||||
*/
|
*/
|
||||||
insertAfterEnd(schema: ISchema) {
|
insertAfterEnd(schema: ISchema, options: InsertAdjacentOptions = {}) {
|
||||||
if (!Schema.isSchemaInstance(this.current)) {
|
if (!Schema.isSchemaInstance(this.current)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const properties = {};
|
const properties = {};
|
||||||
let start = false;
|
let start = false;
|
||||||
|
const { wrap = defaultWrap, removeEmptyParents } = options;
|
||||||
if (Schema.isSchemaInstance(schema)) {
|
if (Schema.isSchemaInstance(schema)) {
|
||||||
schema.parent.removeProperty(schema.name);
|
schema.parent.removeProperty(schema.name);
|
||||||
schema.parent = this.current.parent;
|
if (removeEmptyParents) {
|
||||||
|
this.removeIfChildrenEmpty(schema.parent);
|
||||||
|
}
|
||||||
|
schema.parent = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.current.parent.mapProperties((property, key) => {
|
this.current.parent.mapProperties((property, key) => {
|
||||||
@ -208,7 +271,11 @@ export class Designable {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.prepareProperty(schema);
|
this.prepareProperty(schema);
|
||||||
const s = this.current.parent.addProperty(schema.name || uid(), schema);
|
|
||||||
|
const wrapped = wrap(schema);
|
||||||
|
|
||||||
|
const s = this.current.parent.addProperty(wrapped.name || uid(), wrapped);
|
||||||
|
s.parent = this.current.parent;
|
||||||
this.current.parent.setProperties(properties);
|
this.current.parent.setProperties(properties);
|
||||||
this.emit('afterInsertAdjacent', 'afterEnd', s);
|
this.emit('afterInsertAdjacent', 'afterEnd', s);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user