SchemaInitializer

new SchemaInitializer(options)

1interface SchemaInitializerOptions<P1 = ButtonProps, P2 = {}> {
2  Component?: ComponentType<P1>;
3  componentProps?: P1;
4  style?: React.CSSProperties;
5  title?: string;
6  icon?: ReactNode;
7
8  items?: SchemaInitializerItemType[];
9  ItemsComponent?: ComponentType<P2>;
10  itemsComponentProps?: P2;
11  itemsComponentStyle?: React.CSSProperties;
12
13  insertPosition?: 'beforeBegin' | 'afterBegin' | 'beforeEnd' | 'afterEnd';
14  designable?: boolean;
15  wrap?: (s: ISchema) => ISchema;
16  onSuccess?: (data: any) => void;
17  insert?: InsertType;
18  useInsert?: () => InsertType;
19
20  popover?: boolean;
21  popoverProps?: PopoverProps;
22}
23
24class SchemaInitializer<P1 = ButtonProps, P2 = {}> {
25    constructor(options: SchemaInitializerOptions<P1, P2> & { name: string }): SchemaInitializer<P1, P2>;
26    add(name: string, item: Omit<SchemaInitializerItemType, 'name'>): void
27    get(nestedName: string): SchemaInitializerItemType | undefined
28    remove(nestedName: string): void
29}

详细解释

  • name:唯一标识,必填

  • Component 相关

    • Component:触发组件,默认是 Button 组件
    • componentProps: 组件属性,默认是 ButtonProps
    • title: 按钮的文本
    • icon:按钮的 icon 属性
    • style:组件的样式
  • Items 相关

    • items:列表项配置
    • ItemsComponent:默认是渲染成一个列表的形式,可通过此参数自定义 items
    • itemsComponentProps:ItemsComponent 的属性
    • itemsComponentStyle:ItemsComponent 的样式
  • popover 组件相关

    • popover:是否使用 popover,默认为 true
    • popoverProps:popover 的属性
  • Schema 操作相关

    • insertPosition:插入位置,参考:useDesignable()
    • designable:是否显示设计模式,参考:useDesignable()
    • wrap:对 Schema 的二次处理,参考:useDesignable()
    • onSuccess:Schema 更新到服务端后的回调,参考:useDesignable()
    • insert:自定义 Schema 插入逻辑,默认为 useDesignable()insertAdjacent
    • useInsert:当自定义插入 Schema 的逻辑需要用到 Hooks 时,可以使用此参数

示例

基础用法

1const myInitializer = new SchemaInitializer({
2  name: 'myInitializer',
3  title: 'Add Block',
4  // 插入位置
5  insertPosition: 'beforeEnd',
6  items: [
7    {
8      name: 'a',
9      title: 'Item A',
10      Component: Demo,
11    },
12  ],
13});

定制化Component

1const myInitializer = new SchemaInitializer({
2  name: 'myInitializer',
3  Component: (props) => (
4    <Avatar style={{ cursor: 'pointer' }} {...props}>
5      C
6    </Avatar>
7  ),
8  componentProps: {
9    size: 'large',
10  },
11  items: [
12    {
13      name: 'a',
14      title: 'Item A',
15      Component: Demo,
16    }
17  ],
18});

不使用 Popover

关于 useDesignable() 的说明请参考 useDesignable

1const schema = {
2  type: 'void',
3  title: Math.random(),
4  'x-component': 'Hello',
5};
6const MyInitializerComponent = () => {
7    const { insertBeforeEnd } = useDesignable();
8    return <Button onClick={() => insertBeforeEnd(schema)}>Add block</Button>
9}
10
11const myInitializer = new SchemaInitializer({
12  name: 'myInitializer',
13  title: 'Add block',
14  popover: false,
15  Component: MyInitializerComponent,
16});

定制化 Items

