SchemaInitializer

new SchemaInitializer(options)

interface SchemaInitializerOptions<P1 = ButtonProps, P2 = {}> {
  Component?: ComponentType<P1>;
  componentProps?: P1;
  style?: React.CSSProperties;
  title?: string;
  icon?: ReactNode;

  items?: SchemaInitializerItemType[];
  ItemsComponent?: ComponentType<P2>;
  itemsComponentProps?: P2;
  itemsComponentStyle?: React.CSSProperties;

  insertPosition?: 'beforeBegin' | 'afterBegin' | 'beforeEnd' | 'afterEnd';
  designable?: boolean;
  wrap?: (s: ISchema) => ISchema;
  onSuccess?: (data: any) => void;
  insert?: InsertType;
  useInsert?: () => InsertType;

  popover?: boolean;
  popoverProps?: PopoverProps;
}

class SchemaInitializer<P1 = ButtonProps, P2 = {}> {
    constructor(options: SchemaInitializerOptions<P1, P2> & { name: string }): SchemaInitializer<P1, P2>;
    add(name: string, item: Omit<SchemaInitializerItemType, 'name'>): void
    get(nestedName: string): SchemaInitializerItemType | undefined
    remove(nestedName: string): void
}

详细解释

  • 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 时,可以使用此参数

示例

基础用法

const myInitializer = new SchemaInitializer({
  name: 'myInitializer',
  title: 'Add Block',
  // 插入位置
  insertPosition: 'beforeEnd',
  items: [
    {
      name: 'a',
      title: 'Item A',
      Component: Demo,
    },
  ],
});

定制化Component

const myInitializer = new SchemaInitializer({
  name: 'myInitializer',
  Component: (props) => (
    <Avatar style={{ cursor: 'pointer' }} {...props}>
      C
    </Avatar>
  ),
  componentProps: {
    size: 'large',
  },
  items: [
    {
      name: 'a',
      title: 'Item A',
      Component: Demo,
    }
  ],
});

不使用 Popover

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

const schema = {
  type: 'void',
  title: Math.random(),
  'x-component': 'Hello',
};
const MyInitializerComponent = () => {
    const { insertBeforeEnd } = useDesignable();
    return <Button onClick={() => insertBeforeEnd(schema)}>Add block</Button>
}

const myInitializer = new SchemaInitializer({
  name: 'myInitializer',
  title: 'Add block',
  popover: false,
  Component: MyInitializerComponent,
});

定制化 Items

const CustomListGridMenu: FC<SchemaInitializerItemsProps<ButtonProps, ListProps<any>>> = (props) => {
  const { items, options, ...others } = props;
  return (
    <List
      {...others}
      style={{ marginTop: 20 }}
      dataSource={items}
      grid={{ gutter: 16, column: 2 }}
      renderItem={(item) => (
        <List.Item style={{ minWidth: 100, textAlign: 'center' }}>
          <SchemaInitializerChild {...item} />
        </List.Item>
      )}
    ></List>
  );
};

const myInitializer = new SchemaInitializer({
  name: 'myInitializer',
  title: 'Button Text',
  ItemsComponent: CustomListGridMenu,
  items: [
    {
      name: 'a',
      title: 'Item A',
      Component: Demo,
    }
  ],
});

options.items 配置详解

类型

interface SchemaInitializerComponentCommonProps {
  title?: string;
  schema?: ISchema;
  style?: React.CSSProperties;
  className?: string;
}

interface SchemaInitializerItemBaseType<T = {}> extends SchemaInitializerComponentCommonProps {
  name: string;
  sort?: number;
  type?: string;
  Component?: string | ComponentType<T>;
  componentProps?: Omit<T, 'children'>;
  useComponentProps?: () => Omit<T, 'children'>;
  useVisible?: () => boolean;
  children?: SchemaInitializerItemType[];
  useChildren?: () => SchemaInitializerItemType[];
  [index: string]: any;
}

两种定义方式:Componenttype

  • 通过 Component 定义
const Demo = () => {
  // 最终渲染 `SchemaInitializerItem`
  return <SchemaInitializerItem title='Demo' />
}

const myInitializer = new SchemaInitializer({
  name: 'myInitializer',
  items: [
    {
      name: 'a',
      Component: Demo, // 通过 Component 定义
    }
  ],
});
  • 通过 type 定义

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

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

const myInitializer = new SchemaInitializer({
  name: 'myInitializer',
  items: [
    {
      name: 'a',
      type: 'item',
      title: 'Demo'
    }
  ],
});

children 和动态方式useChildren

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

动态显示隐藏useVisible

组件属性componentProps 和动态属性useComponentProps

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

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

公共属性和组件属性

{
  name: 'demo',
  title: 'Demo',
  foo: 'bar',
  Component: Demo,
  componentProps: {
    zzz: 'xxx',
  },
}

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

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

在获取上

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

实例方法

