# Schema design capabilities The design capabilities of Schema are mainly - Neighborhood insertion, which can be used to - Insertion of new schema nodes - Drag-and-drop movement of existing schema nodes - schema parameter modification The core designer APIs and parameters are - Designer API: `createDesignable()` & `useDesignable()` - Schema parameters: `x-designer`, used to adapt the designer component ## Designer API ### createDesignable() ```ts import { Schema } from '@nocobase/client'; const current = new Schema({ type: 'void', 'x-component': 'div', }); const { designable, // whether it is configurable remove, insertAdjacent, // insert at a position, four positions: beforeBegin, afterBegin, beforeEnd, afterEnd insertBeforeBegin, // insert in front of the current node insertAfterBegin, // insert in front of the first child node of the current node insertBeforeEnd, // after the last child of the current node insertAfterEnd, // after the current node } = createDesignable({ current, }); const newSchema = { type: 'void', name: 'hello', 'x-component': 'Hello', }; insertAfterBegin(newSchema); console.log(current.toJSON()); { type: 'void', 'x-component': 'div', properties: { hello: { type: 'void', 'x-component': 'Hello', }, }, } ``` ### useDesignable() React Hook scenarios can also use `useDesignable()` to get the API of the current schema component designer ```ts const { designable, // whether it is configurable remove, insertAdjacent, // insert at a position, four positions: beforeBegin, afterBegin, beforeEnd, afterEnd insertBeforeBegin, // insert in front of the current node insertAfterBegin, // insert in front of the first child node of the current node insertBeforeEnd, // after the last child of the current node insertAfterEnd, // after the current node } = useDesignable(); const schema = { name: uid(), 'x-component': 'Hello', }; // Insert in front of the current node insertBeforeBegin(schema); // Equivalent to insertAdjacent('beforeBegin', schema); // insert in front of the first child of the current node insertAfterBegin(schema); // Equivalent to insertAdjacent('afterBegin', schema); // after the last child of the current node insertBeforeEnd(schema); // Equivalent to insertAdjacent('beforeEnd', schema); // After the current node insertAfterEnd(schema); // Equivalent to insertAdjacent('afterEnd', schema); ``` ## Neighborhood insertion Similar to the DOM's [insert adjacent](https://dom.spec.whatwg.org/#insert-adjacent) concept, Schema also provides the `insertAdjacent()` method for solving the insertion of adjacent positions. The four adjacent positions ```ts { properties: { // beforeBegin insert before the current node node1: { properties: { // afterBegin inserted before the first child of the current node // ... // beforeEnd after the last child of the current node }, }, // afterEnd after the current node }, } ``` Like HTML tags, the components of the Schema component library can be combined with each other and inserted in reasonable proximity as needed via the insertAdjacent API. ### Inserting a new schema node Within a Schema component, a new node can be inserted directly into the adjacent position of the current Schema with `useDesignable()`. Example ```tsx import React from 'react'; import { SchemaComponentProvider, SchemaComponent, useDesignable } from '@nocobase/client'; import { observer, Schema, useFieldSchema } from '@formily/react'; import { Button, Space } from 'antd'; import { uid } from '@formily/shared'; const Hello = observer((props) => { const { insertAdjacent } = useDesignable(); const fieldSchema = useFieldSchema(); return (

{fieldSchema.name}

{props.children}
); }, { displayName: 'Hello' }); const Page = observer((props) => { return
{props.children}
; }, { displayName: 'Page' }); export default () => { return ( ); } ``` ### Drag-and-drop movement of existing schema nodes Methods such as insertAdjacent can also be used to drag and drop nodes ```tsx import React from 'react'; import { uid } from '@formily/shared'; import { observer, useField, useFieldSchema } from '@formily/react'; import { DndContext, DragEndEvent, useDraggable, useDroppable } from '@dnd-kit/core'; import { SchemaComponent, SchemaComponentProvider, createDesignable, useDesignable } from '@nocobase/client'; const useDragEnd = () => { const { refresh } = useDesignable(); return ({ active, over }: DragEndEvent) => { const activeSchema = active?.data?.current?.schema; const overSchema = over?.data?.current?.schema; if (!activeSchema || !overSchema) { return; } const dn = createDesignable({ current: overSchema, }); dn.on('insertAdjacent', refresh); dn.insertBeforeBeginOrAfterEnd(activeSchema); }; }; const Page = observer((props) => { return {props.children}; }, { displayName: 'Page' }); 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 ( ); } function Droppable(props) { const { isOver, setNodeRef } = useDroppable({ id: props.id, data: props.data, }); const style = { color: isOver ? 'green' : undefined, }; return (
{props.children}
); } const Block = observer((props) => { const field = useField(); const fieldSchema = useFieldSchema(); return (
Block {fieldSchema.name}{' '} Drag
); }, { displayName: 'Block' }); export default function App() { return ( ); } ``` ## Applications of `x-designer` `x-designer` is usually used only in wrapper components such as BlockItem, CardItem, FormItem, etc. ```ts { type: 'object', properties: { title: { type: 'string', title: '标题', 'x-decorator': 'FormItem', 'x-component': 'Input', 'x-designer': 'FormItem.Designer', }, status: { type: 'string', title: '状态', 'x-decorator': 'FormItem', 'x-component': 'Select', 'x-designer': 'FormItem.Designer', }, }, } ``` Note: The Schema designer provided by NocoBase is directly embedded in the interface in the form of a toolbar. When the UI configuration is activated (`designable = true`), the `x-designer` component (designer toolbar) will be displayed and the current schema component can be updated through the toolbar. - Drag and Drop: DndContext + DragHandler - Inserting new nodes: SchemaInitializer - Parameter configuration: SchemaSettings