1const CustomListGridMenu: FC<SchemaInitializerItemsProps<ButtonProps, ListProps<any>>> = (props) => {
2  const { items, options, ...others } = props;
3  return (
4    <List
5      {...others}
6      style={{ marginTop: 20 }}
7      dataSource={items}
8      grid={{ gutter: 16, column: 2 }}
9      renderItem={(item) => (
10        <List.Item style={{ minWidth: 100, textAlign: 'center' }}>
11          <SchemaInitializerChild {...item} />
12        </List.Item>
13      )}
14    ></List>
15  );
16};
17
18const myInitializer = new SchemaInitializer({
19  name: 'myInitializer',
20  title: 'Button Text',
21  ItemsComponent: CustomListGridMenu,
22  items: [
23    {
24      name: 'a',
25      title: 'Item A',
26      Component: Demo,
27    }
28  ],
29});

options.items 配置详解

类型

1interface SchemaInitializerComponentCommonProps {
2  title?: string;
3  schema?: ISchema;
4  style?: React.CSSProperties;
5  className?: string;
6}
7
8interface SchemaInitializerItemBaseType<T = {}> extends SchemaInitializerComponentCommonProps {
9  name: string;
10  sort?: number;
11  type?: string;
12  Component?: string | ComponentType<T>;
13  componentProps?: Omit<T, 'children'>;
14  useComponentProps?: () => Omit<T, 'children'>;
15  useVisible?: () => boolean;
16  children?: SchemaInitializerItemType[];
17  useChildren?: () => SchemaInitializerItemType[];
18  [index: string]: any;
19}

两种定义方式:Componenttype

  • 通过 Component 定义
1const Demo = () => {
2  // 最终渲染 `SchemaInitializerItem`
3  return <SchemaInitializerItem title='Demo' />
4}
5
6const myInitializer = new SchemaInitializer({
7  name: 'myInitializer',
8  items: [
9    {
10      name: 'a',
11      Component: Demo, // 通过 Component 定义
12    }
13  ],
14});
  • 通过 type 定义

Tachybase 内置了一些常用的 type,例如 type: 'item',相当于 Component: SchemaInitializerItem

更多内置类型,请参考:内置组件和类型

1const myInitializer = new SchemaInitializer({
2  name: 'myInitializer',
3  items: [
4    {
5      name: 'a',
6      type: 'item',
7      title: 'Demo'
8    }
9  ],
10});

children 和动态方式useChildren

对于某些组件而言是有子列表项的,例如 type: 'itemGroup',那么我们使用 children 属性,同时考虑到某些场景下 children 是动态的,需要从 Hooks 里面获取,那么就可以通过 useChildren 来定义。

动态显示隐藏useVisible

组件属性componentProps 和动态属性useComponentProps

对于一些通用组件,我们可以通过 componentProps 来定义组件属性,同时考虑到某些场景下组件属性是动态的,需要从 Hooks 里面获取,那么就可以通过 useComponentProps 来定义。

当然也可以不使用这两个属性,直接封装成一个组件,然后通过 Component 来定义。

公共属性和组件属性

1{
2  name: 'demo',
3  title: 'Demo',
4  foo: 'bar',
5  Component: Demo,
6  componentProps: {
7    zzz: 'xxx',
8  },
9}

从上面的示例中我么看到,从配置项中获取组件组件所需的数据有两个方式:

  • 组件属性:通过 componentProps 来定义,例如 zzz: 'xxx'
  • 公共属性:将属性直接定义在配置项上,例如 foo: 'bar'nametitle

在获取上

  • componentProps 定义的数据会被传递给组件的 props
  • 直接定义在配置项上的数据会则需要通过 useSchemaInitializerItem() 获取
1const Demo = (props) => {
2  console.log(props); // { zzz: 'xxx' }
3  const { foo } = useSchemaInitializerItem(); // { foo: 'bar' }
4}

实例方法