const myInitializer = new SchemaInitializer({
  name: 'myInitializer',
  title: 'Button Text',
  items: [
    {
      name: 'a',
      title: 'item a',
      type: 'itemGroup',
      children: [
          {
              name: 'a1',
              title: 'item a1',
          }
      ],
    },
  ],
});
import { SchemaInitializer, Application, useSchemaInitializerRender } from '@tachybase/client';
const myInitializer = new SchemaInitializer({
  name: 'myInitializer',
  title: 'Button Text',
  items: [
    {
      name: 'a',
      title: 'item a',
      type: 'itemGroup',
      children: [
          {
              name: 'a1',
              title: 'item a1',
              type: 'item',
          }
      ],
    },
  ],
});

const Root = () => {
  const { render } = useSchemaInitializerRender('myInitializer');
  return render();
}
const app = new Application({
  schemaInitializers: [myInitializer],
  providers: [Root],
  designable: true,
});

export default app.getRootComponent();

schemaInitializer.add()

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

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

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

  • 示例
myInitializer.add('b', {
    type: 'item',
    title: 'item b',
})

myInitializer.add('a.a2', {
    type: 'item',
    title: 'item a2',
})
import { SchemaInitializer, Application, useSchemaInitializerRender } from '@tachybase/client';
const myInitializer = new SchemaInitializer({
  name: 'myInitializer',
  title: 'Button Text',
  items: [
    {
      name: 'a',
      title: 'item a',
      type: 'itemGroup',
      children: [
          {
              name: 'a1',
              title: 'item a1',
              type: 'item',
          }
      ],
    },
  ],
});

myInitializer.add('b', {
    type: 'item',
    title: 'item b',
})

myInitializer.add('a.a2', {
    type: 'item',
    title: 'item a2',
})

const Root = () => {
  const { render } = useSchemaInitializerRender('myInitializer');
  return render();
}
const app = new Application({
  schemaInitializers: [myInitializer],
  providers: [Root],
  designable: true,
});


export default app.getRootComponent();

schemaInitializer.get()

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

const itemA1 = myInitializer.add('a.a1')

schemaInitializer.remove()

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

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

myInitializer.remove('a')

Hooks

useSchemaInitializer()

用于获取 SchemaInitializer 上下文内容。

  • 类型
export type InsertType = (s: ISchema) => void;

const useSchemaInitializer: () => {
    insert: InsertType;
    options: SchemaInitializerOptions<any>;
    visible?: boolean;
    setVisible?: (v: boolean) => void;
}
  • 参数详解

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

const schema = {
  type: 'void',
  'x-component': 'Hello',
}
const Demo = () => {
    const { insert } = useSchemaInitializer();
     const handleClick = () => {
      insert(schema);
    };
    return  <SchemaInitializerItem title={'Demo'} onClick={handleClick}></SchemaInitializerItem>;
}

useSchemaInitializerRender()

用于渲染 SchemaInitializer

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

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

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

useSchemaInitializerItem()

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

  • 类型
const useSchemaInitializerItem: <T = any>() => T
  • 示例
const myInitializer = new SchemaInitializer({
  name: 'myInitializer',
  title: 'Button Text',
  items: [
    {
      name: 'a',
      title: 'Item A',
      foo: 'bar',
      Component: Demo,
    },
  ],
});

/**
 * 通过 useSchemaInitializerItem() 获取到的是
 *  {
 *    name: 'a',
 *    title: 'Item A',
 *    foo: 'bar',
 *    Component: Demo,
 * }
 */
const Demo = () => {
  const { title, foo } = useSchemaInitializerItem();
  return <div>{ title } - { foo }</div>
}

内置组件和类型

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

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

type: 'item' &SchemaInitializerItem

文本项。

interface SchemaInitializerItemProps {
  style?: React.CSSProperties;
  className?: string;
  name?: string;
  icon?: React.ReactNode;
  title?: React.ReactNode;
  items?: SchemaInitializerItemType[];
  onClick?: (args?: any) => any;
}

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

type: 'itemGroup' & SchemaInitializerItemGroup

分组。

interface SchemaInitializerItemGroupProps {
  name: string;
  title: string;
  children?: SchemaInitializerOptions['items'];
  divider?: boolean;
}

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

type: 'switch' & SchemaInitializerSwitch

Switch 切换按钮。

interface SchemaInitializerSwitchItemProps extends SchemaInitializerItemProps {
  checked?: boolean;
  disabled?: boolean;
}

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

type: 'subMenu' & SchemaInitializerSubMenu

子菜单。

type: 'divider' & SchemaInitializerDivider

分割线。

type: 'actionModal' & SchemaInitializerActionModal

Component Mode

Item Mode

SchemaInitializerActionModal 需要加上 isItem 属性

渲染组件

SchemaInitializerChildren

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

const Demo = ({ children }) => {
  // children: [{ name: 'a1', Component: ItemA1 }, { name: 'a2', type: 'item', title: 'ItemA2' }]
  return <SchemaInitializerChildren>{ children }</SchemaInitializerChildren>
}

const myInitializer = new SchemaInitializer({
  name: 'myInitializer',
  title: 'Button Text',
  items: [
    {
      name: 'test',
      Component: Demo,
      children: [
        {
          name: 'a1',
          Component: ItemA1,
        },
        {
          name: 'a2',
          type: 'item',
          title: 'ItemA2',
        }
      ]
    }
  ],
});

SchemaInitializerChild

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

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