1const myInitializer = new SchemaInitializer({
2  name: 'myInitializer',
3  title: 'Button Text',
4  items: [
5    {
6      name: 'a',
7      title: 'item a',
8      type: 'itemGroup',
9      children: [
10          {
11              name: 'a1',
12              title: 'item a1',
13          }
14      ],
15    },
16  ],
17});
1import { SchemaInitializer, Application, useSchemaInitializerRender } from '@tachybase/client';
2const myInitializer = new SchemaInitializer({
3  name: 'myInitializer',
4  title: 'Button Text',
5  items: [
6    {
7      name: 'a',
8      title: 'item a',
9      type: 'itemGroup',
10      children: [
11          {
12              name: 'a1',
13              title: 'item a1',
14              type: 'item',
15          }
16      ],
17    },
18  ],
19});
20
21const Root = () => {
22  const { render } = useSchemaInitializerRender('myInitializer');
23  return render();
24}
25const app = new Application({
26  schemaInitializers: [myInitializer],
27  providers: [Root],
28  designable: true,
29});
30
31export default app.getRootComponent();

schemaInitializer.add()

用于新增 Item,另一种添加方式参考 schemaInitializerManager.addItem();

  • 类型
1class SchemaInitializer {
2    add(name: string, item: Omit<SchemaInitializerItemType, 'name'>): void
3}
  • 参数说明

第一个参数是 name,作为唯一标识,用于增删改查,并且 name 支持 . 用于分割层级。

  • 示例
1myInitializer.add('b', {
2    type: 'item',
3    title: 'item b',
4})
5
6myInitializer.add('a.a2', {
7    type: 'item',
8    title: 'item a2',
9})
1import { SchemaInitializer, Application, useSchemaInitializerRender } from '@tachybase/client';
2const myInitializer = new SchemaInitializer({
3  name: 'myInitializer',
4  title: 'Button Text',
5  items: [
6    {
7      name: 'a',
8      title: 'item a',
9      type: 'itemGroup',
10      children: [
11          {
12              name: 'a1',
13              title: 'item a1',
14              type: 'item',
15          }
16      ],
17    },
18  ],
19});
20
21myInitializer.add('b', {
22    type: 'item',
23    title: 'item b',
24})
25
26myInitializer.add('a.a2', {
27    type: 'item',
28    title: 'item a2',
29})
30
31const Root = () => {
32  const { render } = useSchemaInitializerRender('myInitializer');
33  return render();
34}
35const app = new Application({
36  schemaInitializers: [myInitializer],
37  providers: [Root],
38  designable: true,
39});
40
41
42export default app.getRootComponent();

schemaInitializer.get()

  • 类型
1class SchemaInitializer {
2    get(nestedName: string): SchemaInitializerItemType | undefined
3}
  • 示例
1const itemA = myInitializer.get('a')
2
3const itemA1 = myInitializer.add('a.a1')

schemaInitializer.remove()

另一种移除方式参考 schemaInitializerManager.addItem();

  • 类型
1class SchemaInitializer {
2    remove(nestedName: string): void
3}
  • 示例
1myInitializer.remove('a.a1')
2
3myInitializer.remove('a')

Hooks

useSchemaInitializer()

用于获取 SchemaInitializer 上下文内容。

  • 类型
1export type InsertType = (s: ISchema) => void;
2
3const useSchemaInitializer: () => {
4    insert: InsertType;
5    options: SchemaInitializerOptions<any>;
6    visible?: boolean;
7    setVisible?: (v: boolean) => void;
8}
  • 参数详解

    • insert:参数是 Schema 对象,用于插入 Schema
    • options:获取 new SchemaInitializer(options) 时 options 配置
    • visible:popover 是否显示
    • setVisible:设置 popover 显示状态
  • 示例

1const schema = {
2  type: 'void',
3  'x-component': 'Hello',
4}
5const Demo = () => {
6    const { insert } = useSchemaInitializer();
7     const handleClick = () => {
8      insert(schema);
9    };
10    return  <SchemaInitializerItem title={'Demo'} onClick={handleClick}></SchemaInitializerItem>;
11}

useSchemaInitializerRender()

用于渲染 SchemaInitializer

  • 类型
1function useSchemaInitializerRender(name: string, options?: SchemaInitializerOptions): {
2    exists: boolean;
3    render: (props?: SchemaInitializerOptions) => React.FunctionComponentElement;
4}
  • 参数详解

返回的 render 方法可以接收一个参数,用于覆盖 new SchemaInitializer(options)options 配置。

  • 示例
1const Demo = () => {
2    const filedSchema = useFieldSchema();
3    const { render } = useSchemaInitializerRender(fieldSchema['x-initializer'], fieldSchema['x-initializer-props']);
4    return <div>
5        <div>{ render() }</div>
6        <div>可以进行参数的二次覆盖:{ render({ style: { color: 'red' } }) }</div>
7    </div>
8}

useSchemaInitializerItem()

用于获取配置项内容的,配置项是指的 SchemaInitializer 中的 items 中的一项。

  • 类型
1const useSchemaInitializerItem: <T = any>() => T
  • 示例
1const myInitializer = new SchemaInitializer({
2  name: 'myInitializer',
3  title: 'Button Text',
4  items: [
5    {
6      name: 'a',
7      title: 'Item A',
8      foo: 'bar',
9      Component: Demo,
10    },
11  ],
12});
13
14/**
15 * 通过 useSchemaInitializerItem() 获取到的是
16 *  {
17 *    name: 'a',
18 *    title: 'Item A',
19 *    foo: 'bar',
20 *    Component: Demo,
21 * }
22 */
23const Demo = () => {
24  const { title, foo } = useSchemaInitializerItem();
25  return <div>{ title } - { foo }</div>
26}

内置组件和类型

typeComponent效果
itemSchemaInitializerItem文本
itemGroupSchemaInitializerItemGroup分组,同 antd Menu 组件的 type: 'group'
subMenuSchemaInitializerSubMenu子菜单,同 antd Menu 组件的子菜单
dividerSchemaInitializerDivider分割线,同 antd Menu 组件的 type: 'divider'
switchSchemaInitializerSwitch开关
actionModalSchemaInitializerActionModal弹窗

以下每个示例都提供了 2 种定义方式,一种是通过 Component 定义,另一种是通过 type 定义。

type: 'item' &SchemaInitializerItem

文本项。

1interface SchemaInitializerItemProps {
2  style?: React.CSSProperties;
3  className?: string;
4  name?: string;
5  icon?: React.ReactNode;
6  title?: React.ReactNode;
7  items?: SchemaInitializerItemType[];
8  onClick?: (args?: any) => any;
9}

核心参数是 titleicononClickitems,其中 onClick 用于插入 Schema,items 用于渲染子列表项。

type: 'itemGroup' & SchemaInitializerItemGroup

分组。

1interface SchemaInitializerItemGroupProps {
2  name: string;
3  title: string;
4  children?: SchemaInitializerOptions['items'];
5  divider?: boolean;
6}

核心参数是 titlechildren,其中 children 用于渲染子列表项,divider 用于渲染分割线。

type: 'switch' & SchemaInitializerSwitch

Switch 切换按钮。

1interface SchemaInitializerSwitchItemProps extends SchemaInitializerItemProps {
2  checked?: boolean;
3  disabled?: boolean;
4}

核心参数是 checkedonClick,其中 onClick 用于插入或者移除 Schema。

type: 'subMenu' & SchemaInitializerSubMenu

子菜单。

type: 'divider' & SchemaInitializerDivider

分割线。

type: 'actionModal' & SchemaInitializerActionModal

Component Mode

Item Mode

SchemaInitializerActionModal 需要加上 isItem 属性

渲染组件

SchemaInitializerChildren

用于自定义渲染多个列表项。

1const Demo = ({ children }) => {
2  // children: [{ name: 'a1', Component: ItemA1 }, { name: 'a2', type: 'item', title: 'ItemA2' }]
3  return <SchemaInitializerChildren>{ children }</SchemaInitializerChildren>
4}
5
6const myInitializer = new SchemaInitializer({
7  name: 'myInitializer',
8  title: 'Button Text',
9  items: [
10    {
11      name: 'test',
12      Component: Demo,
13      children: [
14        {
15          name: 'a1',
16          Component: ItemA1,
17        },
18        {
19          name: 'a2',
20          type: 'item',
21          title: 'ItemA2',
22        }
23      ]
24    }
25  ],
26});

SchemaInitializerChild

用于自定义渲染单个列表项。

1const Demo = (props) => {
2  return <SchemaInitializerChild {...props}/>
3}