[
  {
    "path": ".dumi/loading.tsx",
    "content": "import React from 'react';\nimport { Spin } from 'antd';\n\nexport default () => {\n  return (\n    <div style={{ width: '100%', height: '100vh', display: 'flex', justifyContent: 'center', paddingTop: '340px'}}>\n      <Spin />\n    </div>\n  );\n}"
  },
  {
    "path": ".dumi/theme/builtins/TypeSchema/index.less",
    "content": ".xr-doc-hero-demo {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  max-width: 961px;\n  margin: 0 auto;\n  height: 500px;\n  position: relative;\n\n  p {\n    margin: 0;\n    padding: 0;\n  }\n\n  .xr-doc-hero-box {\n    position: absolute;\n    top: 0;\n    left: 0;\n    width: 100%;\n    height: 500px;\n  }\n\n  .xr-doc-hero-schema {\n    height: 350px;\n    color: #24292f;\n    font-size: 18px;\n    font-family: \"SFMono-Regular\", consolas, \"Liberation Mono\", menlo, courier, monospace, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";\n  }\n\n  .xr-doc-hero-form {\n    width: 40%;\n    position: absolute;\n    top: -30px;\n    right: 30px;\n    flex: 1;\n    transition: all 0.5s;\n    margin-left: 50px;\n  }\n\n  .xr-doc-hero-loading {\n    margin-bottom: 20px;\n    color: #333;\n    font-size: 16px;\n  }\n\n  .ant-form-item-extra {\n    margin-bottom: -6px;\n    margin-left: 1px;\n  }\n}\n\np {\n  margin: 0;\n  padding: 0;\n}\n"
  },
  {
    "path": ".dumi/theme/builtins/TypeSchema/index.tsx",
    "content": "import React from 'react';\nimport TypeIt from 'typeit-react';\nimport FormRender, { useForm } from 'form-render';\nimport { Button, Card } from 'antd';\nimport './index.less';\n\nconst prefix = 'xr-doc-hero';\n\nconst time = (str: string, time: number) => {\n  let res = '';\n  for (let i = 0; i < time; i++) {\n    res += str;\n  }\n  return res;\n}\nconst red = (val: string) => `<span style=\"color:#cf222e\">${val}</span>`\nconst violet = (val: string) => `<span style=\"color:#8250df\">${val}</span>`\nconst blue = (val: string) => `<span style=\"color:#0550ae\">${val}</span>`\nconst black = (val: string) => `<span style=\"color:#0a3069\">${val}</span>`\nconst br = (times: number) => time('<br/>', times);\nconst space = (times: number) => time('&nbsp;', times);\n\n\nconst TypeSchema: React.FC = () => {\n  const form = useForm();\n  const [schema, setSchema] = React.useState<any>();\n\n  return (\n    <div className={`${prefix}-demo`}>\n      <Card className={`${prefix}-box`}>\n        <div className={`${prefix}-schema`}>\n          <TypeIt\n            getBeforeInit={instance => {\n              instance\n                .options({\n                  speed: 40,\n                })\n                .type('{}')\n                .move(-1, { instant: true })\n                .type(br(2), { instant: true })\n                .move(-1, { instant: true })\n                .type(space(2), { instant: true })\n                .type(`${blue('name')}: {},`)\n                .move(-2, { instant: true })\n                .type(`${br(2)}${space(2)}`, { instant: true })\n                .move(-3, { instant: true })\n                .type(space(4), { instant: true })\n                .type(`${blue('title')}: ${black(`'Name'`)},`)\n                .type(`${br(1)}${space(4)}`, { instant: true })\n                .type(`${blue('type')}: ${black(`'string'`)},`)\n                .type(`${br(1)}${space(4)}`, { instant: true })\n                .exec(() => {\n                  setSchema({\n                    type: 'object',\n                    properties: {\n                      name: {\n                        title: 'Name',\n                        type: 'string',\n                      },\n                    },\n                  });\n                })\n                .type(`${blue('placeholder')}: ${black(`'Please input your name.'`)},`)\n                .exec(() => {\n                  setSchema({\n                    type: 'object',\n                    properties: {\n                      name: {\n                        title: 'Name',\n                        type: 'string',\n                        placeholder: 'Please input your name.'\n                      },\n                    },\n                  });\n                })\n                .move(5)\n                .type(`${br(1)}${space(2)}`, { instant: true })\n                .type(`${blue('fruit')}: {}`)\n                .move(-1, { instant: true })\n                .type(`${br(2)}${space(2)}`, { instant: true })\n                .move(-3, { instant: true })\n                .type(space(4), { instant: true })\n                .type(`${blue('title')}: ${black(`'Favorite Fruit'`)},`)\n                .type(`${br(1)}${space(4)}`, { instant: true })\n                .type(`${blue('type')}: ${black(`'number'`)},`)\n                .exec(() => {\n                  setSchema({\n                    type: 'object',\n                    properties: {\n                      name: {\n                        title: 'Name',\n                        type: 'string',\n                        placeholder: 'Please input your name.'\n                      },\n                      fruit: {\n                        title: 'Favorite Fruit',\n                        type: 'string',\n                      },\n                    },\n                  });\n                })\n                .type(`${br(1)}${space(4)}`, { instant: true })\n                .type(`${blue('enum')}: ${black(`[${violet('1')}, ${violet('2')}, ${violet('3')}]`)},`)\n                .type(`${br(1)}${space(4)}`, { instant: true })\n                .type(`${blue('enumNames')}:  ${black(`['apple', 'cherry', 'pear'],`)}`)\n                .exec(() => {\n                  setSchema({\n                    type: 'object',\n                    properties: {\n                      name: {\n                        title: 'Name',\n                        type: 'string',\n                        placeholder: 'Please input your name.'\n                      },\n                      fruit: {\n                        title: 'Favorite Fruit',\n                        type: 'number',\n                        enum: [1, 2, 3],\n                        enumNames: ['apple', 'cherry', 'pear'],\n                      },\n                    },\n                  });\n                })\n                .type(`${br(1)}${space(4)}`, { instant: true })\n                .type(`${blue('required')}: ${violet('true')},`)\n                .exec(() => {\n                  setSchema({\n                    type: 'object',\n                    properties: {\n                      name: {\n                        title: 'Name',\n                        type: 'string',\n                        placeholder: 'Please input your name.'\n                      },\n                      fruit: {\n                        title: 'Favorite Fruit',\n                        type: 'number',\n                        enum: [1, 2, 3],\n                        enumNames: ['apple', 'cherry', 'pear'],\n                        required: true,\n                      },\n                    },\n                  });\n                })\n                .type(`${br(1)}${space(4)}`, { instant: true })\n                .type(`${blue('default')}: ${black(`2,`)}`)\n                .exec(() => {\n                  setSchema({\n                    type: 'object',\n                    properties: {\n                      name: {\n                        title: 'Name',\n                        type: 'string',\n                        placeholder: 'Please input your name.'\n                      },\n                      fruit: {\n                        title: 'Favorite Fruit',\n                        type: 'number',\n                        enum: [1, 2, 3],\n                        enumNames: ['apple', 'cherry', 'pear'],\n                        required: true,\n                        default: 2,\n                      },\n                    },\n                  });\n                })\n                .move(-167, { instant: true })\n                .type(`${br(1)}${space(4)}`, { instant: true })\n                .type(`${blue('extra')}: ${red(`'Easiest way to build a form!'`)}`)\n                .exec(() => {\n                  setSchema({\n                    type: 'object',\n                    properties: {\n                      name: {\n                        title: 'Name',\n                        type: 'string',\n                        placeholder: 'Please input your name.',\n                        extra: 'Easiest way to build a form!'\n                      },\n                      fruit: {\n                        title: 'Favorite Fruit',\n                        type: 'number',\n                        enum: [1, 2, 3],\n                        enumNames: ['apple', 'cherry', 'pear'],\n                        required: true,\n                        default: 2,\n                      },\n                    },\n                  });\n                })\n              return instance;\n            }}>\n          </TypeIt>\n        </div>\n      </Card>\n      <Card title=\"FormRender\" className={`${prefix}-form`}>\n        {schema ? (\n          <FormRender\n            schema={schema}\n            form={form}\n            onFinish={(data) => {\n              window.alert(JSON.stringify(data))\n            }}\n          />\n        ) : (\n          <div className={`${prefix}-loading`}>Transform schema to form...</div>\n        )}\n        <Button onClick={form.submit} type=\"primary\">\n          Submit\n        </Button>\n      </Card>\n    </div>\n  );\n};\n\nexport default TypeSchema;\n"
  },
  {
    "path": ".dumi/theme/layouts/GlobalLayout/index.tsx",
    "content": "import React from 'react';\nimport { ConfigProvider } from 'antd';\nimport { useOutlet } from 'dumi';\n\nconst GlobalLayout: React.FC = () => {\n  const outlet = useOutlet();\n  return (\n    <ConfigProvider\n    >\n      {outlet}\n    </ConfigProvider>\n  );\n};\n\nexport default GlobalLayout;\n"
  },
  {
    "path": ".dumi/theme/slots/Header/GithubLink.tsx",
    "content": "import React from 'react';\nimport { Button, Tooltip } from 'antd';\n\nimport { GithubFilled } from '@ant-design/icons';\n\nconst GithubLink: React.FC = () => {\n  return (\n    <Tooltip title=\"Github\" placement=\"bottom\">\n      <Button\n        href=\"https://github.com/alibaba/x-render\"\n        target=\"_blank\"\n        rel=\"noreferrer\"\n        icon={<GithubFilled style={{ fontSize: 16 }} />}\n        type=\"text\"\n      />\n    </Tooltip>\n  );\n};\n\nexport default GithubLink;\n"
  },
  {
    "path": ".dumi/theme/slots/Header/Navigation.tsx",
    "content": "import { CodeOutlined, DownOutlined, NodeIndexOutlined } from '@ant-design/icons';\nimport { Menu } from 'antd';\nimport { Link } from 'dumi';\nimport React from 'react';\n\nconst Navigation: React.FC = () => {\n  const items: any = [\n    {\n      label: <Link to=\"/form-render\">FormRender</Link>,\n      key: 'form-render',\n    },\n    {\n      label: <Link to=\"/table-render\">TableRender</Link>,\n      key: 'table-render',\n    },\n    {\n      label: <Link to=\"/xflow\">XFlow</Link>,\n      key: 'xflow',\n    },\n    // {\n    //   label: <Link to=\"/xflow\">XFlow</Link>,\n    //   key: 'xflow',\n    // },\n    {\n      label: <Link to=\"/form-render-mobile\">FRMobile</Link>,\n      key: 'form-render-mobile',\n    },\n    // {\n    //   label: <Link to=\"/data-render\">DataView</Link>,\n    //   key: 'data-render'\n    // },\n    {\n      label: <Link to=\"/playground\">Playground</Link>,\n      key: 'playground',\n    },\n    // {\n    //   label: <Link to=\"/schema-builder\">SchemaBuilder</Link>,\n    //   key: 'schema-builder',\n    // },\n    {\n      label: (\n        <div>\n          <span>更多</span>\n          <DownOutlined\n            style={{ fontSize: 12, color: '#666', marginLeft: 3 }}\n          />\n        </div>\n      ),\n      children: [\n        {\n          label: <Link to=\"/schema-builder\">SchemaBuilder</Link>,\n          key: 'schema-builder',\n          icon: <NodeIndexOutlined />,\n        },\n        {\n          label: (\n            <a href=\"https://1.xrender.fun/chart-render\" target=\"_black\">\n              ChartRender\n            </a>\n          ),\n          key: 'chart-render',\n          icon: <CodeOutlined />,\n        },\n      ],\n    },\n  ];\n\n  return <Menu disabledOverflow items={items} mode=\"horizontal\" />;\n};\n\nexport default Navigation;\n"
  },
  {
    "path": ".dumi/theme/slots/Header/ThemeSwitch.tsx",
    "content": "import React from 'react';\nimport { Dropdown, Button } from 'antd';\nimport { usePrefersColor } from 'dumi';\nimport { createFromIconfontCN } from '@ant-design/icons';\n\nconst themes = [\n  {\n    key: 'light',\n    label: 'Light',\n    icon: 'icon-Daytimemode',\n  },\n  {\n    key: 'dark',\n    label: 'Dark',\n    icon: 'icon-nightmode',\n  },\n  {\n    key: 'auto',\n    label: 'System',\n    icon: 'icon-computer',\n  },\n];\n\nconst IconFont = createFromIconfontCN({\n  scriptUrl: '//at.alicdn.com/t/a/font_3889511_s7v69kz4pz.js',\n});\n\nconst ThemeSwitch: React.FC = () => {\n  const [theme, preferTheme, changeTheme] = usePrefersColor();\n\n  const { icon } = themes.find(i => i.key === preferTheme) || {};\n\n  const getIconName = (name: string) => {\n    return name + `${theme === 'dark' ? '-dark' : ''}`;\n  };\n\n  return (\n    <Dropdown\n      menu={{\n        items: themes.map(i => ({\n          label: i.label,\n          key: i.key,\n          icon: (\n            <IconFont type={getIconName(i.icon)} style={{ fontSize: 20 }} />\n          ),\n          onClick: () => changeTheme(i.key as 'light' | 'dark' | 'auto'),\n        })),\n      }}\n    >\n      <Button\n        type=\"text\"\n        icon={\n          icon && <IconFont type={getIconName(icon)} style={{ fontSize: 22 }} />\n        }\n      />\n    </Dropdown>\n  );\n};\n\nexport default ThemeSwitch;\n"
  },
  {
    "path": ".dumi/theme/slots/Header/index.less",
    "content": ".xr-doc-header {\n  backdrop-filter: blur(6px);\n  background-color: rgba(255, 255,255, 0.8);\n  box-shadow: 0 1px 2px 0 rgb(0 0 0 / 3%), 0 1px 6px -1px rgb(0 0 0 / 2%), 0 2px 4px 0 rgb(0 0 0 / 2%);\n  width: 100%;\n  position: sticky;\n  top: 0;\n  z-index: 10;\n  margin-bottom: 20px;\n\n  @{dark-selector} & {\n    background-color: #050709e6;\n  }\n\n  &-nav-item {\n    color: #333;\n    cursor: pointer;\n  }\n\n  &-nva-item:hover {\n    color: #1677ff;\n  }\n\n  .ant-menu {\n    border: none;\n    line-height: unset;\n    display: flex;\n    align-items: center;\n    background: transparent;\n    font-size: 15px;\n\n    @{dark-selector} & {\n      color: #c6c9cd;\n    }\n\n    .ant-menu-item::after, .ant-menu-submenu::after {\n      bottom: -23px;\n    }\n  }\n\n  .dumi-default-search-bar-input {\n    border: none;\n    width: 220px;\n  }\n\n  .dumi-default-search-shortcut {\n    margin-left: 10px;\n  }\n}\n\n.dumi-default-doc-layout > main {\n  max-width: 100% !important;\n  padding: 0 !important;\n}\n\n.dumi-default-sidebar {\n  width: 200px;\n}\n\n.dumi-default-content {\n  border-top-left-radius: 0 !important;\n  border-top-right-radius: 0 !important;\n}\n\n.dumi-default-hero {\n  padding-top: 140px !important;\n  height: 600px !important;\n}\n\n.dumi-default-hero-title {\n  font-size: 80px !important;\n}\n\n.dumi-default-hero > p {\n  margin: 0 !important;\n}\n\n.dumi-default-hero-actions {\n  margin-top: 32px !important;\n\n}\n\n.dumi-default-features {\n  max-width: 960px !important;\n}\n\n.dumi-default-features-item {\n  text-align: center;\n}\n\n.dumi-default-sidebar ~ .dumi-default-content {\n  padding: 48px 48px 0 !important;\n}\n\n.dumi-default-content {\n  padding: 0 !important;\n}\n\n.xr-doc-header ~ main {\n  padding: 0 20px !important;\n}"
  },
  {
    "path": ".dumi/theme/slots/Header/index.tsx",
    "content": "import React from 'react';\nimport DumiLogo from 'dumi/theme-default/slots/Logo';\nimport DumiSearch from 'dumi/theme-default/slots/SearchBar';\nimport { Row, Col, Space, Divider, Select, Badge } from 'antd';\nimport Navigation from './Navigation';\nimport ThemeSwitch from './ThemeSwitch';\nimport GithubLink from './GithubLink';\n\nimport './index.less';\n\nconst prefix = 'xr-doc-header';\n\nconst Header: React.FC = () => {\n  if (window.top.location.href.includes('/schema-builder-online')) {\n    return null;\n  }\n\n  return (\n    <header className={prefix}>\n      <Row align=\"middle\" justify=\"space-between\" style={{ padding: '10px 24px' }}>\n        <Col span={12}>\n          <Space size={20}>\n            <div style={{ paddingRight: '12px' }}>\n              <DumiLogo/>\n            </div>\n          </Space>\n        </Col>\n        <Col span={12}>\n          <Space size={0} style={{ float: 'right'}}>\n            <DumiSearch/>\n            <Navigation />\n            <Select\n              options={[{ label: '2.x', value: 2 }, { label: '1.x', value: 1}]}\n              style={{ width: '70px' }}\n              defaultValue={2}\n              onChange={(value) => {\n                if (value === 2) {\n                  return;\n                }\n                window.open('https://1.xrender.fun/')\n              }}\n            />\n            <Divider type=\"vertical\" style={{ marginLeft: 0 }} />\n            <Space>\n              <ThemeSwitch />\n              <GithubLink />\n            </Space>\n          </Space>\n        </Col>\n      </Row>\n    </header>\n  );\n};\n\nexport default Header;\n"
  },
  {
    "path": ".dumi/tmp-production/core/EmptyRoute.tsx",
    "content": "// @ts-nocheck\n// This file is generated by Umi automatically\n// DO NOT CHANGE IT MANUALLY!\nimport React from 'react';\nimport { Outlet, useOutletContext } from 'umi';\nexport default function EmptyRoute() {\n  const context = useOutletContext();\n  return <Outlet context={context} />;\n}\n"
  },
  {
    "path": ".dumi/tmp-production/core/defineApp.ts",
    "content": "// @ts-nocheck\n// This file is generated by Umi automatically\n// DO NOT CHANGE IT MANUALLY!\ninterface IDefaultRuntimeConfig {\n  onRouteChange?: (props: { routes: any, clientRoutes: any, location: any, action: any, isFirst: boolean }) => void;\n  patchRoutes?: (props: { routes: any }) => void;\n  patchClientRoutes?: (props: { routes: any }) => void;\n  render?: (oldRender: () => void) => void;\n  rootContainer?: (lastRootContainer: JSX.Element, args?: any) => void;\n  [key: string]: any;\n}\nexport type RuntimeConfig = IDefaultRuntimeConfig\n\nexport function defineApp(config: RuntimeConfig): RuntimeConfig {\n  return config;\n}\n"
  },
  {
    "path": ".dumi/tmp-production/core/exportStaticRuntimePlugin.ts",
    "content": "// @ts-nocheck\n// This file is generated by Umi automatically\n// DO NOT CHANGE IT MANUALLY!\nexport function modifyClientRenderOpts(memo: any) {\n  const { history, hydrate } = memo;\n\n  return {\n    ...memo,\n    hydrate: hydrate && ![\"/~demos/:id\"].includes(history.location.pathname),\n  };\n}\n"
  },
  {
    "path": ".dumi/tmp-production/core/helmet.ts",
    "content": "// @ts-nocheck\n// This file is generated by Umi automatically\n// DO NOT CHANGE IT MANUALLY!\nimport React from 'react';\nimport { HelmetProvider } from '/Users/zhanbo/happy/x-render/node_modules/@umijs/preset-umi/node_modules/@umijs/renderer-react';\nimport { context } from './helmetContext';\n\nexport const innerProvider = (container) => {\n  return React.createElement(HelmetProvider, { context }, container);\n}\n"
  },
  {
    "path": ".dumi/tmp-production/core/helmetContext.ts",
    "content": "// @ts-nocheck\n// This file is generated by Umi automatically\n// DO NOT CHANGE IT MANUALLY!\nexport const context = {};\n"
  },
  {
    "path": ".dumi/tmp-production/core/history.ts",
    "content": "// @ts-nocheck\n// This file is generated by Umi automatically\n// DO NOT CHANGE IT MANUALLY!\nimport { createHashHistory, createMemoryHistory, createBrowserHistory } from '/Users/zhanbo/happy/x-render/node_modules/@umijs/preset-umi/node_modules/@umijs/renderer-react';\nimport type { UmiHistory } from './historyIntelli';\n\nlet history: UmiHistory;\nlet basename: string = '/';\nexport function createHistory(opts: any) {\n  let h;\n  if (opts.type === 'hash') {\n    h = createHashHistory();\n  } else if (opts.type === 'memory') {\n    h = createMemoryHistory(opts);\n  } else {\n    h = createBrowserHistory();\n  }\n  if (opts.basename) {\n    basename = opts.basename;\n  }\n\n  history = {\n    ...h,\n    push(to, state) {\n      h.push(patchTo(to, h), state);\n    },\n    replace(to, state) {\n      h.replace(patchTo(to, h), state);\n    },\n    get location() {\n      return h.location;\n    },\n    get action() {\n      return h.action;\n    }\n  }\n\n  return h;\n}\n\n// Patch `to` to support basename\n// Refs:\n// https://github.com/remix-run/history/blob/3e9dab4/packages/history/index.ts#L484\n// https://github.com/remix-run/history/blob/dev/docs/api-reference.md#to\nfunction patchTo(to: any, h: History) {\n  if (typeof to === 'string') {\n    return `${stripLastSlash(basename)}${to}`;\n  } else if (typeof to === 'object') {\n\n    const currentPathname = h.location.pathname;\n\n    return {\n      ...to,\n      pathname: to.pathname? `${stripLastSlash(basename)}${to.pathname}` : currentPathname,\n    };\n  } else {\n    throw new Error(`Unexpected to: ${to}`);\n  }\n}\n\nfunction stripLastSlash(path) {\n  return path.slice(-1) === '/' ? path.slice(0, -1) : path;\n}\n\nexport { history };\n"
  },
  {
    "path": ".dumi/tmp-production/core/historyIntelli.ts",
    "content": "// @ts-nocheck\n// This file is generated by Umi automatically\n// DO NOT CHANGE IT MANUALLY!\nimport { getRoutes } from './route'\nimport type { History } from '/Users/zhanbo/happy/x-render/node_modules/@umijs/preset-umi/node_modules/@umijs/renderer-react'\n\ntype Routes = Awaited<ReturnType<typeof getRoutes>>['routes']\ntype AllRoute = Routes[keyof Routes]\ntype IsRoot<T extends any> = 'parentId' extends keyof T ? false : true\n\n// show `/` in not `layout / wrapper` only\ntype GetAllRouteWithoutLayout<Item extends AllRoute> = Item extends any\n  ? 'isWrapper' extends keyof Item\n    ? never\n    : 'isLayout' extends keyof Item\n    ? never\n    : Item\n  : never\ntype AllRouteWithoutLayout = GetAllRouteWithoutLayout<AllRoute>\ntype IndexRoutePathname = '/' extends AllRouteWithoutLayout['path']\n  ? '/'\n  : never\n\ntype GetChildrens<T extends any> = T extends any\n  ? IsRoot<T> extends true\n    ? never\n    : T\n  : never\ntype Childrens = GetChildrens<AllRoute>\ntype Root = Exclude<AllRoute, Childrens>\ntype AllIds = AllRoute['id']\n\ntype GetChildrensByParentId<\n  Id extends AllIds,\n  Item = AllRoute\n> = Item extends any\n  ? 'parentId' extends keyof Item\n    ? Item['parentId'] extends Id\n      ? Item\n      : never\n    : never\n  : never\n\ntype RouteObject<\n  Id extends AllIds,\n  Item = GetChildrensByParentId<Id>\n> = IsNever<Item> extends true\n  ? ''\n  : Item extends AllRoute\n  ? {\n      [Key in Item['path'] as TrimSlash<Key>]: UnionMerge<\n        RouteObject<Item['id']>\n      >\n    }\n  : never\n\ntype GetRootRouteObject<Item extends Root> = Item extends Root\n  ? {\n      [K in Item['path'] as TrimSlash<K>]: UnionMerge<RouteObject<Item['id']>>\n    }\n  : never\ntype MergedResult = UnionMerge<GetRootRouteObject<Root>>\n\n// --- patch history types ---\n\ntype HistoryTo = Parameters<History['push']>['0']\ntype HistoryPath = Exclude<HistoryTo, string>\n\ntype UmiPathname = Path<MergedResult> | (string & {})\ninterface UmiPath extends HistoryPath {\n  pathname: UmiPathname\n}\ntype UmiTo = UmiPathname | UmiPath\n\ntype UmiPush = (to: UmiTo, state?: any) => void\ntype UmiReplace = (to: UmiTo, state?: any) => void\nexport interface UmiHistory extends History {\n  push: UmiPush\n  replace: UmiReplace\n}\n\n// --- type utils ---\ntype TrimLeftSlash<T extends string> = T extends `/${infer R}`\n  ? TrimLeftSlash<R>\n  : T\ntype TrimRightSlash<T extends string> = T extends `${infer R}/`\n  ? TrimRightSlash<R>\n  : T\ntype TrimSlash<T extends string> = TrimLeftSlash<TrimRightSlash<T>>\n\ntype IsNever<T> = [T] extends [never] ? true : false\ntype IsEqual<A, B> = (<G>() => G extends A ? 1 : 2) extends <G>() => G extends B\n  ? 1\n  : 2\n  ? true\n  : false\n\ntype UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (\n  k: infer I\n) => void\n  ? I\n  : never\ntype UnionMerge<U> = UnionToIntersection<U> extends infer O\n  ? { [K in keyof O]: O[K] }\n  : never\n\ntype ExcludeEmptyKey<T> = IsEqual<T, ''> extends true ? never : T\n\ntype PathConcat<\n  TKey extends string,\n  TValue,\n  N = TrimSlash<TKey>\n> = TValue extends string\n  ? ExcludeEmptyKey<N>\n  :\n      | ExcludeEmptyKey<N>\n      | `${N & string}${IsNever<ExcludeEmptyKey<N>> extends true\n          ? ''\n          : '/'}${UnionPath<TValue>}`\n\ntype UnionPath<T> = {\n  [K in keyof T]-?: PathConcat<K & string, T[K]>\n}[keyof T]\n\ntype MakeSureLeftSlash<T> = T extends any\n  ? `/${TrimRightSlash<T & string>}`\n  : never\n\n// exclude `/*`, because it always at the top of the IDE tip list\ntype Path<T, K = UnionPath<T>> = Exclude<MakeSureLeftSlash<K>, '/*'> | IndexRoutePathname\n"
  },
  {
    "path": ".dumi/tmp-production/core/plugin.ts",
    "content": "// @ts-nocheck\n// This file is generated by Umi automatically\n// DO NOT CHANGE IT MANUALLY!\nimport * as Plugin_0 from '@@/core/exportStaticRuntimePlugin.ts';\nimport * as Plugin_1 from '@@/core/helmet.ts';\nimport * as Plugin_2 from '@@/dumi/meta/runtime.ts';\nimport * as Plugin_3 from '@@/dumi/locales/runtime.tsx';\nimport * as Plugin_4 from '@@/dumi/exportStaticRuntimePlugin.ts';\nimport { PluginManager } from 'umi';\n\nfunction __defaultExport (obj) {\n  if (obj.default) {\n    return typeof obj.default === 'function' ? obj.default() :  obj.default\n  }\n  return obj;\n}\nexport function getPlugins() {\n  return [\n    {\n      apply: Plugin_0,\n      path: process.env.NODE_ENV === 'production' ? void 0 : '@@/core/exportStaticRuntimePlugin.ts',\n    },\n    {\n      apply: Plugin_1,\n      path: process.env.NODE_ENV === 'production' ? void 0 : '@@/core/helmet.ts',\n    },\n    {\n      apply: Plugin_2,\n      path: process.env.NODE_ENV === 'production' ? void 0 : '@@/dumi/meta/runtime.ts',\n    },\n    {\n      apply: Plugin_3,\n      path: process.env.NODE_ENV === 'production' ? void 0 : '@@/dumi/locales/runtime.tsx',\n    },\n    {\n      apply: Plugin_4,\n      path: process.env.NODE_ENV === 'production' ? void 0 : '@@/dumi/exportStaticRuntimePlugin.ts',\n    },\n  ];\n}\n\nexport function getValidKeys() {\n  return ['patchRoutes','patchClientRoutes','modifyContextOpts','modifyClientRenderOpts','rootContainer','innerProvider','i18nProvider','accessProvider','dataflowProvider','outerProvider','render','onRouteChange',];\n}\n\nlet pluginManager = null;\n\nexport function createPluginManager() {\n  pluginManager = PluginManager.create({\n    plugins: getPlugins(),\n    validKeys: getValidKeys(),\n  });\n\n\n  return pluginManager;\n}\n\nexport function getPluginManager() {\n  return pluginManager;\n}\n"
  },
  {
    "path": ".dumi/tmp-production/core/pluginConfig.ts",
    "content": "// @ts-nocheck\n// This file is generated by Umi automatically\n// DO NOT CHANGE IT MANUALLY!\nimport { IConfigFromPluginsJoi } from \"./pluginConfigJoi.d\";\n\ninterface IConfigTypes {\n    codeSplitting: {\n    jsStrategy: \"bigVendors\" | \"depPerChunk\" | \"granularChunks\";\n    jsStrategyOptions?: ({\n\n} | undefined);\n    cssStrategy?: (\"mergeAll\" | undefined);\n    cssStrategyOptions?: ({\n\n} | undefined);\n};\n    title: string;\n    styles: Array<string | {\n    src?: (string | undefined);\n} | {\n    content?: (string | undefined);\n} | { [x: string]: any }>;\n    scripts: Array<string | {\n    src?: (string | undefined);\n} | {\n    content?: (string | undefined);\n} | { [x: string]: any }>;\n    routes: Array<{\n    component?: (string | undefined);\n    layout?: (false | undefined);\n    path?: (string | undefined);\n    redirect?: (string | undefined);\n    routes?: IConfigTypes['routes'];\n    wrappers?: (Array<string> | undefined);\n} | { [x: string]: any }>;\n    reactRouter5Compat: boolean | {\n\n};\n    presets: Array<string>;\n    plugins: Array<string>;\n    npmClient: \"pnpm\" | \"tnpm\" | \"cnpm\" | \"yarn\" | \"npm\";\n    mountElementId: string;\n    metas: Array<{\n    charset?: (string | undefined);\n    content?: (string | undefined);\n    \"http-equiv\"?: (string | undefined);\n    name?: (string | undefined);\n} | { [x: string]: any }>;\n    links: Array<{\n    crossorigin?: (string | undefined);\n    href?: (string | undefined);\n    hreflang?: (string | undefined);\n    media?: (string | undefined);\n    referrerpolicy?: (string | undefined);\n    rel?: (string | undefined);\n    sizes?: (any | undefined);\n    title?: (any | undefined);\n    type?: (any | undefined);\n} | { [x: string]: any }>;\n    historyWithQuery: {\n\n};\n    history: {\n    type: \"browser\" | \"hash\" | \"memory\";\n};\n    headScripts: Array<string | {\n    src?: (string | undefined);\n} | {\n    content?: (string | undefined);\n} | { [x: string]: any }>;\n    esbuildMinifyIIFE: boolean;\n    conventionRoutes: {\n    base?: (string | undefined);\n    exclude?: (Array<any> | undefined);\n};\n    base: string;\n    analyze: {\n\n};\n    writeToDisk: boolean;\n    theme: { [x: string]: any };\n    targets: { [x: string]: any };\n    svgr: { [x: string]: any };\n    svgo: { [x: string]: any } | boolean;\n    styleLoader: { [x: string]: any };\n    srcTranspilerOptions: {\n    esbuild?: ({ [x: string]: any } | undefined);\n    swc?: ({ [x: string]: any } | undefined);\n};\n    srcTranspiler: \"babel\" | \"esbuild\" | \"swc\" | \"none\";\n    sassLoader: { [x: string]: any };\n    runtimePublicPath: {\n\n};\n    purgeCSS: { [x: string]: any };\n    publicPath: string;\n    proxy: { [x: string]: any } | Array<any>;\n    postcssLoader: { [x: string]: any };\n    outputPath: string;\n    normalCSSLoaderModules: { [x: string]: any };\n    mfsu: {\n    cacheDirectory?: (string | undefined);\n    chainWebpack?: (((...args: any[]) => unknown) | undefined);\n    esbuild?: (boolean | undefined);\n    exclude?: (Array<string | any> | undefined);\n    include?: (Array<string> | undefined);\n    mfName?: (string | undefined);\n    remoteAliases?: (Array<string> | undefined);\n    remoteName?: (string | undefined);\n    runtimePublicPath?: (boolean | undefined);\n    shared?: ({ [x: string]: any } | undefined);\n    strategy?: (\"eager\" | \"normal\" | undefined);\n} | boolean;\n    mdx: {\n    loader?: (string | undefined);\n    loaderOptions?: ({ [x: string]: any } | undefined);\n};\n    manifest: {\n    basePath?: (string | undefined);\n    fileName?: (string | undefined);\n};\n    lessLoader: { [x: string]: any };\n    jsMinifierOptions: { [x: string]: any };\n    jsMinifier: \"esbuild\" | \"swc\" | \"terser\" | \"uglifyJs\" | \"none\";\n    inlineLimit: number;\n    ignoreMomentLocale: boolean;\n    https: {\n    cert?: (string | undefined);\n    hosts?: (Array<string> | undefined);\n    http2?: (boolean | undefined);\n    key?: (string | undefined);\n};\n    hash: boolean;\n    forkTSChecker: { [x: string]: any };\n    fastRefresh: boolean;\n    extraPostCSSPlugins: Array<any>;\n    extraBabelPresets: Array<string | Array<any>>;\n    extraBabelPlugins: Array<string | Array<any>>;\n    extraBabelIncludes: Array<string | any>;\n    externals: { [x: string]: any } | string | ((...args: any[]) => unknown);\n    esm: {\n\n};\n    devtool: \"cheap-source-map\" | \"cheap-module-source-map\" | \"eval\" | \"eval-source-map\" | \"eval-cheap-source-map\" | \"eval-cheap-module-source-map\" | \"eval-nosources-cheap-source-map\" | \"eval-nosources-cheap-module-source-map\" | \"eval-nosources-source-map\" | \"source-map\" | \"hidden-source-map\" | \"hidden-nosources-cheap-source-map\" | \"hidden-nosources-cheap-module-source-map\" | \"hidden-nosources-source-map\" | \"hidden-cheap-source-map\" | \"hidden-cheap-module-source-map\" | \"inline-source-map\" | \"inline-cheap-source-map\" | \"inline-cheap-module-source-map\" | \"inline-nosources-cheap-source-map\" | \"inline-nosources-cheap-module-source-map\" | \"inline-nosources-source-map\" | \"nosources-source-map\" | \"nosources-cheap-source-map\" | \"nosources-cheap-module-source-map\" | \"#cheap-source-map\" | \"#cheap-module-source-map\" | \"#eval\" | \"#eval-source-map\" | \"#eval-cheap-source-map\" | \"#eval-cheap-module-source-map\" | \"#eval-nosources-cheap-source-map\" | \"#eval-nosources-cheap-module-source-map\" | \"#eval-nosources-source-map\" | \"#source-map\" | \"#hidden-source-map\" | \"#hidden-nosources-cheap-source-map\" | \"#hidden-nosources-cheap-module-source-map\" | \"#hidden-nosources-source-map\" | \"#hidden-cheap-source-map\" | \"#hidden-cheap-module-source-map\" | \"#inline-source-map\" | \"#inline-cheap-source-map\" | \"#inline-cheap-module-source-map\" | \"#inline-nosources-cheap-source-map\" | \"#inline-nosources-cheap-module-source-map\" | \"#inline-nosources-source-map\" | \"#nosources-source-map\" | \"#nosources-cheap-source-map\" | \"#nosources-cheap-module-source-map\" | \"@cheap-source-map\" | \"@cheap-module-source-map\" | \"@eval\" | \"@eval-source-map\" | \"@eval-cheap-source-map\" | \"@eval-cheap-module-source-map\" | \"@eval-nosources-cheap-source-map\" | \"@eval-nosources-cheap-module-source-map\" | \"@eval-nosources-source-map\" | \"@source-map\" | \"@hidden-source-map\" | \"@hidden-nosources-cheap-source-map\" | \"@hidden-nosources-cheap-module-source-map\" | \"@hidden-nosources-source-map\" | \"@hidden-cheap-source-map\" | \"@hidden-cheap-module-source-map\" | \"@inline-source-map\" | \"@inline-cheap-source-map\" | \"@inline-cheap-module-source-map\" | \"@inline-nosources-cheap-source-map\" | \"@inline-nosources-cheap-module-source-map\" | \"@inline-nosources-source-map\" | \"@nosources-source-map\" | \"@nosources-cheap-source-map\" | \"@nosources-cheap-module-source-map\" | \"#@cheap-source-map\" | \"#@cheap-module-source-map\" | \"#@eval\" | \"#@eval-source-map\" | \"#@eval-cheap-source-map\" | \"#@eval-cheap-module-source-map\" | \"#@eval-nosources-cheap-source-map\" | \"#@eval-nosources-cheap-module-source-map\" | \"#@eval-nosources-source-map\" | \"#@source-map\" | \"#@hidden-source-map\" | \"#@hidden-nosources-cheap-source-map\" | \"#@hidden-nosources-cheap-module-source-map\" | \"#@hidden-nosources-source-map\" | \"#@hidden-cheap-source-map\" | \"#@hidden-cheap-module-source-map\" | \"#@inline-source-map\" | \"#@inline-cheap-source-map\" | \"#@inline-cheap-module-source-map\" | \"#@inline-nosources-cheap-source-map\" | \"#@inline-nosources-cheap-module-source-map\" | \"#@inline-nosources-source-map\" | \"#@nosources-source-map\" | \"#@nosources-cheap-source-map\" | \"#@nosources-cheap-module-source-map\" | boolean;\n    depTranspiler: \"babel\" | \"esbuild\" | \"swc\" | \"none\";\n    define: { [x: string]: any };\n    deadCode: {\n    context?: (string | undefined);\n    detectUnusedExport?: (boolean | undefined);\n    detectUnusedFiles?: (boolean | undefined);\n    exclude?: (Array<string> | undefined);\n    failOnHint?: (boolean | undefined);\n    patterns?: (Array<string> | undefined);\n};\n    cssMinifierOptions: { [x: string]: any };\n    cssMinifier: \"cssnano\" | \"esbuild\" | \"parcelCSS\" | \"none\";\n    cssLoaderModules: { [x: string]: any };\n    cssLoader: { [x: string]: any };\n    copy: Array<{\n    from: string;\n    to: string;\n} | string>;\n    cacheDirectoryPath: string;\n    babelLoaderCustomize: string;\n    autoprefixer: { [x: string]: any };\n    autoCSSModules: boolean;\n    alias: { [x: string]: any };\n    crossorigin: boolean | {\n    includes?: (Array<any> | undefined);\n};\n    esmi: {\n    cdnOrigin: string;\n    shimUrl?: (string | undefined);\n};\n    exportStatic: {\n    extraRoutePaths?: (((...args: any[]) => unknown) | Array<string> | undefined);\n};\n    favicons: Array<string>;\n    helmet: boolean;\n    icons: {\n    autoInstall?: ({\n\n} | undefined);\n    defaultComponentConfig?: ({\n\n} | undefined);\n    alias?: ({\n\n} | undefined);\n    include?: (Array<string> | undefined);\n};\n    mock: {\n    exclude?: (Array<string> | undefined);\n    include?: (Array<string> | undefined);\n};\n    mpa: {\n    template?: (string | undefined);\n    layout?: (string | undefined);\n    getConfigFromEntryFile?: (boolean | undefined);\n    entry?: ({\n\n} | undefined);\n};\n    phantomDependency: {\n    exclude?: (Array<string> | undefined);\n};\n    polyfill: {\n    imports?: (Array<string> | undefined);\n};\n    routePrefetch: {\n\n};\n    terminal: {\n\n};\n    tmpFiles: boolean;\n    clientLoader: {\n\n};\n    routeProps: {\n\n};\n    ssr: {\n    serverBuildPath?: (string | undefined);\n    platform?: (string | undefined);\n    builder?: (\"esbuild\" | \"webpack\" | undefined);\n};\n    lowImport: {\n    libs?: (Array<any> | undefined);\n    css?: (string | undefined);\n};\n    vite: {\n\n};\n    apiRoute: {\n    platform?: (string | undefined);\n};\n    monorepoRedirect: boolean | {\n    srcDir?: (Array<string> | undefined);\n    exclude?: (Array<any> | undefined);\n    peerDeps?: (boolean | undefined);\n};\n    test: {\n\n};\n    clickToComponent: {\n    /** 默认情况下，点击将默认编辑器为vscode, 你可以设置编辑器 vscode 或者 vscode-insiders */\n    editor?: (string | undefined);\n};\n    legacy: {\n    buildOnly?: (boolean | undefined);\n    nodeModulesTransform?: (boolean | undefined);\n    checkOutput?: (boolean | undefined);\n};\n    /** 设置 babel class-properties 启用 loose \n @doc https://umijs.org/docs/api/config#classpropertiesloose */\n    classPropertiesLoose: boolean | {\n\n};\n    ui: {\n\n};\n};\n\ntype PrettifyWithCloseable<T> = {\n  [K in keyof T]: T[K] | false;\n} & {};\n\nexport type IConfigFromPlugins = PrettifyWithCloseable<\n  IConfigFromPluginsJoi & Partial<IConfigTypes>\n>;\n"
  },
  {
    "path": ".dumi/tmp-production/core/pluginConfigJoi.d.ts",
    "content": "// This file is generated by Umi automatically\n// DO NOT CHANGE IT MANUALLY!\n// Created by Umi Plugin\n\nexport interface IConfigFromPluginsJoi {\nverifyCommit?: {\nscope?: string[]\nallowEmoji?: boolean\n}\nrun?: {\nglobals?: string[]\n}\nlogo?: string\nthemeConfig?: {\n\n}\nextraRehypePlugins?: unknown[]\nextraRemarkPlugins?: unknown[]\nresolve?: {\ndocDirs?: unknown[]\natomDirs?: {\ntype?: string\ndir?: string\n}[]\nentityDirs?: unknown\ncodeBlockMode?: (\"active\" | \"passive\")\nentryFile?: string\nforceKebabCaseRouting?: boolean\n}\nautoAlias?: boolean\nanalytics?: ({\nbaidu?: string\nga?: string\nga_v2?: string\n} | boolean)\nlocales?: ({\nid?: string\nname?: string\nbase?: string\n}[] | {\nid?: string\nname?: string\nsuffix?: \"\"\n}[])\napiParser?: {\nunpkgHost?: string\nresolveFilter?: (() => any)\nparseOptions?: {\n\n}\n}\nassets?: {\n\n}\nsitemap?: {\nhostname?: string\nexclude?: string[]\n}\n}\n"
  },
  {
    "path": ".dumi/tmp-production/core/polyfill.ts",
    "content": "// @ts-nocheck\n// This file is generated by Umi automatically\n// DO NOT CHANGE IT MANUALLY!\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/es.error.cause.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/es.aggregate-error.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/es.aggregate-error.cause.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/es.array.at.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/es.array.find-last.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/es.array.find-last-index.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/es.array.push.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/es.array.reduce.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/es.array.reduce-right.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/es.object.has-own.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/es.promise.any.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/es.reflect.to-string-tag.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/es.regexp.flags.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/es.string.at-alternative.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/es.string.replace-all.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/es.typed-array.at.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/es.typed-array.find-last.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/es.typed-array.find-last-index.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/es.typed-array.set.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.suppressed-error.constructor.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.array.from-async.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.array.filter-out.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.array.filter-reject.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.array.group.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.array.group-by.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.array.group-by-to-map.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.array.group-to-map.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.array.is-template-object.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.array.last-index.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.array.last-item.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.array.to-reversed.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.array.to-sorted.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.array.to-spliced.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.array.unique-by.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.array.with.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.async-disposable-stack.constructor.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.async-iterator.constructor.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.async-iterator.as-indexed-pairs.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.async-iterator.async-dispose.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.async-iterator.drop.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.async-iterator.every.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.async-iterator.filter.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.async-iterator.find.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.async-iterator.flat-map.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.async-iterator.for-each.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.async-iterator.from.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.async-iterator.indexed.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.async-iterator.map.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.async-iterator.reduce.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.async-iterator.some.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.async-iterator.take.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.async-iterator.to-array.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.bigint.range.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.composite-key.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.composite-symbol.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.disposable-stack.constructor.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.function.is-callable.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.function.is-constructor.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.function.un-this.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.iterator.constructor.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.iterator.as-indexed-pairs.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.iterator.dispose.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.iterator.drop.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.iterator.every.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.iterator.filter.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.iterator.find.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.iterator.flat-map.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.iterator.for-each.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.iterator.from.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.iterator.indexed.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.iterator.map.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.iterator.reduce.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.iterator.some.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.iterator.take.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.iterator.to-array.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.iterator.to-async.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.map.delete-all.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.map.emplace.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.map.every.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.map.filter.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.map.find.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.map.find-key.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.map.from.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.map.group-by.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.map.includes.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.map.key-by.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.map.key-of.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.map.map-keys.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.map.map-values.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.map.merge.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.map.of.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.map.reduce.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.map.some.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.map.update.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.map.update-or-insert.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.map.upsert.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.math.clamp.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.math.deg-per-rad.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.math.degrees.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.math.fscale.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.math.iaddh.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.math.imulh.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.math.isubh.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.math.rad-per-deg.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.math.radians.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.math.scale.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.math.seeded-prng.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.math.signbit.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.math.umulh.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.number.from-string.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.number.range.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.object.iterate-entries.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.object.iterate-keys.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.object.iterate-values.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.observable.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.promise.try.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.reflect.define-metadata.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.reflect.delete-metadata.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.reflect.get-metadata.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.reflect.get-metadata-keys.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.reflect.get-own-metadata.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.reflect.get-own-metadata-keys.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.reflect.has-metadata.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.reflect.has-own-metadata.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.reflect.metadata.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.set.add-all.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.set.delete-all.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.set.difference.v2.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.set.difference.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.set.every.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.set.filter.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.set.find.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.set.from.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.set.intersection.v2.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.set.intersection.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.set.is-disjoint-from.v2.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.set.is-disjoint-from.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.set.is-subset-of.v2.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.set.is-subset-of.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.set.is-superset-of.v2.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.set.is-superset-of.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.set.join.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.set.map.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.set.of.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.set.reduce.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.set.some.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.set.symmetric-difference.v2.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.set.symmetric-difference.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.set.union.v2.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.set.union.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.string.at.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.string.cooked.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.string.code-points.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.string.dedent.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.string.is-well-formed.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.string.to-well-formed.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.symbol.async-dispose.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.symbol.dispose.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.symbol.matcher.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.symbol.metadata.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.symbol.metadata-key.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.symbol.observable.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.symbol.pattern-match.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.symbol.replace-all.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.typed-array.from-async.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.typed-array.filter-out.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.typed-array.filter-reject.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.typed-array.group-by.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.typed-array.to-reversed.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.typed-array.to-sorted.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.typed-array.to-spliced.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.typed-array.unique-by.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.typed-array.with.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.weak-map.delete-all.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.weak-map.from.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.weak-map.of.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.weak-map.emplace.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.weak-map.upsert.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.weak-set.add-all.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.weak-set.delete-all.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.weak-set.from.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/esnext.weak-set.of.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/web.dom-exception.stack.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/web.immediate.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/web.self.js\";\nimport \"/Users/zhanbo/happy/x-render/node_modules/core-js/modules/web.structured-clone.js\";\nimport '/Users/zhanbo/happy/x-render/node_modules/regenerator-runtime/runtime.js';\nexport {};\n"
  },
  {
    "path": ".dumi/tmp-production/core/route.tsx",
    "content": "// @ts-nocheck\n// This file is generated by Umi automatically\n// DO NOT CHANGE IT MANUALLY!\nimport React from 'react';\n\nexport async function getRoutes() {\n  const routes = {\"404\":{\"id\":\"404\",\"path\":\"*\",\"parentId\":\"DocLayout\"},\"GlobalLayout\":{\"id\":\"GlobalLayout\",\"path\":\"/\",\"parentId\":\"dumi-context-layout\",\"isLayout\":true},\"dumi-context-layout\":{\"id\":\"dumi-context-layout\",\"path\":\"/\",\"isLayout\":true},\"DocLayout\":{\"id\":\"DocLayout\",\"path\":\"/\",\"parentId\":\"GlobalLayout\",\"isLayout\":true},\"docs/chart-render/0.1.X/demo/pivot-table\":{\"path\":\"chart-render/0/1/x/demo/pivot-table\",\"id\":\"docs/chart-render/0.1.X/demo/pivot-table\",\"parentId\":\"DocLayout\"},\"docs/form-render/advanced-slimrender\":{\"path\":\"form-render/advanced-slimrender\",\"id\":\"docs/form-render/advanced-slimrender\",\"parentId\":\"DocLayout\"},\"docs/form-render/demo/disaply-column\":{\"path\":\"form-render/demo/disaply-column\",\"id\":\"docs/form-render/demo/disaply-column\",\"parentId\":\"DocLayout\"},\"docs/chart-render/0.1.X/demo/column\":{\"path\":\"chart-render/0/1/x/demo/column\",\"id\":\"docs/chart-render/0.1.X/demo/column\",\"parentId\":\"DocLayout\"},\"docs/form-render/advanced-validate\":{\"path\":\"form-render/advanced-validate\",\"id\":\"docs/form-render/advanced-validate\",\"parentId\":\"DocLayout\"},\"docs/chart-render/0.1.X/demo/line\":{\"path\":\"chart-render/0/1/x/demo/line\",\"id\":\"docs/chart-render/0.1.X/demo/line\",\"parentId\":\"DocLayout\"},\"docs/form-render/advanced-linkage\":{\"path\":\"form-render/advanced-linkage\",\"id\":\"docs/form-render/advanced-linkage\",\"parentId\":\"DocLayout\"},\"docs/table-render/promptly-search\":{\"path\":\"table-render/promptly-search\",\"id\":\"docs/table-render/promptly-search\",\"parentId\":\"DocLayout\"},\"docs/form-render/advaced-example\":{\"path\":\"form-render/advaced-example\",\"id\":\"docs/form-render/advaced-example\",\"parentId\":\"DocLayout\"},\"docs/form-render/advanced-layout\":{\"path\":\"form-render/advanced-layout\",\"id\":\"docs/form-render/advanced-layout\",\"parentId\":\"DocLayout\"},\"docs/form-render/advanced-widget\":{\"path\":\"form-render/advanced-widget\",\"id\":\"docs/form-render/advanced-widget\",\"parentId\":\"DocLayout\"},\"docs/chart-render/widget/custom\":{\"path\":\"chart-render/widget/custom\",\"id\":\"docs/chart-render/widget/custom\",\"parentId\":\"DocLayout\"},\"docs/form-render/disaply-search\":{\"path\":\"form-render/disaply-search\",\"id\":\"docs/form-render/disaply-search\",\"parentId\":\"DocLayout\"},\"docs/form-render/advanced-path\":{\"path\":\"form-render/advanced-path\",\"id\":\"docs/form-render/advanced-path\",\"parentId\":\"DocLayout\"},\"docs/table-render/custom-table\":{\"path\":\"table-render/custom-table\",\"id\":\"docs/table-render/custom-table\",\"parentId\":\"DocLayout\"},\"docs/chart-render/0.1.X/index\":{\"path\":\"chart-render/0/1/x\",\"id\":\"docs/chart-render/0.1.X/index\",\"parentId\":\"DocLayout\"},\"docs/chart-render/widget/line\":{\"path\":\"chart-render/widget/line\",\"id\":\"docs/chart-render/widget/line\",\"parentId\":\"DocLayout\"},\"docs/form-render/disaply-row\":{\"path\":\"form-render/disaply-row\",\"id\":\"docs/form-render/disaply-row\",\"parentId\":\"DocLayout\"},\"docs/chart-render/0.1.X/faq\":{\"path\":\"chart-render/0/1/x/faq\",\"id\":\"docs/chart-render/0.1.X/faq\",\"parentId\":\"DocLayout\"},\"docs/form-render/api-schema\":{\"path\":\"form-render/api-schema\",\"id\":\"docs/form-render/api-schema\",\"parentId\":\"DocLayout\"},\"docs/table-render/collapsed\":{\"path\":\"table-render/collapsed\",\"id\":\"docs/table-render/collapsed\",\"parentId\":\"DocLayout\"},\"docs/table-render/valueType\":{\"path\":\"table-render/value-type\",\"id\":\"docs/table-render/valueType\",\"parentId\":\"DocLayout\"},\"docs/form-render/api-props\":{\"path\":\"form-render/api-props\",\"id\":\"docs/form-render/api-props\",\"parentId\":\"DocLayout\"},\"docs/form-render/changelog\":{\"path\":\"form-render/changelog\",\"id\":\"docs/form-render/changelog\",\"parentId\":\"DocLayout\"},\"docs/form-render/demo/demo\":{\"path\":\"form-render/demo/demo\",\"id\":\"docs/form-render/demo/demo\",\"parentId\":\"DocLayout\"},\"docs/table-render/noSearch\":{\"path\":\"table-render/no-search\",\"id\":\"docs/table-render/noSearch\",\"parentId\":\"DocLayout\"},\"docs/tools/proptypes/index\":{\"path\":\"tools/proptypes\",\"id\":\"docs/tools/proptypes/index\",\"parentId\":\"DocLayout\"},\"docs/generator/playground\":{\"path\":\"generator/playground\",\"id\":\"docs/generator/playground\",\"parentId\":\"DocLayout\"},\"docs/table-render/migrate\":{\"path\":\"table-render/migrate\",\"id\":\"docs/table-render/migrate\",\"parentId\":\"DocLayout\"},\"docs/form-render/migrate\":{\"path\":\"form-render/migrate\",\"id\":\"docs/form-render/migrate\",\"parentId\":\"DocLayout\"},\"docs/table-render/norBar\":{\"path\":\"table-render/nor-bar\",\"id\":\"docs/table-render/norBar\",\"parentId\":\"DocLayout\"},\"docs/chart-render/index\":{\"path\":\"chart-render\",\"id\":\"docs/chart-render/index\",\"parentId\":\"DocLayout\"},\"docs/table-render/basic\":{\"path\":\"table-render/basic\",\"id\":\"docs/table-render/basic\",\"parentId\":\"DocLayout\"},\"docs/table-render/index\":{\"path\":\"table-render\",\"id\":\"docs/table-render/index\",\"parentId\":\"DocLayout\"},\"docs/tools/vscode/index\":{\"path\":\"tools/vscode\",\"id\":\"docs/tools/vscode/index\",\"parentId\":\"DocLayout\"},\"docs/chart-render/demo\":{\"path\":\"chart-render/demo\",\"id\":\"docs/chart-render/demo\",\"parentId\":\"DocLayout\"},\"docs/form-render/index\":{\"path\":\"form-render\",\"id\":\"docs/form-render/index\",\"parentId\":\"DocLayout\"},\"docs/table-render/tabs\":{\"path\":\"table-render/tabs\",\"id\":\"docs/table-render/tabs\",\"parentId\":\"DocLayout\"},\"docs/chart-render/faq\":{\"path\":\"chart-render/faq\",\"id\":\"docs/chart-render/faq\",\"parentId\":\"DocLayout\"},\"docs/playground/index\":{\"path\":\"playground\",\"id\":\"docs/playground/index\",\"parentId\":\"DocLayout\"},\"docs/table-render/api\":{\"path\":\"table-render/api\",\"id\":\"docs/table-render/api\",\"parentId\":\"DocLayout\"},\"docs/form-render/faq\":{\"path\":\"form-render/faq\",\"id\":\"docs/form-render/faq\",\"parentId\":\"DocLayout\"},\"docs/generator/index\":{\"path\":\"generator\",\"id\":\"docs/generator/index\",\"parentId\":\"DocLayout\"},\"docs/generator/demo\":{\"path\":\"generator/demo\",\"id\":\"docs/generator/demo\",\"parentId\":\"DocLayout\"},\"docs/generator/faq\":{\"path\":\"generator/faq\",\"id\":\"docs/generator/faq\",\"parentId\":\"DocLayout\"},\"docs/index.en-US\":{\"path\":\"index/en--us\",\"id\":\"docs/index.en-US\",\"parentId\":\"DocLayout\"},\"docs/index.zh-CN\":{\"path\":\"\",\"id\":\"docs/index.zh-CN\",\"parentId\":\"DocLayout\"},\"demo-render\":{\"id\":\"demo-render\",\"path\":\"~demos/:id\",\"parentId\":\"GlobalLayout\",\"prerender\":false}} as const;\n  return {\n    routes,\n    routeComponents: {\n'404': React.lazy(() => import(/* webpackChunkName: \"nm__dumi__dist__client__pages__404\" */'/Users/zhanbo/happy/x-render/node_modules/dumi/dist/client/pages/404.js')),\n'GlobalLayout': React.lazy(() => import(/* webpackChunkName: \"dumi__theme__layouts__GlobalLayout__index\" */'/Users/zhanbo/happy/x-render/.dumi/theme/layouts/GlobalLayout/index.tsx')),\n'dumi-context-layout': React.lazy(() => import(/* webpackChunkName: \"dumi__tmp-production__dumi__theme__ContextWrapper\" */'/Users/zhanbo/happy/x-render/.dumi/tmp-production/dumi/theme/ContextWrapper.tsx')),\n'DocLayout': React.lazy(() => import(/* webpackChunkName: \"nm__dumi__theme-default__layouts__DocLayout__index\" */'/Users/zhanbo/happy/x-render/node_modules/dumi/theme-default/layouts/DocLayout/index.js')),\n'docs/chart-render/0.1.X/demo/pivot-table': React.lazy(() => import(/* webpackChunkName: \"docs__chart-render__0.1.X__demo__pivot-table.md\" */'/Users/zhanbo/happy/x-render/docs/chart-render/0.1.X/demo/pivot-table.md')),\n'docs/form-render/advanced-slimrender': React.lazy(() => import(/* webpackChunkName: \"docs__form-render__advanced-slimrender.md\" */'/Users/zhanbo/happy/x-render/docs/form-render/advanced-slimrender.md')),\n'docs/form-render/demo/disaply-column': React.lazy(() => import(/* webpackChunkName: \"docs__form-render__demo__disaply-column.md\" */'/Users/zhanbo/happy/x-render/docs/form-render/demo/disaply-column.md')),\n'docs/chart-render/0.1.X/demo/column': React.lazy(() => import(/* webpackChunkName: \"docs__chart-render__0.1.X__demo__column.md\" */'/Users/zhanbo/happy/x-render/docs/chart-render/0.1.X/demo/column.md')),\n'docs/form-render/advanced-validate': React.lazy(() => import(/* webpackChunkName: \"docs__form-render__advanced-validate.md\" */'/Users/zhanbo/happy/x-render/docs/form-render/advanced-validate.md')),\n'docs/chart-render/0.1.X/demo/line': React.lazy(() => import(/* webpackChunkName: \"docs__chart-render__0.1.X__demo__line.md\" */'/Users/zhanbo/happy/x-render/docs/chart-render/0.1.X/demo/line.md')),\n'docs/form-render/advanced-linkage': React.lazy(() => import(/* webpackChunkName: \"docs__form-render__advanced-linkage.md\" */'/Users/zhanbo/happy/x-render/docs/form-render/advanced-linkage.md')),\n'docs/table-render/promptly-search': React.lazy(() => import(/* webpackChunkName: \"docs__table-render__promptly-search.md\" */'/Users/zhanbo/happy/x-render/docs/table-render/promptly-search.md')),\n'docs/form-render/advaced-example': React.lazy(() => import(/* webpackChunkName: \"docs__form-render__advaced-example.md\" */'/Users/zhanbo/happy/x-render/docs/form-render/advaced-example.md')),\n'docs/form-render/advanced-layout': React.lazy(() => import(/* webpackChunkName: \"docs__form-render__advanced-layout.md\" */'/Users/zhanbo/happy/x-render/docs/form-render/advanced-layout.md')),\n'docs/form-render/advanced-widget': React.lazy(() => import(/* webpackChunkName: \"docs__form-render__advanced-widget.md\" */'/Users/zhanbo/happy/x-render/docs/form-render/advanced-widget.md')),\n'docs/chart-render/widget/custom': React.lazy(() => import(/* webpackChunkName: \"docs__chart-render__widget__custom.md\" */'/Users/zhanbo/happy/x-render/docs/chart-render/widget/custom.md')),\n'docs/form-render/disaply-search': React.lazy(() => import(/* webpackChunkName: \"docs__form-render__disaply-search.md\" */'/Users/zhanbo/happy/x-render/docs/form-render/disaply-search.md')),\n'docs/form-render/advanced-path': React.lazy(() => import(/* webpackChunkName: \"docs__form-render__advanced-path.md\" */'/Users/zhanbo/happy/x-render/docs/form-render/advanced-path.md')),\n'docs/table-render/custom-table': React.lazy(() => import(/* webpackChunkName: \"docs__table-render__custom-table.md\" */'/Users/zhanbo/happy/x-render/docs/table-render/custom-table.md')),\n'docs/chart-render/0.1.X/index': React.lazy(() => import(/* webpackChunkName: \"docs__chart-render__0.1.X__index.md\" */'/Users/zhanbo/happy/x-render/docs/chart-render/0.1.X/index.md')),\n'docs/chart-render/widget/line': React.lazy(() => import(/* webpackChunkName: \"docs__chart-render__widget__line.md\" */'/Users/zhanbo/happy/x-render/docs/chart-render/widget/line.md')),\n'docs/form-render/disaply-row': React.lazy(() => import(/* webpackChunkName: \"docs__form-render__disaply-row.md\" */'/Users/zhanbo/happy/x-render/docs/form-render/disaply-row.md')),\n'docs/chart-render/0.1.X/faq': React.lazy(() => import(/* webpackChunkName: \"docs__chart-render__0.1.X__faq.md\" */'/Users/zhanbo/happy/x-render/docs/chart-render/0.1.X/faq.md')),\n'docs/form-render/api-schema': React.lazy(() => import(/* webpackChunkName: \"docs__form-render__api-schema.md\" */'/Users/zhanbo/happy/x-render/docs/form-render/api-schema.md')),\n'docs/table-render/collapsed': React.lazy(() => import(/* webpackChunkName: \"docs__table-render__collapsed.md\" */'/Users/zhanbo/happy/x-render/docs/table-render/collapsed.md')),\n'docs/table-render/valueType': React.lazy(() => import(/* webpackChunkName: \"docs__table-render__valueType.md\" */'/Users/zhanbo/happy/x-render/docs/table-render/valueType.md')),\n'docs/form-render/api-props': React.lazy(() => import(/* webpackChunkName: \"docs__form-render__api-props.md\" */'/Users/zhanbo/happy/x-render/docs/form-render/api-props.md')),\n'docs/form-render/changelog': React.lazy(() => import(/* webpackChunkName: \"docs__form-render__changelog.md\" */'/Users/zhanbo/happy/x-render/docs/form-render/changelog.md')),\n'docs/form-render/demo/demo': React.lazy(() => import(/* webpackChunkName: \"docs__form-render__demo__demo.md\" */'/Users/zhanbo/happy/x-render/docs/form-render/demo/demo.md')),\n'docs/table-render/noSearch': React.lazy(() => import(/* webpackChunkName: \"docs__table-render__noSearch.md\" */'/Users/zhanbo/happy/x-render/docs/table-render/noSearch.md')),\n'docs/tools/proptypes/index': React.lazy(() => import(/* webpackChunkName: \"docs__tools__proptypes__index.md\" */'/Users/zhanbo/happy/x-render/docs/tools/proptypes/index.md')),\n'docs/generator/playground': React.lazy(() => import(/* webpackChunkName: \"docs__generator__playground.md\" */'/Users/zhanbo/happy/x-render/docs/generator/playground.md')),\n'docs/table-render/migrate': React.lazy(() => import(/* webpackChunkName: \"docs__table-render__migrate.md\" */'/Users/zhanbo/happy/x-render/docs/table-render/migrate.md')),\n'docs/form-render/migrate': React.lazy(() => import(/* webpackChunkName: \"docs__form-render__migrate.md\" */'/Users/zhanbo/happy/x-render/docs/form-render/migrate.md')),\n'docs/table-render/norBar': React.lazy(() => import(/* webpackChunkName: \"docs__table-render__norBar.md\" */'/Users/zhanbo/happy/x-render/docs/table-render/norBar.md')),\n'docs/chart-render/index': React.lazy(() => import(/* webpackChunkName: \"docs__chart-render__index.md\" */'/Users/zhanbo/happy/x-render/docs/chart-render/index.md')),\n'docs/table-render/basic': React.lazy(() => import(/* webpackChunkName: \"docs__table-render__basic.md\" */'/Users/zhanbo/happy/x-render/docs/table-render/basic.md')),\n'docs/table-render/index': React.lazy(() => import(/* webpackChunkName: \"docs__table-render__index.md\" */'/Users/zhanbo/happy/x-render/docs/table-render/index.md')),\n'docs/tools/vscode/index': React.lazy(() => import(/* webpackChunkName: \"docs__tools__vscode__index.md\" */'/Users/zhanbo/happy/x-render/docs/tools/vscode/index.md')),\n'docs/chart-render/demo': React.lazy(() => import(/* webpackChunkName: \"docs__chart-render__demo.md\" */'/Users/zhanbo/happy/x-render/docs/chart-render/demo.md')),\n'docs/form-render/index': React.lazy(() => import(/* webpackChunkName: \"docs__form-render__index.md\" */'/Users/zhanbo/happy/x-render/docs/form-render/index.md')),\n'docs/table-render/tabs': React.lazy(() => import(/* webpackChunkName: \"docs__table-render__tabs.md\" */'/Users/zhanbo/happy/x-render/docs/table-render/tabs.md')),\n'docs/chart-render/faq': React.lazy(() => import(/* webpackChunkName: \"docs__chart-render__faq.md\" */'/Users/zhanbo/happy/x-render/docs/chart-render/faq.md')),\n'docs/playground/index': React.lazy(() => import(/* webpackChunkName: \"docs__playground__index.md\" */'/Users/zhanbo/happy/x-render/docs/playground/index.md')),\n'docs/table-render/api': React.lazy(() => import(/* webpackChunkName: \"docs__table-render__api.md\" */'/Users/zhanbo/happy/x-render/docs/table-render/api.md')),\n'docs/form-render/faq': React.lazy(() => import(/* webpackChunkName: \"docs__form-render__faq.md\" */'/Users/zhanbo/happy/x-render/docs/form-render/faq.md')),\n'docs/generator/index': React.lazy(() => import(/* webpackChunkName: \"docs__generator__index.md\" */'/Users/zhanbo/happy/x-render/docs/generator/index.md')),\n'docs/generator/demo': React.lazy(() => import(/* webpackChunkName: \"docs__generator__demo.md\" */'/Users/zhanbo/happy/x-render/docs/generator/demo.md')),\n'docs/generator/faq': React.lazy(() => import(/* webpackChunkName: \"docs__generator__faq.md\" */'/Users/zhanbo/happy/x-render/docs/generator/faq.md')),\n'docs/index.en-US': React.lazy(() => import(/* webpackChunkName: \"docs__index.en-US.md\" */'/Users/zhanbo/happy/x-render/docs/index.en-US.md')),\n'docs/index.zh-CN': React.lazy(() => import(/* webpackChunkName: \"docs__index.zh-CN.md\" */'/Users/zhanbo/happy/x-render/docs/index.zh-CN.md')),\n'demo-render': React.lazy(() => import(/* webpackChunkName: \"nm__dumi__dist__client__pages__Demo__index\" */'/Users/zhanbo/happy/x-render/node_modules/dumi/dist/client/pages/Demo/index.js')),\n},\n  };\n}\n"
  },
  {
    "path": ".dumi/tmp-production/core/terminal.ts",
    "content": "// @ts-nocheck\n// This file is generated by Umi automatically\n// DO NOT CHANGE IT MANUALLY!\nlet count = 0;\nlet groupLevel = 0;\nfunction send(type: string, message?: string) {\n  if(process.env.NODE_ENV==='production'){\n    return;\n  }else{\n    const encodedMessage = message ? `&m=${encodeURI(message)}` : '';\n    fetch(`/__umi/api/terminal?type=${type}&t=${Date.now()}&c=${count++}&g=${groupLevel}${encodedMessage}`, { mode: 'no-cors' })\n  }\n}\nfunction prettyPrint(obj: any) {\n  return JSON.stringify(obj, null, 2);\n}\nfunction stringifyObjs(objs: any[]) {\n  const obj = objs.length > 1 ? objs.map(stringify).join(' ') : objs[0];\n  return typeof obj === 'object' ? `${prettyPrint(obj)}` : obj.toString();\n}\nfunction stringify(obj: any) {\n  return typeof obj === 'object' ? `${JSON.stringify(obj)}` : obj.toString();\n}\nconst terminal = {\n  log(...objs: any[]) { send('log', stringifyObjs(objs)) },\n  info(...objs: any[]) { send('info', stringifyObjs(objs)) },\n  warn(...objs: any[]) { send('warn', stringifyObjs(objs)) },\n  error(...objs: any[]) { send('error', stringifyObjs(objs)) },\n  group() { groupLevel++ },\n  groupCollapsed() { groupLevel++ },\n  groupEnd() { groupLevel && --groupLevel },\n  clear() { send('clear') },\n  trace(...args: any[]) { console.trace(...args) },\n  profile(...args: any[]) { console.profile(...args) },\n  profileEnd(...args: any[]) { console.profileEnd(...args) },\n};\nexport { terminal };\n"
  },
  {
    "path": ".dumi/tmp-production/dumi/exportStaticRuntimePlugin.ts",
    "content": "// @ts-nocheck\n// This file is generated by Umi automatically\n// DO NOT CHANGE IT MANUALLY!\nexport function modifyClientRenderOpts(memo: any) {\n  const { history, hydrate } = memo;\n\n  return {\n    ...memo,\n    hydrate: hydrate && !history.location.pathname.startsWith('/~demos'),\n  };\n}\n"
  },
  {
    "path": ".dumi/tmp-production/dumi/exports.ts",
    "content": "// @ts-nocheck\n// This file is generated by Umi automatically\n// DO NOT CHANGE IT MANUALLY!\nexport * from '../exports';\nexport * from '/Users/zhanbo/happy/x-render/node_modules/dumi/dist/client/theme-api/index.js';\n"
  },
  {
    "path": ".dumi/tmp-production/dumi/locales/config.ts",
    "content": "// @ts-nocheck\n// This file is generated by Umi automatically\n// DO NOT CHANGE IT MANUALLY!\nexport const locales = [\n  {\n    \"id\": \"zh-CN\",\n    \"name\": \"中文\",\n    \"base\": \"/\"\n  }\n];\nexport const messages = {\n  \"en-US\": {\n    \"header.search.placeholder\": \"Type keywords...\",\n    \"header.color.mode.light\": \"Light Mode\",\n    \"header.color.mode.dark\": \"Dark Mode\",\n    \"header.color.mode.auto\": \"Follow System\",\n    \"header.social.github\": \"GitHub\",\n    \"header.social.weibo\": \"Weibo\",\n    \"header.social.twitter\": \"Twitter\",\n    \"header.social.gitlab\": \"GitLab\",\n    \"header.social.facebook\": \"Facebook\",\n    \"header.social.zhihu\": \"Zhihu\",\n    \"header.social.yuque\": \"Yuque\",\n    \"header.social.linkedin\": \"Linkedin\",\n    \"previewer.actions.code.expand\": \"Show Code\",\n    \"previewer.actions.code.shrink\": \"Hide Code\",\n    \"previewer.actions.codesandbox\": \"Open in CodeSandbox\",\n    \"previewer.actions.codepen\": \"Open in CodePen (Not implemented)\",\n    \"previewer.actions.stackblitz\": \"Open in StackBlitz\",\n    \"previewer.actions.separate\": \"Open in separate page\",\n    \"404.title\": \"PAGE NOT FOUND\",\n    \"404.back\": \"Back to homepage\",\n    \"api.component.name\": \"Name\",\n    \"api.component.description\": \"Description\",\n    \"api.component.type\": \"Type\",\n    \"api.component.default\": \"Default\",\n    \"api.component.required\": \"(required)\",\n    \"api.component.unavailable\": \"apiParser must be enabled to use auto-generated API\",\n    \"api.component.loading\": \"Properties definition is resolving, wait a moment...\",\n    \"api.component.not.found\": \"Properties definition not found for {id} component\",\n    \"content.tabs.default\": \"Doc\",\n    \"search.not.found\": \"No content was found\",\n    \"layout.sidebar.btn\": \"Sidebar\"\n  },\n  \"zh-CN\": {\n    \"header.search.placeholder\": \"输入关键字搜索...\",\n    \"header.color.mode.light\": \"亮色模式\",\n    \"header.color.mode.dark\": \"暗色模式\",\n    \"header.color.mode.auto\": \"跟随系统\",\n    \"header.social.github\": \"GitHub\",\n    \"header.social.weibo\": \"微博\",\n    \"header.social.twitter\": \"Twitter\",\n    \"header.social.gitlab\": \"GitLab\",\n    \"header.social.facebook\": \"Facebook\",\n    \"header.social.zhihu\": \"知乎\",\n    \"header.social.yuque\": \"语雀\",\n    \"header.social.linkedin\": \"Linkedin\",\n    \"previewer.actions.code.expand\": \"展开代码\",\n    \"previewer.actions.code.shrink\": \"收起代码\",\n    \"previewer.actions.codesandbox\": \"在 CodeSandbox 中打开\",\n    \"previewer.actions.codepen\": \"在 CodePen 中打开（未实现）\",\n    \"previewer.actions.stackblitz\": \"在 StackBlitz 中打开\",\n    \"previewer.actions.separate\": \"在独立页面中打开\",\n    \"404.title\": \"页面未找到\",\n    \"404.back\": \"返回首页\",\n    \"api.component.name\": \"属性名\",\n    \"api.component.description\": \"描述\",\n    \"api.component.type\": \"类型\",\n    \"api.component.default\": \"默认值\",\n    \"api.component.required\": \"(必选)\",\n    \"api.component.unavailable\": \"必须启用 apiParser 才能使用自动 API 特性\",\n    \"api.component.loading\": \"属性定义正在解析中，稍等片刻...\",\n    \"api.component.not.found\": \"未找到 {id} 组件的属性定义\",\n    \"content.tabs.default\": \"文档\",\n    \"search.not.found\": \"未找到相关内容\",\n    \"layout.sidebar.btn\": \"侧边菜单\"\n  }\n};\n"
  },
  {
    "path": ".dumi/tmp-production/dumi/locales/runtime.tsx",
    "content": "// @ts-nocheck\n// This file is generated by Umi automatically\n// DO NOT CHANGE IT MANUALLY!\nimport { history } from 'dumi';\nimport React, { useState, useLayoutEffect, useCallback, type ReactNode } from 'react';\nimport { RawIntlProvider, createIntl, createIntlCache } from '/Users/zhanbo/happy/x-render/node_modules/react-intl';\nimport { useIsomorphicLayoutEffect } from '/Users/zhanbo/happy/x-render/node_modules/dumi/dist/client/theme-api/utils.js'\nimport { locales, messages } from './config';\n\nconst cache = createIntlCache();\n\nconst LocalesContainer: FC<{ children: ReactNode }> = (props) => {\n  const getIntl = useCallback(() => {\n    const matched = locales.slice().reverse().find((locale) => (\n      'suffix' in locale\n        // suffix mode\n        ? history.location.pathname.replace(/([^/])\\/$/, '$1').endsWith(locale.suffix)\n        // base mode\n        : history.location.pathname.replace(/([^/])\\/$/, '$1').startsWith(locale.base)\n    ));\n    const locale = matched ? matched.id : locales[0].id;\n\n    return createIntl({ locale, messages: messages[locale] || {} }, cache);\n  }, []);\n  const [intl, setIntl] = useState(() => getIntl());\n\n  useIsomorphicLayoutEffect(() => {\n    return history.listen(() => {\n      setIntl(getIntl());\n    });\n  }, []);\n\n  return <RawIntlProvider value={intl} key={intl.locale}>{props.children}</RawIntlProvider>;\n}\n\nexport function i18nProvider(container: Element) {\n  return React.createElement(LocalesContainer, null, container);\n}\n"
  },
  {
    "path": ".dumi/tmp-production/dumi/meta/atoms.ts",
    "content": "// @ts-nocheck\n// This file is generated by Umi automatically\n// DO NOT CHANGE IT MANUALLY!\nexport const components = null;\n"
  },
  {
    "path": ".dumi/tmp-production/dumi/meta/index.ts",
    "content": "// @ts-nocheck\n// This file is generated by Umi automatically\n// DO NOT CHANGE IT MANUALLY!\nimport { demos as dm0, frontmatter as fm0, toc as toc0, texts as txt0 } from '/Users/zhanbo/happy/x-render/docs/chart-render/0.1.X/demo/pivot-table.md?type=meta';\nimport { demos as dm1, frontmatter as fm1, toc as toc1, texts as txt1 } from '/Users/zhanbo/happy/x-render/docs/form-render/advanced-slimrender.md?type=meta';\nimport { demos as dm2, frontmatter as fm2, toc as toc2, texts as txt2 } from '/Users/zhanbo/happy/x-render/docs/form-render/demo/disaply-column.md?type=meta';\nimport { demos as dm3, frontmatter as fm3, toc as toc3, texts as txt3 } from '/Users/zhanbo/happy/x-render/docs/chart-render/0.1.X/demo/column.md?type=meta';\nimport { demos as dm4, frontmatter as fm4, toc as toc4, texts as txt4 } from '/Users/zhanbo/happy/x-render/docs/form-render/advanced-validate.md?type=meta';\nimport { demos as dm5, frontmatter as fm5, toc as toc5, texts as txt5 } from '/Users/zhanbo/happy/x-render/docs/chart-render/0.1.X/demo/line.md?type=meta';\nimport { demos as dm6, frontmatter as fm6, toc as toc6, texts as txt6 } from '/Users/zhanbo/happy/x-render/docs/form-render/advanced-linkage.md?type=meta';\nimport { demos as dm7, frontmatter as fm7, toc as toc7, texts as txt7 } from '/Users/zhanbo/happy/x-render/docs/table-render/promptly-search.md?type=meta';\nimport { demos as dm8, frontmatter as fm8, toc as toc8, texts as txt8 } from '/Users/zhanbo/happy/x-render/docs/form-render/advaced-example.md?type=meta';\nimport { demos as dm9, frontmatter as fm9, toc as toc9, texts as txt9 } from '/Users/zhanbo/happy/x-render/docs/form-render/advanced-layout.md?type=meta';\nimport { demos as dm10, frontmatter as fm10, toc as toc10, texts as txt10 } from '/Users/zhanbo/happy/x-render/docs/form-render/advanced-widget.md?type=meta';\nimport { demos as dm11, frontmatter as fm11, toc as toc11, texts as txt11 } from '/Users/zhanbo/happy/x-render/docs/chart-render/widget/custom.md?type=meta';\nimport { demos as dm12, frontmatter as fm12, toc as toc12, texts as txt12 } from '/Users/zhanbo/happy/x-render/docs/form-render/disaply-search.md?type=meta';\nimport { demos as dm13, frontmatter as fm13, toc as toc13, texts as txt13 } from '/Users/zhanbo/happy/x-render/docs/form-render/advanced-path.md?type=meta';\nimport { demos as dm14, frontmatter as fm14, toc as toc14, texts as txt14 } from '/Users/zhanbo/happy/x-render/docs/table-render/custom-table.md?type=meta';\nimport { demos as dm15, frontmatter as fm15, toc as toc15, texts as txt15 } from '/Users/zhanbo/happy/x-render/docs/chart-render/0.1.X/index.md?type=meta';\nimport { demos as dm16, frontmatter as fm16, toc as toc16, texts as txt16 } from '/Users/zhanbo/happy/x-render/docs/chart-render/widget/line.md?type=meta';\nimport { demos as dm17, frontmatter as fm17, toc as toc17, texts as txt17 } from '/Users/zhanbo/happy/x-render/docs/form-render/disaply-row.md?type=meta';\nimport { demos as dm18, frontmatter as fm18, toc as toc18, texts as txt18 } from '/Users/zhanbo/happy/x-render/docs/chart-render/0.1.X/faq.md?type=meta';\nimport { demos as dm19, frontmatter as fm19, toc as toc19, texts as txt19 } from '/Users/zhanbo/happy/x-render/docs/form-render/api-schema.md?type=meta';\nimport { demos as dm20, frontmatter as fm20, toc as toc20, texts as txt20 } from '/Users/zhanbo/happy/x-render/docs/table-render/collapsed.md?type=meta';\nimport { demos as dm21, frontmatter as fm21, toc as toc21, texts as txt21 } from '/Users/zhanbo/happy/x-render/docs/table-render/valueType.md?type=meta';\nimport { demos as dm22, frontmatter as fm22, toc as toc22, texts as txt22 } from '/Users/zhanbo/happy/x-render/docs/form-render/api-props.md?type=meta';\nimport { demos as dm23, frontmatter as fm23, toc as toc23, texts as txt23 } from '/Users/zhanbo/happy/x-render/docs/form-render/changelog.md?type=meta';\nimport { demos as dm24, frontmatter as fm24, toc as toc24, texts as txt24 } from '/Users/zhanbo/happy/x-render/docs/form-render/demo/demo.md?type=meta';\nimport { demos as dm25, frontmatter as fm25, toc as toc25, texts as txt25 } from '/Users/zhanbo/happy/x-render/docs/table-render/noSearch.md?type=meta';\nimport { demos as dm26, frontmatter as fm26, toc as toc26, texts as txt26 } from '/Users/zhanbo/happy/x-render/docs/tools/proptypes/index.md?type=meta';\nimport { demos as dm27, frontmatter as fm27, toc as toc27, texts as txt27 } from '/Users/zhanbo/happy/x-render/docs/generator/playground.md?type=meta';\nimport { demos as dm28, frontmatter as fm28, toc as toc28, texts as txt28 } from '/Users/zhanbo/happy/x-render/docs/table-render/migrate.md?type=meta';\nimport { demos as dm29, frontmatter as fm29, toc as toc29, texts as txt29 } from '/Users/zhanbo/happy/x-render/docs/form-render/migrate.md?type=meta';\nimport { demos as dm30, frontmatter as fm30, toc as toc30, texts as txt30 } from '/Users/zhanbo/happy/x-render/docs/table-render/norBar.md?type=meta';\nimport { demos as dm31, frontmatter as fm31, toc as toc31, texts as txt31 } from '/Users/zhanbo/happy/x-render/docs/chart-render/index.md?type=meta';\nimport { demos as dm32, frontmatter as fm32, toc as toc32, texts as txt32 } from '/Users/zhanbo/happy/x-render/docs/table-render/basic.md?type=meta';\nimport { demos as dm33, frontmatter as fm33, toc as toc33, texts as txt33 } from '/Users/zhanbo/happy/x-render/docs/table-render/index.md?type=meta';\nimport { demos as dm34, frontmatter as fm34, toc as toc34, texts as txt34 } from '/Users/zhanbo/happy/x-render/docs/tools/vscode/index.md?type=meta';\nimport { demos as dm35, frontmatter as fm35, toc as toc35, texts as txt35 } from '/Users/zhanbo/happy/x-render/docs/chart-render/demo.md?type=meta';\nimport { demos as dm36, frontmatter as fm36, toc as toc36, texts as txt36 } from '/Users/zhanbo/happy/x-render/docs/form-render/index.md?type=meta';\nimport { demos as dm37, frontmatter as fm37, toc as toc37, texts as txt37 } from '/Users/zhanbo/happy/x-render/docs/table-render/tabs.md?type=meta';\nimport { demos as dm38, frontmatter as fm38, toc as toc38, texts as txt38 } from '/Users/zhanbo/happy/x-render/docs/chart-render/faq.md?type=meta';\nimport { demos as dm39, frontmatter as fm39, toc as toc39, texts as txt39 } from '/Users/zhanbo/happy/x-render/docs/playground/index.md?type=meta';\nimport { demos as dm40, frontmatter as fm40, toc as toc40, texts as txt40 } from '/Users/zhanbo/happy/x-render/docs/table-render/api.md?type=meta';\nimport { demos as dm41, frontmatter as fm41, toc as toc41, texts as txt41 } from '/Users/zhanbo/happy/x-render/docs/form-render/faq.md?type=meta';\nimport { demos as dm42, frontmatter as fm42, toc as toc42, texts as txt42 } from '/Users/zhanbo/happy/x-render/docs/generator/index.md?type=meta';\nimport { demos as dm43, frontmatter as fm43, toc as toc43, texts as txt43 } from '/Users/zhanbo/happy/x-render/docs/generator/demo.md?type=meta';\nimport { demos as dm44, frontmatter as fm44, toc as toc44, texts as txt44 } from '/Users/zhanbo/happy/x-render/docs/generator/faq.md?type=meta';\nimport { demos as dm45, frontmatter as fm45, toc as toc45, texts as txt45 } from '/Users/zhanbo/happy/x-render/docs/index.en-US.md?type=meta';\nimport { demos as dm46, frontmatter as fm46, toc as toc46, texts as txt46 } from '/Users/zhanbo/happy/x-render/docs/index.zh-CN.md?type=meta';\n\nexport { components } from './atoms';\nexport { tabs } from './tabs';\n\nexport const filesMeta = {\n  'docs/chart-render/0.1.X/demo/pivot-table': {\n    frontmatter: fm0,\n    toc: toc0,\n    texts: txt0,\n    demos: dm0,\n  },\n  'docs/form-render/advanced-slimrender': {\n    frontmatter: fm1,\n    toc: toc1,\n    texts: txt1,\n    demos: dm1,\n  },\n  'docs/form-render/demo/disaply-column': {\n    frontmatter: fm2,\n    toc: toc2,\n    texts: txt2,\n    demos: dm2,\n  },\n  'docs/chart-render/0.1.X/demo/column': {\n    frontmatter: fm3,\n    toc: toc3,\n    texts: txt3,\n    demos: dm3,\n  },\n  'docs/form-render/advanced-validate': {\n    frontmatter: fm4,\n    toc: toc4,\n    texts: txt4,\n    demos: dm4,\n  },\n  'docs/chart-render/0.1.X/demo/line': {\n    frontmatter: fm5,\n    toc: toc5,\n    texts: txt5,\n    demos: dm5,\n  },\n  'docs/form-render/advanced-linkage': {\n    frontmatter: fm6,\n    toc: toc6,\n    texts: txt6,\n    demos: dm6,\n  },\n  'docs/table-render/promptly-search': {\n    frontmatter: fm7,\n    toc: toc7,\n    texts: txt7,\n    demos: dm7,\n  },\n  'docs/form-render/advaced-example': {\n    frontmatter: fm8,\n    toc: toc8,\n    texts: txt8,\n    demos: dm8,\n  },\n  'docs/form-render/advanced-layout': {\n    frontmatter: fm9,\n    toc: toc9,\n    texts: txt9,\n    demos: dm9,\n  },\n  'docs/form-render/advanced-widget': {\n    frontmatter: fm10,\n    toc: toc10,\n    texts: txt10,\n    demos: dm10,\n  },\n  'docs/chart-render/widget/custom': {\n    frontmatter: fm11,\n    toc: toc11,\n    texts: txt11,\n    demos: dm11,\n  },\n  'docs/form-render/disaply-search': {\n    frontmatter: fm12,\n    toc: toc12,\n    texts: txt12,\n    demos: dm12,\n  },\n  'docs/form-render/advanced-path': {\n    frontmatter: fm13,\n    toc: toc13,\n    texts: txt13,\n    demos: dm13,\n  },\n  'docs/table-render/custom-table': {\n    frontmatter: fm14,\n    toc: toc14,\n    texts: txt14,\n    demos: dm14,\n  },\n  'docs/chart-render/0.1.X/index': {\n    frontmatter: fm15,\n    toc: toc15,\n    texts: txt15,\n    demos: dm15,\n  },\n  'docs/chart-render/widget/line': {\n    frontmatter: fm16,\n    toc: toc16,\n    texts: txt16,\n    demos: dm16,\n  },\n  'docs/form-render/disaply-row': {\n    frontmatter: fm17,\n    toc: toc17,\n    texts: txt17,\n    demos: dm17,\n  },\n  'docs/chart-render/0.1.X/faq': {\n    frontmatter: fm18,\n    toc: toc18,\n    texts: txt18,\n    demos: dm18,\n  },\n  'docs/form-render/api-schema': {\n    frontmatter: fm19,\n    toc: toc19,\n    texts: txt19,\n    demos: dm19,\n  },\n  'docs/table-render/collapsed': {\n    frontmatter: fm20,\n    toc: toc20,\n    texts: txt20,\n    demos: dm20,\n  },\n  'docs/table-render/valueType': {\n    frontmatter: fm21,\n    toc: toc21,\n    texts: txt21,\n    demos: dm21,\n  },\n  'docs/form-render/api-props': {\n    frontmatter: fm22,\n    toc: toc22,\n    texts: txt22,\n    demos: dm22,\n  },\n  'docs/form-render/changelog': {\n    frontmatter: fm23,\n    toc: toc23,\n    texts: txt23,\n    demos: dm23,\n  },\n  'docs/form-render/demo/demo': {\n    frontmatter: fm24,\n    toc: toc24,\n    texts: txt24,\n    demos: dm24,\n  },\n  'docs/table-render/noSearch': {\n    frontmatter: fm25,\n    toc: toc25,\n    texts: txt25,\n    demos: dm25,\n  },\n  'docs/tools/proptypes/index': {\n    frontmatter: fm26,\n    toc: toc26,\n    texts: txt26,\n    demos: dm26,\n  },\n  'docs/generator/playground': {\n    frontmatter: fm27,\n    toc: toc27,\n    texts: txt27,\n    demos: dm27,\n  },\n  'docs/table-render/migrate': {\n    frontmatter: fm28,\n    toc: toc28,\n    texts: txt28,\n    demos: dm28,\n  },\n  'docs/form-render/migrate': {\n    frontmatter: fm29,\n    toc: toc29,\n    texts: txt29,\n    demos: dm29,\n  },\n  'docs/table-render/norBar': {\n    frontmatter: fm30,\n    toc: toc30,\n    texts: txt30,\n    demos: dm30,\n  },\n  'docs/chart-render/index': {\n    frontmatter: fm31,\n    toc: toc31,\n    texts: txt31,\n    demos: dm31,\n  },\n  'docs/table-render/basic': {\n    frontmatter: fm32,\n    toc: toc32,\n    texts: txt32,\n    demos: dm32,\n  },\n  'docs/table-render/index': {\n    frontmatter: fm33,\n    toc: toc33,\n    texts: txt33,\n    demos: dm33,\n  },\n  'docs/tools/vscode/index': {\n    frontmatter: fm34,\n    toc: toc34,\n    texts: txt34,\n    demos: dm34,\n  },\n  'docs/chart-render/demo': {\n    frontmatter: fm35,\n    toc: toc35,\n    texts: txt35,\n    demos: dm35,\n  },\n  'docs/form-render/index': {\n    frontmatter: fm36,\n    toc: toc36,\n    texts: txt36,\n    demos: dm36,\n  },\n  'docs/table-render/tabs': {\n    frontmatter: fm37,\n    toc: toc37,\n    texts: txt37,\n    demos: dm37,\n  },\n  'docs/chart-render/faq': {\n    frontmatter: fm38,\n    toc: toc38,\n    texts: txt38,\n    demos: dm38,\n  },\n  'docs/playground/index': {\n    frontmatter: fm39,\n    toc: toc39,\n    texts: txt39,\n    demos: dm39,\n  },\n  'docs/table-render/api': {\n    frontmatter: fm40,\n    toc: toc40,\n    texts: txt40,\n    demos: dm40,\n  },\n  'docs/form-render/faq': {\n    frontmatter: fm41,\n    toc: toc41,\n    texts: txt41,\n    demos: dm41,\n  },\n  'docs/generator/index': {\n    frontmatter: fm42,\n    toc: toc42,\n    texts: txt42,\n    demos: dm42,\n  },\n  'docs/generator/demo': {\n    frontmatter: fm43,\n    toc: toc43,\n    texts: txt43,\n    demos: dm43,\n  },\n  'docs/generator/faq': {\n    frontmatter: fm44,\n    toc: toc44,\n    texts: txt44,\n    demos: dm44,\n  },\n  'docs/index.en-US': {\n    frontmatter: fm45,\n    toc: toc45,\n    texts: txt45,\n    demos: dm45,\n  },\n  'docs/index.zh-CN': {\n    frontmatter: fm46,\n    toc: toc46,\n    texts: txt46,\n    demos: dm46,\n  },\n}\n\n// generate demos data in runtime, for reuse route.id to reduce bundle size\nexport const demos = Object.entries(filesMeta).reduce((acc, [id, meta]) => {\n  // append route id to demo\n  Object.values(meta.demos).forEach((demo) => {\n    demo.routeId = id;\n  });\n  // merge demos\n  Object.assign(acc, meta.demos);\n\n  // remove demos from meta, to avoid deep clone demos in umi routes/children compatible logic\n  delete meta.demos;\n\n  return acc;\n}, {});\n"
  },
  {
    "path": ".dumi/tmp-production/dumi/meta/runtime.ts",
    "content": "// @ts-nocheck\n// This file is generated by Umi automatically\n// DO NOT CHANGE IT MANUALLY!\nimport { filesMeta, tabs } from '.';\nimport deepmerge from '/Users/zhanbo/happy/x-render/node_modules/deepmerge';\nexport const patchRoutes = ({ routes }) => {\n  Object.values(routes).forEach((route) => {\n    if (filesMeta[route.id]) {\n      if (process.env.NODE_ENV === 'production' && (route.meta?.frontmatter?.debug || filesMeta[route.id].frontmatter.debug)) {\n        // hide route in production which set hide frontmatter\n        delete routes[route.id];\n      } else {\n        // merge meta to route object\n        route.meta = deepmerge(route.meta, filesMeta[route.id]);\n\n        // apply real tab data from id\n        route.meta.tabs = route.meta.tabs?.map((id) => {\n          const meta = {\n            frontmatter: { title: tabs[id].title },\n            toc: [],\n            texts: [],\n          }\n          return {\n            ...tabs[id],\n            meta: filesMeta[id] || meta,\n          }\n        });\n      }\n    }\n  });\n}\n"
  },
  {
    "path": ".dumi/tmp-production/dumi/meta/tabs.ts",
    "content": "// @ts-nocheck\n// This file is generated by Umi automatically\n// DO NOT CHANGE IT MANUALLY!\nexport const tabs = {\n}\n"
  },
  {
    "path": ".dumi/tmp-production/dumi/theme/ContextWrapper.tsx",
    "content": "// @ts-nocheck\n// This file is generated by Umi automatically\n// DO NOT CHANGE IT MANUALLY!\nimport React, { useState, useEffect, useRef } from 'react';\nimport { useOutlet, history } from 'dumi';\nimport { SiteContext } from '/Users/zhanbo/happy/x-render/node_modules/dumi/dist/client/theme-api/context.js';\nimport { demos, components } from '../meta';\nimport { locales } from '../locales/config';\n\nconst entryExports = {\n  \n  \n};\n\nexport default function DumiContextWrapper() {\n  const outlet = useOutlet();\n  const [loading, setLoading] = useState(true);\n  const prev = useRef(history.location.pathname);\n\n  useEffect(() => {\n    return history.listen((next) => {\n      if (next.location.pathname !== prev.current) {\n        prev.current = next.location.pathname;\n\n        // mark loading when route change, page component will set false when loaded\n        setLoading(true);\n\n        // scroll to top when route changed\n        document.documentElement.scrollTo(0, 0);\n      }\n    });\n  }, []);\n\n  return (\n    <SiteContext.Provider value={{\n      pkg: {\"name\":\"root\",\"version\":\"0.0.1\",\"repository\":{\"type\":\"git\",\"url\":\"git+https://github.com/alibaba/x-render.git\",\"branch\":\"master\",\"platform\":\"github\"}},\n      historyType: \"browser\",\n      entryExports,\n      demos,\n      components,\n      locales,\n      loading,\n      setLoading,\n      themeConfig: {\"footer\":\" Please feel free to use and contribute to the development.\",\"prefersColor\":{\"default\":\"light\",\"switch\":true},\"name\":\"XRender\",\"logo\":\"https://img.alicdn.com/tfs/TB17UtINiLaK1RjSZFxXXamPFXa-606-643.png\",\"socialLinks\":{\"github\":\"https://github.com/alibaba/x-render\"},\"nav\":{\"zh-CN\":[{\"title\":\"FormRender\",\"link\":\"/form-render\"},{\"title\":\"TableRender\",\"link\":\"/table-render\"},{\"title\":\"ChartRender\",\"link\":\"/chart-render\"},{\"title\":\"表单设计器\",\"link\":\"/generator\"},{\"title\":\"Playground\",\"link\":\"/playground\"}]}},\n    }}>\n      {outlet}\n    </SiteContext.Provider>\n  );\n}\n"
  },
  {
    "path": ".dumi/tmp-production/dumi/theme/builtins/API.ts",
    "content": "// @ts-nocheck\n// This file is generated by Umi automatically\n// DO NOT CHANGE IT MANUALLY!\nexport { default } from '/Users/zhanbo/happy/x-render/node_modules/dumi/theme-default/builtins/API/index.js';\n"
  },
  {
    "path": ".dumi/tmp-production/dumi/theme/builtins/Badge.ts",
    "content": "// @ts-nocheck\n// This file is generated by Umi automatically\n// DO NOT CHANGE IT MANUALLY!\nexport { default } from '/Users/zhanbo/happy/x-render/node_modules/dumi/theme-default/builtins/Badge/index.js';\n"
  },
  {
    "path": ".dumi/tmp-production/dumi/theme/builtins/Container.ts",
    "content": "// @ts-nocheck\n// This file is generated by Umi automatically\n// DO NOT CHANGE IT MANUALLY!\nexport { default } from '/Users/zhanbo/happy/x-render/node_modules/dumi/theme-default/builtins/Container/index.js';\n"
  },
  {
    "path": ".dumi/tmp-production/dumi/theme/builtins/Previewer.ts",
    "content": "// @ts-nocheck\n// This file is generated by Umi automatically\n// DO NOT CHANGE IT MANUALLY!\nexport { default } from '/Users/zhanbo/happy/x-render/node_modules/dumi/theme-default/builtins/Previewer/index.js';\n"
  },
  {
    "path": ".dumi/tmp-production/dumi/theme/builtins/SourceCode.ts",
    "content": "// @ts-nocheck\n// This file is generated by Umi automatically\n// DO NOT CHANGE IT MANUALLY!\nexport { default } from '/Users/zhanbo/happy/x-render/node_modules/dumi/theme-default/builtins/SourceCode/index.js';\n"
  },
  {
    "path": ".dumi/tmp-production/dumi/theme/builtins/Table.ts",
    "content": "// @ts-nocheck\n// This file is generated by Umi automatically\n// DO NOT CHANGE IT MANUALLY!\nexport { default } from '/Users/zhanbo/happy/x-render/node_modules/dumi/theme-default/builtins/Table/index.js';\n"
  },
  {
    "path": ".dumi/tmp-production/dumi/theme/layouts/DocLayout.ts",
    "content": "// @ts-nocheck\n// This file is generated by Umi automatically\n// DO NOT CHANGE IT MANUALLY!\nexport { default } from '/Users/zhanbo/happy/x-render/node_modules/dumi/theme-default/layouts/DocLayout/index.js';\n"
  },
  {
    "path": ".dumi/tmp-production/dumi/theme/slots/ColorSwitch.ts",
    "content": "// @ts-nocheck\n// This file is generated by Umi automatically\n// DO NOT CHANGE IT MANUALLY!\nexport { default } from '/Users/zhanbo/happy/x-render/node_modules/dumi/theme-default/slots/ColorSwitch/index.js';\n"
  },
  {
    "path": ".dumi/tmp-production/dumi/theme/slots/Content.ts",
    "content": "// @ts-nocheck\n// This file is generated by Umi automatically\n// DO NOT CHANGE IT MANUALLY!\nexport { default } from '/Users/zhanbo/happy/x-render/node_modules/dumi/theme-default/slots/Content/index.js';\n"
  },
  {
    "path": ".dumi/tmp-production/dumi/theme/slots/ContentTabs.ts",
    "content": "// @ts-nocheck\n// This file is generated by Umi automatically\n// DO NOT CHANGE IT MANUALLY!\nexport { default } from '/Users/zhanbo/happy/x-render/node_modules/dumi/theme-default/slots/ContentTabs/index.js';\n"
  },
  {
    "path": ".dumi/tmp-production/dumi/theme/slots/Features.ts",
    "content": "// @ts-nocheck\n// This file is generated by Umi automatically\n// DO NOT CHANGE IT MANUALLY!\nexport { default } from '/Users/zhanbo/happy/x-render/node_modules/dumi/theme-default/slots/Features/index.js';\n"
  },
  {
    "path": ".dumi/tmp-production/dumi/theme/slots/Footer.ts",
    "content": "// @ts-nocheck\n// This file is generated by Umi automatically\n// DO NOT CHANGE IT MANUALLY!\nexport { default } from '/Users/zhanbo/happy/x-render/node_modules/dumi/theme-default/slots/Footer/index.js';\n"
  },
  {
    "path": ".dumi/tmp-production/dumi/theme/slots/HeadeExtra.ts",
    "content": "// @ts-nocheck\n// This file is generated by Umi automatically\n// DO NOT CHANGE IT MANUALLY!\nexport { default } from '/Users/zhanbo/happy/x-render/node_modules/dumi/theme-default/slots/HeadeExtra/index.js';\n"
  },
  {
    "path": ".dumi/tmp-production/dumi/theme/slots/Header.ts",
    "content": "// @ts-nocheck\n// This file is generated by Umi automatically\n// DO NOT CHANGE IT MANUALLY!\nexport { default } from '/Users/zhanbo/happy/x-render/node_modules/dumi/theme-default/slots/Header/index.js';\n"
  },
  {
    "path": ".dumi/tmp-production/dumi/theme/slots/Hero.ts",
    "content": "// @ts-nocheck\n// This file is generated by Umi automatically\n// DO NOT CHANGE IT MANUALLY!\nexport { default } from '/Users/zhanbo/happy/x-render/node_modules/dumi/theme-default/slots/Hero/index.js';\n"
  },
  {
    "path": ".dumi/tmp-production/dumi/theme/slots/HeroTitle.ts",
    "content": "// @ts-nocheck\n// This file is generated by Umi automatically\n// DO NOT CHANGE IT MANUALLY!\nexport { default } from '/Users/zhanbo/happy/x-render/node_modules/dumi/theme-default/slots/HeroTitle/index.js';\n"
  },
  {
    "path": ".dumi/tmp-production/dumi/theme/slots/LangSwitch.ts",
    "content": "// @ts-nocheck\n// This file is generated by Umi automatically\n// DO NOT CHANGE IT MANUALLY!\nexport { default } from '/Users/zhanbo/happy/x-render/node_modules/dumi/theme-default/slots/LangSwitch/index.js';\n"
  },
  {
    "path": ".dumi/tmp-production/dumi/theme/slots/Logo.ts",
    "content": "// @ts-nocheck\n// This file is generated by Umi automatically\n// DO NOT CHANGE IT MANUALLY!\nexport { default } from '/Users/zhanbo/happy/x-render/node_modules/dumi/theme-default/slots/Logo/index.js';\n"
  },
  {
    "path": ".dumi/tmp-production/dumi/theme/slots/Navbar.ts",
    "content": "// @ts-nocheck\n// This file is generated by Umi automatically\n// DO NOT CHANGE IT MANUALLY!\nexport { default } from '/Users/zhanbo/happy/x-render/node_modules/dumi/theme-default/slots/Navbar/index.js';\n"
  },
  {
    "path": ".dumi/tmp-production/dumi/theme/slots/NavbarExtra.ts",
    "content": "// @ts-nocheck\n// This file is generated by Umi automatically\n// DO NOT CHANGE IT MANUALLY!\nexport { default } from '/Users/zhanbo/happy/x-render/node_modules/dumi/theme-default/slots/NavbarExtra/index.js';\n"
  },
  {
    "path": ".dumi/tmp-production/dumi/theme/slots/NotFound.ts",
    "content": "// @ts-nocheck\n// This file is generated by Umi automatically\n// DO NOT CHANGE IT MANUALLY!\nexport { default } from '/Users/zhanbo/happy/x-render/node_modules/dumi/theme-default/slots/NotFound/index.js';\n"
  },
  {
    "path": ".dumi/tmp-production/dumi/theme/slots/PreviewerActions.ts",
    "content": "// @ts-nocheck\n// This file is generated by Umi automatically\n// DO NOT CHANGE IT MANUALLY!\nexport { default } from '/Users/zhanbo/happy/x-render/node_modules/dumi/theme-default/slots/PreviewerActions/index.js';\n"
  },
  {
    "path": ".dumi/tmp-production/dumi/theme/slots/PreviewerActionsExtra.ts",
    "content": "// @ts-nocheck\n// This file is generated by Umi automatically\n// DO NOT CHANGE IT MANUALLY!\nexport { default } from '/Users/zhanbo/happy/x-render/node_modules/dumi/theme-default/slots/PreviewerActionsExtra/index.js';\n"
  },
  {
    "path": ".dumi/tmp-production/dumi/theme/slots/RtlSwitch.ts",
    "content": "// @ts-nocheck\n// This file is generated by Umi automatically\n// DO NOT CHANGE IT MANUALLY!\nexport { default } from '/Users/zhanbo/happy/x-render/node_modules/dumi/theme-default/slots/RtlSwitch/index.js';\n"
  },
  {
    "path": ".dumi/tmp-production/dumi/theme/slots/SearchBar.ts",
    "content": "// @ts-nocheck\n// This file is generated by Umi automatically\n// DO NOT CHANGE IT MANUALLY!\nexport { default } from '/Users/zhanbo/happy/x-render/node_modules/dumi/theme-default/slots/SearchBar/index.js';\nexport * from '/Users/zhanbo/happy/x-render/node_modules/dumi/theme-default/slots/SearchBar/index.js';\n"
  },
  {
    "path": ".dumi/tmp-production/dumi/theme/slots/SearchResult.ts",
    "content": "// @ts-nocheck\n// This file is generated by Umi automatically\n// DO NOT CHANGE IT MANUALLY!\nexport { default } from '/Users/zhanbo/happy/x-render/node_modules/dumi/theme-default/slots/SearchResult/index.js';\n"
  },
  {
    "path": ".dumi/tmp-production/dumi/theme/slots/Sidebar.ts",
    "content": "// @ts-nocheck\n// This file is generated by Umi automatically\n// DO NOT CHANGE IT MANUALLY!\nexport { default } from '/Users/zhanbo/happy/x-render/node_modules/dumi/theme-default/slots/Sidebar/index.js';\n"
  },
  {
    "path": ".dumi/tmp-production/dumi/theme/slots/SocialIcon.ts",
    "content": "// @ts-nocheck\n// This file is generated by Umi automatically\n// DO NOT CHANGE IT MANUALLY!\nexport { default } from '/Users/zhanbo/happy/x-render/node_modules/dumi/theme-default/slots/SocialIcon/index.js';\n"
  },
  {
    "path": ".dumi/tmp-production/dumi/theme/slots/Toc.ts",
    "content": "// @ts-nocheck\n// This file is generated by Umi automatically\n// DO NOT CHANGE IT MANUALLY!\nexport { default } from '/Users/zhanbo/happy/x-render/node_modules/dumi/theme-default/slots/Toc/index.js';\n"
  },
  {
    "path": ".dumi/tmp-production/exports.ts",
    "content": "// @ts-nocheck\n// This file is generated by Umi automatically\n// DO NOT CHANGE IT MANUALLY!\nexport { defineApp } from './core/defineApp'\nexport type { RuntimeConfig } from './core/defineApp'\n// @umijs/renderer-*\nexport { createBrowserHistory, createHashHistory, createMemoryHistory, Helmet, HelmetProvider, createSearchParams, generatePath, matchPath, matchRoutes, Navigate, NavLink, Outlet, resolvePath, useLocation, useMatch, useNavigate, useOutlet, useOutletContext, useParams, useResolvedPath, useRoutes, useSearchParams, useAppData, useClientLoaderData, useRouteProps, useSelectedRoutes, useServerLoaderData, renderClient, __getRoot, Link, useRouteData, __useFetcher, withRouter } from '/Users/zhanbo/happy/x-render/node_modules/@umijs/preset-umi/node_modules/@umijs/renderer-react';\nexport type {  History } from '/Users/zhanbo/happy/x-render/node_modules/@umijs/preset-umi/node_modules/@umijs/renderer-react'\n// umi/client/client/plugin\nexport { ApplyPluginsType, PluginManager } from '/Users/zhanbo/happy/x-render/node_modules/dumi/node_modules/umi/client/client/plugin.js';\nexport { history, createHistory } from './core/history';\nexport { terminal } from './core/terminal';\n// plugins\n// plugins types.d.ts\n"
  },
  {
    "path": ".dumi/tmp-production/testBrowser.tsx",
    "content": "// @ts-nocheck\n// This file is generated by Umi automatically\n// DO NOT CHANGE IT MANUALLY!\nimport React, { useEffect, useState } from 'react';\nimport { ApplyPluginsType } from 'umi';\nimport { renderClient, RenderClientOpts } from '/Users/zhanbo/happy/x-render/node_modules/@umijs/preset-umi/node_modules/@umijs/renderer-react';\nimport { createHistory } from './core/history';\nimport { createPluginManager } from './core/plugin';\nimport { getRoutes } from './core/route';\nimport type { Location } from 'history';\n\n\nconst publicPath = '/';\nconst runtimePublicPath = false;\n\ntype TestBrowserProps = {\n  location?: Partial<Location>;\n  historyRef?: React.MutableRefObject<Location>;\n};\n\nexport function TestBrowser(props: TestBrowserProps) {\n  const pluginManager = createPluginManager();\n  const [context, setContext] = useState<RenderClientOpts | undefined>(\n    undefined\n  );\n  useEffect(() => {\n    const genContext = async () => {\n      const { routes, routeComponents } = await getRoutes(pluginManager);\n      // allow user to extend routes\n      await pluginManager.applyPlugins({\n        key: 'patchRoutes',\n        type: ApplyPluginsType.event,\n        args: {\n          routes,\n          routeComponents,\n        },\n      });\n      const contextOpts = pluginManager.applyPlugins({\n        key: 'modifyContextOpts',\n        type: ApplyPluginsType.modify,\n        initialValue: {},\n      });\n      const basename = contextOpts.basename || '/';\n      const history = createHistory({\n        type: 'memory',\n        basename,\n      });\n      const context = {\n        routes,\n        routeComponents,\n        pluginManager,\n        rootElement: contextOpts.rootElement || document.getElementById('root'),\n        loadingComponent: Loading,\n        publicPath,\n        runtimePublicPath,\n        history,\n        basename,\n        components: true,\n      };\n      const modifiedContext = pluginManager.applyPlugins({\n        key: 'modifyClientRenderOpts',\n        type: ApplyPluginsType.modify,\n        initialValue: context,\n      });\n      return modifiedContext;\n    };\n    genContext().then((context) => {\n      setContext(context);\n      if (props.location) {\n        context?.history?.push(props.location);\n      }\n      if (props.historyRef) {\n        props.historyRef.current = context?.history;\n      }\n    });\n  }, []);\n\n  if (context === undefined) {\n    return <div id=\"loading\" />;\n  }\n\n  const Children = renderClient(context);\n  return (\n    <React.Fragment>\n      <Children />\n    </React.Fragment>\n  );\n}\n"
  },
  {
    "path": ".dumi/tmp-production/umi.ts",
    "content": "// @ts-nocheck\n// This file is generated by Umi automatically\n// DO NOT CHANGE IT MANUALLY!\nimport './core/polyfill';\n\nimport { renderClient } from '/Users/zhanbo/happy/x-render/node_modules/@umijs/preset-umi/node_modules/@umijs/renderer-react';\nimport { getRoutes } from './core/route';\nimport { createPluginManager } from './core/plugin';\nimport { createHistory } from './core/history';\nimport Loading from '/Users/zhanbo/happy/x-render/.dumi/loading.tsx';\nimport { ApplyPluginsType } from 'umi';\n\n\nconst publicPath = \"/\";\nconst runtimePublicPath = false;\n\nasync function render() {\n  const pluginManager = createPluginManager();\n  const { routes, routeComponents } = await getRoutes(pluginManager);\n\n  // allow user to extend routes\n  await pluginManager.applyPlugins({\n    key: 'patchRoutes',\n    type: ApplyPluginsType.event,\n    args: {\n      routes,\n      routeComponents,\n    },\n  });\n\n  const contextOpts = pluginManager.applyPlugins({\n    key: 'modifyContextOpts',\n    type: ApplyPluginsType.modify,\n    initialValue: {},\n  });\n\n  const basename = contextOpts.basename || '/';\n  const historyType = contextOpts.historyType || 'browser';\n\n  const history = createHistory({\n    type: historyType,\n    basename,\n    ...contextOpts.historyOpts,\n  });\n\n  return (pluginManager.applyPlugins({\n    key: 'render',\n    type: ApplyPluginsType.compose,\n    initialValue() {\n      const context = {\n        routes,\n        routeComponents,\n        pluginManager,\n        rootElement: contextOpts.rootElement || document.getElementById('root'),\n        loadingComponent: Loading,\n        publicPath,\n        runtimePublicPath,\n        history,\n        historyType,\n        basename,\n        callback: contextOpts.callback,\n      };\n      const modifiedContext = pluginManager.applyPlugins({\n        key: 'modifyClientRenderOpts',\n        type: ApplyPluginsType.modify,\n        initialValue: context,\n      });\n      return renderClient(modifiedContext);\n    },\n  }))();\n}\n\n\n// always remove trailing slash from location.pathname\nif (\n  typeof history !== 'undefined' &&\n  location.pathname.length > 1 &&\n  location.pathname.endsWith('/')\n) {\n  history.replaceState(\n    {},\n    '',\n    location.pathname.slice(0, -1) + location.search + location.hash,\n  );\n}\n\n(function () {\n  var cache = typeof navigator !== 'undefined' && navigator.cookieEnabled && typeof window.localStorage !== 'undefined' && localStorage.getItem('dumi:prefers-color') || 'light';\n  var isDark = window.matchMedia('(prefers-color-scheme: dark)').matches;\n  var enums = ['light', 'dark', 'auto'];\n\n  document.documentElement.setAttribute(\n    'data-prefers-color',\n    cache === enums[2]\n      ? (isDark ? enums[1] : enums[0])\n      : (enums.indexOf(cache) > -1 ? cache : enums[0])\n  );\n})();\n\n    if(location.origin.includes('gitee')) location.href = 'https://xrender.fun'\n    \nrender();\n\nwindow.g_umi = {\n  version: '4.0.64',\n};\n"
  },
  {
    "path": ".dumirc.ts",
    "content": "import { defineConfig } from 'dumi';\nimport path from 'path';\nimport MonacoWebpackPlugin from 'monaco-editor-webpack-plugin';\n\nexport default defineConfig({\n  favicons: ['https://img.alicdn.com/tfs/TB17UtINiLaK1RjSZFxXXamPFXa-606-643.png'],\n  outputPath: 'docs-dist',\n  // locales: [{ id: 'zh-CN', name: '中文' }, { id: 'en-US', name: 'English' }],\n  locales: [{ id: 'zh-CN', name: '中文' }],\n  themeConfig: {\n    name: 'XRender',\n    logo: 'https://img.alicdn.com/tfs/TB17UtINiLaK1RjSZFxXXamPFXa-606-643.png',\n    footer: false,\n    socialLinks: {\n      github: 'https://github.com/alibaba/x-render',\n    },\n    nav: {\n      'zh-CN': [\n        {\n          title: 'FormRender',\n          link: '/form-render',\n        },\n        {\n          title: 'TableRender',\n          link: '/table-render',\n        },\n        {\n          title: 'ChartRender',\n          link: '/chart-render',\n        },\n        {\n          title: 'Playground',\n          link: '/playground',\n        }\n      ],\n      // 'en-US': [\n      //   {\n      //     title: 'FormRender',\n      //     link: '/en/form-render',\n      //   }\n      // ]\n    },\n  },\n  extraBabelPlugins: [\n    [\n      'import',\n      {\n        libraryName: 'antd',\n        libraryDirectory: 'es',\n        style: true,\n      },\n      'antd',\n    ],\n    [\n      'import',\n      {\n        libraryName: '@alifd/next',\n        libraryDirectory: 'lib',\n      },\n      '@alifd/next',\n    ],\n  ],\n  ignoreMomentLocale: true,\n  chainWebpack(config, { webpack }) {\n    config.plugin('monaco-editor').use(MonacoWebpackPlugin);\n  },\n  headScripts: [\n    'https://g.alicdn.com/code/lib/react/17.0.1/umd/react.production.min.js',\n    'https://g.alicdn.com/code/lib/react-dom/17.0.1/umd/react-dom.production.min.js'\n  ],\n  plugins: [require.resolve('./scripts/dumi-plugin/redirect')],\n  alias: { \n    'form-render/es':  path.resolve(__dirname, 'packages/form-render/src'),\n    'form-render':  path.resolve(__dirname, 'packages/form-render/src'),\n    'table-render':  path.resolve(__dirname, 'packages/table-render/src'),\n    'form-render-mobile':  path.resolve(__dirname, 'packages/form-render-mobile/src'),\n    '@xrenders/schema-builder':  path.resolve(__dirname, 'tools/schema-builder/src'),\n    '@xrenders/data-render':  path.resolve(__dirname, 'packages/data-render/src'),\n    '@xrenders/xflow':  path.resolve(__dirname, 'packages/x-flow/src'),\n  },\n  codeSplitting: { jsStrategy: 'granularChunks' },\n  //...(process.env.NODE_ENV === 'development' ? {} : { ssr: {} }),\n});\n\n"
  },
  {
    "path": ".editorconfig",
    "content": "# http://editorconfig.org\nroot = true\n\n[*]\nindent_style = space\nindent_size = 2\nend_of_line = lf\ncharset = utf-8\ntrim_trailing_whitespace = true\ninsert_final_newline = true\n\n[*.md]\ntrim_trailing_whitespace = false\n\n[Makefile]\nindent_style = tab\n"
  },
  {
    "path": ".fatherrc.js",
    "content": "// 通用的配置，可以在每个package里写 fatherrc.js 来覆盖\nexport default {\n  esm: 'rollup',\n  disableTypeCheck: false, // 如果出了问题，这个可以改成true\n  cjs: { type: 'babel', lazy: true },\n};\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug-report.md",
    "content": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: lhbxs\n---\n\n<!---\n  1. 提问前务必先搜索issue，查看是否已有类似问题。\n  1. try searching for similar issues before reporting yours\n  2. 非bug or feature类问题，欢迎加入我们的开源社区钉钉群进行讨论。\n-->\n\n**1.依赖仓库的版本(Dependencies versions)**：\n\n- react：\n- form-render：\n- table-render:\n- antd：\n\n**2.问题描述(Bug description)**：\n\n**3.出现问题的 schema demo(Reproduction schema demo)**：\n```js\nconst schema = {\n  // ...\n}\n```\n\n**4.最小复现 demo(Reproduction demo)**：\n\n<!--\n  请尽可能提供复现demo，有 demo 的 bug report 会在第一时间处理\n  Please provide reproduction of your bug. Bug report with reproduction demo will be dealt first\n-->\n\nform-render demo https://codesandbox.io/s/unruffled-flower-jl78h\ntable-render demo https://codesandbox.io/s/sweet-euler-bdoty\nfr-generator demo https://codesandbox.io/s/s13sh\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: ''\nassignees: lhbxs\n---\n\n### 期望的新功能 (describe the expected new feature)\n\n### 简述一下使用场景，便于开发者更好理解新功能的必要性 (describe your scenario for us to understand the need)\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: 🚀 Build CI\n\nenv:\n  NODE_OPTIONS: --max-old-space-size=6144\n\non: \n  push:\n    branches: [master, dev]\n  pull_request:\n    branches: [master, dev]\n\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n    strategy:\n      matrix:\n        node: [ '14', '16' ]\n    name: build steps\n    steps:\n      - uses: actions/checkout@v2\n      - name: Use Node.js ${{ matrix.node }}\n        uses: actions/setup-node@v2\n        with:\n          node-version: ${{ matrix.node }}\n      - name: Cache Node Dependencies\n        uses: actions/cache@v3\n        with:\n          path: node_modules\n          key: ${{runner.OS}}-${{ hashFiles('**/yarn.lock') }}\n      - name: Install\n        run: yarn install\n        if: steps.cache.outputs.cache-hit != 'true'\n      \n      - name: Build\n        run: yarn build\n\n      - name: Test\n        run: yarn test\n"
  },
  {
    "path": ".github/workflows/coverage.yml",
    "content": "# name: 💯 Coverage CI\n\n# on: \n#   workflow_run:\n#     workflows: [\"🚀 Build CI\"]\n#     types:\n#       - completed\n\n\n# jobs:\n#   build:\n#     runs-on: ubuntu-latest\n#     strategy:\n#       matrix:\n#         node: [ '14', '16' ]\n#     name: build steps\n#     steps:\n#       - uses: actions/checkout@v2\n#       - name: Use Node.js ${{ matrix.node }}\n#         uses: actions/setup-node@v2\n#         with:\n#           node-version: ${{ matrix.node }}\n#       - name: Run Test\n#       - run: yarn run test\n"
  },
  {
    "path": ".github/workflows/deploy.yml",
    "content": "# name: Deploy CI\n\n# on:\n#   push:\n#     branches:\n#       - master\n\n# jobs:\n#   build-and-deploy:\n#     runs-on: ubuntu-latest\n#     name: deploy steps\n#     steps:\n#       - uses: actions/checkout@v2\n#       - name: install \n#         run: yarn install\n#       - name: build\n#         run: yarn doc\n#       - name: Deploy Action\n#         uses: peaceiris/actions-gh-pages@v3\n#         with:\n#           deploy_key: ${{ secrets.ACTIONS_DEPLOY_TOKEN }}\n#           publish_dir: ./docs-dist\n#           force_orphan: true\n        "
  },
  {
    "path": ".github/workflows/emoji-helper.yml",
    "content": "name: Emoji Helper\n\non:\n  release:\n    types: [published]\n\njobs:\n  emoji:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions-cool/emoji-helper@v1.0.0\n        with:\n          type: 'release'\n          emoji: '+1, laugh, heart, hooray, rocket, eyes'\n"
  },
  {
    "path": ".github/workflows/issue-helper.yml",
    "content": "name: Issue Reply\n\non:\n  issues:\n    types: [labeled]\n\njobs:\n  reply-helper:\n    runs-on: ubuntu-latest\n    steps:\n      - name: need demo\n        if: github.event.label.name == 'need demo'\n        uses: actions-cool/issues-helper@v2.5.0\n        with:\n          actions: 'create-comment'\n          token: ${{ secrets.GITHUB_TOKEN }}\n          issue-number: ${{ github.event.issue.number }}\n          body: |\n            Hello @${{ github.event.issue.user.login }}. To fix it quickly, We need you to provide a complete demo on codesandbox .\n\n            你好 @${{ github.event.issue.user.login }}，我们需要你在codesandbox上提供一个完整的demo，便于我们快速修复哦。\n\n      - name: pr welcome\n        if: github.event.label.name == 'pr welcome'\n        uses: actions-cool/issues-helper@v2.5.0\n        with:\n          actions: 'create-comment'\n          token: ${{ secrets.GITHUB_TOKEN }}\n          issue-number: ${{ github.event.issue.number }}\n          body: |\n            Hello @${{ github.event.issue.user.login }}. We totally like your proposal/feedback, welcome PR 🎉。\n\n            你好 @${{ github.event.issue.user.login }}，我们完全同意你的提议/反馈，欢迎PR 🎉。"
  },
  {
    "path": ".github/workflows/mirror.yml",
    "content": "# name: 🔀 Sync mirror to Vercel\n\n# on:\n#   push:\n#     branches:\n#       - master\n\n# jobs:\n#   mirror:\n#     runs-on: ubuntu-latest\n#     steps:\n#       - name: mirror actions\n#         continue-on-error: true\n#         uses: wearerequired/git-mirror-action@v1\n#         env:\n#           SSH_PRIVATE_KEY: ${{ secrets.FORK_PRIVATE_KEY }}\n#         with:\n#           source-repo: 'https://github.com/alibaba/x-render.git'\n#           destination-repo: 'git@github.com:siyi98/x-render.git'\n      "
  },
  {
    "path": ".gitignore",
    "content": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n/npm-debug.log*\n/yarn-error.log\n/yarn.lock\n/package-lock.json\n\n*/*/yarn-error.log\n\n.yarn\n.turbo/cookies\n.turbo/daemon\n.turbo\n\n# turbo\n*/*/.turbo\n\n\n# production\n/dist\n/lib\n/docs-dist\n# misc\n.idea\n.DS_Store\n.idea\n.vscode\n\n# umi\n.dumi/tmp\n.dumi-production\n.dumi-test\n.env.local\n\n# lerna\nnode_modules\n\n/.local\n\n/packages/*/es\n/packages/*/lib\n/packages/*/.local\n/packages/*/dist\n\n/widgets/*/es\n/widgets/*/lib\n/widgets/*/.local\n/widgets/*/dist\n\n/tools/*/es\n/tools/*/lib\n/tools/*/.local\n/tools/*/dist\n\n# custom\nold-doc\ntest.json\n/docs/guide/.test\n\n/coverage\n"
  },
  {
    "path": ".npmrc",
    "content": "registry=https://registry.npmjs.org/\n"
  },
  {
    "path": ".prettierignore",
    "content": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n**/*.svg\n**/**/*.svg\n**/*.ejs\n**/**/*.ejs\n**/*.html\n**/**/*.html\n\n# dependencies\n/node_modules\n/npm-debug.log*\n/yarn-error.log\n/yarn.lock\n/package-lock.json\n\n\n# production\n/dist\n/lib\n/docs-dist\n# misc\n.idea\n.DS_Store\n.idea\n.vscode\n\n# umi\n.umi\n.umi-production\n.umi-test\n.env.local\n\n# lerna\nnode_modules\n\n/.local\n\n/packages/*/es\n/packages/*/lib\n/packages/*/.local\n/packages/*/dist\n\n/widgets/*/es\n/widgets/*/lib\n/widgets/*/.local\n/widgets/*/dist\n\n/tools/*/es\n/tools/*/lib\n/tools/*/.local\n/tools/*/dist\n\n# custom\nold-doc\ntest.json\n/docs/guide/.test\n/docs/*/*.md\n/docs/playground/monaco/index.js"
  },
  {
    "path": ".prettierrc",
    "content": "{\n  \"singleQuote\": true,\n  \"trailingComma\": \"es5\",\n  \"printWidth\": 80,\n  \"arrowParens\": \"avoid\",\n  \"overrides\": [\n    {\n      \"files\": \".prettierrc\",\n      \"options\": { \"parser\": \"json\" }\n    }\n  ]\n}\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, we as\ncontributors and maintainers pledge to making participation in our project and\nour community a harassment-free experience for everyone, regardless of age, body\nsize, disability, ethnicity, sex characteristics, gender identity and expression,\nlevel of experience, education, socio-economic status, nationality, personal\nappearance, race, religion, or sexual identity and orientation.\n\n## Our Standards\n\nExamples of behavior that contributes to creating a positive environment\ninclude:\n\n- Using welcoming and inclusive language\n- Being respectful of differing viewpoints and experiences\n- Gracefully accepting constructive criticism\n- Focusing on what is best for the community\n- Showing empathy towards other community members\n\nExamples of unacceptable behavior by participants include:\n\n- The use of sexualized language or imagery and unwelcome sexual attention or\n  advances\n- Trolling, insulting/derogatory comments, and personal or political attacks\n- Public or private harassment\n- Publishing others' private information, such as a physical or electronic\n  address, without explicit permission\n- Other conduct which could reasonably be considered inappropriate in a\n  professional setting\n\n## Our Responsibilities\n\nProject maintainers are responsible for clarifying the standards of acceptable\nbehavior and are expected to take appropriate and fair corrective action in\nresponse to any instances of unacceptable behavior.\n\nProject maintainers have the right and responsibility to remove, edit, or\nreject comments, commits, code, wiki edits, issues, and other contributions\nthat are not aligned to this Code of Conduct, or to ban temporarily or\npermanently any contributor for other behaviors that they deem inappropriate,\nthreatening, offensive, or harmful.\n\n## Scope\n\nThis Code of Conduct applies both within project spaces and in public spaces\nwhen an individual is representing the project or its community. Examples of\nrepresenting a project or community include using an official project e-mail\naddress, posting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event. Representation of a project may be\nfurther defined and clarified by project maintainers.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be\nreported by contacting the project team at tw93@qq.com. All\ncomplaints will be reviewed and investigated and will result in a response that\nis deemed necessary and appropriate to the circumstances. The project team is\nobligated to maintain confidentiality with regard to the reporter of an incident.\nFurther details of specific enforcement policies may be posted separately.\n\nProject maintainers who do not follow or enforce the Code of Conduct in good\nfaith may face temporary or permanent repercussions as determined by other\nmembers of the project's leadership.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,\navailable at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html\n\n[homepage]: https://www.contributor-covenant.org\n\nFor answers to common questions about this code of conduct, see\nhttps://www.contributor-covenant.org/faq\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "## 如何开发\n\n#### 1. 项目使用 lerna 管理，所有的大包在 `/packages`，所有的自定义组件包在 `/widgets`\n\n#### 2. 如何跑起来（以下操作全在根目录）\n\n```sh\n# 安装依赖，请使用yarn，会去重的安装所有 node_modules (注意安装必须用 yarn，其他命令无所谓)\nyarn\n# 首次开发需要 build 一下，否则有些仓库的 import 在 dumi 里会找不到\nyarn build\n# 将文档网站跑起来\nyarn start\n```\n\n#### 3. 进入文档网站了，如何开发呢？\n\n- 例如要开发 form-render 在 /docs/form-render/guide 目录下写一个 test.md 文件, 参考 /docs/form-render/guide/new-feature.md 的写法即可。发布前把\n  test.md 干掉，或者放到不会被展示的文件目录下\n\n- 提交前注意必须**格式化**。提交前注意必须**格式化**。提交前注意必须**格式化**。请安装 prettier 插件，或者在提交前执行\n\n```sh\nyarn format\n```\n\n#### 4. 重新安装依赖\n\n```sh\n# 先清空\nyarn clean\n# 再安装\nyarn\n```\n\n\n注意 lerna clean 不会清除顶层的 node_modules，所以如果因为特殊原因要彻底清空依赖，请执行 `rm -rf node_modules`\n\n#### 5. 发布\n\n此操作只针对有 npm 发布权限的贡献，进入相应项目的文件夹，例如 form-render，执行 publish\n\n```sh\n# 进入\ncd packages/form-render\n# 换版本号、打tag。注意tag要打，便于release note的维护\nyarn version --new-version 1.1.0\n# 发布\nnpm publish\n```\n\n发布后记得到 [releases 页](https://github.com/alibaba/x-render/releases/) 补上最新发布日志\n\n#### 6. 实际装包测试（本地 or 发布 beta 版本）\n\n- 本地测试\n\n推荐一下 yalc。是一个完全可以当做 yarn 来使用，但是发包和装包都在本地的工具，个人调试强烈推荐\n\n- 发 beta 包\n\nbeta 版本的版本号规范为 x.x.x-beta.x，一般用于大功能上线前的真实测试，不会被正常 npm i 安装。\n进入 package/form-render 文件夹，执行\n\n```sh\n# 换版本号、打tag。注意tag要打，便于release note的维护\nyarn version --new-version 1.1.0-beta.0\n# 发布\nnpm run beta\n```\n\n#### 7. 分支管理\n\n外部同学请 fork，内部同学请在 dev 分支开发，然后都发 pull-request 到 master 分支，由负责同学审核后合并，master 分支请勿人为去动\n\n#### 8. 如何检验一个 pull request\n\n```\n# ID 为 pr 的 id\ngit fetch origin pull/ID/head && git checkout FETCH_HEAD\n```\n\n#### 9. 编辑文档\n\n编辑文档请参考 [文档规范](https://github.com/alibaba/x-render/wiki/%E6%96%87%E6%A1%A3%E8%A7%84%E8%8C%83)\n"
  },
  {
    "path": "README.md",
    "content": "<p align=\"center\">\n  <a href=\"https://xrender.fun/\" target=\"_blank\"><img src=\"https://img.alicdn.com/tfs/TB17UtINiLaK1RjSZFxXXamPFXa-606-643.png\" alt=\"logo\" width=\"20%\"/></a>\n</p>\n<h1 align= \"center\">\n<a href=\"https://xrender.fun/\" target=\"_blank\">XRender</a>\n</h1>\n\n<p align=\"center\">\n  <a href=\"https://www.npmjs.com/package/form-render?_blank\">\n    <img alt=\"npm\" src=\"https://img.shields.io/npm/v/form-render.svg?maxAge=3600&style=flat-square\"></a>\n  <a href=\"https://github.com/alibaba/form-render/commits/dev\">\n    <img alt=\"GitHub last commit\" src=\"https://img.shields.io/github/last-commit/alibaba/form-render.svg?style=flat-square\"></a>\n  <a href=\"https://github.com/alibaba/form-render\">\n    <img alt=\"GitHub repo size\" src=\"https://img.shields.io/github/repo-size/alibaba/form-render\"></a>\n  <a href=\"https://github.com/alibaba/form-render/issues?utf8=%E2%9C%93&q=\">\n    <img alt = \"GitHub closed issues\" src=\"https://img.shields.io/github/issues-closed/alibaba/form-render.svg?style=flat-square\"></a>\n  <a href=\"https://npmjs.org/package/form-render\">\n    <img alt = \"NPM downloads\" src=\"https://img.shields.io/npm/dm/form-render.svg?style=flat-square\"></a>\n  <a href=\"https://npmjs.org/package/form-render\">\n    <img alt = \"NPM all downloads\" src=\"https://img.shields.io/npm/dt/form-render.svg?style=flat-square\"></a>\n  <a>\n    <img alt = \"PRs Welcome\" src=\"https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square\"></a>\n</p>\n\n> 中后台「表单 / 表格 / 图表」开箱即用解决方案\n\n## 优势\n\n- <a href=\"https://xrender.fun/form-render\" target=\"_blank\">FormRender</a>：像写一个 input 一样写表单\n- <a href=\"https://xrender.fun/table-render\" target=\"_blank\">TableRender</a>：协议生成 & 高度灵活的搜索列表\n- <a href=\"https://xrender.fun/chart-render\" target=\"_blank\">ChartRender</a>：傻瓜式的图表绘制库\n- <a href=\"https://xrender.fun/generator\" target=\"_blank\">FormGenerator</a>：中后台表单可视化搭建生成利器\n\n\n<div style=\"max-width: 961px; margin: auto;\">\n  <h2>谁在使用?</h4>\n  <table style=\"border: none; background: #fff\">\n    <tr>\n      <td width=\"160\" align=\"center\">\n        <img\n          src=\"https://gw.alicdn.com/bao/tfs/TB1mFZneMmH3KVjSZKzXXb2OXXa-748-467.png\"\n          width=\"160\"\n        />\n      </td>\n      <td width=\"160\" align=\"center\">\n        <img\n          src=\"https://gw.alipayobjects.com/zos/k/sx/kJRfKV.jpg\"\n          width=\"160\"\n        />\n      </td>\n      <td width=\"160\" align=\"center\">\n        <img\n          src=\"https://gw.alicdn.com/tfs/TB176rg4VP7gK0jSZFjXXc5aXXa-286-118.png\"\n          width=\"160\"\n        />\n      </td>\n      <td width=\"160\" align=\"center\">\n        <img\n          src=\"https://img.alicdn.com/tfs/TB13DzOjXP7gK0jSZFjXXc5aXXa-212-48.png\"\n          width=\"160\"\n        />\n      </td>\n      <td width=\"160\" align=\"center\">\n        <img\n          src=\"https://img.alicdn.com/imgextra/i1/O1CN01Uw1xNd1H1GnHYCaOr_!!6000000000697-2-tps-1920-1920.png\"\n          width=\"160\"\n        />\n      </td>\n      <td width=\"160\" align=\"center\">\n        <img\n          src=\"https://gw.alipayobjects.com/zos/k/dl/zJ2uhY.jpg\"\n          width=\"110\"\n        />\n      </td>\n    </tr>\n    <tr>\n      <td width=\"160\" align=\"center\">\n        <img\n          src=\"https://img.alicdn.com/imgextra/i4/O1CN01SUv7rt1gMfdYr2Bnc_!!6000000004128-0-tps-800-373.jpg\"\n          width=\"160\"\n        />\n      </td>\n      <td width=\"160\" align=\"center\">\n        <img\n          src=\"https://gw.alipayobjects.com/zos/k/9l/RZCG03.jpg\"\n          width=\"160\"\n        />\n      </td>\n      <td width=\"160\" align=\"center\">\n        <img\n          src=\"https://gw.alipayobjects.com/zos/k/rh/4PXvlp.jpg\"\n          width=\"160\"\n        />\n      </td>\n      <td width=\"160\" align=\"center\">\n        <img\n          src=\"https://img.alicdn.com/imgextra/i3/O1CN01xDuypG1V78PWpnnPz_!!6000000002605-2-tps-600-120.png\"\n          width=\"160\"\n        />\n      </td>\n      <td width=\"160\" align=\"center\">\n        <img\n          src=\"https://gw.alipayobjects.com/zos/k/wu/TzIbI0.jpg\"\n          width=\"160\"\n        />\n      </td>\n      <td width=\"160\" align=\"center\">\n        <img\n          src=\"https://img.alicdn.com/imgextra/i1/O1CN01K3AD1b1WZMSHolALT_!!6000000002802-0-tps-700-207.jpg\"\n          width=\"160\"\n        />\n      </td>\n    </tr>\n    <tr>\n      <td width=\"160\" align=\"center\">\n        <img\n          src=\"https://gw.alipayobjects.com/zos/k/w7/02gpTA.jpg\"\n          width=\"160\"\n        />\n      </td>\n      <td width=\"160\" align=\"center\">\n        <img\n          src=\"https://img.alicdn.com/imgextra/i4/O1CN01BJ26gA1cYLKkfiaCG_!!6000000003612-2-tps-287-176.png\"\n          width=\"160\"\n        />\n      </td>\n      <td width=\"160\" align=\"center\">\n        <img\n          src=\"https://img.alicdn.com/imgextra/i3/O1CN017E63ji1W3InNkUvJE_!!6000000002732-2-tps-1000-500.png\"\n          width=\"160\"\n        />\n      </td>\n      <td width=\"160\" align=\"center\">\n        <img\n          src=\"https://img.alicdn.com/imgextra/i2/O1CN01Zu5QsE1OJm7GYKH06_!!6000000001685-2-tps-363-139.png\"\n          width=\"160\"\n        />\n      </td>\n      <td width=\"160\" align=\"center\">\n        <img\n          src=\"https://img.alicdn.com/imgextra/i1/O1CN01RqiiQ81j0dUdUfoIN_!!6000000004486-2-tps-280-66.png\"\n          width=\"160\"\n        />\n      </td>\n      <td width=\"160\" align=\"center\">\n        <img\n          src=\"https://img.alicdn.com/imgextra/i1/O1CN01B0UOM61lnIoMVJU6f_!!6000000004863-2-tps-500-397.png\"\n        />\n      </td>\n    </tr>\n    <tr>\n      <td width=\"160\" align=\"center\">\n        <img\n          src=\"https://img.alicdn.com/imgextra/i2/O1CN01DiuhTS1u3o0WsTsNb_!!6000000005982-2-tps-367-137.png\"\n        />\n      </td>\n      <td width=\"160\" align=\"center\">\n        <img\n          src=\"https://img.alicdn.com/imgextra/i3/O1CN01Vj8xN329If5dlvb50_!!6000000008045-2-tps-495-405.png\"\n        />\n      </td>\n    </tr>\n  </table>\n</div>\n\n更多可见[使用场景](https://github.com/alibaba/form-render/issues/94)，也很欢迎提交～\n\n## 支持\n\n- 如果你觉得 XRender 还不错，可以通过 [Star](https://github.com/alibaba/form-render/stargazers) 来表示你的喜欢\n- 在公司或个人项目中使用 XRender，并帮忙推广给伙伴使用\n\n## 贡献\n\n想贡献代码、解 BUG 或者提高文档可读性？非常欢迎一起参与进来，在提交 PR 前阅读一下 [Contributing Guide](https://github.com/alibaba/form-render/blob/master/CONTRIBUTING.md)。\n\n![Alt](https://repobeats.axiom.co/api/embed/2b2e9f5fdcdddeea164ef615d55816d8c2d2dc66.svg 'Repobeats analytics image')\n\n感谢给 XRender 贡献代码的你们：\n\n  <a href=\"https://github.com/alibaba/x-render/graphs/contributors\">\n    <img src=\"https://contrib.rocks/image?repo=alibaba/form-render\" />\n  </a>\n\n<br>\n\nhttps://user-images.githubusercontent.com/8736212/123383626-ff187a80-d5c5-11eb-803f-296762fe72d0.mp4\n\n## 协议\n\n- 遵循 MIT 协议\n- 请自由地享受和参与开源\n\n## 互助答疑群\n<img style=\"height: 400px\" src=\"https://img.alicdn.com/imgextra/i3/O1CN01HksNgs1DZNuQsORIp_!!6000000000230-0-tps-1242-1602.jpg\">\n\n\n## Star 趋势\n\n[![Star History Chart](https://api.star-history.com/svg?repos=alibaba/x-render&type=Date)](https://star-history.com/#alibaba/x-render&Date)\n"
  },
  {
    "path": "app.ts",
    "content": "window.publicPath = '/';\n\nif (location.origin.includes('gitee')) {\n  location.href = 'https://xrender.fun/';\n}\n\nif (location.origin.includes('alibaba')) {\n  window.publicPath = '/x-render/';\n}\n"
  },
  {
    "path": "docs/data-render/data/basic.ts",
    "content": "export default {\n  \"creator\": \"清风徐来\",\n  \"relevanceCode\": \"421421\",\n  \"desc\": \"浙江省杭州市工专路\",\n  \"create-time\": \"2019-10-10\",\n  \"effective-date\": \"2019-10-10 ～ 2020-10-31\",\n  \"safety\": {\n    \"name\": \"Test demo 001\",\n    \"app\": \"中后台详情页面\",\n    \"mode\": \"代码包\",\n    \"yum\": \"592342323904823489\",\n    \"fore\": \"23\"\n  },\n  \"operLog\": [{\n    \"type\": \"创建测试\",\n    \"creator\": \"清风徐来\",\n    \"time\": \"2019-10-30 12:23:45\",\n    \"result\": \"1\",\n    \"desc\": \"这是备注\"\n  }, {\n    \"type\": \"创建测试\",\n    \"creator\": \"清风徐来\",\n    \"time\": \"2019-10-30 12:23:45\",\n    \"result\": \"1\",\n    \"desc\": \"这是备注\"\n  }, {\n    \"type\": \"创建测试\",\n    \"creator\": \"清风徐来\",\n    \"time\": \"2019-10-30 12:23:45\",\n    \"result\": \"1\",\n    \"desc\": \"这是备注\"\n  }, {\n    \"type\": \"创建测试\",\n    \"creator\": \"清风徐来\",\n    \"time\": \"2019-10-30 12:23:45\",\n    \"result\": \"1\",\n    \"desc\": \"这是备注\"\n  }, {\n    \"type\": \"创建测试\",\n    \"creator\": \"清风徐来\",\n    \"time\": \"2019-10-30 12:23:45\",\n    \"result\": \"1\",\n    \"desc\": \"这是备注\"\n  }, {\n    \"type\": \"创建测试\",\n    \"creator\": \"清风徐来\",\n    \"time\": \"2019-10-30 12:23:45\",\n    \"result\": \"1\",\n    \"desc\": \"这是备注\"\n  }]\n}"
  },
  {
    "path": "docs/data-render/index.md",
    "content": "---\norder: 0\ntitle: 开始使用\nmobile: false\n---\n\n<div style=\"display:flex;align-items:center;margin-bottom:24px\">\n  <img src=\"https://img.alicdn.com/tfs/TB17UtINiLaK1RjSZFxXXamPFXa-606-643.png\" alt=\"logo\" width=\"48px\"/>\n  <span style=\"font-size:30px;font-weight:600;display:inline-block;margin-left:12px\">DataView</span>\n</div>\n<p style=\"display:flex;justify-content:space-between;width:440px\">\n  <a href=\"https://www.npmjs.com/package/@xrenders/data-render\" target=\"_blank\">\n    <img alt=\"npm\" src=\"https://img.shields.io/npm/v/@xrenders/data-render.svg?maxAge=3600&style=flat-square\">\n  </a>\n  <a href=\"https://npmjs.org/package/@xrenders/data-render\" target=\"_blank\">\n    <img alt=\"NPM downloads\" src=\"https://img.shields.io/npm/dm/@xrenders/data-render.svg?style=flat-square\">\n  </a>\n  <a href=\"https://npmjs.org/package/@xrenders/data-render\" target=\"_blank\">\n    <img alt=\"NPM all downloads\" src=\"https://img.shields.io/npm/dt/@xrenders/data-render.svg?style=flat-square\">\n  </a>\n  <a>\n    <img alt=\"PRs Welcome\" src=\"https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square\">\n  </a>\n</p>\n\n中后台详情页解决方案，通过 schema 协议渲染页面\n\n\n## 安装\n```shell\nnpm i @xrenders/data-render --save\n```\n\n## 使用方式\n\n**函数组件**\n\n```jsx\n/**\n * transform: true\n * defaultShowCode: true\n */\nimport React from 'react';\nimport DataView from '@xrenders/data-render';\nimport schema from './schema/basic';\nimport data from './data/basic';\n\nexport default () => {\n  return (\n    <DataView schema={schema} data={data} />\n  );\n}\n```"
  },
  {
    "path": "docs/data-render/schema/basic.ts",
    "content": "export default [\n  {\n    \"widget\": \"FPanel\",\n    \"style\": {\n      \"paddingTop\": \"20px\",\n      \"paddingLeft\": \"20px\",\n      \"paddingBottom\": \"20px\",\n      \"paddingRight\": \"20px\",\n      \"backgroundColor\": \"#ffffff\",\n      \"marginBottom\": \"12px\"\n    },\n    \"children\": [\n      {\n        \"widget\": \"FTitle\",\n        \"data\": \"基础信息\"\n      },\n      {\n        \"widget\": \"FDescriptions\",\n        \"column\": 3,\n        \"items\": [\n          {\n            \"label\": \"创建人\",\n            \"dataKey\": \"creator\"\n          },\n          {\n            \"label\": \"关联单据\",\n            \"dataKey\": \"relevanceCode\"\n          },\n          {\n            \"label\": \"单据备注\",\n            \"dataKey\": \"desc\"\n          },\n          {\n            \"label\": \"创建时间\",\n            \"dataKey\": \"create-time\"\n          },\n          {\n            \"label\": \"生效日期\",\n            \"dataKey\": \"effective-date\"\n          },\n          {\n            \"label\": \"描述项\",\n            \"showLevel\": 1\n          }\n        ],\n        \"style\": {\n          \"backgroundColor\": \"#ffffff\",\n          \"paddingTop\": \"0px\",\n          \"paddingLeft\": \"0px\",\n          \"paddingRight\": \"0px\",\n          \"paddingBottom\": \"0px\"\n        },\n        \"itemShowLevel\": 1,\n        \"getCompProps\": \"xxxx\"\n      }\n    ]\n  },\n  {\n    \"widget\": \"FTabs\",\n    \"items\": [\n      {\n        \"label\": \"负载均衡(SLB)\",\n        \"children\": [\n          {\n            \"widget\": \"FPanel\",\n            \"style\": {\n              \"paddingTop\": \"20px\",\n              \"paddingLeft\": \"20px\",\n              \"paddingBottom\": \"20px\",\n              \"paddingRight\": \"20px\",\n              \"backgroundColor\": \"#ffffff\",\n              \"marginBottom\": \"12px\"\n            },\n            \"children\": [\n              {\n                \"widget\": \"FTitle\",\n                \"data\": \"安全信息\"\n              },\n              {\n                \"widget\": \"FDescriptions\",\n                \"column\": 2,\n                \"items\": [\n                  {\n                    \"label\": \"安全构建名称\",\n                    \"dataKey\": \"name\"\n                  },\n                  {\n                    \"label\": \"所属应用\",\n                    \"dataKey\": \"app\"\n                  },\n                  {\n                    \"label\": \"构建模式\",\n                    \"dataKey\": \"mode\"\n                  },\n                  {\n                    \"label\": \"公网域名\",\n                    \"dataKey\": \"yum\"\n                  },\n                  {\n                    \"label\": \"保留计算实例\",\n                    \"dataKey\": \"fore\"\n                  }\n                ],\n                \"dataKey\": \"safety\"\n              }\n            ]\n          },\n          {\n            \"widget\": \"FPanel\",\n            \"style\": {\n              \"paddingTop\": \"20px\",\n              \"paddingLeft\": \"20px\",\n              \"paddingBottom\": \"20px\",\n              \"paddingRight\": \"20px\",\n              \"backgroundColor\": \"#ffffff\",\n              \"marginBottom\": \"12px\"\n            },\n            \"children\": [\n              {\n                \"widget\": \"FTitle\",\n                \"data\": \"操作日志\"\n              },\n              {\n                \"widget\": \"FTable\",\n                \"pagination\": {\n                  \"pageSize\": \"3\"\n                },\n                \"style\": {\n                  \"backgroundColor\": \"#ffffff\"\n                },\n                \"dataKey\": \"operLog\",\n                \"column\": {\n                  \"type\": {\n                    \"title\": \"操作类型\",\n                    \"dataKey\": \"type\"\n                  },\n                  \"creator\": {\n                    \"title\": \"操作人\",\n                    \"dataKey\": \"creator\"\n                  },\n                  \"time\": {\n                    \"title\": \"操作时间\",\n                    \"dataKey\": \"time\"\n                  },\n                  \"result\": {\n                    \"title\": \"执行结果\",\n                    \"dataKey\": \"result\"\n                  },\n                  \"desc\": {\n                    \"title\": \"备注\",\n                    \"dataKey\": \"desc\"\n                  }\n                }\n              }\n            ]\n          }\n        ]\n      },\n      {\n        \"label\": \"云服务器（ECS）\",\n        \"children\": []\n      }\n    ]\n  }\n]"
  },
  {
    "path": "docs/form-render/advaced-example.md",
    "content": "---\norder: 3\ntoc: content\nmobile: false\ngroup: \n  title: 高级用法\n  order: 2\n---\n\n# 常用交互\n\n## 表单数据提交\n通过 `onFinish` 方法监听表单提交，外部可通过调用 `form.submit` 触发 `onFinish`。\n\n```jsx\nimport React from 'react';\nimport { message } from 'antd';\nimport FormRender, { useForm } from 'form-render';\nimport schema from './schema/simple';\n\nexport default () => {\n  const form = useForm();\n\n  const onFinish = (data) => {\n    message.info(JSON.stringify(data));\n  };\n\n  return (\n    <FormRender form={form} schema={schema} onFinish={onFinish} footer={true} maxWidth={360} />\n  );\n}\n```\n\n## 表单数据初始化\n\n表单的初始化数据一般是通过接口异步查询获取的，当获取到数据时可以通过 `form.setValues` 方法进行表单数据初始化。\n```jsx\nimport React, { useState, useEffect } from 'react';\nimport { Button, Space, message } from 'antd';\nimport FormRender, { useForm } from 'form-render';\nimport { fakeApi, delay } from './utils';\nimport schema from './schema/simple';\n\nexport default () => {\n  const form = useForm();\n\n  const getRemoteData = () => {\n    fakeApi('xxx/getForm').then(_ => {\n      form.setValues({ input: '表单数据获取成功', select: 'c' });\n    });\n  };\n\n  return (\n    <div>\n      <FormRender form={form} schema={schema} maxWidth={360} />\n      <Button type='primary' onClick={getRemoteData}>加载服务端数据</Button>\n    </div>\n  );\n}\n```\n\n## 下拉选项异步加载\n下拉选项的数据有时候来源于服务端下发，这时我们需要异步修改 Schema。\n\n- 单个加载：通过 `form.setSchemaByPath` 方法进行加载\n\n```jsx\nimport React, { useEffect } from 'react';\nimport FormRender, { useForm } from 'form-render';\nconst schema = {\n  type: 'object',\n  properties: {\n    select: {\n      title: '下拉框',\n      type: 'string',\n      widget: 'select',\n    }\n  }\n};\nexport default () => {\n  const form = useForm();\n\n  const onMount = () => {\n    // 根据服务端下发内容，重置下拉选项\n    form.setSchemaByPath('select', {\n      props: {\n        options: [\n          {label: '东', value: 'east'},\n          {label: '西', value: 'west'},\n          {label: '南', value: 'south'},\n          {label: '北', value: 'north'}\n        ]\n      }\n    });\n  };\n  \n  return (\n    <FormRender form={form} schema={schema} onMount={onMount} maxWidth={360}\n    />\n  );\n}\n```\n\n- 多个加载：通过 `form.setSchema` 方法进行加载\n\n```jsx\nimport React, { useEffect } from 'react';\nimport FormRender, { useForm } from 'form-render';\n\nconst schema = {\n  type: 'object',\n  properties: {\n    select1: {\n      title: '下拉框一',\n      type: 'string',\n      widget: 'select',\n    },\n    select2: {\n      title: '下拉框二',\n      type: 'string',\n      widget: 'select',\n    }\n  }\n};\n\nexport default () => {\n  const form = useForm();\n\n  const onMount = () => {\n    form.setSchema({\n      select1: {\n        props: {\n          options: [\n            {label: '东', value: 'east'},\n            {label: '西', value: 'west'},\n            {label: '南', value: 'south'},\n            {label: '北', value: 'north'}\n          ]\n        }\n      },\n      select2: {\n        props: {\n          options: [\n            { label: '早', value: 'a' },\n            { label: '中', value: 'b' },\n            { label: '晚', value: 'c' }\n          ]\n        }\n      }\n    });\n  };\n\n  return (\n    <FormRender\n      form={form}\n      schema={schema}\n      onMount={onMount}\n      maxWidth={360}\n    />\n  );\n}\n```\n\n## 表单协议重置\n通过 `form.setSchema(schema, true)` 方法进行加载\n\n```jsx\nimport React, { useState, useEffect } from 'react';\nimport { Button } from 'antd';\nimport FormRender, { useForm } from 'form-render';\nimport { fakeApi, delay } from './utils';\nimport schema from './schema/simple';\nimport basic from './schema/basic';\n\nconst Demo = () => {\n  const form = useForm();\n\n  const getRemoteSchema = () => {\n    fakeApi('xxx/getForm').then(_ => {\n      form.setSchema(basic, true);\n    });\n  };\n\n  return (\n    <div>\n      <FormRender form={form} schema={schema} />\n      <Button type='primary' onClick={getRemoteSchema}>重置表单 Schema </Button>\n    </div>\n  );\n};\n\nexport default Demo;\n```\n\n## 表单服务端校验\n通过 `beforeFinish` 从外部回填 error 信息到表单，注意 `beforeFinish` 需返回要回填的 error\n\n```jsx\nimport React from 'react';\nimport { Button, message } from 'antd';\nimport FormRender, { useForm } from 'form-render';\nimport schema from './schema/simple';\nimport { fakeApi } from './utils';\n\nexport default () => {\n  const form = useForm();\n\n  const onFinish = (data) => {\n    message.info(JSON.stringify(data));\n  };\n\n  // 服务端校验在这里做\n  const beforeFinish = ({ data, schema }) => {\n    return fakeApi('xxx/validation').then(_ => {\n      if (data.select1) {\n        return [{ name: 'select', errors: [] }];\n      }\n      return [{ name: 'select', errors: ['外部校验错误, 请进行选择'] }];\n    });\n  };\n\n  return (\n    <div>\n      <FormRender\n        form={form}\n        schema={schema}\n        beforeFinish={beforeFinish}\n        onFinish={onFinish}\n        maxWidth={400} \n      />\n      <Button type='primary' onClick={form.submit}>\n        提交\n      </Button>\n    </div>\n  );\n}\n```\n\n## 表单数据字段转换\n服务端数据与展示经常会不符，通过配置 `bind` 字段进行转换（List嵌套下暂时不支持）\n\n- 例如：日期范围组件接收的是一个数组，而服务端的数据是 startDate，endDate 两个字段。\n\n```jsx\nimport React from 'react';\nimport { Button, message, Space } from 'antd';\nimport FormRender, { useForm } from 'form-render';\nimport { fakeApi } from './utils';\n\nconst schema = {\n  type: 'object',\n  properties: {\n    dateRange: {\n      bind: ['startDate', 'endDate'],\n      title: '日期范围',\n      type: 'range',\n      format: 'date',\n    },\n  },\n};\n\nexport default () => {\n  const form = useForm();\n\n  const onFinish = (data) => {\n    message.info(JSON.stringify(data));\n  };\n\n  const getRemoteData = () => {\n    fakeApi('xxx/getForm').then(_ => {\n      form.setValues({ startDate: '2023-01-01', endDate: '2023-12-31' });\n    });\n  };\n\n  return (\n    <div>\n      <FormRender form={form} schema={schema} onFinish={onFinish} maxWidth={400} />\n      <Space>\n        <Button onClick={getRemoteData}>加载服务端数据</Button>\n        <Button type='primary' onClick={form.submit}>\n          提交\n        </Button>\n      </Space>\n    </div>\n  );\n}\n```\n"
  },
  {
    "path": "docs/form-render/advanced-bind.md",
    "content": "---\norder: 3\ntoc: content\nmobile: false\ntitle: 数据转换\ngroup: \n  title: 高级用法\n  order: 1\n---\n\n# bind\n\n类型：string | string[] | false\n\n有时候我们会遇到这样的问题：提交的数据，数据结构不符合服务端的要求，需要进行转换。这时候 bind 这个魔法字段就派上用场了。\n\n\n注意点：`魔法虽好，切记滥用`\n- `setValues`、`getValues`、`onFinish` 这三个 API，也就是`输入`、`输出` 数据的格式是转换之后的，其他情况下数据格式都是表单原始数据格式\n- 最好不要跨层级转换，转换前后数据最好还是保持在同一层级，\n\n`点击提交按钮，浏览器控制台会打印出提交的数据`\n## 简单\n`{ date: ['2023-04-01', '2023-04-23'] }` => `{ startDate: '2023-04-01', endDate: '2023-04-23' }`\n```jsx\nimport React from 'react';\nimport { Button, Alert} from 'antd';\nimport FormRender, { useForm } from 'form-render';\n\nconst delay = ms => new Promise(res => setTimeout(res, ms));\n\nconst schema = {\n  type: 'object',\n  properties: {\n    date: {\n      bind: ['startDate', 'endDate'],\n      title: '日期',\n      type: 'range',\n      format: 'date',\n      description: 'bind 转换',\n    },\n    date1: {\n      title: '日期',\n      type: 'range',\n      format: 'date',\n      description: '未进行转换',\n    }\n  }\n};\n\nconst Demo = () => {\n  const form = useForm();\n\n  const onFinish = (formData) => {\n    console.log(formData, 'formData');\n  };\n\n  return (\n    <FormRender\n      form={form}\n      schema={schema}\n      onFinish={onFinish}\n      footer={true}\n      maxWidth={400}\n    />\n  );\n};\n\nexport default Demo;\n\n```\n\n## 进阶\n- 对象嵌套情况， bind 要写绝对路径\n- List 组件嵌套情况，bind 路径从 List 嵌套的子节点开始写\n```jsx\nimport React from 'react';\nimport { Button } from 'antd';\nimport FormRender, { useForm } from 'form-render';\n\nconst delay = ms => new Promise(res => setTimeout(res, ms));\n\nconst schema = {\n  type: 'object',\n  properties: {\n    obj: {\n      title: '对象',\n      type: 'object',\n      description: '对象嵌套 bind 要写决定路径',\n      properties: {\n        range1: {\n          bind: ['obj.startDate', 'obj.endDate'],\n          title: '日期',\n          type: 'range',\n          format: 'date'\n        }\n      }\n    },\n    list: {\n      type: 'array',\n      widget: 'cardList',\n      items: {\n        type: 'object',\n        title: 'List-Item',\n        column: 3,\n        properties: {\n          obj: {\n            title: '对象',\n            type: 'object',\n            description: 'List 组件嵌套下，bind 路径从 List 嵌套的节点开始写',\n            properties: {\n              range1: {\n                bind: ['obj.startDate', 'obj.endDate'],\n                title: '日期',\n                type: 'range',\n                format: 'date'\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n};\n\nconst Demo = () => {\n  const form = useForm();\n\n  const onFinish = (formData) => {\n    console.log(formData, 'formData');\n  };\n\n  return (\n    <FormRender\n      form={form}\n      schema={schema}\n      onFinish={onFinish}\n      footer={true}\n      maxWidth={400}\n    />\n  );\n};\n\nexport default Demo;\n\n```\n\n## bind:false\n某些字段数据只做纯展示，使用 bind: false 可避免字段在提交时出现。\n\n```jsx\nimport React from 'react';\nimport { Button } from 'antd';\nimport FormRender, { useForm } from 'form-render';\n\nconst delay = ms => new Promise(res => setTimeout(res, ms));\n\nconst schema = {\n  type: 'object',\n  properties: {\n    input: {\n      title: '输入框',\n      type: 'string',\n      props: {},\n    },\n    input1: {\n      title: '输入框',\n      type: 'string',\n      description: '纯展示',\n      readOnly: true,\n      bind: false\n    },\n  }\n};\n\nconst Demo = () => {\n  const form = useForm();\n\n  const onMount = () => {\n    form.setValues({ input: '1', input1: '2' });\n  };\n\n  const onFinish = (formData) => {\n    console.log(formData, 'formData');\n  };\n\n  return (\n    <FormRender\n      form={form}\n      schema={schema}\n      onFinish={onFinish}\n      footer={true}\n      maxWidth={400}\n      onMount={onMount}\n    />\n  );\n};\n\nexport default Demo;\n\n```\n\n## bind:root\n\n`{ list: [{ size: 1 }, { size: 2}] }` => `{ list: [1, 2] }`;\n\n解决 List 嵌套，数组元素的数据格式只能是对象的问题\n\n\n```jsx\nimport React from 'react';\nimport { Button } from 'antd';\nimport FormRender, { useForm } from 'form-render';\n\nconst delay = ms => new Promise(res => setTimeout(res, ms));\n\nconst schema = {\n  type: 'object',\n  properties: {\n    list: {\n      type: 'array',\n      widget: 'simpleList',\n      items: {\n        type: 'object',\n        column: 3,\n        properties: {\n          input: {\n            bind: 'root',\n            title: '大小',\n            type: 'number',\n          }\n        }\n      }\n    }\n  }\n};\n\nconst Demo = () => {\n  const form = useForm();\n\n  const onFinish = (formData) => {\n    console.log(formData, 'formData');\n  };\n\n  return (\n    <FormRender\n      form={form}\n      schema={schema}\n      onFinish={onFinish}\n      footer={true}\n      maxWidth={400}\n    />\n  );\n};\n\nexport default Demo;\n\n```\n\n\n\n\n\n\n\n\n\n"
  },
  {
    "path": "docs/form-render/advanced-layout.md",
    "content": "---\norder: 0\ntoc: content\nmobile: false\ngroup: \n  title: 高级用法\n  order: 1\n---\n\n# 表单布局\n- `displayType` 标签排列方式\n- `column` 表单一行能展示的表单项个数\n- `labelWidth` 标签固定宽度\n- `cellSpan` 表单项跨列\n- `span` 自定义宽度\n- `maxWidth` 输入控件最长宽度  \n- `labelCol`、`FieldCol` 表单项内部布局\n- `footer` 内置按钮\n## displayType\n\n- displayType（标签排列方式）：`row`（水平）| `column`（垂直）| `inline`(紧凑)\n- 默认值：`column`\n\n```jsx\nimport React, { useState } from 'react';\nimport { Button, Space, Form, Radio } from 'antd';\nimport FormRender, { useForm } from 'form-render';\nimport schema from './schema/basic';\n\nexport default () => {\n  const form = useForm();\n  const [displayType, setDisplay] = useState('row');\n\n  const handRadioChange = (ev) => {\n    const value = ev.target.value;\n    schema.displayType = value;\n    form.setSchema(schema, true);\n    setDisplay(value);\n  };\n\n  return (\n    <div>\n      <Form.Item label='displayType' style={{ marginBottom: '50px' }}>\n        <Radio.Group value={displayType} onChange={handRadioChange}>\n          <Radio.Button value='row'>row</Radio.Button>\n          <Radio.Button value='column'>column</Radio.Button>\n          <Radio.Button value='inline'>Inline</Radio.Button>\n        </Radio.Group>\n      </Form.Item>\n      <FormRender\n        schema={schema}\n        form={form}\n        labelCol={6}\n        fieldCol={17}\n      />\n    </div>\n  );\n}\n```\n\n## column\n一行可以展示多少个表单项，默认值: 1\n\n```jsx\nimport React, { useState } from 'react';\nimport { Button, Space, Form, Radio } from 'antd';\nimport FormRender, { useForm } from 'form-render';\nimport schema from './schema/basic';\n\nexport default () => {\n  const form = useForm();\n  const [column, setColumn] = useState(3);\n\n  const handRadioChange = (ev) => {\n    const value = ev.target.value;\n    schema.column = value;\n\n    form.setSchema(schema, true);\n    setColumn(value);\n  };\n\n  return (\n    <>\n      <Form.Item label='column' style={{ marginBottom: '50px' }}>\n        <Radio.Group value={column} onChange={handRadioChange}>\n          <Radio.Button value={1}>一列</Radio.Button>\n          <Radio.Button value={2}>两列</Radio.Button>\n          <Radio.Button value={3}>三列</Radio.Button>\n          <Radio.Button value={4}>四列</Radio.Button>\n        </Radio.Group>\n      </Form.Item>\n      <FormRender\n        form={form}\n        schema={schema} \n        labelCol={6}\n        fieldCol={17}\n      />\n    </>\n  );\n}\n```\n\n## lableWidth\n设置标签固定宽度\n\n```jsx\nimport React, { useState } from 'react';\nimport { InputNumber } from 'antd';\nimport FormRender, { useForm } from 'form-render';\nimport schema from './schema/basic';\n\nexport default () => {\n  const form = useForm();\n  const [labelWidth, setLabelWidth] = useState(60);\n\n  return (\n    <>\n      <div style={{ marginBottom: '50px' }}>\n        labelWidth：\n        <InputNumber onChange={setLabelWidth} value={labelWidth} />\n      </div>\n      <FormRender\n        form={form}\n        schema={schema}\n        labelWidth={labelWidth}\n        column={3}\n      />\n    </>\n  );\n};\n```\n\n## cellSpan\n设置表单项跨列展示，目前需配合 `lableWidth` 来使用，否则无法与其他表单项在样式上对齐，通过配置单个表单项的 `labelCol`、`fieldCol` 勉强能改善\n\n```jsx\nimport React, { useState } from 'react';\nimport { InputNumber } from 'antd';\nimport FormRender, { useForm } from 'form-render';\nimport schema from './schema/cellSpan';\n\nexport default () => {\n  const form = useForm();\n  const [labelWidth, setLabelWidth] = useState(60);\n\n  return (\n    <FormRender\n      form={form}\n      schema={schema}\n      labelWidth={60}\n      column={3}\n    />\n  );\n};\n```\n\n## span\n设置表单项列宽度，表单布局会被切割成 24 等份，可以通过设置 span 来自定义表单项所占的宽度\n\n```jsx\nimport React, { useState } from 'react';\nimport { InputNumber } from 'antd';\nimport FormRender, { useForm } from 'form-render';\nimport schema from './schema/span';\n\nexport default () => {\n  const form = useForm();\n  const [labelWidth, setLabelWidth] = useState(60);\n\n  return (\n    <FormRender\n      form={form}\n      schema={schema}\n      labelWidth={80}\n      maxWidth={300}\n    />\n  );\n};\n```\n\n## maxWidth\n表单项控件的最大宽度\n\n```jsx\nimport React, { useState } from 'react';\nimport { InputNumber } from 'antd';\nimport FormRender, { useForm } from 'form-render';\nimport schema from './schema/basic';\n\nexport default () => {\n  const form = useForm();\n  const [maxWidth, setMaxWidth] = useState(320);\n  \n  return (\n    <>\n      <div style={{ marginBottom: '50px' }}>\n        maxWidth：\n        <InputNumber onChange={setMaxWidth} value={maxWidth} />\n      </div>\n      <FormRender\n        form={form}\n        schema={{\n          ...schema,\n          column: 1\n        }}\n        maxWidth={maxWidth}\n        labelWidth={60}\n      />\n    </>\n  );\n};\n```\n\n## labelCol & fieldCol\n- `labelCol`（标签占位格数），`fieldCol`（控件占位格数）\n\n- 默认配置：\n```js\n// 当表单一行 一列 展示时\nlabelCol: 5\nfieldCol: 9\n\n// 当表单一行 两列 展示时\nlabelCol: 6\nfieldCol: 14\n\n// 当表单一行 两列以上 展示时\nlabelCol: 7\nfieldCol: 16\n```\n实际业务中标签可能会比较长，默认配置无法满足布局，可以通过配置 `labelCol`、`fieldCol` 进行调整，两者加起来不超过 `24` 格数即可。\n\n`labelCol`、`fieldCol` 也可以是复杂对象，具体配置规则参照 Antd Col 组件\n\n\n```jsx\nimport React, { useState } from 'react';\nimport { InputNumber, Space } from 'antd';\nimport FormRender, { useForm } from 'form-render';\nimport schema from './schema/basic';\n\nexport default () => {\n  const form = useForm();\n  const [labelCol, setLabelCol] = useState(6);\n  const [fieldCol, setFieldcol] = useState(17);\n\n  return (\n    <>\n      <Space style={{ marginBottom: '50px' }}>\n        <span>\n          labelCol：\n          <InputNumber onChange={setLabelCol} value={labelCol} />\n        </span>\n        <span>\n          fieldCol：\n          <InputNumber onChange={setFieldcol} value={fieldCol} />\n        </span>\n      </Space>\n      <FormRender\n        form={form}\n        schema={schema}\n        labelCol={labelCol}\n        fieldCol={fieldCol}\n      />\n    </>\n  )\n};\n```\n\n## footer\n- `footer`：true，显示默认配置\n```jsx\nimport React, { useState } from 'react';\nimport { Button, Space, Form, Radio } from 'antd';\nimport FormRender, { useForm } from 'form-render';\nimport schema from './schema/simple';\n\nexport default () => {\n  const form = useForm();\n\n  return (\n    <FormRender\n      schema={schema}\n      form={form}\n      maxWidth={360}\n      footer={true}\n    />\n  );\n}\n```\n\n- 按钮属性配置\n```jsx\nimport React, { useState } from 'react';\nimport { Button, Space, Form, Radio } from 'antd';\nimport FormRender, { useForm } from 'form-render';\nimport schema from './schema/simple';\n\nexport default () => {\n  const form = useForm();\n\n  return (\n    <FormRender\n      schema={schema}\n      form={form}\n      maxWidth={360}\n      footer={{\n        submit: {\n          text: '确定',\n          // loading: true\n          // hide: true\n          // ...btnProps\n        },\n        reset: {\n          text: '清空',\n          // hide: true\n          // ...btnProps\n        }\n      }}\n    />\n  );\n}\n```\n\n- `footer` 自定义\n```jsx\nimport React, { useState } from 'react';\nimport { Button, Space, Form, Radio } from 'antd';\nimport FormRender, { useForm } from 'form-render';\nimport schema from './schema/simple';\n\nexport default () => {\n  const form = useForm();\n\n  return (\n    <FormRender\n      schema={schema}\n      form={form}\n      maxWidth={360}\n      footer={() => (\n        <Button>自定义</Button>\n      )}\n    />\n  );\n}\n```\n\n- `footer` dom 元素透传\n```jsx\nimport React, { useState } from 'react';\nimport { Button, Space, Form, Radio } from 'antd';\nimport FormRender, { useForm } from 'form-render';\nimport schema from './schema/simple';\n\nexport default () => {\n  const form = useForm();\n\n  return (\n    <FormRender\n      schema={schema}\n      form={form}\n      maxWidth={360}\n      footer={(dom) => (\n        <Space>{dom}</Space>\n      )}\n    />\n  );\n}\n```\n"
  },
  {
    "path": "docs/form-render/advanced-linkage.md",
    "content": "---\norder: 1\ntoc: content\nmobile: false\ngroup: \n  title: 高级用法\n  order: 1\n---\n\n# 表单联动\n\n表单联动是开发中常见的交互，FormRender 提供以下几种方式来满足不同的交互场景\n\n- `{{ }}` 函数表达式，实现简单联动\n- `watch` watch 监听，实现复杂联动\n- `dependencies` 依赖字段设置，当依赖项数据发生变化时，触发更新\n\n## {{ }} 函数表达式\n\n函数表达式为字符串格式，并以双花括号`\"{{...}}\"`为语法特征，用一种简洁的配置方式来支持联动。例如：控制表单项禁用、隐藏等交互。\n\n```json\n{\n  \"disabled\": \"{{ formData.switch1 === true }}\",\n  \"hidden\": \"{{ rootValue.input1 }}\"\n}\n```\n\n- formData: 整个表单的值\n- rootValue: 用于 List 场景使用，表示 List.Item 的值\n\n#### 示例\n```jsx\nimport React from 'react';\nimport FormRender, { useForm } from 'form-render';\n\nconst schema = {\n  type: 'object',\n  properties: {\n    switch1: {\n      title: '禁用输入框',\n      type: 'boolean',\n      widget: 'switch'\n    },\n    input1: {\n      title: '输入框',\n      type: 'string',\n      disabled: '{{ formData.switch1 === true }}'\n    },\n    list: {\n      title: 'List 场景',\n      type: 'array',\n      widget: 'CardList',\n      defaultValue: [{}],\n      items: {\n        type: 'object',\n        widget: 'card',\n        title: 'List.Item',\n        properties: {\n          switch1: {\n            title: '隐藏输入框 2 ',\n            type: 'boolean',\n            widget: 'switch'\n          },\n          input1: {\n            title: '输入框 1',\n            type: 'string',\n            description: '给输入框 赋值'\n          },\n          input2: {\n            title: '输入框 2',\n            type: 'string',\n            defaultValue: '{{ rootValue.input1 }}',\n            hidden: '{{ rootValue.switch1 }}'\n          }\n        }\n      }\n    }\n  }\n};\n\nexport default () => {\n  const form = useForm();\n  \n  return (\n     <FormRender \n      schema={schema} \n      form={form}\n      maxWidth={400}\n    />\n  );\n}\n```\n\n## watch 监听\n`watch` 其实就是 `onValuesChange`（不提供对外使用）的增强版，用于监听表单数据改变，可以做到单字段细粒度的监听。\n\n语法特征：`[path]: () => {}`，path 按照表单的数据结构路径书写就可以了，List 组件的比较特殊，例如对应的表单字段是 cityList 需要写成 `cityList[]`。\n\n```js\nconst watch = {\n  '#': (allValues, changedValues) => { // '#': () => {} 等同于 onValuesChange\n    console.log('表单 allValues：', allValues);\n    console.log('表单 changedValues：', changedValues);\n  },\n  'input1': value => {\n    console.log('input1：', value);\n  },\n  'obj.input2': (value) => {\n    console.log('input2：', value);\n  },\n  'list[].input4': (value, indexList) => {\n    console.log('list[].input4：', value, '，indexList：', indexList);\n  },\n  \n};\n```\n#### 示例\n```jsx\nimport React from 'react';\nimport FormRender, { useForm } from 'form-render';\n\nconst schema = {\n  type: 'object',\n  maxWidth: 400,\n  properties: {\n    input1: {\n      title: '输入框 1',\n      type: 'string',\n    },\n    obj: {\n      type: 'object',\n      widget: 'card',\n      title: '一级嵌套',\n      properties: {\n        input2: {\n          title: '输入框 2',\n          type: 'string'\n        }\n      }\n    },\n    list: {\n      title: 'List 嵌套',\n      type: 'array',\n      widget: 'cardList',\n      items: {\n        type: 'object',\n        title: 'List.Item',\n        properties: {\n          input4: {\n            title: '输入框 4',\n            type: 'string',\n          },\n          obj: {\n            type: 'object',\n            widget: 'card',\n            title: '一级嵌套',\n            properties: {\n              input5: {\n                title: '输入框 5',\n                type: 'string',\n              },\n              list: {\n                type: 'array',\n                widget: 'cardList',\n                items: {\n                  type: 'object',\n                  title: '二级 List.Item',\n                  properties: {\n                    input6: {\n                      title: '输入框 6',\n                      type: 'string'\n                    }\n                  }\n                }\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n};\n\nexport default () => {\n  const form = useForm();\n  const watch = {\n    '#': (allValues, changedValues) => { // '#': () => {} 等同于 onValuesChange\n      console.log('表单 allValues：', allValues);\n      console.log('表单 changedValues：', changedValues);\n\n    },\n    'input1': (value) => {\n      console.log('input1：', value);\n    },\n    'obj.input2': (value) => {\n      console.log('input2：', value);\n    },\n    'obj.obj.input3': (value) => {\n      console.log('input3：', value);\n    },\n    'list': (value) => {\n     console.log('list：', value);\n    },\n    'list[].input4': (value, indexList) => {\n      console.log('list[].input4：', value, '，indexList：', indexList);\n    },\n    'list[].obj.input5': (value, indexList) => {\n      console.log('list[].obj.input5：', value, '，indexList：', indexList);\n    },\n    'list[].obj.list': (value, indexList) => {\n      console.log('list[].obj.list：', value, '，indexList：', indexList);\n    },\n    'list[].obj.list[]': (value, indexList) => {\n      console.log('list[].obj.list：', value, '，indexList：', indexList);\n    },\n    'list[].obj.list[].input6': (value, indexList) => {\n      console.log('list[].obj.list[].input6：', value, '，indexList：', indexList);\n    }\n  }\n\n  return <FormRender schema={schema} form={form} watch={watch}/>;\n};\n\n```\n\n\n\n\n#### 修改表单项的值 (value) \n\nform.setValueByPath：指定路径对值进行修改。[path 路径详见](/form-render/advanced-path)。\n\n```jsx\nimport React from 'react';\nimport FormRender, { useForm } from 'form-render';\n\nconst schema = {\n  type: 'object',\n  displayType: 'row',\n  properties: {\n    input1: {\n      title: '输入框 A',\n      type: 'string',\n      placeholder: '给输入框 B 赋值',\n    },\n    input2: {\n      title: '输入框 B',\n      type: 'string',\n      disabled: true\n    },\n  },\n};\n\nexport default () => {\n  const form = useForm();\n\n  const watch = {\n    input1: val => {\n      form.setValueByPath('input2', val);\n    }\n  };\n\n  return (\n    <FormRender \n      form={form} \n      schema={schema} \n      watch={watch} \n      labelWidth={200}\n      maxWidth={400}\n    />\n  );\n};\n```\n\n#### 修改表单项的协议（schema）\n\nform.setSchemaByPath：指定路径对 schema 进行修改 (不允许通过此 API 修改 default)。[path 路径详见](/form-render/advanced-path)。\n\n```jsx\nimport React, { useEffect } from 'react';\nimport FormRender, { useForm } from 'form-render';\n\nconst schema = {\n  type: 'object',\n  displayType: 'row',\n  properties: {\n    input1: {\n      title: '输入框',\n      type: 'number',\n      placeholder: '当值大于1时，会改变下拉框的选项',\n    },\n    select: {\n      title: '下拉框',\n      type: 'string',\n      enum: ['a', 'b', 'c'],\n      enumNames: ['早', '中', '晚'],\n      widget: 'radio'\n    }\n  }\n};\n\nexport default () => {\n  const form = useForm();\n\n  const watch = {\n    input1: val => {\n      if (val > 1) {\n        form.setSchemaByPath('select', ({ enumNames }) => {\n          return {\n            enumNames: enumNames.map(item => item + 'a'),\n          };\n        });\n        return ;\n      }\n\n      form.setSchemaByPath('select', { enumNames: ['早', '中', '晚'] });\n    }\n  };\n\n  return (\n    <FormRender\n      form={form}\n      schema={schema}\n      watch={watch}\n      labelWidth={200}\n      maxWidth={400}\n    />\n  );\n};\n```\n\n## dependencies 依赖字段\n当依赖项的值发生改变时，组件自身会触发`更新`和`校验`，可以通过组件 props.addons.dependValues 拿到依赖项的值\n\n\n- dependencies：[`string`] 设置依赖字段，支持设置多个\n- 用 `{{ }}` 函数表达式同样能使组件触发更新，但是无法实现触发校验的效果，所以 dependencies 的核心场景是 `被动触发校验`\n- 同 `{{ }}` 函数表达式另一不同的地方就是值传递给组件的方式不同，设置 dependencies，依赖项的值会统一收集到 props.addons.dependValues 里面。\n\n\n#### 示例 1：触发更新\n依赖值发生变化，自定义组件触发更新\n\n```json\n{ \n  \"dependencies\": [\"input1\"], // 方式一\n\n  \"props\": { // 方式二\n    \"rows\": \"{{ formData.input1 }}\"\n  }\n}\n```\n```jsx\nimport React from 'react';\nimport { Input, } from 'antd';\nimport FormRender, { useForm } from 'form-render';\n\nconst { TextArea } = Input;\n\nconst CustomTextArea = props => {\n  console.log(props.addons.dependValues, 'dependValues');\n\n  return <TextArea rows={props.addons.dependValues?.[0] || 2} {...props} />;\n};\n\nexport default () => {\n  const form = useForm();\n\n  const schema = {\n    type: 'object',\n    displayType: 'row',\n    displayType: 'row',\n    properties: {\n      input1: {\n        title: '输入框高度',\n        type: 'number'\n      },\n      select1: {\n        title: '输入框',\n        type: 'string',\n        widget: 'CustomTextArea',\n        dependencies: ['input1'],\n        props: {\n          rows: '{{ formData.input1 }}'\n        }\n      }\n    }\n  };\n\n  return (\n    <FormRender\n      form={form}\n      schema={schema}\n      widgets={{ CustomTextArea }}\n      labelWidth={200}\n      maxWidth={400}\n    />\n  );\n}\n```\n\n#### 示例 2：触发校验\n当`密码输入框`的值和`确认密码输入框`的值一致时，再次修改密码会重新触发 `确认密码输入框` 的校验\n```jsx\nimport React from 'react';\nimport { Input, } from 'antd';\nimport FormRender, { useForm } from 'form-render';\n\nconst { TextArea } = Input;\n\nconst CustomTextArea = props => {\n  const { dependValues } = props;\n\n  console.log(dependValues, 'dependValues');\n\n  return <TextArea rows={dependValues?.[0] || 2} />;\n};\n\nexport default () => {\n  const form = useForm();\n\n  const schema = {\n  type: 'object',\n  displayType: 'row',\n  properties: {\n    input1: {\n      title: '密码',\n      type: 'string',\n      required: true,\n    },\n    input2: {\n      title: '确认密码',\n      type: 'string',\n      dependencies: ['input1'],\n      required: true,\n      rules: [\n        { \n          validator: (_, value, { form }) => {\n            if (!value || form.getValueByPath('input1') === value) {\n              return true;\n            }\n            \n            return false;\n          }, \n          message: '你输入的两个密码不匹配' \n        }\n      ]\n    }\n  }\n};\n\n  return (\n    <FormRender\n      form={form}\n      schema={schema}\n      widgets={{ CustomTextArea }}\n      labelWidth={200}\n      maxWidth={400}\n    />\n  );\n}\n```\n\n#### Form List 中的依赖\n\n- `[index]` 表示特定位置的 List Item，比如 `list[1].foo`。\n- 只传 `[]` 则表示相同位置的其他 List Item，比如 `list[].foo`。\n\n\n<code src=\"./demo/linkage/list.tsx\"></code>\n\n\n"
  },
  {
    "path": "docs/form-render/advanced-path.md",
    "content": "---\norder: 3\ntoc: content\nmobile: false\ngroup: \n  title: 高级用法\n  order: 1\n---\n\n# path 书写\n\n调用 setSchemaByPath 时，需要根据 path 改动表单元素的 schema。如果元素结构很深，如何正确书写 path 呢？\n\n## 基础型：path\n设置选项：form.setSchemaByPath('radio', { enum: [1, 2, 3] });\n```jsx\nimport { Button } from 'antd';\nimport FormRender, { useForm } from 'form-render';\nimport React from 'react';\n\nconst Demo = () => {\n  const form = useForm();\n\n  const onMount = () => {\n    setTimeout(() => {\n      form.setSchemaByPath('radio', { enum: [1, 2, 3] });\n    }, 1000);\n  };\n\n  const schema = {\n    type: 'object',\n    displayType: 'row',\n    properties: {\n      radio : {\n        title: '选择框',\n        type: 'string',\n        widget: 'radio',\n        enum: []\n      }\n    }\n  };\n\n  return (\n    <FormRender\n      form={form}\n      schema={schema}\n      onMount={onMount}\n      labelWidth={100}\n      maxWidth={400}\n    />\n  );\n};\n\nexport default Demo;\n\n```\n\n## 嵌套型：path\n设置选项：form.setSchemaByPath('x.radio', { enum: [1, 2, 3] });\n```jsx\nimport { Button } from 'antd';\nimport FormRender, { useForm } from 'form-render';\nimport React from 'react';\n\nconst Demo = () => {\n  const form = useForm();\n\n  const schema = {\n    type: 'object',\n    displayType: 'row',\n    properties: {\n      x: {\n        type: 'object',\n        title: 'xxx',\n        properties: {\n          radio: {\n            title: '选择框',\n            type: 'string',\n            widget: 'radio',\n            enum: []\n          }\n        }\n      }\n    }\n  };\n\n  const onMount = () => {\n    setTimeout(() => {\n      form.setSchemaByPath('x.radio', { enum: [1, 2, 3] });\n    }, 1000);\n  };\n\n  return (\n    <FormRender\n      form={form}\n      schema={schema}\n      onMount={onMount}\n      labelWidth={100}\n      maxWidth={400}\n    />\n  );\n};\n\nexport default Demo;\n\n```\n\n\n\n## 列表型：path\n设置选项：form.setSchemaByPath('x[].radio', { enum: [1, 2, 3] });\n```jsx\nimport { Button } from 'antd';\nimport FormRender, { useForm } from 'form-render';\nimport React from 'react';\n\nconst Demo = () => {\n  const form = useForm();\n\n  const schema = {\n    type: 'object',\n    displayType: 'row',\n    properties: {\n      x: {\n        title: '对象数组',\n        type: 'array',\n        default: [{}],\n        items: {\n          type: 'object',\n          properties: {\n            radio : {\n              title: '选择框',\n              type: 'string',\n              widget: 'radio',\n              enum: []\n            }\n          }\n        }\n      }\n    }\n  };\n\n  const onMount = () => {\n    setTimeout(() => {\n      form.setSchemaByPath('x[].radio', { enum: [1, 2, 3] });\n    }, 1000);\n  };\n\n  return (\n    <FormRender\n      form={form}\n      schema={schema}\n      onMount={onMount}\n      labelWidth={100}\n      maxWidth={400}\n    />\n  );\n};\n\nexport default Demo;\n\n```\n\n## 复杂嵌套：path\n设置选项：form.setSchemaByPath('x.y[].radio', { enum: [1, 2, 3] });\n```jsx\nimport { Button } from 'antd';\nimport FormRender, { useForm } from 'form-render';\nimport React from 'react';\n\nconst Demo = () => {\n  const form = useForm();\n\n  const schema = {\n    type: 'object',\n    displayType: 'row',\n    properties: {\n      select1: {\n        title: '输入框 A',\n        type: 'string',\n      },\n      select2: {\n        title: '输入框 B',\n        type: 'string',\n      },\n      x: {\n        type: 'object',\n        title: '复杂型',\n        widget: 'lineTitle',\n        properties: {\n          y: {\n            title: '对象数组',\n            type: 'array',\n            display: 'inline',\n            default: [{}],\n            items: {\n              type: 'object',\n              title: '基础信息',\n              properties: {\n                radio : {\n                  title: '选择框',\n                  type: 'string',\n                  widget: 'radio',\n                  enum: []\n                }\n              }\n            }\n          }\n        }\n      }\n    }\n  };\n\n  const onMount = () => {\n    setTimeout(() => {\n      form.setSchemaByPath('x.y[].radio', { enum: [1, 2, 3] });\n    }, 1000);\n  };\n\n  return (\n    <FormRender\n      form={form}\n      schema={schema}\n      onMount={onMount}\n      labelWidth={100}\n      maxWidth={400}\n    />\n  );\n};\n\nexport default Demo;\n\n```\n\n"
  },
  {
    "path": "docs/form-render/advanced-slimrender.md",
    "content": "---\norder: 4\ntoc: content\nmobile: false\ngroup: \n  title: 高级用法\n  order: 1\n---\n\n# 组件按需\nFormRender 内置了很多控件，包括基础控件、嵌套控件、以及列表控件。有些组件可能在项目中从来都不会用到，希望组件按需引入。\n\n这种情况下可以使用 `FormSlimRender` 按需加载组件，将需要的组件传入内部。\n\n\n## 使用方式\n\n<code src=\"./demo/form-slim/basic.tsx\"></code>\n\n## 列表组件\n\n对于列表组件的按需使用，除了引入列表本身，还需引入列表嵌套组件，否则不能正常渲染。\n\n<code src=\"./demo/form-slim/form-list.tsx\"></code>\n\n默认的嵌套组件为 `Collapse`，如需使用其他嵌套组件，需要在 schema 中指定 widget 属性。\n```js\nconst schema = {\n  type: 'object',\n  displayType: 'row',\n  properties: {\n    list: {\n      title: '列表按需',\n      type: 'array',\n      widget: 'simpleList',\n      items: {\n        type: 'object',\n        widget: 'Card', // 自定义嵌套组件\n        properties: {\n          input1: {\n            title: '输入框',\n            type: 'string',\n          },\n        },\n      },\n    },\n  },\n};\n```\n\n## 内置的组件\n```js\n\n// 基础控件\nInput,\nNumber,\nTextArea,\nSelect,\nMultiSelect,\nSwitch,\nRadio,\nCheckBox,\nCheckboxes,\nDate,\nDateRange,\nTime,\nTimeRange,\n\nColor,\nRate,\nTreeSelect,\nImageInput,\nUrlInput,\nSlider,\nUpload,\nHtml,\nPercentSlider,\n\n// 嵌套控件\nCard,\nCollapse,\nSubInline,\nLineTitle,\n\n// 列表控件\nSimpleList,\nCardList,\nTableList,\nDrawerList,\nVirtualList,\nTabList,\n```\n"
  },
  {
    "path": "docs/form-render/advanced-validate.md",
    "content": "---\norder: 2\ntoc: content\nmobile: false\ngroup: \n  title: 高级用法\n  order: 1\n---\n\n# 表单校验\n- 通过内置校验字段配置，实现简单校验逻辑\n- 通过 rules 配置，实现复杂校验逻辑\n- 通过 validateMessages 实现校验提示模版定制\n### 一、内置校验\n\n- required：必填\n- max：最大长度 | 最大值\n- min：最小长度 | 最小值\n- format：url ｜ email ｜ image | color\n\n```jsx\nimport React from 'react';\nimport FormRender, { useForm } from 'form-render';\n\nconst schema = {\n  type: 'object',\n  displayType: 'row',\n  properties: {\n    input1: {\n      title: '必填',\n      type: 'string',\n      required: true,\n    },\n    input2: {\n      title: '数字最大值',\n      type: 'number',\n      max: 2,\n      required: true\n    },\n    input3: {\n      title: '数字最小值',\n      type: 'number',\n      min: 10,\n      required: true,\n    },\n    input4: {\n      title: '字符最大长度',\n      type: 'string',\n      max: 2,\n      required: true,\n    },\n    input5: {\n      title: '字符最小长度',\n      type: 'string',\n      min: 10,\n      required: true,\n    },\n    input6: {\n      title: 'url 校验',\n      type: 'string',\n      required: true,\n      format: 'url',\n    },\n    input7: {\n      title: 'email 校验',\n      type: 'string',\n      required: true,\n      format: 'email',\n    },\n    input8: {\n      title: '图片格式校验',\n      type: 'string',\n      required: true,\n      format: 'image',\n    }\n  }\n};\n\nexport default () => {\n  const form = useForm();\n  \n  return (\n    <FormRender \n      schema={schema} \n      form={form} \n      footer={true}\n    />\n  )\n};\n```\n\n\n### 二、Rules 校验\n- 全面拥抱 Antd Form Rules\n- 自定义校验 validator：做了一点小小的改变，validator 直接返回布尔值。\n\n\n```jsx\nimport React from 'react';\nimport FormRender, { useForm } from 'form-render';\n\nconst schema = {\n  type: 'object',\n  displayType: 'row',\n  properties: {\n    input1: {\n      title: '正则表达式',\n      type: 'string',\n      required: true,\n      rules: [\n        { pattern: '^[\\u4E00-\\u9FA5]+$', message: '请输入中文！' }\n      ]\n    },\n    input2: {\n      title: '自定义校验',\n      type: 'string',\n      rules: [\n        { \n          validator: (_, value) => {\n            const pattern = '^[\\u4E00-\\u9FA5]+$';\n            const result = new RegExp(pattern).test(value);\n            return result;\n            // 或者是返回一个对象，用于动态设置 message 内容\n            // return {\n            //   status: result,\n            //   message: '请输入中文！',\n            // }\n          }, \n          message: '请输入中文！' \n        }\n      ]\n    }\n  }\n};\n\nexport default () => {\n  const form = useForm();\n  \n  return (\n     <FormRender \n      schema={schema} \n      form={form} \n      footer={true}\n    />\n  )\n};\n```\n\n### 三、子表单校验\n自定义组件是一个子表单时，表单提交是无法触发子表单进行校验的，所以这种类型的子组件需要单独处理\n```js\nimport { useImperativeHandle } from 'react';\nconst ChildForm = (props) => {\n  \n\t// 内部校验方法，异步校验请用 async、await 语法\n  const validator = async () => { \n    return true; // 返回 boolean 值，true 表示内部校验通过\n  \n    // 如果需在外部显示子表单错误信息可以使用对象形式返回\n    // retrun { status: boolean, message: string };\n  };\n\n  useImperativeHandle(props.addons.fieldRef, () => {\n    // 将校验方法暴露出去，方便外部表单提交时，触发校验\n    return {\n      validator\n    };\n  });\n\n  return (\n  \t...// 表单渲染代码\n  );\n}\n\nexport default ChildForm;\n```\n### 四、定制校验模版\n- 全面拥抱 Antd Form Rules\n- validateMessages：通过配置 validateMessages 定制校验模版，可以按需定制，定制模版会和默认校验模版进行合并处理\n\n\n```jsx\nimport React from 'react';\nimport FormRender, { useForm } from 'form-render';\n\nconst schema = {\n  type: 'object',\n  displayType: 'row',\n  properties: {\n    input1: {\n      title: '地址（必填）',\n      type: 'string',\n      required: true,\n    }\n  }\n};\n\nexport default () => {\n  const form = useForm();\n\n  const validateMessages = {\n    required: '${label} 不能为空',\n  };\n  \n  return (\n     <FormRender \n      schema={schema} \n      form={form} \n      footer={true}\n      validateMessages={validateMessages}\n    />\n  )\n};\n```\n\n\n### 五、默认模版内容\n\n- 中文模版\n```Js\nconst typeTemplate = '${label}的类型不是${label}';\n\nconst validateMessagesCN = {\n  default: '${label}未通过校验',\n  required: '${label}必填',\n  whitespace: '${label}不能为空',\n  date: {\n    format: '${label}的格式错误',\n    parse: '${label}无法被解析',\n    invalid: '${label}数据不合法',\n  },\n  types: {\n    string: typeTemplate,\n    method: typeTemplate,\n    array: typeTemplate,\n    object: typeTemplate,\n    number: typeTemplate,\n    date: typeTemplate,\n    boolean: typeTemplate,\n    integer: typeTemplate,\n    float: typeTemplate,\n    regexp: typeTemplate,\n    email: typeTemplate,\n    url: typeTemplate,\n    hex: typeTemplate,\n  },\n  string: {\n    len: '${label}长度不是${len}',\n    min: '${label}长度不能小于${min}',\n    max: '${label}长度不能大于${max}',\n    range: '${label}长度需在${min}与${max}之间',\n  },\n  number: {\n    len: '${label}不等于${len}',\n    min: '${label}不能小于${min}',\n    max: '${label}不能大于${max}',\n    range: '${label}需在${min}与${max}之间',\n  },\n  array: {\n    len: '${label}长度不是${len}',\n    min: '${label}长度不能小于${min}',\n    max: '${label}长度不能大于${max}',\n    range: '${label}长度需在${min}与${max}之间',\n  },\n  pattern: {\n    mismatch: '${label}未通过正则判断${pattern}',\n  },\n};\n```\n\n- 英文模版\n```Js\nconst typeTemplate = \"'${label}' is not a valid ${type}\";\nconst validateMessages = {\n  default: \"Validation error on field '${label}'\",\n  required: \"'${label}' is required\",\n  enum: \"'${label}' must be one of [${enum}]\",\n  whitespace: \"'${label}' cannot be empty\",\n  date: {\n    format: \"'${label}' is invalid for format date\",\n    parse: \"'${label}' could not be parsed as date\",\n    invalid: \"'${label}' is invalid date\",\n  },\n  types: {\n    string: typeTemplate,\n    method: typeTemplate,\n    array: typeTemplate,\n    object: typeTemplate,\n    number: typeTemplate,\n    date: typeTemplate,\n    boolean: typeTemplate,\n    integer: typeTemplate,\n    float: typeTemplate,\n    regexp: typeTemplate,\n    email: typeTemplate,\n    url: typeTemplate,\n    hex: typeTemplate,\n  },\n  string: {\n    len: \"'${label}' must be exactly ${len} characters\",\n    min: \"'${label}' must be at least ${min} characters\",\n    max: \"'${label}' cannot be longer than ${max} characters\",\n    range: \"'${label}' must be between ${min} and ${max} characters\",\n  },\n  number: {\n    len: \"'${label}' must equal ${len}\",\n    min: \"'${label}' cannot be less than ${min}\",\n    max: \"'${label}' cannot be greater than ${max}\",\n    range: \"'${label}' must be between ${min} and ${max}\",\n  },\n  array: {\n    len: \"'${label}' must be exactly ${len} in length\",\n    min: \"'${label}' cannot be less than ${min} in length\",\n    max: \"'${label}' cannot be greater than ${max} in length\",\n    range: \"'${label}' must be between ${min} and ${max} in length\",\n  },\n  pattern: {\n    mismatch: \"'${label}' does not match pattern ${pattern}\",\n  },\n};\n```"
  },
  {
    "path": "docs/form-render/advanced-widget.md",
    "content": "---\norder: 2\ntoc: content\nmobile: false\ngroup: \n  title: 高级用法\n  order: 1\n---\n\n# 自定义组件\n\n在实际的开发中，可能会遇到如下的应用场景：\n\n- 我需要写一个异步加载的搜索输入框（普适性不高/难以用 schema 描述的组件）\n- 我们团队使用 xxx ui，与 antd 不搭，希望能适配一套 xxx ui 组件的 FormRender（欢迎 Pull Request）\n- 我需要在表单内部写一个 excel 上传按钮（完全定制化的需求）\n\nFormRender 内置的控件可能不能满足功能上的需要，这时就需要自定义组件 widget 的支持\n\n使用自定义组件前，也许已经有内置组件支持。具体见 [内置组件](/form-render/display-row)\n\n:::info\n如果是新增一个常用组件，建议给 FormRender 维护的同学来提 Pull Request 或 Issue 并说明你的使用场景，这样可以更好扩展其生态，FormRender 的社区以及提供了部分 [常用自定义组件](https://github.com/alibaba/x-render/tree/master/widgets)。\n:::\n\n## 什么是 Widget\n\nwidget 只是一个普通的 React 组件，它会接收到 FormRender 传递给它的一些 props。开发者可以根据这些 props 完成控件的受控、联动、校验等操作。\n\n比方说，我想在一个常规输入框的后面放一个按钮用于发送验证码。FormRender 的内置组件不能满足需求，那么我可以写一个如下的自定义组件：\n\n```js\nconst CaptchaInput = (props: any) => {\n  const { value, onChange } = props;\n  console.log('widget props:', props);\n\n  const sendCaptcha = (phone: string) => {\n    console.log('send captcha to:', phone);\n  }\n\n  return (\n    <Space>\n      <Input\n        value={value}\n        onChange={(e) => onChange(e.target.value)}\n        placeholder=\"请输入手机号\"\n      />\n      <Button onClick={() => sendCaptcha(value)}>发送验证码</Button>\n    </Space>\n  );\n};\n```\n\n## 使用 Widget\n\n首先在 `<FormRender />` 中注册 widget。\n\n```js\nimport CaptchaInput from 'my/widgets';\n\n<FormRender widgets={{ CaptchaInput }} />\n```\n\n之后在 Schema 中指定 item 的 widget 属性为刚刚注册的 widget。\n\n```js\nconst schema = {\n  type: 'object',\n  properties: {\n    phone: {\n      title: '网址输入自定义组件',\n      type: 'string',\n      // 指定为刚刚注册的 widget\n      widget: 'CaptchaInput',\n    }\n  }\n};\n```\n\n完整代码如下：\n\n<code src=\"./demo/widget/basic.tsx\"></code>\n\n## Widget 接收到的 props\n\n默认情况下 Widget 会接收到如下的 props：\n\n### id\n- 类型：`string`\n- 描述：当前 item 在表单中的唯一 key，一般用不到\n\n### schema\n- 类型：`Schema`\n- 描述：当前 item 的 schema\n\n### value\n- 类型：`any`\n- 描述：当前 item 的值，用于 widget 的受控\n\n### onChange\n- 类型：`(value: any) => void`\n- 描述：当前 item 的值变化时的回调用于 widget 的受控\n\n### disabled\n- 类型：`boolean`\n- 描述：当前 item 是否为禁用状态，如果没有单独为这个 item 指定，那么就继承全局的 `disabled` 属性\n\n### readOnly\n- 类型：`boolean`\n- 描述：当前 item 是否为只读状态，如果没有单独为这个 item 指定，那么就继承全局的 `readOnly` 属性\n\n### addons\n\naddons 包含了全部的表单实例方法，详见 [FormInstance](/form-render/api-props#forminstance)，这里不再赘述。除此之外 addons 还包含了如下一些额外属性。\n\n#### addons.globalProps\n- 类型：`Record<string, any>`\n- 描述：全局属性\n\n#### addons.dataIndex\n- 类型：`string[]`\n- 描述：是自上到下所有经过的数组的 index 按顺序存放的一个数组类型。例如当前的 `dataPath` 为 `a.b[2].c[0].d`，那么这时的 `dataIndex` 就为 `[2,0]`。\n\n:::info\n如果不是在 Form List 中，那么 `dataIndex` 始终为一个空数据 `[]`。\n:::\n\n#### addons.dataPath\n- 类型：`string`\n- 描述：目前数据所在的 path，例如`a.b[2].c[0].d`，详见 [Path 书写](/form-render/advanced-path)\n\n#### addons.schemaPath\n- 类型：`string`\n- 描述：当前 item 的 schema 在整体中的路径\n\n#### addons.dependValues\n- 类型：`any[]`\n- 描述：当自定义组件对应的 schema 使用到 dependencies 字段时，在此获得 dependencies 对应的表单项的实时的值\n\n### others\n实际上任何写入当前 schema 的 props 中的属性都会透传给 widget，所以你不必把所有自定义的属性都写在 widget 内部，更好的方式是通过 schema 控制，以得到更好的复用性。\n\n:::error\n在编写 props 时请避开 `value`、`onChange`、`addons` 等字段防止 FormRender 注入的 props 被覆盖。\n:::\n\n```js\nconst shcema = {\n    type: 'object',\n    properties: {\n        name: {\n            title: '姓名',\n            type: 'string',\n            widget: 'MyInput',\n            // props 中的属性都会透传给自定义组件 MyInput\n            props: {\n                addonAfter: 'name',\n                allowClear: true,\n                foo: 'xxx',\n            }\n        }\n    }\n}\n```\n\n## 使用表达式联动\n表达式是 FormRender 实现简单联动的一个方式。上面提到 widget 会接收到 props 中的属性，这其中也包括使用表达式的字段。FormRender 会先表达式根据表单状态表达式进行转换，然后传递给 widget，并且实时的更新这个属性。我们可以利用这一点很方便的实现一些表单联动。\n\n`formData` 关键字当前全部的表单状态，对于 Form List 的场景，使用 `rootValue` 关键字来表示当前 List Item 的数据。详见 [表单联动](/form-render/advanced-linkage)\n\n```js\nconst shcema = {\n    type: 'object',\n    properties: {\n        age: {\n            title: '年龄',\n            type: 'string',\n        },\n        name: {\n            title: '姓名',\n            type: 'string',\n            widget: 'MyInput',\n            props: {\n                // 当 age 字段更新时，自定义组件 MyInput 会接收到最新的 age 属性\n                age: '{{ formData.age }}' \n            }\n        },\n    }\n}\n```\n\n完整示例如下：\n\n<code src=\"./demo/widget/linkage.tsx\"></code>\n\n\n## 使用 dependencies 联动\n除了使用表达式联动，widget 还可以使用 `dependencies` 属性进行联动。首先在 schema 中定义好 `dependencies`，比如：\n```js\nconst shcema = {\n    type: 'object',\n    properties: {\n        age: {\n            title: '年龄',\n            type: 'string',\n        },\n        name: {\n            title: '姓名',\n            type: 'string',\n            widget: 'MyInput',\n            // 指定依赖的字段\n            dependencies: ['age']\n        },\n    }\n}\n```\n\n之后在 widget 的 `props.addons.dependValues` 中可以拿到依赖的值。\n\n```js\nconst MyInput = (props) => {\n    const { addons } = props;\n    console.log('dependValues:', addons.dependValues);\n    // dependValues: ['xxxx']\n\n    return (\n        // ...\n    )\n}\n```\n与上面同样的例子，使用 `dependencies` 的代码如下：\n<code src=\"./demo/widget/depend-linkage.tsx\"></code>\n\n:::info\n`dependencies` 除了触自动更新之外，还能触发校验，详见 [表单联动](/form-render/advanced-linkage#dependencies-依赖字段)\n:::\n\n## 其他 Widget\n\n除了输入控件可以自定义 widget 之外，Form Render 还提供了自定义一个表单项其他部分的能力。\n\n### readOnlyWidget\n只读模式下，默认会渲染内置的 html 组件，但有时 html 组件并不能满足一个自定义组件在只读模式下需要的展示，此时可使用`readOnlyWidget`字段来指定只读模式下的展示。\n\n```js\nconst schema = {\n  type: 'object',\n  properties: {\n    string: {\n      title: 'ReadOnly widget',\n      type: 'string',\n      widget: 'SiteInput',\n      readOnlyWidget: 'ReadOnlySiteInput',\n    },\n  },\n};\n```\n\n如果你打算在一个自定义组件里通过 readOnly 参数判断条件展示，既是说，site 组件已经写了只读和非只读情况下的渲染\n\n```js\nconst SiteInput = ({ readOnly, value, ...rest }: WidgetProps) => {\n  if (readOnly) return <a href={`https://${value}.com`}>{`https://${value || ''}.com`}</a>;\n  return (\n    <Input addonBefore=\"https://\" addonAfter=\".com\" value={value} {...rest} />\n  );\n};\n```\n\n此时可以指定 `readOnlyWidget` 和 `widget` 为同一个组件：\n\n```js\nconst schema = {\n  type: 'object',\n  properties: {\n    string: {\n      title: 'ReadOnly widget',\n      type: 'string',\n      widget: 'SiteInput',\n      readOnlyWidget: 'SiteInput',\n    },\n  },\n};\n```\n完整代码如下：\n\n<code src=\"./demo/widget/readonly-widget.tsx\"></code>\n\n### labelWidget\n\n使用 `labelWidget` 自定义 label 组件，此时 widget 接收到的 props 只有 `schema`。\n\n<code src=\"./demo/widget/label-widget.tsx\"></code>\n\n### descWidget\n\n使用 `descWidget` 自定义 description 组件，此时 widget 接收到的 props 只有 `schema`。\n\n<code src=\"./demo/widget/desc-widget.tsx\"></code>\n\n## 统一管理 Widget\n\n同一个项目下不同的 form 里，使用到的自定义组件可能大致相同，但也有可能互相不同，我们建议是中心化一个 Form 组件，并一次性将所有需要的自定义组件注入其中。在项目的各处引入对应组件：\n\n```js\n//  /Component/FormRender.js\nimport Form from 'form-render';\nimport Cascade from './Cascade';\nimport Percentage from './Percentage';\nimport MyCheckBox from './MyCheckBox';\nimport ExcelUploader from './ExcelUploader';\n\nexport default props => (\n    <Form \n      widgets={{ \n        Percentage, \n        Cascade, \n        MyCheckBox, \n        ExcelUploader\n      }}\n      {...props}\n    />\n  );\n```\n\n然后在每个 form 页面统一引入使用\n\n```js\nimport { useForm } from 'form-render';\nimport FormRender from './Component/FormRender';\nconst Demo = props => {\n  const form = useForm();\n  return <FormRender form={form} onFinish={() => {}} />;\n};\n```\n\n## 使用 TS\n使用 `WidgetProps` 获得自定义组件 props 的类型支持。\n\n```ts\nimport { FC } from 'react';\nimport type { WidgetProps } from 'form-render';\n\nconst MyWidget: FC<WidgetProps> = (props) => {\n    const { value, onChange, addons } = props;\n\n    return (\n        <div>My Widget</div>\n    )\n}\n\nexport default MyWidget;\n```\n"
  },
  {
    "path": "docs/form-render/api-props.md",
    "content": "---\norder: 0\nmobile: false\ngroup: \n  title: API\n  order: 4\n---\n\n# 表单属性\n\n## Props\n\n| <div style=\"width:200px\">参数</div>          | 说明                                                                                     | 类型                                                                                                  | <div style=\"width:100px\">默认值</div> |\n| ---------------- | ----------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | ------ |\n| schema           | **必填**，描述表单的 schema，详见 [协议配置项](/form-render/api-schema)                              | <a target=\"_blank\" href=\"https://github.com/alibaba/x-render/blob/e2feff8fdb3bef5537b92a2157dbbf40b9d4eb17/packages/form-render/src/type.ts#L32\">SchemaBase</a> | -      |\n| form             | **必填**，`useForm` 创建的表单实例，与 Form 一对一绑定，详见[Forminstance](#forminstance) | `FormInstance`                                                                                        | -      |\n| onFinish         | 提交后的回调，执行 `form.submit()` 后触发                                                 | `(data) => void`                                                                     | -      |\n| beforeFinish     | 在 onFinish 前触发，一般用于外部校验逻辑的回填                                            | `({ data, errors, schema, ...rest }) => Error[] \\| Promise<Error[]>`                                  | -      |\n| onMount          | 表单首次加载时触发                                                            | `() => void`                                                                                          | -      |\n| displayType      | 表单元素与 label 同行 or 分两行展示, inline 则整个展示自然顺排                            | `'column' \\| 'row' \\| 'inline'`                                                                       | column |\n| labelAlign       | label 标签的文本对齐方式                                                                  | `'left' \\| 'right'`                                                                                   | right  |\n| colon            | 是否显示 label 后面的冒号                                                                 | `boolean`                                                                                             | true   |\n| globalConfig     | 表单全局配置，详见 [GlobalConfig](/form-render/api-props#globalconfig)                                                                 | `GlobalConfig`                                                                                             | true   |\n| globalProps     |   全局属性，注入到 widget 的 `addons.globalProps` 中                                                              | `Record<string, any>`                                                                                             | -   |\n| widgets          | 自定义组件，当内置组件无法满足时使用，详见[自定义组件](/form-render/advanced-widget)                             | `Record<string, ReactNode>`                                                                           | -      |\n| watch            | 监听表单的数据变化，详见 [Watch 监听](/form-render/advanced-linkage#watch-监听)                                                   | `Record<string, (val: any) => void \\| { handler:(val:any) => void,immediate?: boolean }>`             | -      |\n| removeHiddenData | 提交数据的时候是否去掉已经被隐藏的元素的数据，默认隐藏                                  | `boolean`                                                                                             | true   |\n| readOnly         | 只读模式，一般用于预览展示，全文 text 展示                                                | `boolean`                                                                                             | false  |\n| className        | 顶层 className                                                                            | `string`                                                                                              | -      |\n| style            | 顶层 style                                                                                | `CSSProperties`                                                                                       | -      |\n| column           | 一行展示多少列                                                                            | `number`                                                                                              | 1      |\n| disabled         | 禁用全部表单项                                                                            | `boolean`                                                                                             | false  |\n|scrollToFirstError | 提交失败自动滚动到第一个错误字段，默认关闭                                                                                                            |  `boolean \\|`  <a href=\"https://github.com/scroll-into-view/scroll-into-view-if-needed/tree/ece40bd9143f48caf4b99503425ecb16b0ad8249#options\" target=\"_blank\">ScrollOptions</a>  | false       \n| locale           | 展示语言，目前只支持中文、英文                                                            | `'zh-CN' \\| 'en-US'`                                                                                        | zh-CN     |\n| configProvider   | antd 的 configProvider，配置透传                                                          | <a href=\"https://ant-design.antgroup.com/components/config-provider-cn/#API\" target=\"_blank\">ConfigProviderProps</a>             | -      |\n| validateMessages | 修改默认的校验提示信息，详见[ValidateMessages](/form-render/advanced-validate)                         | `Record<string, string>`                                                                              | -      |\n| id               | 表单的 id，一般用于标识一个表单的语义化名称                                               | `string \\| number`                                                                                    | -      |\n| antdVersion              | antd 的版本                                               | `v4 \\| v5`                                                                                    | `v5`     |\n\n## FormInstance\n\n| <div style=\"width:200px\">参数</div>              | 描述                                                                                                                  | 类型                                                                                                                |\n| ----------------- | --------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |\n| submit            | 触发提交流程，一般在提交按钮上使用                                                                                    | `() => void`                                                                                                        |\n| resetFields       | 清空表单（也会清空一些内置状态，例如校验）                                                                            | `(fieldPath?: Path[]) => void` |\n| setErrorFields    | 外部手动修改 errorFields 校验信息，用于外部校验回填                                                                   | `(error: Error[]) => void`                                                                                          |\n| setValues         | 外部手动修改 formData，用于已填写的表单的数据回填                                                                     | `(formData: any) => void`                                                                                           |\n| setValueByPath    | 外部修改指定单个 field 的数据(原名 onItemChange)                                                                      | `(path: Path, value: any) => void`                                                                                  |\n| setSchemaByPath   | 指定路径修改 schema                                                                                                   | `(path: Path, schema: any) => void`                                                                                  |\n| setSchema         | 指定多个路径修改 schema，cover 传true将直接替换 schema                                                                                               | `({ path: value }, cover?: boolean) => void`                                                                                         |\n| getValues         | 获取表单内部维护的数据, 如果参数为空则返回当前所有数据                                                                | `(nameList?: Path[], filterFunc?: (meta: { touched: boolean, validating: boolean }, notFilterUndefined?:boolean,notFilterHideData?:boolean) => boolean) => any`             |\n| getHiddenValues   | 获取隐藏的表单数据                                                                                                    | `() => any`                                                                                                         |\n| getSchema            | 获取表单的 schema                                                                                                         | `()=> object`                                                                                                            |\n| removeErrorField  | 外部手动删除某一个 path 下所有的校验信息                                                                              | `(path: Path) => void`                                                                                              |                                                                                    |\n| isFieldTouched    | 检查某个表单是否被用户操作过                                                                                          | `(name: Path) => boolean`                                                                                           |\n| isFieldsTouched   | 检查一组字段 fields 是否被用户操作过, allTouched 为 true 是检查是否所有字段都被操作过                                 | `(nameList?: Path[], allTouched?: boolean) => boolean`                                                              |\n| isFieldValidating | 检查对应字段 field 是否正在校验                                                                                       | `(name: Path) => boolean`                                                                                           |\n| scrollToPath      | 滚动到 path 对应的位置                                                                                                | `(name: Path , options?:` <a href=\"https://github.com/stipsan/scroll-into-view-if-needed/tree/ece40bd9143f48caf4b99503425ecb16b0ad8249#options\" target=\"_blank\">ScrollOptions</a>`) => boolean`                                                                                               |                                                                                                                                   |\n| getFieldError     | 获取对应字段 field 的错误信息                                                                                         | `(name: Path) => string[]`                                                                                          |\n| getFieldsError    | 获取一组字段 fields 对应的错误信息, 返回数组形式; 入参为空则获取所有字段对应的错误信息 | `(nameList: Path[]) => Error[]`                                                                                     |\n| validateFields    | 触发表单校验 | `(nameList?: Path[]) => Promise<any>`                                                                                     |\n\n## GlobalConfig\n\n| <div style=\"width:200px\">参数</div>              | 描述                                                 | 类型              |\n| ----------------- | ----------------------------------------------------------------------------------- | ----------------------------- |\n| mustacheDisabled  | 是否禁用表达式                                                            | `boolean`              |\n| listOperate       | 列表表单配置                                         | `ListOperate` |\n\n### ListOperate\n| <div style=\"width:200px\">参数</div>              | 描述                                                 | 类型              |\n| ----------------- | ----------------------------------------------------------------------------------- | ----------------------------- |\n| btnType  |   列表表单操作按钮样式                                                          | `'icon'\\|'text'`              |\n| hideMove       | 是否隐藏移动按钮                                         | `boolean` |\n"
  },
  {
    "path": "docs/form-render/api-schema.md",
    "content": "---\norder: 1\ntoc: content\nmobile: false\ngroup: \n  title: API\n  order: 4\n---\n\n# 协议配置项\n\n`schema` 用于描述表单的基本信息、结构和校验。`schema` 在结构上遵循 <a href=\"https://json-schema.org/understanding-json-schema/\" target=\"_blank\">JSON Schema 国际规范</a>。\n表单元素类型总共有三种：`item`，`object`，`list`。\n\n## 一、表单配置项\n\nschema 最顶层的一些配置，主要为表单全局的样式。\n\n```js\n{\n  type: 'object', \n  displayType: 'column', \n  colmn: 1, \n  properties: {}\n}\n```\n### type\n- 描述：固定配置为 `type: 'object'`\n\n### displayType\n- 描述：表单项 label 布局方式\n- 类型：`'row' | 'column' | 'inline'`\n\n\n### column\n- 描述：表单布局，一行应该有几列\n- 类型：`number`\n- 默认：`3`\n\n### labelWidth\n- 描述：表单项标签的宽度\n- 类型：`number`\n\n### properties\n- 描述：表单元素的集合\n\n## 二、基础控件配置项\n`item`：即最基本的输入框，选择框等。\n\n如下这样一段 block 在 form-render schema 中就叫做一个 `item`，其中 `url` 就是这个 `item` 在表单中对应的字段。\n\n```js\nurl: {\n  title: 'url输入框',\n  type: 'string',\n  widget: 'input'\n}\n```\n\n### type\n- 描述：表单字段的类型\n- 类型：`'string' | 'number' | 'boolean' | 'array' | 'range' | 'html' | 'void'`\n\n### title \n- 描述：表单字段的标签\n- 类型：`string`\n\n### widget\n- 描述：指定渲染的控件，可以是 form-render 的 [内置组件](/form-render/display-row)，也可以是 [自定义组件](/form-render/advanced-widget)\n- 类型：`string`\n\n### placeholder\n- 描述：输入内容提示\n- 类型：`string | [string, string]`\n\n### description\n- 描述：副标题描述\n- 类型：`string`\n\n### tooltip\n- 描述：气泡提示，支持 `html` 格式，可传入一个对象支持更多配置。详见 <a href=\"https://ant.design/components/tooltip-cn#api\" target=\"_blank\">Antd Tooltip Props</a>。\n- 类型：`string | TooltipProps`\n\n```js\ntooltip: {\n  title: 'xxx<br/>xxx',\n  color: 'red',\n}\n```\n### descWidget\n- 描述：自定义副标题提示组件\n- 类型：`string`\n\n### extra\n- 描述：更多的说明信息，支持 `html` 格式，会紧贴在元素下面一行展示\n- 类型：`string`\n\n### required\n- 描述：是否必填\n- 类型：`boolean`\n- 默认：`false`\n\n### min\n- 描述：string 类型为字符串最小长度；number 类型时为最小值；array 类型时为数组最小长度\n- 类型：`number`\n\n### max\n- 描述：string 类型为字符串最大长度；number 类型时为最大值；array 类型时为数组最大长度\n- 类型：`number`\n\n### format\n- 描述：在已设置的 `type` 下，如何处理这个 `type`\n- 类型：`'image' | 'textarea' | 'color' | 'email' | 'url' | 'dateTime' | 'date' | 'time' | 'upload'`\n\n:::warning\n在 1.x 中，form-render 会根据 `type` 和 `format` 自动选择适合的 `widget`。在 2.x 中，我们更推荐显式的指定 `widget`，而不是自动选择。\n:::\n\n### rules\n- 描述：校验规则，参考 <a href=\"https://ant-design.antgroup.com/components/form-cn#rule\" target=\"_blank\">Antd Form RuleConfig</a>\n- 类型：`Rule[]`\n```js\nrules: [\n  { pattern: '^[\\u4E00-\\u9FA5]+$', message: '请输入中文！' }\n]\n```\n\n### hidden\n- 描述：是否隐藏\n- 类型：`boolean`\n- 默认：`false`\n\n### disabled\n- 描述：是否禁用\n- 类型：`boolean`\n- 默认：`false`\n\n### readOnly\n- 描述：是否禁用\n- 类型：`boolean`\n- 默认：`false`\n\n### readOnlyWidget\n- 描述：指定只读渲染组件\n- 类型：`string`\n\n### dependencies\n- 描述：当依赖的元素更新时，会触发本元素的重新渲染，用于复杂的表单联动，[详见](/form-render/advanced-linkage#dependencies-依赖字段)。\n- 类型：`string[]`\n\n### className\n- 描述：自定义控件 class 名称\n- 类型：`string`\n\n### reserveLabel\n- 描述：当 title 未设置时，通过配置 reserveLabel: true，可以保留 labelWidth 占位，使得输入控件和其他控件上下对齐\n- 类型：`boolean`\n\n### props\n配置额外属性，如果使用的是 antd 组件 对应的就是 antd 组件的其他属性。例如：\n\n\n### action\n- 描述：输入控件支持配置自定义功能槽，显示在输入控件右边，通过 action 值和 widgets 字段间映射，渲染自定义自定义功能槽。\n- 类型\n```js\naction: 'toolWidget' | { widget: 'toolWidget' } // toolWidget 通过 widgets 透传\n```\n\n## 三、嵌套控件配置项\n一个包含其他元素的 block，可用于表单项的分类\n\n```js\ndetail: { // detail 是字段名\n  title: '基础信息',\n  type: 'object',\n  colmn: 1,\n  widget: 'collapse',\n  props: {},\n  properties: {}\n}\n```\n### type\n- 描述：固定配置为 `type: 'object'`\n\n### title\n- 描述：标题\n- 类型：`string`\n\n### widget\n- 描述：希望使用的嵌套组件\n- 类型：`'collapse' | 'card' | 'lineTitle' | 'subInline'`\n- 默认：`'card'`\n\n### properties\n- 描述：表单元素集合\n\n### column\n- 描述：表单布局，一行应该有几列\n- 类型：`number`\n- 默认：`3`\n\n### description\n- 描述：副标题描述\n- 类型：`string`\n\n### tooltip\n- 描述：气泡提示，支持 `html` 格式，可传入一个对象支持更多配置。详见 <a href=\"https://ant.design/components/tooltip-cn#api\" target=\"_blank\">Antd Tooltip Props</a>。\n- 类型：`string | TooltipProps`\n\n```js\ntooltip: {\n  title: 'xxx<br/>xxx',\n  color: 'red',\n}\n```\n\n### props\n- 描述：额外属性，透传到对应的嵌套组件中\n\n## 四、列表控件配置项\n可动态增减的表单项\n\n```js\nlist: { // list 是字段名\n  title: '人员列表',\n  type: 'array',\n  widget: 'card', \n  min: 1,\n  max: 5,\n  items: {\n    title: '基础信息',\n    type: 'object',\n    properties: {}\n  },\n}\n```\n### type\n- 描述：固定配置为 `type: 'array'`\n\n### title\n- 描述：标题\n- 类型：`string`\n\n### widget\n- 描述：希望使用的列表组件\n- 类型：`'cardList' | 'simpleList' | 'tableList' | 'drawerList' | 'virtualList'`\n- 默认：`'cardList'`\n\n### max\n- 描述：列表的最大长度\n- 类型：`number`\n\n### min\n- 描述：列表的最小长度\n- 类型：`number`\n\n### props\n配置列表控件，可配置如下属性\n\n#### props.addBtnProps\n- 描述：添加按钮属性，参考 <a href=\"https://ant.design/components/button-cn#api\" target=\"_blank\">Antd Button Props</a>\n- 类型：`ButtonProps`\n\n#### props.delConfirmProps\n- 描述：删除确认弹窗属性，参考 <a href=\"https://ant.design/components/popconfirm-cn#api\" target=\"_blank\">Antd PopConfirm Props</a>\n- 类型：`PopConfirmProps`\n\n#### props.drawerProps\n- 描述：`drawerList` 中抽屉的属性，参考 <a href=\"https://ant.design/components/drawer-cn#api\" target=\"_blank\">Antd Drawer Props</a>\n- 类型：`DrawerProps`\n\n#### props.actionColumnProps\n- 描述：`tableList | drawerList | virtualList` 中操作列的属性，参考 <a href=\"https://ant.design/components/table-cn#column\" target=\"_blank\">Antd Table ColumnType</a>。 其中 `title` 使用 `colHeaderText` 代替。\n- 类型：`ColumnType`\n\n#### props.hideAdd\n- 描述：是否隐藏添加按钮\n- 类型：`boolean`\n\n#### props.hideCopy\n- 描述：是否隐藏复制按钮\n- 类型：`boolean`\n\n#### props.hideMove\n- 描述：是否隐藏移动按钮\n- 类型：`boolean`\n\n#### props.hideDelete\n- 描述：是否隐藏删除按钮\n- 类型：`boolean`\n\n#### props.hideColumnNestedObject \n- 描述：是否隐藏`drawerList`表格内部嵌套复杂元素的情况\n- 类型：`false` | `hide` | `collapse`\n- 默认：`false`\n\n#### props.onAdd\n- 描述：点击添加按钮回调函数\n- 类型：`(operation, { schema, data }) => void`\n\n#### props.onRemove\n- 描述：点击删除按钮回调函数\n- 类型：`(operation, { schema, data, index }) => void`\n\n#### props.onMove\n- 描述：点击移动按钮回调函数\n- 类型：`(operation, { schema, from, to }) => void`\n\n#### props.onCopy\n- 描述：点击复制按钮回调函数\n- 类型：`(operation, { schema, data, copyIndex }) => void`\n\n### items\n动态项配置，可以是一个嵌套控件\n\n```js\n{\n  title: '基础信息', // 标题\n  type: 'object',    // 固定配置\n  properties: {}     // 表单元素\n  // widget: 'collapse', 支持配置嵌套控件\n},\n```\n\n"
  },
  {
    "path": "docs/form-render/changelog.md",
    "content": "---\norder: 2\ntoc: content\nmobile: false\ngroup: \n  title: 其他\n  order: 5\n---\n\n<embed src=\"../../packages/form-render/CHANGELOG.md\"></embed>\n"
  },
  {
    "path": "docs/form-render/demo/FormRender.jsx",
    "content": "import FormRender, { useForm } from 'form-render';\nimport React from 'react';\n\nconst Demo = ({ schema, ...otherProps }) => {\n  const form = useForm();\n  \n  return <FormRender form={form} schema={schema} {...otherProps} displayType='row' />\n};\n\nexport default Demo;\n"
  },
  {
    "path": "docs/form-render/demo/bind.tsx",
    "content": "import React from 'react';\nimport { Button } from 'antd';\nimport FormRender, { useForm } from 'form-render';\n\nconst delay = ms => new Promise(res => setTimeout(res, ms));\n\nconst schema = {\n  type: 'object',\n  properties: {\n    range1: {\n      bind: ['startData', 'endData'],\n      title: '日期',\n      type: 'range',\n      format: 'date'\n    },\n    objectName: {\n      title: '对象',\n      bind: 'obj',\n      description: '这是一个对象类型',\n      type: 'object',\n      properties: {\n        input1: {\n          bind: 'a.b.c',\n          title: '简单输入框',\n          tooltip: { title: '输入‘123’，避免外部校验错误' }, \n          type: 'string',\n          required: true\n        },\n        input2: {\n          title: '简单输入框2',\n          type: 'string',\n          required: true\n        },\n        select1: {\n          title: '单选',\n          type: 'string',\n          props: {\n            options: [\n              { label: 'a', value: '早'},\n              { label: 'b', value: '中'},\n              { label: 'c', value: '晚'}\n            ]\n          },\n          widget: 'radio'\n        }\n      }\n    }\n  }\n};\n\nconst Demo = () => {\n  const form = useForm();\n\n  const beforeFinish: any = ({ data, errors, schema }) => {\n    if (data.objectName && data.objectName.input1 === '123') return;\n    return delay(1000).then(() => {\n      return {\n        name: 'objectName.select1',\n        error: ['外部校验错误'],\n      };\n    });\n  };\n\n  const onFinish = (formData: any) => {\n    console.log(formData, 'formData');\n  };\n\n  return (\n    <div>\n      <FormRender\n        displayType='row'\n        form={form}\n        schema={schema}\n        beforeFinish={beforeFinish}\n        onFinish={onFinish} // 如果beforeFinish返回一个promise，onFinish会等promise resolve之后执行\n        debug={true}\n      />\n      <Button onClick={form.submit} type='primary'>提交</Button>\n    </div>\n  );\n};\n\nexport default Demo;\n"
  },
  {
    "path": "docs/form-render/demo/defaultChange.tsx",
    "content": "import React from 'react';\nimport { Button } from 'antd';\nimport FormRender, { useForm } from 'form-render';\n\nconst delay = ms => new Promise(res => setTimeout(res, ms));\n\nconst schema = {\n  type: 'object',\n  column: 3,\n  properties: {\n    input1: {\n      bind: 'a.b.c',\n      title: 'Field A',\n      type: 'string',\n      required: true,\n      //default: '1111',\n      defaultValue: '1111sss'\n    },\n    input2: {\n      title: 'Field B',\n      type: 'string',\n      required: true,\n      default: '{{formData.input1}}'\n    },\n    obj: {\n      type: 'object',\n      title: '卡片主题',\n      widget: 'card',\n      description: '这是一个对象类型',\n      column: 3,\n      properties: {\n        input1: {\n          title: 'Field A',\n          type: 'string',\n        },\n        input2: {\n          title: 'Field B',\n          type: 'string',\n          default: '{{formData.obj.input1}}'\n        }\n      }\n    },\n    list: {\n      title: '对象数组',\n      description: '对象数组嵌套功能',\n      type: 'array',\n      widget: 'cardList',\n      default: [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}],\n      items: {\n        type: 'object',\n        title: '卡片主题',\n        description: '这是一个对象类型',\n        column: 3,\n        widget: 'lineTitle',\n        properties: {\n          input1: {\n            title: 'Field A',\n            type: 'string',\n          },\n          input2: {\n            title: 'Field B',\n            type: 'string',\n            default: '{{rootValue.input1}}'\n          },\n          obj: {\n            type: 'object',\n            title: '卡片主题',\n            widget: 'card',\n            description: '这是一个对象类型',\n            column: 3,\n            properties: {\n              input1: {\n                title: 'Field A',\n                type: 'string',\n              },\n              input2: {\n                title: 'Field B',\n                type: 'string',\n                default: '{{formData.obj.input1}}'\n              },\n              list: {\n                title: '对象数组',\n                description: '对象数组嵌套功能',\n                type: 'array',\n                widget: 'cardList',\n                items: {\n                  type: 'object',\n                  title: '卡片主题',\n                  description: '这是一个对象类型',\n                  column: 3,\n                  widget: 'lineTitle',\n                  properties: {\n                    input1: {\n                      title: 'Field A',\n                      type: 'string',\n                    },\n                    input2: {\n                      title: 'Field B',\n                      type: 'string',\n                      default: '{{rootValue.input1}}'\n                    }\n                  }\n                }\n              }\n            }\n          },\n          // list: {\n          //   title: '对象数组',\n          //   description: '对象数组嵌套功能',\n          //   type: 'array',\n          //   widget: 'cardList',\n          //   items: {\n          //     type: 'object',\n          //     title: '卡片主题',\n          //     description: '这是一个对象类型',\n          //     column: 3,\n          //     widget: 'lineTitle',\n          //     properties: {\n          //       input1: {\n          //         title: 'Field A',\n          //         type: 'string',\n          //       },\n          //       input2: {\n          //         title: 'Field B',\n          //         type: 'string',\n          //         default: '{{rootValue.input1}}'\n          //       }\n          //     }\n          //   }\n          // }\n        }\n      }\n    }\n  }\n};\n\nconst Demo = () => {\n  const form = useForm();\n\n  const beforeFinish: any = ({ data, errors, schema }) => {\n    if (data.objectName && data.objectName.input1 === '123') return;\n    return delay(1000).then(() => {\n      return {\n        name: 'objectName.select1',\n        error: ['外部校验错误'],\n      };\n    });\n  };\n\n  const onFinish = (formData: any) => {\n    console.log(formData, 'formData');\n  };\n\n  return (\n    <div>\n      <FormRender\n        displayType='row'\n        form={form}\n        schema={schema}\n        beforeFinish={beforeFinish}\n        onFinish={onFinish} // 如果beforeFinish返回一个promise，onFinish会等promise resolve之后执行\n        debug={true}\n        widgets={{\n          title: ({ children }) => {\n            return children;\n          }\n        }}\n      />\n      <Button onClick={form.submit} type='primary'>提交</Button>\n    </div>\n  );\n};\n\nexport default Demo;\n"
  },
  {
    "path": "docs/form-render/demo/dymic.tsx",
    "content": "import React, { useState } from 'react';\nimport { Button, Modal } from 'antd';\nimport FormRender, { useForm } from 'form-render';\n\nconst delay = ms => new Promise(res => setTimeout(res, ms));\n\nexport enum Language {\n  CN = 'cn',\n  EN = 'en',\n  CA = 'ca',\n}\n\nexport const LanguageText = {\n  [Language.CN]: '中文简体',\n  [Language.EN]: '英文',\n  [Language.CA]: '中文繁体',\n};\n\nexport const LanguageEmailContent = {\n  [Language.CN]: 'emailContent',\n  [Language.EN]: 'emailContentEn',\n  [Language.CA]: 'emailContentCa',\n}\n\nconst items = Object.entries(LanguageText).map(([key, value]) => {\n  return {\n      label: value,\n      key,\n  }\n})\n\nconst schema = {\n  type: 'object',\n  displayType: 'row',\n  properties: {\n      templateName: {\n          title: '模版名称',\n          type: 'string',\n          displayType: 'row',\n          required: true,\n          props: {},\n      },\n      // language: {\n      //     default: Language.CN,\n      //     widget: 'tabs',\n      //     type: 'string',\n      //     props: {\n      //         type: 'card',\n      //         items: items\n      //     },\n      // },\n      emailSubject: {\n          title: '邮件标题',\n          type: 'string',\n          displayType: 'row',\n          required: true,\n          props: {},\n          hidden: `{{formData.language !== 'cn'}}`\n      },\n      list: {\n          title: '活动模版',\n          type: 'array',\n          widget: 'simpleList',\n          items: {\n              type: 'object',\n              properties: {\n                  input1: {\n                      title: '输入框 A',\n                      type: 'string',\n                  },\n                  input2: {\n                      title: '输入框 B',\n                      type: 'string',\n                  },\n                  input3: {\n                      title: '输入框 C',\n                      type: 'string',\n                  },\n              },\n          },\n      },\n  },\n};\n\nconst Demo = () => {\n  const form = useForm();\n  const [visible, setVisible] = useState();\n\n  const onFinish = (formData: any) => {\n    console.log(formData, 'formData');\n  };\n\n  return (\n    <div>\n      <FormRender\n          displayType='row'\n          form={form}\n          schema={schema}\n          widgets={{\n            \"xxx\": () => {\n              return 'sdfadf'\n            }\n          }}\n          onFinish={onFinish} // 如果beforeFinish返回一个promise，onFinish会等promise resolve之后执行\n          debug={true}\n        />\n      \n     \n    </div>\n  );\n};\n\nexport default Demo;\n"
  },
  {
    "path": "docs/form-render/demo/form-slim/basic.tsx",
    "content": "/**\n * transform: true\n * defaultShowCode: true\n */\nimport React from 'react';\nimport { FormSlimRender, useForm, Input, Select } from 'form-render';\n\nconst schema = {\n  type: 'object',\n  displayType: 'row',\n  properties: {\n    input1: {\n      title: '输入框',\n      type: 'string',\n      props: {},\n    },\n    select1: {\n      title: '单选',\n      type: 'string',\n      props: {\n        options: [\n          { label: '早', value: 'a' },\n          { label: '中', value: 'b' },\n          { label: '晚', value: 'c' }\n        ]\n      }\n    }\n  }\n};\n\nexport default () => {\n  const form = useForm();\n\n  return <FormSlimRender schema={schema} form={form} widgets={{ Input, Select }}/>;\n};\n"
  },
  {
    "path": "docs/form-render/demo/form-slim/form-list.tsx",
    "content": "/**\n * transform: true\n * defaultShowCode: true\n */\nimport React from 'react';\nimport { FormSlimRender, useForm, Input, SimpleList, Collapse } from 'form-render';\n\nconst schema = {\n  type: 'object',\n  displayType: 'row',\n  properties: {\n    list: {\n      title: '列表按需',\n      type: 'array',\n      widget: 'simpleList',\n      items: {\n        type: 'object',\n        properties: {\n          input1: {\n            title: '输入框',\n            type: 'string',\n          },\n        },\n      },\n    },\n  },\n};\n\nexport default () => {\n  const form = useForm();\n\n  return (\n    <FormSlimRender\n      schema={schema}\n      form={form}\n      widgets={{\n        Input,\n        SimpleList,\n        Collapse, // 需引入嵌套组件\n    }}/>\n  );\n};\n"
  },
  {
    "path": "docs/form-render/demo/linkage/list.tsx",
    "content": "import React from 'react';\nimport { Input } from 'antd';\nimport FormRender, { useForm } from 'form-render';\n\nconst schema = {\n  type: 'object',\n  displayType: 'row',\n  properties: {\n    list: {\n      title: '对象数组',\n      type: 'array',\n      default: [{}],\n      items: {\n        type: 'object',\n        properties: {\n          radio : {\n            title: '选择框',\n            type: 'string',\n            widget: 'radio',\n            enum: [1 ,2],\n            enumNames: [1, 2],\n          },\n          input: {\n            dependencies: ['list[].radio'],\n            title: '输入框',\n            type: 'string',\n            widget: 'MyInput',\n          },\n        }\n      }\n    }\n  }\n};\n\nconst MyInput = (props: any) => {\n  const disabled = props.addons.dependValues?.[0] === 1;\n  return (\n    <Input {...props} disabled={disabled} />\n  )\n}\n\nconst Demo = () => {\n  const form = useForm();\n\n  return (\n    <FormRender\n      displayType='row'\n      form={form}\n      schema={schema}\n      widgets={{ MyInput }}\n    />\n  );\n};\n\nexport default Demo;\n"
  },
  {
    "path": "docs/form-render/demo/outLabelCol.tsx",
    "content": "import React from 'react';\nimport FormRender, { useForm } from 'form-render';\nimport { Button } from 'antd';\n\nconst schema = {\n  type: 'object',\n\n  properties: {\n    input1: {\n      title: '简单输入框',\n      type: 'string',\n      required: true,\n    },\n    select1: {\n      title: '单选',\n      type: 'string',\n      required: true,\n      props: {\n        options: [\n          { label: '早', value: 'a' },\n          { label: '中', value: 'b' },\n          { label: '晚', value: 'c' }\n        ]\n      }\n    }\n  }\n};\n\nexport default () => {\n  const form = useForm();\n\n  const onValidateFields = () => {\n    form.validateFields(['input1'])\n    .then(values => {\n      console.log(values, 'values');\n    })\n    .catch(errors => {\n      console.log(errors, 'errors');\n    });\n  }\n\n  return (\n    <>\n      <FormRender \n        form={form} \n        schema={schema}\n        labelCol={5}\n        fieldCol={8}\n      />\n      <div>\n        <Button onClick={onValidateFields}>validateFields Test</Button>\n      </div>\n    </>\n  );\n}\n"
  },
  {
    "path": "docs/form-render/demo/validateFields.tsx",
    "content": "import React from 'react';\nimport FormRender, { useForm } from 'form-render';\nimport { Button } from 'antd';\n\nconst schema = {\n  type: 'object',\n  properties: {\n    input1: {\n      title: '简单输入框',\n      type: 'string',\n      required: true,\n    },\n    select1: {\n      title: '单选',\n      type: 'string',\n      required: true,\n      props: {\n        options: [\n          { label: '早', value: 'a' },\n          { label: '中', value: 'b' },\n          { label: '晚', value: 'c' }\n        ]\n      }\n    }\n  }\n};\n\nexport default () => {\n  const form = useForm();\n\n  const onValidateFields = () => {\n    form.validateFields(['input1'])\n    .then(values => {\n      console.log(values, 'values');\n    })\n    .catch(errors => {\n      console.log(errors, 'errors');\n    });\n  }\n\n  return (\n    <>\n      <FormRender \n        form={form} \n        schema={schema} \n        displayType='row' \n      />\n      <div>\n        <Button onClick={onValidateFields}>validateFields Test</Button>\n      </div>\n    </>\n  );\n}\n"
  },
  {
    "path": "docs/form-render/demo/widget/basic.tsx",
    "content": "import React from 'react';\nimport { Input, Button, Space } from 'antd';\nimport Form, { useForm } from 'form-render';\nimport type { WidgetProps } from 'form-render';\n\nconst schema = {\n  type: 'object',\n  properties: {\n    phone: {\n      title: '自定义 Input',\n      type: 'string',\n      widget: 'CaptchaInput',\n      foo: 'xxx',\n      props: {\n        foo: 'xxxx',\n      }\n    },\n  },\n};\n\nconst CaptchaInput: React.FC<WidgetProps> = (props) => {\n  const { value, onChange } = props;\n  console.log('widget props:', props);\n\n  const sendCaptcha = (phone: string) => {\n    console.log('send captcha to:', phone);\n  }\n\n  return (\n    <Space>\n      <Input\n        value={value}\n        placeholder=\"请输入手机号\"\n        onChange={(e) => onChange(e.target.value)}\n      />\n      <Button onClick={() => sendCaptcha(value)}>发送验证码</Button>\n    </Space>\n  );\n};\n\nconst Demo = () => {\n  const form = useForm();\n  return (\n    <Form\n      form={form}\n      schema={schema}\n      widgets={{ CaptchaInput }}\n      onFinish={formData => alert(JSON.stringify(formData, null, 2))}\n      footer\n    />\n  );\n};\n\nexport default Demo;\n"
  },
  {
    "path": "docs/form-render/demo/widget/depend-linkage.tsx",
    "content": "import React from 'react';\nimport { Input, Space } from 'antd';\nimport Form, { useForm } from 'form-render';\nimport type { WidgetProps } from 'form-render';\n\nconst schema = {\n  type: 'object',\n  properties: {\n    age: {\n      title: '年龄',\n      type: 'string',\n      props: {\n        style: {\n          width: 200\n        }\n      }\n    },\n    name: {\n      title: '姓名',\n      type: 'string',\n      widget: 'MyInput',\n      dependencies: ['age'],\n    },\n  }\n}\n\nconst MyInput: React.FC<WidgetProps> = (props) => {\n  const { value, onChange, addons } = props;\n  const { dependValues } = addons;\n  const [age] = dependValues;\n\n  return (\n    <Space>\n      <Input\n        style={{ width: 200 }}\n        value={value}\n        onChange={(e) => onChange(e.target.value)}\n      />\n      <div>{age}</div>\n    </Space>\n  );\n};\n\nconst Demo = () => {\n  const form = useForm();\n  return (\n    <Form\n      form={form}\n      schema={schema}\n      widgets={{ MyInput }}\n    />\n  );\n};\n\nexport default Demo;\n"
  },
  {
    "path": "docs/form-render/demo/widget/desc-widget.tsx",
    "content": "import React from 'react';\nimport Form, { useForm } from 'form-render';\n\nconst schema = {\n  type: 'object',\n  properties: {\n    string: {\n      title: '自定义 desc',\n      type: 'string',\n      descWidget: 'MyDesc',\n      description: '这是一段描述',\n    },\n  },\n};\n\nconst MyDesc = (props) => {\n  const { schema } = props;\n  console.log('props:', props)\n  return (\n    <div style={{color: 'red', fontWeight: 'bolder'}}>{schema.description}</div>\n  )\n}\n\nconst Demo = () => {\n  const form = useForm();\n  return (\n    <Form\n      form={form}\n      schema={schema}\n      widgets={{ MyDesc }}\n    />\n  );\n};\n\nexport default Demo;\n"
  },
  {
    "path": "docs/form-render/demo/widget/label-widget.tsx",
    "content": "import React from 'react';\nimport Form, { useForm } from 'form-render';\n\nconst schema = {\n  type: 'object',\n  properties: {\n    string: {\n      title: '自定义 label',\n      type: 'string',\n      labelWidget: 'MyLabel',\n    },\n  },\n};\n\nconst MyLabel = (props) => {\n  const { schema } = props;\n  console.log('props:', props)\n  return (\n    <div style={{color: 'red', fontWeight: 'bolder'}}>{schema.title}</div>\n  )\n}\n\nconst Demo = () => {\n  const form = useForm();\n  return (\n    <Form\n      form={form}\n      schema={schema}\n      widgets={{ MyLabel }}\n    />\n  );\n};\n\nexport default Demo;\n"
  },
  {
    "path": "docs/form-render/demo/widget/linkage.tsx",
    "content": "import React from 'react';\nimport { Input, Space } from 'antd';\nimport Form, { useForm } from 'form-render';\nimport type { WidgetProps } from 'form-render';\n\nconst schema = {\n  type: 'object',\n  properties: {\n    age: {\n      title: '年龄',\n      type: 'string',\n      props: {\n        style: {\n          width: 200\n        }\n      }\n    },\n    name: {\n      title: '姓名',\n      type: 'string',\n      widget: 'MyInput',\n      props: {\n        // 当 age 字段更新时，自定义组件 MyInput 会接收到最新的 age 属性\n        age: '{{ formData.age }}'\n      }\n    },\n  }\n}\n\nconst MyInput: React.FC<WidgetProps> = (props) => {\n  const { value, onChange, age } = props;\n\n  return (\n    <Space>\n      <Input\n        style={{ width: 200 }}\n        value={value}\n        onChange={(e) => onChange(e.target.value)}\n      />\n      <div>{age}</div>\n    </Space>\n  );\n};\n\nconst Demo = () => {\n  const form = useForm();\n  return (\n    <Form\n      form={form}\n      schema={schema}\n      widgets={{ MyInput }}\n    />\n  );\n};\n\nexport default Demo;\n"
  },
  {
    "path": "docs/form-render/demo/widget/readonly-widget.tsx",
    "content": "import React from 'react';\nimport { Input, Button, Switch } from 'antd';\nimport Form, { useForm } from 'form-render';\nimport type { WidgetProps } from 'form-render';\n\nconst schema = {\n  type: 'object',\n  properties: {\n    string: {\n      title: 'ReadOnly widget',\n      type: 'string',\n      widget: 'SiteInput',\n      readOnlyWidget: 'SiteInput',\n    },\n  },\n};\n\nconst SiteInput = ({ readOnly, value, ...rest }: WidgetProps) => {\n  if (readOnly) return <a href={`https://${value}.com`}>{`https://${value || ''}.com`}</a>;\n  return (\n    <Input addonBefore=\"https://\" addonAfter=\".com\" value={value} {...rest} />\n  );\n};\n\nconst Demo = () => {\n  const form = useForm();\n  const [readOnly, setReadOnly] = React.useState(false);\n  return (\n  <div>\n    <Switch\n      style={{ marginBottom: 10 }}\n      onChange={(checked) => setReadOnly(checked)}\n      checkedChildren=\"只读\"\n      unCheckedChildren=\"编辑\"\n    />\n    <Form\n      form={form}\n      schema={schema}\n      widgets={{ SiteInput }}\n      readOnly={readOnly}\n    />\n  </div>\n  );\n};\n\nexport default Demo;\n"
  },
  {
    "path": "docs/form-render/disaply-search.md",
    "content": "---\norder: 1\ntoc: content\nmobile: false\ngroup: \n  title: 最佳示例\n  order: 2\n---\n\n# 查询表单\n\n## 基础\n\n```jsx\nimport React from 'react';\nimport { useForm, SearchForm } from 'form-render';\n\nconst schema = {\n  type: 'object',\n  displayType: 'row',\n  properties: {\n    inputA: {\n      title: 'Field A',\n      type: 'string',\n      props: {},\n    },\n    inputB: {\n      title: 'Field B',\n      type: 'string',\n      props: {},\n    },\n    input3: {\n      title: 'Field C',\n      type: 'string',\n      props: {},\n    },\n    selectA: {\n      title: 'Field D',\n      type: 'string',\n      widget: 'select',\n      props: {\n        options: [\n          { label: '早', value: 'a' },\n          { label: '中', value: 'b' },\n          { label: '晚', value: 'c' },\n        ]\n      }\n    }\n  }\n};\n\nexport default () => {\n  const form = useForm();\n\n  const onSearch = (values) => {\n    console.log(values, 'searchData');\n  };\n\n  return (\n    <SearchForm \n      schema={schema} \n      form={form}\n      labelWidth={100}\n      onSearch={onSearch}\n      // loading={true}\n    />\n  )\n};\n```\n\n\n## 可折叠\n\n```jsx\nimport React from 'react';\nimport FormRender, { useForm, SearchForm } from 'form-render';\n\nconst schema = {\n  type: 'object',\n  displayType: 'column',\n  properties: {\n    inputA: {\n      title: 'Field A',\n      type: 'string',\n      props: {},\n    },\n    inputB: {\n      title: 'Field B',\n      type: 'string',\n      props: {},\n    },\n    inputC: {\n      title: 'Field C',\n      type: 'string',\n      props: {},\n    },\n    inputD: {\n      title: 'Field D',\n      type: 'string',\n      props: {},\n    },\n    selectA: {\n      title: 'Field E',\n      type: 'string',\n      widget: 'select',\n      props: {\n        options: [\n          { label: '早', value: 'a' },\n          { label: '中', value: 'b' },\n          { label: '晚', value: 'c' },\n        ]\n      }\n    },\n    selectB: {\n      title: 'Field F',\n      type: 'string',\n      widget: 'select',\n      props: {\n        options: [\n          { label: '早', value: 'a' },\n          { label: '中', value: 'b' },\n          { label: '晚', value: 'c' },\n        ]\n      }\n    },\n    selectC: {\n      title: 'Field G',\n      type: 'string',\n      widget: 'select',\n      props: {\n        options: [\n          { label: '早', value: 'a' },\n          { label: '中', value: 'b' },\n          { label: '晚', value: 'c' },\n        ]\n      }\n    },\n    selectD: {\n      title: 'Field H',\n      type: 'string',\n      widget: 'select',\n      props: {\n        options: [\n          { label: '早', value: 'a' },\n          { label: '中', value: 'b' },\n          { label: '晚', value: 'c' },\n        ]\n      }\n    }\n  }\n};\n\nexport default () => {\n  const form = useForm();\n\n  const onSearch = (values) => {\n    console.log(values, 'searchData');\n  }\n\n  return (\n    <SearchForm \n      schema={schema} \n      form={form}\n      labelWidth={100}\n      collapsed={true}\n      onSearch={onSearch}\n    />\n  )\n};\n```\n\n\n## API\n\n| 参数             | 说明                                                                                     | 类型                                                                                                  | 默认值 |\n| ---------------- | ----------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | ------ |\n| collapsed           | 是否可折叠                             | `boolean`          |  -    |\n| defaultCollapsed    | 折叠收起                               | `boolean`          |  true   |\n| loading             | 查询按钮加载中                          | `boolean`          |  -    |\n| layoutAuto            | 自适应布局，可设置为 true / false 或对象：{ fieldMinWidth: `number` }， 当设置fieldMinWidth 会根据最小宽度动态自适应                         | `boolean` or `object`         |  `false `   |\n| column              | 一行多列                               | `number`           |  4    |\n| onMount             | 表单首次加载时触发                       | `() => void`       |  -    |\n| onSearch            | 点击查询按钮触发                        | `(data) => void`    |  -    |\n| searchText          | 自定义 查询按钮文案                     | `string`            |  查询  |\n| resetText           | 自定义 重置按钮文案                      | `string`           |  重置  |\n| searchOnMount      | 组件初次挂载时，是否默认执行查询动作        | `boolean`           | true   | \n| searchWithError    | 表单校验失败时，是否继续执行查询操作        | `boolean`           | true   |\n|closeReturnSearch   | 关闭回车查询                            | `boolean`           | -      |\n| `...`              | 详见 formRender [Props](/form-render/api-props) |            |        |\n"
  },
  {
    "path": "docs/form-render/display-row.md",
    "content": "---\norder: 0\ntoc: content\nmobile: false\ngroup: \n  title: 最佳示例\n  order: 2\n---\n\n# 内置组件\n\n## 基础组件\n\n```jsx\nimport React from 'react';\nimport FormRender, { useForm } from 'form-render';\nimport schema from './schema/baseControl';\n\n\nexport default () => {\n  const form = useForm();\n\n  const onFinish = (formData) => {\n    console.log('formData：', formData);\n  };\n\n  return <FormRender form={form} schema={schema} footer={true} onFinish={onFinish} />;\n};\n```\n\n## 嵌套组件\n\n### 折叠 collapse\n\n```jsx\nimport React from 'react';\nimport FormRender, { useForm } from 'form-render';\n\nconst schema = {\n  type: 'object',\n  displayType: 'row',\n  properties: {\n    obj: {\n      type: 'object',\n      title: '卡片主题',\n      description: '这是一个对象类型',\n      widget: 'collapse',\n      column: 3,\n      properties: {\n        input1: {\n          title: '输入框 A',\n          type: 'string',\n        },\n        input2: {\n          title: '输入框 B',\n          type: 'string',\n        },\n        input3: {\n          title: '输入框 C',\n          type: 'string',\n        },\n        input4: {\n          title: '输入框 D',\n          type: 'string',\n        },\n      },\n    },\n  },\n};\n\nexport default () => {\n  const form = useForm();\n\n  return <FormRender schema={schema} form={form} />;\n};\n```\n\n### 卡片 card\n\n```jsx\nimport React from 'react';\nimport FormRender, { useForm } from 'form-render';\n\nconst schema = {\n  type: 'object',\n  displayType: 'row',\n  properties: {\n    obj: {\n      type: 'object',\n      widget: 'card',\n      title: '卡片主题',\n      description: '这是一个对象类型',\n      column: 3,\n      properties: {\n        input1: {\n          title: '输入框 A',\n          type: 'string',\n        },\n        input2: {\n          title: '输入框 B',\n          type: 'string',\n        },\n        input3: {\n          title: '输入框 C',\n          type: 'string',\n        },\n        input4: {\n          title: '输入框 D',\n          type: 'string',\n        },\n      },\n    },\n  },\n};\n\nexport default () => {\n  const form = useForm();\n\n  return <FormRender schema={schema} form={form} />;\n};\n```\n\n### 标题线 lineTitle\n\n```jsx\nimport React from 'react';\nimport FormRender, { useForm } from 'form-render';\n\nconst schema = {\n  type: 'object',\n  displayType: 'row',\n  properties: {\n    obj: {\n      type: 'object',\n      title: '卡片主题',\n      widget: 'lineTitle',\n      description: '这是一个对象类型',\n      column: 3,\n      properties: {\n        input1: {\n          title: '输入框 A',\n          type: 'string',\n        },\n        input2: {\n          title: '输入框 B',\n          type: 'string',\n        },\n        input3: {\n          title: '输入框 C',\n          type: 'string',\n        },\n        input4: {\n          title: '输入框 D',\n          type: 'string',\n        },\n      },\n    },\n  },\n};\n\nexport default () => {\n  const form = useForm();\n\n  return <FormRender schema={schema} form={form} />;\n};\n```\n\n### 内联 subInline\n\n```jsx\nimport React from 'react';\nimport FormRender, { useForm } from 'form-render';\n\nconst schema = {\n  type: 'object',\n  displayType: 'row',\n  properties: {\n    obj: {\n      type: 'object',\n      title: '卡片主题',\n      widget: 'subInline',\n      description: '这是一个对象类型',\n      column: 3,\n      properties: {\n        input1: {\n          title: '输入框 A',\n          type: 'string',\n        },\n        input2: {\n          title: '输入框 B',\n          type: 'string',\n        },\n        input3: {\n          title: '输入框 C',\n          type: 'string',\n        },\n        input4: {\n          title: '输入框 D',\n          type: 'string',\n        },\n      },\n    },\n  },\n};\n\nexport default () => {\n  const form = useForm();\n\n  return <FormRender schema={schema} form={form} />;\n};\n```\n\n## 列表组件\n\n列表的展示对于简单需求占位太多，复杂需求定制不够一直是痛点。所以我们给出了 5 种展示，充分满足从极简到复杂的所有需求。\n默认使用 widget: 'cardList'，卡片类型\n\n### SimpleList\n\n用于展示每行只有 1-3 个简单组件的情况，紧凑排列\n\n#### simpleList：标签换行\n\n```jsx\nimport React from 'react';\nimport FormRender, { useForm } from 'form-render';\n\nconst schema = {\n  type: 'object',\n  displayType: 'row',\n  properties: {\n    list: {\n      title: '活动模版',\n      type: 'array',\n      widget: 'simpleList',\n      items: {\n        type: 'object',\n        properties: {\n          input1: {\n            title: '输入框 A',\n            type: 'string',\n          },\n          input2: {\n            title: '输入框 B',\n            type: 'string',\n          },\n          input3: {\n            title: '输入框 C',\n            type: 'string',\n          },\n        },\n      },\n    },\n  },\n};\n\nexport default () => {\n  const form = useForm();\n\n  return <FormRender schema={schema} form={form} />;\n};\n```\n\n#### simpleList：标签换行-带背景\n\n```jsx\nimport React from 'react';\nimport FormRender, { useForm } from 'form-render';\n\nconst schema = {\n  type: 'object',\n  displayType: 'row',\n  properties: {\n    list: {\n      title: '活动模版',\n      type: 'array',\n      widget: 'simpleList',\n      props: {\n        hasBackground: true\n      },\n      items: {\n        type: 'object',\n        properties: {\n          input1: {\n            title: '输入框 A',\n            type: 'string',\n          },\n          input2: {\n            title: '输入框 B',\n            type: 'string',\n          },\n          input3: {\n            title: '输入框 C',\n            type: 'string',\n          },\n        },\n      },\n    },\n  },\n};\n\nexport default () => {\n  const form = useForm();\n\n  return <FormRender schema={schema} form={form} />;\n};\n```\n\n#### simpleList：标签内联\n\n```jsx\nimport React from 'react';\nimport FormRender, { useForm } from 'form-render';\n\nconst schema = {\n  type: 'object',\n  displayType: 'row',\n  properties: {\n    list: {\n      title: '活动模版',\n      type: 'array',\n      widget: 'simpleList',\n      display: 'inline',\n      labelWidth: 100,\n      items: {\n        type: 'object',\n        properties: {\n          input1: {\n            title: '输入框 A',\n            type: 'string',\n          },\n          input2: {\n            title: '输入框 B',\n            type: 'string',\n          },\n          input3: {\n            title: '输入框 C',\n            type: 'string',\n          },\n        },\n      },\n    },\n  },\n};\n\nexport default () => {\n  const form = useForm();\n\n  return <FormRender schema={schema} form={form} />;\n};\n```\n\n#### simpleList：标签内联-带背景\n\n```jsx\nimport React from 'react';\nimport FormRender, { useForm } from 'form-render';\n\nconst schema = {\n  type: 'object',\n  displayType: 'row',\n  properties: {\n    list: {\n      title: '活动模版',\n      type: 'array',\n      widget: 'simpleList',\n      display: 'inline',\n      labelWidth: 100,\n      props: {\n        hasBackground: true,\n      },\n      items: {\n        type: 'object',\n        properties: {\n          input1: {\n            title: '输入框 A',\n            type: 'string',\n          },\n          input2: {\n            title: '输入框 B',\n            type: 'string',\n          },\n          input3: {\n            title: '输入框 C',\n            type: 'string',\n          },\n        },\n      },\n    },\n  },\n};\n\nexport default () => {\n  const form = useForm();\n\n  return <FormRender schema={schema} form={form} />;\n};\n```\n\n### CardList\n\n用于展示结构复杂，但数量不太多的 list\n\n#### cardList：折叠-标签换行\n\n```jsx\nimport React from 'react';\nimport FormRender, { useForm } from 'form-render';\n\nconst schema = {\n  type: 'object',\n  displayType: 'row',\n  properties: {\n    list: {\n      // title: '对象数组',\n      // description: '对象数组嵌套功能',\n      type: 'array',\n      widget: 'cardList',\n      items: {\n        type: 'object',\n        title: '卡片主题',\n        description: '这是一个对象类型',\n        column: 3,\n        properties: {\n          input1: {\n            title: '输入框 A',\n            type: 'string',\n          },\n          input2: {\n            title: '输入框 B',\n            type: 'string',\n          },\n          input3: {\n            title: '输入框 B',\n            type: 'string',\n          },\n          input4: {\n            title: '输入框 C',\n            type: 'string',\n          },\n        },\n      },\n    },\n  },\n};\n\nexport default () => {\n  const form = useForm();\n\n  return <FormRender schema={schema} form={form} />;\n};\n```\n\n#### cardList：卡片-标签换行\n\n```jsx\nimport React from 'react';\nimport FormRender, { useForm } from 'form-render';\n\nconst schema = {\n  type: 'object',\n  displayType: 'row',\n  properties: {\n    list: {\n      title: '对象数组',\n      description: '对象数组嵌套功能',\n      type: 'array',\n      widget: 'cardList',\n      items: {\n        type: 'object',\n        title: '卡片主题',\n        description: '这是一个对象类型',\n        column: 3,\n        widget: 'card',\n        properties: {\n          input1: {\n            title: '输入框 A',\n            type: 'string',\n          },\n          input2: {\n            title: '输入框 B',\n            type: 'string',\n          },\n          input3: {\n            title: '输入框 B',\n            type: 'string',\n          },\n          input4: {\n            title: '输入框 C',\n            type: 'string',\n          },\n        },\n      },\n    },\n  },\n};\n\nexport default () => {\n  const form = useForm();\n\n  return <FormRender schema={schema} form={form} />;\n};\n```\n\n#### cardList：标题线-标签换行\n\n```jsx\nimport React from 'react';\nimport FormRender, { useForm } from 'form-render';\n\nconst schema = {\n  type: 'object',\n  displayType: 'row',\n  properties: {\n    list: {\n      title: '对象数组',\n      description: '对象数组嵌套功能',\n      type: 'array',\n      widget: 'cardList',\n      items: {\n        type: 'object',\n        title: '卡片主题',\n        description: '这是一个对象类型',\n        column: 3,\n        widget: 'lineTitle',\n        properties: {\n          input1: {\n            title: '输入框 A',\n            type: 'string',\n          },\n          input2: {\n            title: '输入框 B',\n            type: 'string',\n          },\n          input3: {\n            title: '输入框 B',\n            type: 'string',\n          },\n          input4: {\n            title: '输入框 C',\n            type: 'string',\n          },\n        },\n      },\n    },\n  },\n};\n\nexport default () => {\n  const form = useForm();\n\n  return <FormRender schema={schema} form={form} />;\n};\n```\n\n#### cardList：折叠-标签内联\n\n```jsx\nimport React from 'react';\nimport FormRender, { useForm } from 'form-render';\n\nconst schema = {\n  type: 'object',\n  displayType: 'row',\n  properties: {\n    list: {\n      title: '对象数组',\n      tooltip: '对象数组嵌套功能',\n      type: 'array',\n      display: 'inline',\n      widget: 'cardList',\n      labelWidth: 100,\n      items: {\n        type: 'object',\n        title: '卡片主题',\n        description: '这是一个对象类型',\n        column: 3,\n        properties: {\n          input1: {\n            title: '输入框 A',\n            type: 'string',\n          },\n          input2: {\n            title: '输入框 B',\n            type: 'string',\n          },\n          input3: {\n            title: '输入框 B',\n            type: 'string',\n          },\n          input4: {\n            title: '输入框 C',\n            type: 'string',\n          },\n        },\n      },\n    },\n  },\n};\n\nexport default () => {\n  const form = useForm();\n\n  return <FormRender schema={schema} form={form} />;\n};\n```\n\n#### cardList：卡片-标签内联\n\n```jsx\nimport React from 'react';\nimport FormRender, { useForm } from 'form-render';\n\nconst schema = {\n  type: 'object',\n  displayType: 'row',\n  properties: {\n    list: {\n      title: '对象数组',\n      tooltip: '对象数组嵌套功能',\n      type: 'array',\n      display: 'inline',\n      widget: 'cardList',\n      labelWidth: 100,\n      items: {\n        type: 'object',\n        title: '卡片主题',\n        description: '这是一个对象类型',\n        column: 3,\n        widget: 'card',\n        properties: {\n          input1: {\n            title: '输入框 A',\n            type: 'string',\n          },\n          input2: {\n            title: '输入框 B',\n            type: 'string',\n          },\n          input3: {\n            title: '输入框 B',\n            type: 'string',\n          },\n          input4: {\n            title: '输入框 C',\n            type: 'string',\n          },\n        },\n      },\n    },\n  },\n};\n\nexport default () => {\n  const form = useForm();\n\n  return <FormRender schema={schema} form={form} />;\n};\n```\n\n#### cardList：标题线-标签内联\n\n```jsx\nimport React from 'react';\nimport FormRender, { useForm } from 'form-render';\n\nconst schema = {\n  type: 'object',\n  displayType: 'row',\n  properties: {\n    list: {\n      title: '对象数组',\n      tooltip: '对象数组嵌套功能',\n      type: 'array',\n      display: 'inline',\n      widget: 'cardList',\n      labelWidth: 100,\n      items: {\n        type: 'object',\n        title: '卡片主题',\n        description: '这是一个对象类型',\n        column: 3,\n        widget: 'lineTitle',\n        properties: {\n          input1: {\n            title: '输入框 A',\n            type: 'string',\n          },\n          input2: {\n            title: '输入框 B',\n            type: 'string',\n          },\n          input3: {\n            title: '输入框 B',\n            type: 'string',\n          },\n          input4: {\n            title: '输入框 C',\n            type: 'string',\n          },\n        },\n      },\n    },\n  },\n};\n\nexport default () => {\n  const form = useForm();\n\n  return <FormRender schema={schema} form={form} />;\n};\n```\n\n### DrawerList\n\n用于展示存在列表套列表，列表套对象等复杂元素的情况\n\n#### drawerList：标签换行\n\n```jsx\nimport React from 'react';\nimport FormRender, { useForm } from 'form-render';\nconst schema = {\n  type: 'object',\n  displayType: 'row',\n  properties: {\n    list: {\n      title: '对象数组',\n      description: '对象数组嵌套功能',\n      type: 'array',\n      widget: 'drawerList',\n      items: {\n        type: 'object',\n        properties: {\n          input1: {\n            title: '简单输入框',\n            type: 'string',\n            required: true,\n          },\n          input2: {\n            title: '简单输入框2',\n            type: 'string',\n          },\n          input3: {\n            title: '简单输入框3',\n            type: 'string',\n          },\n          select1: {\n            title: '单选',\n            type: 'string',\n            enum: ['a', 'b', 'c'],\n            enumNames: ['早', '中', '晚'],\n            widget: 'select',\n          },\n        },\n      },\n    },\n  },\n};\n\nexport default () => {\n  const form = useForm();\n  return <FormRender schema={schema} form={form} />;\n};\n```\n\n#### drawerList：标签内联\n\n```jsx\nimport React from 'react';\nimport FormRender, { useForm } from 'form-render';\nconst schema = {\n  type: 'object',\n  displayType: 'row',\n  properties: {\n    list: {\n      title: '对象数组',\n      tooltip: '对象数组嵌套功能',\n      type: 'array',\n      widget: 'drawerList',\n      display: 'inline',\n      labelWidth: 100,\n      items: {\n        type: 'object',\n        properties: {\n          input1: {\n            title: '简单输入框',\n            type: 'string',\n            required: true,\n          },\n          input2: {\n            title: '简单输入框2',\n            type: 'string',\n          },\n          input3: {\n            title: '简单输入框3',\n            type: 'string',\n          },\n          select1: {\n            title: '单选',\n            type: 'string',\n            enum: ['a', 'b', 'c'],\n            enumNames: ['早', '中', '晚'],\n            widget: 'select',\n          },\n        },\n      },\n    },\n  },\n};\n\nexport default () => {\n  const form = useForm();\n  return <FormRender schema={schema} form={form} />;\n};\n```\n\n### TableList\n\n用于展示每行只有 3 - n 个简单元素的情况，特别是数据量很大需要分页的\n\n#### tableList：标签换行\n\n```jsx\nimport React from 'react';\nimport FormRender, { useForm } from 'form-render';\nconst schema = {\n  type: 'object',\n  displayType: 'row',\n  properties: {\n    list: {\n      title: '对象数组',\n      description: '对象数组嵌套功能',\n      type: 'array',\n      widget: 'tableList',\n      items: {\n        type: 'object',\n        properties: {\n          input1: {\n            title: '简单输入框',\n            type: 'string',\n            required: true,\n          },\n          input2: {\n            title: '简单输入框2',\n            type: 'string',\n          },\n          input3: {\n            title: '简单输入框3',\n            type: 'string',\n          },\n          select1: {\n            title: '单选',\n            type: 'string',\n            enum: ['a', 'b', 'c'],\n            enumNames: ['早', '中', '晚'],\n            widget: 'select'\n          }\n        }\n      }\n    }\n  }\n};\n\nexport default () => {\n  const form = useForm();\n  return <FormRender schema={schema} form={form} />;\n};\n```\n\n#### tableList：标签内联\n\n```jsx\nimport React from 'react';\nimport FormRender, { useForm } from 'form-render';\nconst schema = {\n  type: 'object',\n  displayType: 'row',\n  properties: {\n    list: {\n      title: '对象数组',\n      tooltip: '对象数组嵌套功能',\n      type: 'array',\n      widget: 'tableList',\n      display: 'inline',\n      labelWidth: 100,\n      items: {\n        type: 'object',\n        properties: {\n          input1: {\n            title: '简单输入框',\n            type: 'string',\n            required: true,\n          },\n          input2: {\n            title: '简单输入框2',\n            type: 'string',\n          },\n          input3: {\n            title: '简单输入框3',\n            type: 'string',\n          },\n          select1: {\n            title: '单选',\n            type: 'string',\n            enum: ['a', 'b', 'c'],\n            enumNames: ['早', '中', '晚'],\n            widget: 'select'\n          }\n        }\n      }\n    }\n  }\n};\n\nexport default () => {\n  const form = useForm();\n  return <FormRender schema={schema} form={form} />;\n};\n```\n\n### VirtualList\n\n用于展示每行只有 3 - n 个简单元素的情况，数据量大时使用滚动加载\n\n#### virtualList：标签换行\n\n```jsx\nimport React from 'react';\nimport FormRender, { useForm } from 'form-render';\nconst schema = {\n  type: 'object',\n  displayType: 'row',\n  properties: {\n    list: {\n      title: '对象数组',\n      description: '对象数组嵌套功能',\n      type: 'array',\n      widget: 'virtualList',\n      items: {\n        type: 'object',\n        properties: {\n          input1: {\n            title: '简单输入框',\n            type: 'string',\n            required: true,\n          },\n          input2: {\n            title: '简单输入框2',\n            type: 'string',\n          },\n          input3: {\n            title: '简单输入框3',\n            type: 'string',\n          },\n          select1: {\n            title: '单选',\n            type: 'string',\n            enum: ['a', 'b', 'c'],\n            enumNames: ['早', '中', '晚'],\n            widget: 'select',\n          },\n        },\n      },\n    },\n  },\n};\n\nexport default () => {\n  const form = useForm();\n  return <FormRender schema={schema} form={form} />;\n};\n```\n\n#### virtualList：标签内联\n\n```jsx\nimport React from 'react';\nimport FormRender, { useForm } from 'form-render';\nconst schema = {\n  type: 'object',\n  displayType: 'row',\n  properties: {\n    list: {\n      title: '对象数组',\n      tooltip: '对象数组嵌套功能',\n      type: 'array',\n      widget: 'virtualList',\n      display: 'inline',\n      labelWidth: 100,\n      items: {\n        type: 'object',\n        properties: {\n          input1: {\n            title: '简单输入框',\n            type: 'string',\n            required: true,\n          },\n          input2: {\n            title: '简单输入框2',\n            type: 'string',\n          },\n          input3: {\n            title: '简单输入框3',\n            type: 'string',\n          },\n          select1: {\n            title: '单选',\n            type: 'string',\n            enum: ['a', 'b', 'c'],\n            enumNames: ['早', '中', '晚'],\n            widget: 'select',\n          },\n        },\n      },\n    },\n  },\n};\n\nexport default () => {\n  const form = useForm();\n  return <FormRender schema={schema} form={form} />;\n};\n```\n\n### TabList\n\n用于多标签页展示表单的情况\n\n#### tabList：标签换行\n\n```jsx\nimport React from 'react';\nimport FormRender, { useForm } from 'form-render';\nconst schema = {\n  type: 'object',\n  displayType: 'row',\n  properties: {\n    list: {\n      title: '对象数组',\n      description: '对象数组嵌套功能',\n      type: 'array',\n      widget: 'tabList',\n      items: {\n        type: 'object',\n        properties: {\n          input1: {\n            title: '简单输入框',\n            type: 'string',\n            required: true,\n          },\n          input2: {\n            title: '简单输入框2',\n            type: 'string',\n          },\n          input3: {\n            title: '简单输入框3',\n            type: 'string',\n          },\n          select1: {\n            title: '单选',\n            type: 'string',\n            enum: ['a', 'b', 'c'],\n            enumNames: ['早', '中', '晚'],\n            widget: 'select',\n          },\n        },\n      },\n    },\n  },\n};\n\nexport default () => {\n  const form = useForm();\n  return <FormRender schema={schema} form={form} />;\n};\n```\n\n#### tabList：标签内联\n\n```jsx\nimport React from 'react';\nimport FormRender, { useForm } from 'form-render';\nconst schema = {\n  type: 'object',\n  displayType: 'row',\n  properties: {\n    list: {\n      title: '对象数组',\n      tooltip: '对象数组嵌套功能',\n      type: 'array',\n      widget: 'tabList',\n      display: 'inline',\n      labelWidth: 100,\n      items: {\n        type: 'object',\n        properties: {\n          input1: {\n            title: '简单输入框',\n            type: 'string',\n            required: true,\n          },\n          input2: {\n            title: '简单输入框2',\n            type: 'string',\n          },\n          input3: {\n            title: '简单输入框3',\n            type: 'string',\n          },\n          select1: {\n            title: '单选',\n            type: 'string',\n            enum: ['a', 'b', 'c'],\n            enumNames: ['早', '中', '晚'],\n            widget: 'select',\n          },\n        },\n      },\n    },\n  },\n};\n\nexport default () => {\n  const form = useForm();\n  return <FormRender schema={schema} form={form} />;\n};\n```\n"
  },
  {
    "path": "docs/form-render/faq.md",
    "content": "---\norder: 1\ntoc: content\nmobile: false\ngroup: \n  title: 其他\n  order: 5\n---\n\n\n\n<!-- ---\norder: 11\ntoc: false\n--- -->\n# 常见问题\n\n##### 1. 只读模式下，默认的渲染不能满足要求我想定制怎么办？\n\n参见[只读模式下的自定义组件](/form-render/advanced-widget#只读模式下的自定义组件)\n\n##### 2. 我试着使用 form.setValues, 但是被 set 的值还是空的？\n\nform-render 有生命周期的概念，请在 onMount 这个钩子里 set。\n\n##### 3. type 为 object 类型自定义组件没有接收到 value 与 onChange 属性 \n\n例如下面这种 Schema 结构的自定义组件，2.x 会判定它是容器组件并非表单控件，那如何解决这种误判呢？增加一个 widgetType: 'field'\n```js\n{\n  type: \"object\",\n  widget: \"WidgetObj\",\n  properties: {\n    name: {\n      title: \"name\",\n      type: \"string\",\n    }\n  }\n}\n```"
  },
  {
    "path": "docs/form-render/index.md",
    "content": "---\norder: 0\ntoc: content\ntitle: 开始使用\nmobile: false\n---\n\n<div style=\"display:flex;align-items:center;margin-bottom:24px\">\n  <img src=\"https://img.alicdn.com/tfs/TB17UtINiLaK1RjSZFxXXamPFXa-606-643.png\" alt=\"logo\" width=\"48px\"/>\n  <span style=\"font-size:30px;font-weight:600;display:inline-block;margin-left:12px\">FormRender</span>\n</div>\n<p style=\"display:flex;justify-content:space-between;width:440px\">\n  <a href=\"https://www.npmjs.com/package/form-render?_blank\">\n    <img alt=\"npm\" src=\"https://img.shields.io/npm/v/form-render.svg?maxAge=3600&style=flat-square\">\n  </a>\n  <a href=\"https://npmjs.org/package/form-render\">\n    <img alt=\"NPM downloads\" src=\"https://img.shields.io/npm/dm/form-render.svg?style=flat-square\">\n  </a>\n  <a href=\"https://npmjs.org/package/form-render\">\n    <img alt=\"NPM all downloads\" src=\"https://img.shields.io/npm/dt/form-render.svg?style=flat-square\">\n  </a>\n  <a>\n    <img alt=\"PRs Welcome\" src=\"https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square\">\n  </a>\n</p>\n\n中后台表单解决方案，通过 JsonSchema 协议渲染表单\n\n\n```shell\nnpm i form-render --save\n```\n## 使用方式\n\n**函数组件**\n\n使用 `useForm` hooks 创建 form 实例。\n```jsx\n/**\n * transform: true\n * defaultShowCode: true\n */\nimport React from 'react';\nimport FormRender, { useForm } from 'form-render';\nimport schema from './schema/simple';\n\nexport default () => {\n  const form = useForm();\n\n  const onFinish = (formData) => {\n    console.log('formData:', formData);\n  };\n\n  return (\n    <FormRender \n      form={form} \n      schema={schema} \n      onFinish={onFinish} \n      maxWidth={360} \n      footer={true}\n    />\n  );\n}\n```\n\n**类组件**\n\n对于使用类组件的同学，可以使用 `connectForm` 替代 `useForm` hooks。\n\n```jsx\n/**\n * transform: true\n * defaultShowCode: true\n */\nimport React from 'react';\nimport FormRender, { connectForm } from 'form-render';\nimport schema from './schema/simple';\n\nclass Demo extends React.Component {\n  onFinish = (formData) => {\n    console.log('formData:', formData);\n  };\n\n  render() {\n    const { form } = this.props;\n    return (\n      <FormRender \n        form={form} \n        schema={schema} \n        onFinish={this.onFinish} \n        maxWidth={360} \n        footer={true} \n      />\n    );\n  }\n}\n\nexport default connectForm(Demo);\n```\n## 速写 Schema\n\n对于初学者来说记住 schema 所有的字段和使用方式并非易事。为了让大家能够快速上手，建议以以下的顺序尝试。\n\n1. 去 <a href=\"https://xrender.fun/playground\" target='_black'>Playground</a> 逛逛，那里有从基础玩法、高级功能到完整样例的所有 schema 样例。\n2. 玩转一下 <a href=\"https://xrender.fun/schema-builder\" target='_black'>表单设计器</a>，拖拖拽拽导出 schema，丢到代码里生成可用表单。本质上这是一个可视化的表单生成器，支持 schema 的导入 & 导出。\n\n<div>\n  <img src=\"https://gw.alipayobjects.com/mdn/rms_e18934/afts/img/A*4QYNTbKU6xAAAAAAAAAAAABkARQnAQ?raw=true\" width=\"80%\"/>\n</div>"
  },
  {
    "path": "docs/form-render/migrate.md",
    "content": "---\norder: 0\ntoc: content\nmobile: false\nhide: true\ngroup: \n  title: 其他\n  order: 5\n---\n\n# V2 升级方案\n本文档将帮助你从 1.x 升级到 2.x 版本，2.x 将不再对 0.x 版本进行兼容\n\n## 特性\n\n全新的 form-render 2.0 主要具备以下特性：\n\n- 🚀 **更好的表单性能**：通过对 form-render 重构，底层接入 Antd Form 来实现表单的数据收集、校验等逻辑，提升表单的整体性能。有效的解决了表单数据改变表单全局刷新的问题。\n- 🎨 **全新的UI样式**：通过对中后台表单业务梳理，定制了一套全新的 UI 样式和交互规范，提升表单整体美观度 。[最佳展示](/form-render/display-row)\n- 🚥 **国际化**：国际化多语言支持，内置中英文语言包，英文版 locale: 'en-US'\n- 💎 **Antd V5**：兼容 antd V5 版本，无需配置\n\n## 依赖升级\n```diff\n\"dependencies\": {\n- \"form-render\": \"^1.0.0\",\n+ \"form-render\": \"^2.0.0\",\n}\n```\n\n## 不兼容处理\n\n### form.formData 弃用\nform.formData 弃用，改用 form.getValues() 方式获取\n\n```diff\n- form.formData\n+ form.getValues()\n```\n\n## Schema 相关\n\n### theme 弃用\n嵌套组件 theme 字段 弃用，统一改成 widget 声明。\n默认是 widget: 'collapse' 折叠卡片，其他类型参考 [表单布局](/form-render/advanced-layout) 示例\n\n### description 不再自动调整样式\n在 1.x 中 description 在 displayType 为 `row` 的情况下，会自动转换为 tooltip。在 2.x 中不再自动转换，需要手动调整\n\n```diff\n{\n  displayType: 'row',\n- description: 'xxx',\n+ tooltip: 'xxx'\n}\n```\n\n### bind 会在 `form.setValues`、`form.getValues` 以及 `onFinish` 生效\n\n在 2.x 中 bind 会始终生效，你应该始终使用 bind 之后的 formData。\n\n### required 不支持统一设置\n\n你需要在每个 item 中指定 `required`\n\n```diff\n{\n- required: [\n-   'input',\n- ],\n  properties: {\n    input: {\n      title: '输入框',\n      type: 'string',\n+     required: true,\n    }\n  }\n}\n\n```\n\n### validator 直接返回布尔或对象\n\n2.x 中 validator 可以直接返回布尔，或者对象用于动态设置 message 内容\n\n```diff\n{\n  validator: (_, value) => {\n-   return Promise.reject('xxx')\n+   return {\n+     status: false,\n+     message: 'xxx',\n+   }\n\n-   return Promise.resolve()\n+   return true\n  },\n}\n\n```\n\n## Form 相关\n\n### useForm 入参移除\n```js\n{\n  formData,\n  onChange,\n  onValidate,\n  showValidate,\n  /** 数据分析接口，表单展示完成渲染时触发 */\n  logOnMount: _logOnMount,\n  /** 数据分析接口，表单提交成功时触发，获得本次表单填写的总时长 */\n  logOnSubmit: _logOnSubmit,\n} \n```\nlogOnMount、logOnSubmit 通过 props 传递，其他几个废弃\n\n注意 logOnMount、logOnSubmit 不再兼容从上获取 window.FR_LOGGER\n\n\n\n### onFinish 提交函数\n只有校验通过 onFinish 才会被触发，不在返回错误信息参数，为了兼容1.0版本，错误信息默认返回 []\n\n\n```diff\n- const onFinish = (data, errors) => {\n\n}\n\n+ const onFinish = (data) => {\n\n}\n\n```\n### validateFields\nerrorInfo 的出参名称发生变更\n\n```diff\nvalidateFields()\n  .then(values => {\n    /*\n    values:{\n      input1: 'input1 输入的值'\n    }\n    */\n  })\n  .catch(errorInfo => {\n    /*\n    errorInfo:\n      {\n-       data: {\n-         input1: 'input1 输入的值',\n-        },\n\n+        values: {\n+          input1: 'input1 输入的值',\n+        },\n\n\n-        errors: [\n-          { name: 'input1', error: ['input1 的error信息'] },\n-       ]\n+        errorFields: [\n+          { name: ['password'], errors: ['Please input your Password!'] },\n+        ],\n\n      }\n    */\n  });\n  \n```\n\n### globalProps 变更\n通过`globalProps`注入的数据在任何组件（以及自定义组件）中可以被取到和使用。\n变更：自定义组件 通过 props.addons.globalProps 然后进行解构，不在自动进行属性合并注入到自定义组件中（防止其他组件被污染）\n\n\n### mapping 弃用\nmapping 配置弃用，映射关系通过 widgets 组件实例进行覆盖（如果和内部名字一样会覆盖内部组件）\n```json\ninput,\ncheckbox, // 勾选框\ncheckboxes, // checkbox多选\ncolor,\ndate,\ntime,\ndateRange,\ntimeRange,\nimageInput,\nurl,\nselect,\nmultiSelect, // 下拉多选\nnumber,\nradio, // Radio.Group)\nslider, // 带滚条的number\nswitch,\ntextarea,\nupload,\nhtml,\nrate,\ntreeSelect,\nerrorSchemaWidget // 错误显示\n```\n\n### Watch 不再监听 set 事件\n\n在 2.x 中，`watch` 不再监听由于 `setValues` 和 `setValueByPath` 导致的数据变化。你应该在执行 set 事件的同时执原本的监听事件。\n"
  },
  {
    "path": "docs/form-render/schema/baseControl.ts",
    "content": "export default {\n  type: 'object',\n  displayType: 'row',\n  properties: {\n    void1: {\n      title: '常用组件',\n      type: 'void',\n      widget: 'voidTitle'\n    },\n    input1: {\n      title: '输入框',\n      type: 'string',\n      widget: 'input'\n    },\n    number1: {\n      title: '数字输入框',\n      type: 'number',\n      widget: 'inputNumber'\n    },\n    select1: {\n      title: '下拉单选',\n      type: 'string',\n      widget: 'select',\n      props: {\n        options: [\n          { label: '早', value: 'a' },\n          { label: '中', value: 'b' },\n          { label: '晚', value: 'c' }\n        ]\n      }\n    },\n    multiSelect1: {\n      title: '多选',\n      type: 'array',\n      widget: 'multiSelect',\n      description: '下拉多选',\n      props: {\n        options: [\n          { label: '杭州', value: 'a' },\n          { label: '武汉', value: 'b' },\n          { label: '湖州', value: 'c' },\n          { label: '贵阳', value: 'd' }\n        ]\n      }\n    },\n    radio1: {\n      title: '点击单选',\n      type: 'string',\n      widget: 'radio',\n      props: {\n        options: [\n          { label: '早', value: 'a' },\n          { label: '中', value: 'b' },\n          { label: '晚', value: 'c' }\n        ]\n      }\n    },\n    checkboxes1: {\n      title: '点击多选',\n      type: 'array',\n      widget: 'checkboxes',\n      props: {\n        options: [\n          { label: '杭州', value: 'a' },\n          { label: '武汉', value: 'b' },\n          { label: '湖州', value: 'c' },\n          { label: '贵阳', value: 'd' }\n        ]\n      }\n    },\n    textarea1: {\n      title: '长文本',\n      type: 'string',\n      widget: 'textArea'\n    },\n    date1: {\n      title: '日期选择',\n      type: 'string',\n      widget: 'datePicker'\n    },\n    dateRange1: {\n      title: '日期范围',\n      type: 'range',\n      widget: 'dateRange'\n    },\n    time1: {\n      title: '时间选择',\n      type: 'string',\n      widget: 'timePicker'\n    },\n    timeRange1: {\n      title: '时间范围',\n      type: 'range',\n      widget: 'timeRange'\n    },\n    void2: {\n      title: '其他组件',\n      type: 'void',\n      widget: 'voidTitle'\n    },\n    html1: {\n      title: 'HTML',\n      type: 'string',\n      widget: 'html'\n    },\n    switch1: {\n      title: '开关',\n      type: 'boolean',\n      widget: 'switch'\n    },\n    checkbox1: {\n      title: '是否选择',\n      type: 'boolean',\n      widget: 'checkbox'\n    },\n    slider1: {\n      title: '带滑动条',\n      type: 'number',\n      widget: 'slider'\n    },\n    image1: {\n      title: '图片展示',\n      type: 'string',\n      widget: 'imageInput'\n    },\n    color1: {\n      title: '颜色选择',\n      type: 'string',\n      widget: 'color'\n    },\n    url1: {\n      title: '链接',\n      type: 'string',\n      widget: 'urlInput'\n    }\n  }\n};"
  },
  {
    "path": "docs/form-render/schema/basic.ts",
    "content": "export default {\n  type: 'object',\n  column: 3,\n  displayType: 'row',\n  properties: {\n    input1: {\n      title: 'Field A',\n      type: 'string'\n    },\n    input2: {\n      title: 'Field B',\n      type: 'string'\n    },\n    input3: {\n      title: 'Field C',\n      type: 'string'\n    },\n    input4: {\n      title: 'Field D',\n      type: 'string'\n    }\n  }\n};"
  },
  {
    "path": "docs/form-render/schema/cellSpan.ts",
    "content": "export default {\n  type: 'object',\n  column: 3,\n  displayType: 'row',\n  properties: {\n    input1: {\n      title: 'Field A',\n      type: 'string'\n    },\n    input2: {\n      title: 'Field B',\n      type: 'string'\n    },\n    input3: {\n      title: 'Field C',\n      type: 'string'\n    },\n    input4: {\n      title: 'Field D',\n      type: 'string',\n      cellSpan: 2\n    },\n    input5: {\n      title: 'Field E',\n      type: 'string'\n    }\n  }\n};"
  },
  {
    "path": "docs/form-render/schema/schema.ts",
    "content": "export const basic = {\n  type: 'object',\n  column: 3,\n  displayType: 'row',\n  properties: {\n    input1: {\n      title: 'Input A',\n      type: 'string'\n    },\n    input2: {\n      title: 'Input B',\n      type: 'string'\n    },\n    input3: {\n      title: 'Input C',\n      type: 'string'\n    },\n    input4: {\n      title: 'Input D',\n      type: 'string'\n    }\n  }\n};\n\nexport const expression = {\n  type: 'object',\n  displayType: 'column',\n  properties: {\n    input: {\n      title: '{{formData.config.title || \"输入框\"}}',\n      type: 'string',\n      placeholder: '{{formData.config.placeholder}}',\n      props: {\n        size: '{{formData.config.size}}',\n      },\n      hidden: '{{formData.config.hidden === true}}',\n      readOnly: '{{formData.config.readOnly === true}}',\n      disabled: '{{formData.config.disabled === true}}',\n    },\n    rate: {\n      title: 'rate',\n      type: 'number',\n      widget: 'rate',\n    },\n    config: {\n      title: '配置',\n      type: 'object',\n      properties: {\n        title: {\n          title: 'title',\n          type: 'string',\n        },\n        placeholder: {\n          title: 'placeholder',\n          type: 'string',\n        },\n        size: {\n          title: 'input大小',\n          type: 'string',\n          enum: ['large', 'middle', 'small'],\n          enumNames: ['大', '中', '小'],\n          widget: 'radio',\n        },\n        hidden: {\n          title: '是否隐藏',\n          type: 'boolean',\n        },\n        readOnly: {\n          title: '是否只读',\n          type: 'boolean',\n        },\n        disabled: {\n          title: '是否置灰',\n          type: 'boolean',\n        },\n      },\n    },\n  },\n  required: [],\n};\n\nexport const titleTrick = {\n  displayType: 'row',\n  type: 'object',\n  properties: {\n    inputName1: {\n      title: '简单输入框',\n      type: 'string',\n      width: '50%',\n    },\n    desc: {\n      type: 'html',\n      bind: false,\n      default: \"补充说明 <span style='color:red'>hello<span>\",\n      width: '50%',\n    },\n  },\n};\n\n\n"
  },
  {
    "path": "docs/form-render/schema/simple.ts",
    "content": "export default {\n  type: 'object',\n  properties: {\n    input: {\n      title: '输入框',\n      type: 'string',\n      widget: 'input'\n    },\n    select: {\n      title: '下拉框',\n      type: 'string',\n      widget: 'select',\n      props: {\n        options: [\n          { label: '早', value: 'a' },\n          { label: '中', value: 'b' },\n          { label: '晚', value: 'c' }\n        ]\n      }\n    }\n  }\n};\n"
  },
  {
    "path": "docs/form-render/schema/span.ts",
    "content": "export default {\n  type: 'object',\n  displayType: 'row',\n  properties: {\n    input1: {\n      title: 'Field A',\n      type: 'string',\n      span: 8\n    },\n    input2: {\n      title: 'Field B',\n      type: 'string',\n      span: 8\n    },\n    input3: {\n      title: 'Field C',\n      type: 'string',\n      span: 8\n    },\n    input11: {\n      title: 'Field A',\n      type: 'string',\n      span: 8\n    },\n    input12: {\n      title: 'Field B',\n      type: 'string',\n      span: 8\n    },\n    input4: {\n      title: 'Field D',\n      type: 'string',\n    },\n    input5: {\n      title: 'Field E',\n      type: 'string'\n    }\n  }\n};"
  },
  {
    "path": "docs/form-render/test/bigJson.tsx",
    "content": "import React, { useEffect } from 'react';\nimport FormRender, { useForm } from 'form-render';\nimport { Button, Input } from 'antd';\nimport { get } from 'lodash';\n\nconst schema = {\n  type: 'object',\n  displayType: 'row',\n  properties: {\n    list: {\n      title: '对象数组',\n      description: '对象数组嵌套功能',\n      type: 'array',\n      widget: 'cardList',\n      items: {\n        type: 'object',\n        title: '卡片主题',\n        description: '这是一个对象类型',\n        column: 3,\n        widget: 'card',\n        properties: {\n          input1: {\n            title: '输入框 A',\n            type: 'string',\n          }\n        }\n      }\n    }\n    // obj: {\n    //   type: 'object',\n    //   title: '卡片主题',\n    //   description: '这是一个对象类型',\n    //   widget: 'collapse',\n    //   column: 3,\n    //   properties: {\n    //     list: {\n    //       title: '对象数组',\n    //       description: '对象数组嵌套功能',\n    //       type: 'array',\n    //       widget: 'cardList',\n    //       items: {\n    //         type: 'object',\n    //         title: '卡片主题',\n    //         description: '这是一个对象类型',\n    //         column: 3,\n    //         widget: 'card',\n    //         properties: {\n    //           input1: {\n    //             title: '输入框 A',\n    //             type: 'string',\n    //           }\n    //         }\n    //       }\n    //     }\n    //   }\n    // }\n  }\n};\n\nconst getTest = (schema: any) => {\n\n  for (let i=0; i< 500; i++) {\n    schema.properties.list.items.properties[i] = {\n      title: '输入框 A',\n      type: 'string',\n    };\n    // schema.properties[i] = {\n    //   title: '输入框 A',\n    //   type: 'string',\n    // }\n  }\n  return schema;\n}\n\n\nexport default () => {\n  const form = useForm();\n\n  const _schema = getTest(schema);\n  return (\n    <>\n      <FormRender \n        form={form} \n        schema={_schema}\n        footer={true}\n        onMount={() => {\n          // setTimeout(() => {\n          //   form.setValueByPath('list', [{ 0: 'xxxxx'}, { 1: 'xxxxx'}, { 2: 'xxxxx'},])\n          // }, 1000* 5)\n        }}\n      />\n    </>\n  );\n}"
  },
  {
    "path": "docs/form-render/test/bind.tsx",
    "content": "import React from 'react';\nimport FormRender, { useForm } from 'form-render';\n\nconst schema = {\n  type: 'object',\n  displayType: 'row',\n  labelWidth: 100,\n  maxWidth: 300,\n  properties: {\n    dateRange: {\n      title: '日期范围',\n      type: 'range',\n      format: 'date',\n      bind: ['startDate', 'endDate']\n    },\n    active: {\n      title: '活动模版',\n      type: 'array',\n      widget: 'simpleList',\n      items: {\n        type: 'object',\n        properties: {\n          acitveItem: {\n            type: 'string',\n            title: '活动',\n            bind: 'root'\n          }\n        }\n      }\n    },\n    obj: {\n      type: 'object',\n      title: '卡片主题',\n      description: '这是一个对象类型',\n      widget: 'collapse',\n      column: 3,\n      properties: {\n        dateRange: {\n          bind: ['obj.startDate', 'obj.endDate'],\n          title: '日期范围',\n          type: 'range',\n          format: 'date',\n        }\n      }\n    },\n    list: {\n      title: '活动模版',\n      type: 'array',\n      widget: 'cardList',\n      items: {\n        type: 'object',\n        properties: {\n          obj: {\n            type: 'object',\n            title: '卡片主题',\n            description: '这是一个对象类型',\n            widget: 'collapse',\n            column: 3,\n            properties: {\n              dateRange: {\n                bind: ['obj.startDate', 'obj.endDate'],\n                title: '日期范围',\n                type: 'range',\n                format: 'date',\n              },\n              list: {\n                title: '活动模版',\n                type: 'array',\n                widget: 'cardList',\n                items: {\n                  type: 'object',\n                  properties: {\n                    select: {\n                      title: '下拉框',\n                      type: 'string',\n                      bind: 'root'\n                    }\n                  }\n                }\n              }\n            }\n          },\n        }\n      }\n    }\n  },\n};\n\n\n\nexport default () => {\n  const form = useForm();\n\n  const onFinish = (data) => {\n   console.log(data, '-----data')\n  };\n\n  return (\n    <FormRender \n      form={form} \n      schema={schema} \n      onFinish={onFinish} \n      footer={true}\n      onMount={() => {\n        form.setValues({ active: ['1', '2', '3'], list: [{ obj: { startDate: '2023-04-12', endDate: '2023-04-15', list: ['1']}}], obj: { startDate: '2023-04-12', endDate: '2023-04-15'} })\n      }}\n    />\n  );\n}\n\n"
  },
  {
    "path": "docs/form-render/test/dependencies.tsx",
    "content": "import React from 'react';\nimport { Input, } from 'antd';\nimport FormRender, { useForm } from 'form-render';\n\nconst { TextArea } = Input;\n\nconst CustomTextArea = props => {\n  const { dependValues } = props;\n\n  console.log(dependValues, 'dependValues');\n\n  return <TextArea rows={dependValues?.[0] || 2} />;\n};\n\nexport default () => {\n  const form = useForm();\n\n  const schema = {\n  type: 'object',\n  displayType: 'row',\n  properties: {\n    list: {\n      // title: '对象数组',\n      // description: '对象数组嵌套功能',\n      type: 'array',\n      widget: 'cardList',\n      items: {\n        type: 'object',\n        title: '卡片主题',\n        description: '这是一个对象类型',\n        column: 3,\n        properties: {\n          input1: {\n            title: '密码',\n            type: 'string',\n            required: true,\n          },\n          input2: {\n            title: '确认密码',\n            type: 'string',\n            dependencies: ['list[].list[].input1'],\n            required: true,\n            rules: [\n              { \n                validator: (_, value, { form }) => {\n                  if (!value || form.getFieldValue(['list', 0, 'input1']) === value) {\n                    return true;\n                  }\n                  return false;\n                }, \n                message: '你输入的两个密码不匹配' \n              }\n            ]\n          },\n          list: {\n            // title: '对象数组',\n            // description: '对象数组嵌套功能',\n            type: 'array',\n            widget: 'cardList',\n            items: {\n              type: 'object',\n              title: '卡片主题',\n              description: '这是一个对象类型',\n              column: 3,\n              properties: {\n                input1: {\n                  title: '密码',\n                  type: 'string',\n                  required: true,\n                },\n                input2: {\n                  title: '确认密码',\n                  type: 'string',\n                  dependencies: ['list[].input1'],\n                  required: true,\n                  rules: [\n                    { \n                      validator: (_, value, { form }) => {\n                        if (!value || form.getFieldValue(['list', 0, 'input1']) === value) {\n                          return true;\n                        }\n                        return false;\n                      }, \n                      message: '你输入的两个密码不匹配' \n                    }\n                  ]\n                }\n              },\n            },\n          },\n        },\n      },\n    },\n    // obj: {\n    //   type: 'object',\n    //   title: '卡片主题',\n    //   description: '这是一个对象类型',\n    //   widget: 'collapse',\n    //   column: 3,\n    //   properties: {\n    //     input1: {\n    //       title: '密码',\n    //       type: 'string',\n    //       required: true,\n    //     },\n    //     input2: {\n    //       title: '确认密码',\n    //       type: 'string',\n    //       dependencies: [['obj', 'input1']],\n    //       required: true,\n    //       rules: [\n    //         { \n    //           validator: (_, value, { form }) => {\n    //             debugger;\n    //             if (!value || form.getFieldValue(['obj', 'input1']) === value) {\n    //               return true;\n    //             }\n    //             return false;\n    //           }, \n    //           message: '你输入的两个密码不匹配' \n    //         }\n    //       ]\n    //     }\n    //   }\n    // }\n    \n  }\n};\n\n  return (\n    <FormRender\n      form={form}\n      schema={schema}\n      widgets={{ CustomTextArea }}\n      labelWidth={200}\n      maxWidth={400}\n    />\n  );\n}"
  },
  {
    "path": "docs/form-render/test/disaply-column.md",
    "content": "<!-- ---\norder: 3\ntoc: content\nmobile: false\ngroup: \n  title: 最佳示例\n  order: 2\n---\n\n# 纵向布局\n\n## 基础控件\n\n```jsx\nimport React from 'react';\nimport FormRender, { useForm } from 'form-render';\n\nconst schema = {\n  type: 'object',\n  displayType: 'column',\n  column: 3,\n  properties: {\n    input1: {\n      title: '输入框',\n      type: 'string',\n      props: {},\n    },\n    number1: {\n      title: '数字输入框',\n      type: 'number',\n    },\n    switch1: {\n      title: '是否选择',\n      type: 'boolean',\n      widget: 'switch',\n    },\n    select1: {\n      title: '下拉单选',\n      type: 'string',\n      widget: 'select',\n      props: {\n        options: [\n          { label: '早', value: 'a' },\n          { label: '中', value: 'b' },\n          { label: '晚', value: 'c' },\n        ],\n      },\n    },\n    multiSelect1: {\n      title: '多选',\n      description: '下拉多选',\n      type: 'array',\n      items: {\n        type: 'string',\n      },\n      widget: 'multiSelect',\n      props: {\n        options: [\n          { label: '杭州', value: 'a' },\n          { label: '武汉', value: 'b' },\n          { label: '湖州', value: 'c' },\n          { label: '贵阳', value: 'd' },\n        ],\n      },\n    },\n    radio1: {\n      title: '点击单选',\n      type: 'string',\n      widget: 'radio',\n      props: {\n        options: [\n          { label: '早', value: 'a' },\n          { label: '中', value: 'b' },\n          { label: '晚', value: 'c' },\n        ],\n      },\n    },\n    checkboxes1: {\n      title: '点击多选',\n      type: 'array',\n      widget: 'checkboxes',\n      items: {\n        type: 'string',\n      },\n      props: {\n        options: [\n          { label: '杭州', value: 'a' },\n          { label: '武汉', value: 'b' },\n          { label: '湖州', value: 'c' },\n          { label: '贵阳', value: 'd' },\n        ],\n      },\n    },\n    textarea1: {\n      title: '长文本',\n      type: 'string',\n      format: 'textarea',\n      props: {},\n    },\n    html_1: {\n      title: 'HTML',\n      type: 'string',\n      widget: 'html',\n      props: {},\n    },\n    date1: {\n      title: '日期选择',\n      type: 'string',\n      format: 'date',\n    },\n    dateRange1: {\n      title: '日期范围',\n      type: 'range',\n      format: 'dateTime',\n    },\n    time1: {\n      title: '时间选择',\n      type: 'string',\n      format: 'time',\n    },\n    timeRange1: {\n      title: '时间范围',\n      type: 'range',\n      format: 'time',\n    },\n    checkbox1: {\n      title: '是否选择',\n      type: 'boolean',\n      widget: 'checkbox',\n    },\n    slider1: {\n      title: '带滑动条',\n      type: 'number',\n      widget: 'slider',\n    },\n    image1: {\n      title: '图片展示',\n      type: 'string',\n      format: 'image',\n    },\n    color1: {\n      title: '颜色选择',\n      type: 'string',\n      format: 'color',\n    },\n  },\n};\n\nexport default () => {\n  const form = useForm();\n\n  return <FormRender schema={schema} form={form} />;\n};\n```\n\n## 嵌套控件\n\n对于嵌套类型的表单，我们内置了四种主题，分别为 collapse | card | tile | flex, 默认为 collapse 主题\n\n### 折叠 collapse\n\n```jsx\nimport React from 'react';\nimport FormRender, { useForm } from 'form-render';\n\nconst schema = {\n  type: 'object',\n  displayType: 'column',\n  properties: {\n    obj: {\n      type: 'object',\n      title: '卡片主题',\n      description: '这是一个对象类型',\n      widget: 'collapse',\n      column: 3,\n      properties: {\n        input1: {\n          title: '输入框 A',\n          type: 'string',\n        },\n        input2: {\n          title: '输入框 B',\n          type: 'string',\n        },\n        input3: {\n          title: '输入框 C',\n          type: 'string',\n        },\n        input4: {\n          title: '输入框 D',\n          type: 'string',\n        },\n      },\n    },\n  },\n};\n\nexport default () => {\n  const form = useForm();\n\n  return <FormRender schema={schema} form={form} />;\n};\n```\n\n### 卡片 card\n\n```jsx\nimport React from 'react';\nimport FormRender, { useForm } from 'form-render';\n\nconst schema = {\n  type: 'object',\n  displayType: 'column',\n  properties: {\n    obj: {\n      type: 'object',\n      widget: 'card',\n      title: '卡片主题',\n      description: '这是一个对象类型',\n      column: 3,\n      properties: {\n        input1: {\n          title: '输入框 A',\n          type: 'string',\n        },\n        input2: {\n          title: '输入框 B',\n          type: 'string',\n        },\n        input3: {\n          title: '输入框 C',\n          type: 'string',\n        },\n        input4: {\n          title: '输入框 D',\n          type: 'string',\n        },\n      },\n    },\n  },\n};\n\nexport default () => {\n  const form = useForm();\n\n  return <FormRender schema={schema} form={form} />;\n};\n```\n\n### 标题线 lineTitle\n\n```jsx\nimport React from 'react';\nimport FormRender, { useForm } from 'form-render';\n\nconst schema = {\n  type: 'object',\n  displayType: 'column',\n  properties: {\n    obj: {\n      type: 'object',\n      title: '卡片主题',\n      widget: 'lineTitle',\n      description: '这是一个对象类型',\n      column: 3,\n      properties: {\n        input1: {\n          title: '输入框 A',\n          type: 'string',\n        },\n        input2: {\n          title: '输入框 B',\n          type: 'string',\n        },\n        input3: {\n          title: '输入框 C',\n          type: 'string',\n        },\n        input4: {\n          title: '输入框 D',\n          type: 'string',\n        },\n      },\n    },\n  },\n};\n\nexport default () => {\n  const form = useForm();\n\n  return <FormRender schema={schema} form={form} />;\n};\n```\n\n### 内联 subInline\n\n```jsx\nimport React from 'react';\nimport FormRender, { useForm } from 'form-render';\n\nconst schema = {\n  type: 'object',\n  displayType: 'column',\n  properties: {\n    obj: {\n      type: 'object',\n      title: '卡片主题',\n      widget: 'subInline',\n      description: '这是一个对象类型',\n      column: 3,\n      properties: {\n        input1: {\n          title: '输入框 A',\n          type: 'string',\n        },\n        input2: {\n          title: '输入框 B',\n          type: 'string',\n        },\n        input3: {\n          title: '输入框 C',\n          type: 'string',\n        },\n        input4: {\n          title: '输入框 D',\n          type: 'string',\n        },\n      },\n    },\n  },\n};\n\nexport default () => {\n  const form = useForm();\n\n  return <FormRender schema={schema} form={form} />;\n};\n```\n\n## 列表控件\n\n列表的展示对于简单需求占位太多，复杂需求定制不够一直是痛点。所以我们给出了 5 种展示，充分满足从极简到复杂的所有需求。\n默认使用 widget: 'cardList'，卡片类型\n\n### SimpleList\n\n用于展示每行只有 1-3 个简单控件的情况，紧凑排列\n\n#### simpleList：标签换行\n\n```jsx\nimport React from 'react';\nimport FormRender, { useForm } from 'form-render';\n\nconst schema = {\n  type: 'object',\n  displayType: 'column',\n  properties: {\n    list: {\n      title: '活动模版',\n      type: 'array',\n      widget: 'simpleList',\n      items: {\n        type: 'object',\n        properties: {\n          input1: {\n            title: '输入框 A',\n            type: 'string',\n          },\n          input2: {\n            title: '输入框 B',\n            type: 'string',\n          },\n          input3: {\n            title: '输入框 C',\n            type: 'string',\n          },\n        },\n      },\n    },\n  },\n};\n\nexport default () => {\n  const form = useForm();\n\n  return <FormRender schema={schema} form={form} />;\n};\n```\n\n#### simpleList：标签换行-带背景\n\n```jsx\nimport React from 'react';\nimport FormRender, { useForm } from 'form-render';\n\nconst schema = {\n  type: 'object',\n  displayType: 'column',\n  properties: {\n    list: {\n      title: '活动模版',\n      type: 'array',\n      widget: 'simpleList',\n      props: {\n        hasBackground: true,\n      },\n      items: {\n        type: 'object',\n        properties: {\n          input1: {\n            title: '输入框 A',\n            type: 'string',\n          },\n          input2: {\n            title: '输入框 B',\n            type: 'string',\n          },\n          input3: {\n            title: '输入框 C',\n            type: 'string',\n          },\n        },\n      },\n    },\n  },\n};\n\nexport default () => {\n  const form = useForm();\n\n  return <FormRender schema={schema} form={form} />;\n};\n```\n\n### CardList\n\n用于展示结构复杂，但数量不太多的 list\n\n#### cardList：折叠\n\n```jsx\nimport React from 'react';\nimport FormRender, { useForm } from 'form-render';\n\nconst schema = {\n  type: 'object',\n  displayType: 'column',\n  properties: {\n    list: {\n      title: '对象数组',\n      description: '对象数组嵌套功能',\n      type: 'array',\n      widget: 'cardList',\n      items: {\n        type: 'object',\n        title: '卡片主题',\n        description: '这是一个对象类型',\n        column: 3,\n        properties: {\n          input1: {\n            title: '输入框 A',\n            type: 'string',\n          },\n          input2: {\n            title: '输入框 B',\n            type: 'string',\n          },\n          input3: {\n            title: '输入框 B',\n            type: 'string',\n          },\n          input4: {\n            title: '输入框 C',\n            type: 'string',\n          },\n        },\n      },\n    },\n  },\n};\n\nexport default () => {\n  const form = useForm();\n\n  return <FormRender schema={schema} form={form} />;\n};\n```\n\n#### cardList：卡片\n\n```jsx\nimport React from 'react';\nimport FormRender, { useForm } from 'form-render';\n\nconst schema = {\n  type: 'object',\n  displayType: 'column',\n  properties: {\n    list: {\n      title: '对象数组',\n      description: '对象数组嵌套功能',\n      type: 'array',\n      widget: 'cardList',\n      items: {\n        type: 'object',\n        title: '卡片主题',\n        description: '这是一个对象类型',\n        column: 3,\n        widget: 'card',\n        properties: {\n          input1: {\n            title: '输入框 A',\n            type: 'string',\n          },\n          input2: {\n            title: '输入框 B',\n            type: 'string',\n          },\n          input3: {\n            title: '输入框 B',\n            type: 'string',\n          },\n          input4: {\n            title: '输入框 C',\n            type: 'string',\n          },\n        },\n      },\n    },\n  },\n};\n\nexport default () => {\n  const form = useForm();\n\n  return <FormRender schema={schema} form={form} />;\n};\n```\n\n#### cardList：标题线\n\n```jsx\nimport React from 'react';\nimport FormRender, { useForm } from 'form-render';\n\nconst schema = {\n  type: 'object',\n  displayType: 'column',\n  properties: {\n    list: {\n      title: '对象数组',\n      description: '对象数组嵌套功能',\n      type: 'array',\n      widget: 'cardList',\n      items: {\n        type: 'object',\n        title: '卡片主题',\n        description: '这是一个对象类型',\n        column: 3,\n        widget: 'lineTitle',\n        properties: {\n          input1: {\n            title: '输入框 A',\n            type: 'string',\n          },\n          input2: {\n            title: '输入框 B',\n            type: 'string',\n          },\n          input3: {\n            title: '输入框 B',\n            type: 'string',\n          },\n          input4: {\n            title: '输入框 C',\n            type: 'string',\n          },\n        },\n      },\n    },\n  },\n};\n\nexport default () => {\n  const form = useForm();\n\n  return <FormRender schema={schema} form={form} />;\n};\n```\n\n### DrawerList\n\n用于展示存在列表套列表，列表套对象等复杂元素的情况\n\n```jsx\nimport React from 'react';\nimport FormRender, { useForm } from 'form-render';\nconst schema = {\n  type: 'object',\n  displayType: 'column',\n  properties: {\n    list: {\n      title: '对象数组',\n      description: '对象数组嵌套功能',\n      type: 'array',\n      widget: 'drawerList',\n      items: {\n        type: 'object',\n        properties: {\n          input1: {\n            title: '简单输入框',\n            type: 'string',\n            required: true,\n          },\n          input2: {\n            title: '简单输入框2',\n            type: 'string',\n          },\n          input3: {\n            title: '简单输入框3',\n            type: 'string',\n          },\n          select1: {\n            title: '单选',\n            type: 'string',\n            enum: ['a', 'b', 'c'],\n            enumNames: ['早', '中', '晚'],\n            widget: 'select',\n          },\n        },\n      },\n    },\n  },\n};\n\nexport default () => {\n  const form = useForm();\n  return <FormRender schema={schema} form={form} />;\n};\n```\n\n### TableList\n\n用于展示每行只有 3 - n 个简单元素的情况，特别是数据量很大需要分页的\n\n```jsx\nimport React from 'react';\nimport FormRender, { useForm } from 'form-render';\nconst schema = {\n  type: 'object',\n  displayType: 'column',\n  properties: {\n    list: {\n      title: '对象数组',\n      description: '对象数组嵌套功能',\n      type: 'array',\n      widget: 'tableList',\n      items: {\n        type: 'object',\n        properties: {\n          input1: {\n            title: '简单输入框',\n            type: 'string',\n            required: true,\n          },\n          input2: {\n            title: '简单输入框2',\n            type: 'string',\n          },\n          input3: {\n            title: '简单输入框3',\n            type: 'string',\n          },\n          select1: {\n            title: '单选',\n            type: 'string',\n            enum: ['a', 'b', 'c'],\n            enumNames: ['早', '中', '晚'],\n            widget: 'select',\n          },\n        },\n      },\n    },\n  },\n};\n\nexport default () => {\n  const form = useForm();\n  return <FormRender schema={schema} form={form} />;\n};\n```\n\n### VirtualList\n\n用于展示每行只有 3 - n 个简单元素的情况，数据量大时使用滚动加载\n\n```jsx\nimport React from 'react';\nimport FormRender, { useForm } from 'form-render';\nconst schema = {\n  type: 'object',\n  displayType: 'column',\n  properties: {\n    list: {\n      title: '对象数组',\n      description: '对象数组嵌套功能',\n      type: 'array',\n      widget: 'virtualList',\n      props: {\n        scrollY: 400,\n      },\n      items: {\n        type: 'object',\n        properties: {\n          input1: {\n            title: '简单输入框',\n            type: 'string',\n            required: true,\n          },\n          input2: {\n            title: '简单输入框2',\n            type: 'string',\n          },\n          input3: {\n            title: '简单输入框3',\n            type: 'string',\n          },\n          select1: {\n            title: '单选',\n            type: 'string',\n            enum: ['a', 'b', 'c'],\n            enumNames: ['早', '中', '晚'],\n            widget: 'select',\n          },\n        },\n      },\n    },\n  },\n};\n\nexport default () => {\n  const form = useForm();\n  return <FormRender schema={schema} form={form} />;\n};\n```\n\n### TabList\n\n用于多标签页展示表单的情况\n\n```jsx\nimport React from 'react';\nimport FormRender, { useForm } from 'form-render';\nconst schema = {\n  type: 'object',\n  displayType: 'column',\n  properties: {\n    list: {\n      title: '对象数组',\n      description: '对象数组嵌套功能',\n      type: 'array',\n      widget: 'tabList',\n      items: {\n        type: 'object',\n        properties: {\n          input1: {\n            title: '简单输入框',\n            type: 'string',\n            required: true,\n          },\n          input2: {\n            title: '简单输入框2',\n            type: 'string',\n          },\n          input3: {\n            title: '简单输入框3',\n            type: 'string',\n          },\n          select1: {\n            title: '单选',\n            type: 'string',\n            enum: ['a', 'b', 'c'],\n            enumNames: ['早', '中', '晚'],\n            widget: 'select',\n          },\n        },\n      },\n    },\n  },\n};\n\nexport default () => {\n  const form = useForm();\n  return <FormRender schema={schema} form={form} />;\n};\n``` -->\n"
  },
  {
    "path": "docs/form-render/test/removeHidden.tsx",
    "content": "import React from 'react';\nimport FormRender, { useForm } from 'form-render';\n\nconst schema = {\n  type: 'object',\n  properties: {\n    switch1: {\n      title: '禁用输入框',\n      type: 'boolean',\n      widget: 'switch'\n    },\n    input1: {\n      title: '输入框',\n      type: 'string',\n      hidden: '{{ formData.switch1 === true }}'\n    },\n    \n    list: {\n      title: 'List 场景',\n      type: 'array',\n      widget: 'CardList',\n      items: {\n        type: 'object',\n        widget: 'card',\n        title: 'List.Item',\n        properties: {\n          switch1: {\n            title: '隐藏输入框 2 ',\n            type: 'boolean',\n            widget: 'switch'\n          },\n          input1: {\n            title: '输入框 1',\n            type: 'string',\n            description: '给输入框 赋值'\n          },\n          input2: {\n            title: '输入框 2',\n            type: 'string',\n            defaultValue: '{{ rootValue.input1 }}',\n            hidden: '{{ rootValue.switch1 }}'\n          },\n          obj: {\n            type: 'object',\n            title: '卡片主题',\n            description: '这是一个对象类型',\n            widget: 'collapse',\n            column: 3,\n           \n            properties: {\n              input1: {\n                title: '输入框 A',\n                type: 'string',\n                hidden: '{{ rootValue.obj.switchx }}',\n              },\n              input2: {\n                title: '输入框 B',\n                type: 'string',\n              },\n              input3: {\n                title: '输入框 C',\n                type: 'string',\n              },\n              switchx: {\n                title: '隐藏输入框 2 ',\n                type: 'boolean',\n                widget: 'switch'\n              },\n            },\n          },\n          array: {\n            title: 'array 场景',\n            type: 'array',\n            widget: 'CardList',\n            items: {\n              type: 'object',\n              widget: 'card',\n              title: 'List.Item',\n              properties: {\n                switch1: {\n                  title: '隐藏输入框 2 ',\n                  type: 'boolean',\n                  widget: 'switch'\n                },\n                input2: {\n                  title: '输入框 2',\n                  type: 'string',\n                  hidden: '{{ rootValue.switch1 }}'\n                },\n                obj: {\n                  type: 'object',\n                  title: '卡片主题',\n                  description: '这是一个对象类型',\n                  widget: 'collapse',\n                  column: 3,\n                  // hidden: '{{ rootValue.switch1 }}',\n                  properties: {\n                    input1: {\n                      title: '输入框 A',\n                      type: 'string',\n                      hidden: '{{ rootValue.obj.switch }}',\n                    },\n                    switch: {\n                      title: '隐藏输入框A ',\n                      type: 'boolean',\n                      widget: 'switch'\n                    },\n                  },\n                },\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n};\n\n\nexport default () => {\n  const form = useForm();\n\n  const onFinish = (data) => {\n   \n\n   const values = form.getValues();\n   console.log(data, '-----data', values)\n  };\n\n  return (\n    <FormRender \n      form={form} \n      schema={schema} \n      onFinish={onFinish} \n      footer={true}\n      className='xxx'\n      // removeHiddenData={false}\n      onMount={() => {\n        form.setValues({\n          switch1: true,\n          xxx: 1\n        })\n\n        form.setValueByPath('switch1', false);\n      }}\n    />\n  );\n}\n\n"
  },
  {
    "path": "docs/form-render/test/test.md",
    "content": "---\nmobile: false\n---\n\n\n<!-- <code src='./demo/dymic.tsx'></code> -->\n\n<!-- <code src='./demo/defaultChange.tsx'></code> -->\n\n<!-- <code src='./test/bigJson.tsx'></code> -->\n\n<!-- <code src='./test/bind.tsx'></code> -->\n\n<!-- <code src='./test/list.tsx'></code> -->\n\n<!-- <code src='./test/dependencies.tsx'></code> -->\n\n<!-- <code src='./test/removeHidden.tsx'></code> -->"
  },
  {
    "path": "docs/form-render/utils/index.js",
    "content": "export const delay = ms => new Promise(res => setTimeout(res, ms));\n\nexport const fakeApi = (url, data) => {\n  console.group('request:', url);\n  console.log('params:', data);\n  console.groupEnd();\n  return delay(500);\n};\n"
  },
  {
    "path": "docs/form-render-mobile/api.md",
    "content": "---\norder: 1\ntoc: content\ntitle: API\n---\n\n\n# 表单属性\n\n## Props\n\n| <div style=\"width:150px\">参数</div> | 说明                                                                                      | 类型                                                                                                                          | <div style=\"width:100px\">默认值</div> |\n| ----------------------------------- | ----------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------- |\n| schema                              | **必填**，描述表单的 schema，详见 [schema 规范](/docs/form-render/api-schema.md)                  | <a target=\"_blank\" href=\"https://github.com/alibaba/x-render/blob/e2feff8fdb3bef5537b92a2157dbbf40b9d4eb17/packages/form-render/src/type.ts#L32\">SchemaBase</a>                         | -                                     |\n| beforeFinish                        | 在 onFinish 前触发，一般用于外部校验逻辑的回填                                            | `({ data, errors, schema, ...rest }) => Error[] \\| Promise<Error[]>`                                                          | -                                     |\n| configProvider                      | antd 的 configProvider，配置透传                                                          | <a href=\"https://ant-design.antgroup.com/components/config-provider-cn/#API\" target=\"_blank\">ConfigProviderProps</a>                                     | -                                     |\n| className                           | 顶层 className                                                                            | `string`                                                                                                                      | -                                     |\n| displayType                         | 表单元素与 label 同行 or 分两行展示, inline 则整个展示自然顺排                            | `'column' \\| 'row'`                                                                                                           | `'column'`                            |\n| disabled                            | 是否禁用                                                                                  | `boolean`                                                                                                                     | `false`                               | column |\n| form                                | **必填**，`useForm` 创建的表单实例，与 Form 一对一绑定，详见 [FormInstance](#forminstance) | `FormInstance`                                                                                                                | -                                     |\n| footer                              | 表单尾部的内容，常常用来放置提交按钮                                                      | `ReactNode`                                                                                                                   | -                                     |\n| hasFeedback                         | 是否展示错误反馈                                                                          | `boolean`                                                                                                                     | `true`                                |\n| initialValues                       | 表单默认值，只有初始化以及重置时生效                                                      | `object`                                                                                                                      | -                                     |\n| widgets                             | 自定义组件，当内置组件无法满足时使用，详见 <a href=\"/docs/form-render/advanced-widget.md\" target=\"_blank\">Widgets</a>                             | `Record<string, ReactNode>`                                                                                                   | -                                     |\n| watch                               | 监听表单的数据变化，详见 <a href=\"/docs/form-render/advanced-linkage.md#watch-监听\" target=\"_blank\">Watch</a>                                                   | `Record<string, (val: any) => void \\| { handler:(val:any) => void,immediate?: boolean }>`                                     | -                                     |\n| removeHiddenData                    | 提交数据的时候是否去掉已经被隐藏的元素的数据，默认隐藏                                    | `boolean`                                                                                                                     | `true`                                |\n| readOnly                            | 只读模式，一般用于预览展示，全文 text 展示                                                | `boolean`                                                                                                                     | `false`                               |\n| style                               | 顶层 style                                                                                | `CSSProperties`                                                                                                               | -                                     |\n| mode                                | 支持默认和卡片两种模式                                                                    | `'default'                                                                                                         \\| 'card'` | `'default'`                           |\n| onMount                             | 表单首次加载时触发                                                                        | `(changedFields, allFields) => void`                                                                                          | -                                     |\n| onFinish                            | 提交后的回调，执行 `form.submit()` 后触发                                                 | `(data, errors: Error[]) => void`                                                                                             | -                                     |\n| onFinishFailed                      | 提交表单且数据验证失败后触发                                                              | `({ values, errorFields, outOfDate }) => void`                                                                                | -                                     |\n| requiredMarkStyle                   | 必填选填的标记样式                                                                        | `'asterisk' \\| 'text-required' \\| 'text-optional' \\|'none'`                                                                   | `'asterisk'`                          |\n| locale                              | 展示语言，目前只支持中文、英文                                                            | `'zh-CN' \\| 'en-US'`                                                                                                          | `'zh-CN'`                             |\n| validateMessages                    | 修改默认的校验提示信息，详见 <a href=\"/docs/form-render/advanced-validate.md\" target=\"_blank\">ValidateMessages</a>                         | `Record<string, string>`                                                                                                      | -                                     |\n| validateTrigger                     | 统一设置字段触发验证的时机                                                                | `string                                                                                               \\| string[]`            | `'onChange'`                          |\n| id                                  | 表单的 id，一般用于标识一个表单的语义化名称                                               | `string \\| number`                                                                                                            | -                                     |\n\n## FormInstance\n\n| <div style=\"width:150px\">参数</div> | 描述                                                                                                                  | 类型                                                                                                                                                                 |\n| ----------------------------------- | --------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| submit                              | 触发提交流程，一般在提交按钮上使用                                                                                    | `() => void`                                                                                                                                                         |\n| resetFields                         | 清空表单（也会清空一些内置状态，例如校验）                                                                            | `({formData?: any, submitData?: any, errorFields?: Error[], touchedKeys?: string[], allTouched?: boolean}) => void`                                                  |\n| setErrorFields                      | 外部手动修改 errorFields 校验信息，用于外部校验回填                                                                   | `(error: Error[]) => void`                                                                                                                                           |\n| setValues                           | 外部手动修改 formData，用于已填写的表单的数据回填                                                                     | `(formData: any) => void`                                                                                                                                            |\n| setValueByPath                      | 外部修改指定单个 field 的数据(原名 onItemChange)                                                                      | `(path: Path, value: any) => void`                                                                                                                                   |\n| setSchemaByPath                     | 指定路径修改 schema                                                                                                   | `(path: Path, value: any) => void`                                                                                                                                   |\n| setSchema                           | 指定多个路径修改 schema                                                                                               | `({ path: value }) => void`                                                                                                                                          |\n| getValues                           | 获取表单内部维护的数据, 如果参数为空则返回当前所有数据                                                                | `(nameList?: Path[], filterFunc?: (meta: { touched: boolean, validating: boolean }) => boolean) => any`                                                              |\n| getHiddenValues                     | 获取隐藏的表单数据                                                                                                    | `() => any`                                                                                                                                                          |\n| removeErrorField                    | 外部手动删除某一个 path 下所有的校验信息                                                                              | `(path: Path) => void`                                                                                                                                               |  |\n| isFieldTouched                      | 检查某个表单是否被用户操作过                                                                                          | `(name: Path) => boolean`                                                                                                                                            |\n| isFieldsTouched                     | 检查一组字段 fields 是否被用户操作过, allTouched 为 true 是检查是否所有字段都被操作过                                 | `(nameList?: Path[], allTouched?: boolean) => boolean`                                                                                                               |\n| isFieldValidating                   | 检查对应字段 field 是否正在校验                                                                                       | `(name: Path) => boolean`                                                                                                                                            |\n| getFieldError                       | 获取对应字段 field 的错误信息                                                                                         | `(name: Path) => string[]`                                                                                                                                           |\n| getFieldsError                      | 获取一组字段 fields 对应的错误信息, 返回数组形式; 入参为空则获取所有字段对应的错误信息, 返回错误信息 | `(nameList: Path[]) => Error[]`                                                                                                                                      |\n| setFields                           | 设置一组字段状态                                                                                  | `(nameList: Field[]) => void`                                                                                                                                        |\n| validateFields                      | 触发表单验证                                                                                | `(nameList?: Path[]) => Promise`                                                                                                                                     |\n"
  },
  {
    "path": "docs/form-render-mobile/demo/allWidget.tsx",
    "content": "import React from 'react';\nimport { Button, Dialog } from 'antd-mobile';\nimport FormRender, { useForm } from 'form-render-mobile';\n\nconst schema = {\n  type: 'object',\n  properties: {\n    cascader: {\n      title: '级联',\n      type: 'array',\n      widget: 'cascader',\n      props: {\n        options: [\n          { \n            label: '浙江', \n            value: 1, \n            children: [\n              { label: '杭州', value: 2 }\n            ]\n          },\n        ]\n      }\n    },\n    picker: {\n      title: '选择',\n      type: 'string',\n      widget: 'picker',\n      props: {\n        options: [\n          { label: '火车', value: 1 },\n          { label: '飞机', value: 2 },\n          { label: '火箭', value: 3 }\n        ]\n      }\n    },\n    date: {\n      title: '日期',\n      type: 'string',\n      widget: 'datePicker',\n      props: {\n        precision: 'month'\n      }\n    },\n    input: {\n      title: '输入框',\n      type: 'string',\n      widget: 'input',\n      required: true,\n      placeholder: '请输入'\n    },\n    textarea: {\n      title: '长文本',\n      type: 'string',\n      widget: 'textArea',\n      placeholder: '请输入'\n    },\n    slider: {\n      title: '滑动条',\n      type: 'string',\n      widget: 'slider',\n      props: {\n        range: true,\n      }\n    },\n    switch: {\n      title: '开关',\n      type: 'bool',\n      widget: 'switch',\n      props: {\n        uncheckedText: '关',\n        checkedText: '开'\n      }\n    },\n    stepper: {\n      title: '步进器',\n      type: 'number',\n      widget: 'stepper'\n    },\n    rate: {\n      title: '评分',\n      type: 'string',\n      widget: 'rate'\n    },\n    selector: {\n      title: '选择组',\n      type: 'string',\n      widget: 'selector',\n      props: {\n        multiple: true,\n        options: [\n          { label: 'A', value: 'a' },\n          { label: 'B', value: 'b' },\n          { label: 'C', value: 'c' },\n          { label: 'D', value: 'd' },\n          { label: 'E', value: 'e' },\n          { label: 'F', value: 'f' }\n        ]\n      }\n    },\n    radio: {\n      title: '单选',\n      type: 'string',\n      widget: 'radio',\n      props: {\n        options: [\n          { label: '早', value: 'a' },\n          { label: '中', value: 'b' },\n          { label: '晚', value: 'c' }\n        ]\n      }\n    }\n  }\n};\n\n\nexport default () => {\n  const form = useForm();\n\n  const onFinish = (formData: any) => {\n    Dialog.alert({\n      content: <pre>{JSON.stringify(formData, null, 2)}</pre>,\n    })\n  };\n\n  return (\n    <FormRender\n      schema={schema}\n      form={form}\n      onFinish={onFinish}\n      footer={\n        <Button block type='submit' color='primary' size='large'>\n          提交\n        </Button>\n      }\n    />\n  );\n}\n"
  },
  {
    "path": "docs/form-render-mobile/demo/basic.tsx",
    "content": "import React from 'react';\nimport { Button, Dialog } from 'antd-mobile';\nimport FormRender, { useForm } from 'form-render-mobile';\n\nconst schema = {\n  type: 'object',\n  properties: {\n    input: {\n      title: '输入框',\n      type: 'string',\n      widget: 'input',\n      required: true,\n    },\n    slider: {\n      title: '滑动条',\n      type: 'string',\n      widget: 'slider'\n    },\n    switch: {\n      title: '开关',\n      type: 'bool',\n      widget: 'switch'\n    },\n    stepper: {\n      title: '步进器',\n      type: 'number',\n      widget: 'stepper'\n    },\n    selector: {\n      title: '选择组',\n      type: 'string',\n      widget: 'selector',\n      props: {\n        options: [\n          { label: '早', value: 'a' },\n          { label: '中', value: 'b' },\n          { label: '晚', value: 'c' }\n        ]\n      }\n    },\n    radio: {\n      title: '单选',\n      type: 'string',\n      widget: 'radio',\n      props: {\n        options: [\n          { label: '早', value: 'a' },\n          { label: '中', value: 'b' },\n          { label: '晚', value: 'c' }\n        ]\n      }\n    },\n    date: {\n      title: '日期',\n      type: 'string',\n      widget: 'datePicker',\n      props: {\n        precision: 'month'\n      }\n    },\n    city: {\n      title: '城市',\n      type: 'array',\n      widget: 'cascader',\n      props: {\n        options: [\n          { label: '浙江', value: 1, children: [\n            { label: '杭州', value: 2}\n          ] },\n        ]\n      }\n    }\n  }\n};\n\n\nexport default () => {\n  const form = useForm();\n  \n  const onFinish = (formData: any) => {\n    Dialog.alert({\n      content: <pre>{JSON.stringify(formData, null, 2)}</pre>,\n    })\n  };\n\n  return (\n    <FormRender\n      schema={schema}\n      form={form}\n      onFinish={onFinish}\n      widgets={{\n        InputNumber: () => {return 1},\n        Serialnumber: () => { return 1}\n      }}\n      footer={\n        <Button block type='submit' color='primary' size='large'>\n          提交\n        </Button>\n      }\n    />\n  );\n}\n"
  },
  {
    "path": "docs/form-render-mobile/demo/card.tsx",
    "content": "import React from 'react';\nimport { Button, Dialog } from 'antd-mobile';\nimport FormRender, { useForm } from 'form-render-mobile';\n\nconst schema = {\n  type: 'object',\n  displayType: 'row',\n  properties: {\n    obj: {\n      type: 'object',\n      widget: 'card',\n      title: '卡片主题',\n      description: '这是一个对象类型',\n      properties: {\n        input1: {\n          required: true,\n          title: '输入框 A',\n          type: 'string',\n          widget: 'input',\n        },\n        input2: {\n          title: '输入框 B',\n          type: 'string',\n          widget: 'input',\n        },\n        input3: {\n          title: '输入框 C',\n          type: 'string',\n          widget: 'input',\n        },\n      },\n    },\n    obj1: {\n      type: 'object',\n      widget: 'card',\n      properties: {\n        input1: {\n          title: '输入框 A',\n          type: 'string',\n          widget: 'input',\n        },\n        input2: {\n          title: '输入框 B',\n          type: 'string',\n          widget: 'input',\n        },\n      },\n    }\n  },\n};\n\n\nexport default () => {\n  const form = useForm();\n\n  const onFinish = (formData: any) => {\n    Dialog.alert({\n      content: <pre>{JSON.stringify(formData, null, 2)}</pre>,\n    })\n  };\n\n  return (\n    <div>\n      <FormRender\n        schema={schema}\n        form={form}\n        onFinish={onFinish}\n        footer={\n          <Button block type='submit' color='primary' size='large'>\n            提交\n          </Button>\n        }\n      />\n    </div>\n  );\n}\n"
  },
  {
    "path": "docs/form-render-mobile/demo/collaspa.tsx",
    "content": "import React from 'react';\nimport FormRender, { useForm } from 'form-render-mobile';\n\nconst schema = {\n  type: 'object',\n  displayType: 'row',\n  properties: {\n    obj: {\n      type: 'object',\n      title: '卡片主题',\n      description: '这是一个对象类型',\n      widget: 'collapse',\n      items: [\n        {\n          title: '基础信息',\n          properties: {\n            input1: {\n              title: '输入框 A',\n              type: 'string',\n              widget: 'input',\n            },\n            input2: {\n              title: '输入框 B',\n              type: 'string',\n              widget: 'input',\n            },\n            input3: {\n              title: '输入框 C',\n              type: 'string',\n              widget: 'input',\n            },\n            input4: {\n              title: '输入框 D',\n              type: 'string',\n              widget: 'input',\n            }\n          }\n        },\n        {\n          title: '其他信息',\n          properties: {\n            input1: {\n              title: '输入框 A',\n              type: 'string',\n              widget: 'input',\n            },\n            input2: {\n              title: '输入框 B',\n              type: 'string',\n              widget: 'input',\n            },\n            input3: {\n              title: '输入框 C',\n              type: 'string',\n              widget: 'input',\n            },\n            input4: {\n              title: '输入框 D',\n              type: 'string',\n              widget: 'input',\n            }\n          }\n        }\n      ]\n    },\n  },\n};\n\nexport default () => {\n  const form = useForm();\n\n  return <FormRender schema={schema} form={form} />;\n};\n"
  },
  {
    "path": "docs/form-render-mobile/demo/group.tsx",
    "content": "import React from 'react';\nimport { Button, Dialog } from 'antd-mobile';\nimport FormRender, { useForm } from 'form-render-mobile';\n\nconst schema = {\n  type: 'object',\n  displayType: 'row',\n  properties: {\n    group1: {\n      type: 'object',\n      widget: 'group',\n      title: '分组1',\n      properties: {\n        input: {\n          title: '输入框 A',\n          type: 'string',\n          widget: 'input',\n        },\n        input2: {\n          title: '输入框 B',\n          type: 'string',\n          widget: 'input',\n        },\n      },\n    },\n    group2: {\n      type: 'object',\n      widget: 'group',\n      title: '分组2',\n      description: '这是一个对象类型',\n      properties: {\n        input1: {\n          title: '输入框 A',\n          type: 'string',\n          widget: 'input',\n        },\n        input2: {\n          title: '输入框 B',\n          type: 'string',\n          widget: 'input',\n        },\n      },\n    },\n    group3: {\n      type: 'object',\n      widget: 'group',\n      properties: {\n        input1: {\n          title: '输入框 A',\n          type: 'string',\n          widget: 'input',\n        },\n        input2: {\n          title: '输入框 B',\n          type: 'string',\n          widget: 'input',\n        },\n      },\n    }\n  },\n};\n\n\nexport default () => {\n  const form = useForm();\n\n  const onFinish = (formData: any) => {\n    Dialog.alert({\n      content: <pre>{JSON.stringify(formData, null, 2)}</pre>,\n    })\n  };\n\n  return (\n    <div>\n      <FormRender\n        schema={schema}\n        form={form}\n        onFinish={onFinish}\n        footer={\n          <Button block type='submit' color='primary' size='large'>\n            提交\n          </Button>\n        }\n      />\n    </div>\n  );\n}\n"
  },
  {
    "path": "docs/form-render-mobile/demo/index.tsx",
    "content": "import React from 'react';\nimport { Button, Dialog } from 'antd-mobile';\nimport FormRender, { useForm } from 'form-render-mobile';\n\nconst schema = {\n  type: 'object',\n  properties: {\n    input: {\n      title: '输入框',\n      type: 'string',\n      widget: 'input',\n      required: true,\n    },\n    slider: {\n      title: '滑动条',\n      type: 'string',\n      widget: 'slider'\n    },\n    switch: {\n      title: '开关',\n      type: 'bool',\n      widget: 'switch'\n    },\n    stepper: {\n      title: '步进器',\n      type: 'number',\n      widget: 'stepper'\n    },\n    selector: {\n      title: '选择组',\n      type: 'string',\n      widget: 'selector',\n      props: {\n        options: [\n          { label: '早', value: 'a' },\n          { label: '中', value: 'b' },\n          { label: '晚', value: 'c' }\n        ]\n      }\n    },\n    radio: {\n      title: '单选',\n      type: 'string',\n      widget: 'radio',\n      props: {\n        options: [\n          { label: '早', value: 'a' },\n          { label: '中', value: 'b' },\n          { label: '晚', value: 'c' }\n        ]\n      }\n    },\n    date: {\n      title: '日期',\n      type: 'string',\n      widget: 'datePicker',\n      props: {\n        precision: 'month'\n      }\n    },\n    city: {\n      title: '城市',\n      type: 'array',\n      widget: 'cascader',\n      props: {\n        options: [\n          { label: '浙江', value: 1, children: [\n            { label: '杭州', value: 2}\n          ] },\n        ]\n      }\n    }\n  }\n};\n\nexport default () => {\n  const form = useForm();\n  \n  const onFinish = (formData: any) => {\n    Dialog.alert({\n      content: <pre>{JSON.stringify(formData, null, 2)}</pre>,\n    })\n  };\n\n  return (\n    <FormRender\n      schema={schema}\n      form={form}\n      onFinish={onFinish}\n      widgets={{\n        InputNumber: () => {return 1},\n        Serialnumber: () => { return 1}\n      }}\n      footer={\n        <Button block type='submit' color='primary' size='large'>\n          提交\n        </Button>\n      }\n    />\n  );\n}\n"
  },
  {
    "path": "docs/form-render-mobile/demo/list.tsx",
    "content": "import React from 'react';\nimport FormRender, { useForm } from 'form-render-mobile';\nimport { Button, Dialog } from 'antd-mobile';\n\nconst schema = {\n  type: 'object',\n  displayType: 'row',\n  properties: {\n    list: {\n      title: '对象数组',\n      type: 'array',\n      widget: 'cardList',\n      min: 2,\n      max:3,\n      items: {\n        type: 'object',\n        widget: 'card',\n        properties: {\n          input1: {\n            title: '输入框 A',\n            type: 'string',\n            widget: 'input',\n          },\n          input2: {\n            title: '输入框 B',\n            type: 'string',\n            widget: 'input',\n          },\n          input3: {\n            title: '输入框 B',\n            type: 'string',\n            widget: 'input',\n          },\n        },\n      },\n    },\n  },\n};\n\nexport default () => {\n  const form = useForm();\n\n  const onFinish = (formData: any) => {\n    Dialog.alert({\n      content: <pre>{JSON.stringify(formData, null, 2)}</pre>,\n    })\n  };\n\n  return (\n    <FormRender\n      schema={schema}\n      form={form}\n      onFinish={onFinish}\n      footer={\n        <Button block type='submit' color='primary' size='large'>\n          提交\n        </Button>\n      }\n    />\n  );\n};\n"
  },
  {
    "path": "docs/form-render-mobile/disaply.md",
    "content": "---\norder: 1\ntoc: content\ntitle: 内置控件\n---\n\n## 输入控件\n\n输入控件是由 antd mobile 组件，做了进一步的封装和兼容之后，组成的一系列开箱即用的 widget。如要对组件进行进一步的配置请参考对应的 <a href=\"https://mobile.ant.design/zh/components/button?_blank\" target=\"_blank\">antd mobile 文档</a>。\n\nform-render-mobile 支持如下输入控件。如需要更多自定义样式和功能，请参考 <a href=\"/docs/form-render/advanced-widget.md\" target=\"_blank\">自定义组件</a>。\n\n<code src=\"./demo/allWidget.tsx\" background=\"rgb(245,245,245)\" compact={true}></code>\n\n## 布局控件\n\n对于移动端的表单场景，我们内置了两种常用布局控件，分别为 `card`，`group`。\n\n### 分组 group\n\n<code src=\"./demo/group.tsx\" background=\"rgb(245,245,245)\" compact={true}></code>\n\n<!-- ### 折叠 collapse\n\n<code src=\"./demo/collaspa.tsx\" background=\"rgb(245,245,245)\" compact={true}></code> -->\n\n### 卡片 card\n\n<code src=\"./demo/card.tsx\" background=\"rgb(245,245,245)\" compact={true}></code>\n\n## 列表控件\n\n<code src=\"./demo/list.tsx\" background=\"rgb(245,245,245)\" compact={true}></code>\n"
  },
  {
    "path": "docs/form-render-mobile/index.md",
    "content": "---\norder: 0\ntoc: content\ntitle: 开始使用\n---\n\n<div style=\"display:flex;align-items:center;margin-bottom:24px\">\n  <img src=\"https://img.alicdn.com/tfs/TB17UtINiLaK1RjSZFxXXamPFXa-606-643.png\" alt=\"logo\" width=\"48px\"/>\n  <h4 style=\"font-size:30px;font-weight:600;display:inline-block;margin-left:12px\">FormRender Mobile</h4>\n</div>\n<p style=\"display:flex;justify-content:space-between;width:440px\">\n  <a href=\"https://www.npmjs.com/package/form-render-mobile?_blank\">\n    <img alt=\"npm\" src=\"https://img.shields.io/npm/v/form-render-mobile.svg?maxAge=3600&style=flat-square\">\n  </a>\n  <a href=\"https://npmjs.org/package/form-render-mobile\">\n    <img alt=\"NPM downloads\" src=\"https://img.shields.io/npm/dm/form-render-mobile.svg?style=flat-square\">\n  </a>\n  <a href=\"https://npmjs.org/package/form-render-mobile\">\n    <img alt=\"NPM all downloads\" src=\"https://img.shields.io/npm/dt/form-render-mobile.svg?style=flat-square\">\n  </a>\n  <a>\n    <img alt=\"PRs Welcome\" src=\"https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square\">\n  </a>\n</p>\n\n\n## ✨ 简介\n\nFormRender Mobile 是为移动端设置的开箱即用的表单解决方案，通过 JsonSchema 协议动态渲染表单。基于 <a href=\"https://xrender.fun/form-render\" target=\"_blank\">FormRender2.0</a> 和 <a href=\"https://mobile.ant.design/zh/components/form/\" target=\"_blank\">Ant Design Mobile</a> 实现。API 与 FormRender 2.0 基本一致，如果你熟悉 FromRender 2.0 那么你就已经会使用 FormRender Mobile 了。\n\n## ⚙️ 安装\n\nFormRender Mobile 依赖 Ant Design Mobile，单独使用不要忘记同时安装 `antd-mobile`\n\n```shell\nnpm i form-render-mobile --save\n```\n\n## 🚀 快速上手\n<code src=\"./demo/basic.tsx\" background=\"rgb(245,245,245)\" compact={true}></code>\n"
  },
  {
    "path": "docs/form-render-mobile/test/index.tsx",
    "content": "import React from 'react';\nimport { Button, Dialog } from 'antd-mobile';\nimport FormRender, { useForm } from 'form-render-mobile';\n\nconst schema = {\n  type: 'object',\n  properties: {\n    \n    picker: {\n      title: '选择',\n      type: 'string',\n      widget: 'picker',\n      props: {\n        options: [\n          { label: '火车', value: 1 },\n          { label: '飞机', value: 2 },\n          { label: '火箭', value: 3 }\n        ]\n      }\n    },\n    radio: {\n      title: '单选',\n      type: 'string',\n      widget: 'radio',\n      props: {\n        options: [\n          { label: '早', value: 'a' },\n          { label: '中', value: 'b' },\n          { label: '晚', value: 'c' }\n        ]\n      }\n    }\n  }\n};\n\n\nexport default () => {\n  const form = useForm();\n\n  const onFinish = (formData: any) => {\n    Dialog.alert({\n      content: <pre>{JSON.stringify(formData, null, 2)}</pre>,\n    })\n  };\n\n  return (\n    <FormRender\n      schema={schema}\n      form={form}\n      onFinish={onFinish}\n      onMount={() => [\n       \n        form.setValues({ picker: [1], radio: 'a'})\n\n        \n      ]}\n      footer={\n        <Button block type='submit' color='primary' size='large'>\n          提交\n        </Button>\n      }\n    />\n  );\n}\n"
  },
  {
    "path": "docs/form-render-mobile/test/test.md",
    "content": "<!-- <code src='./test/index.tsx'></code> -->"
  },
  {
    "path": "docs/index.en-US.md",
    "content": "---\ntitle: 首页\nhero:\n  title: XRender 2.0\n  description: |\n    中后台「表单/表格/图表/流程编排」开箱即用解决方案\n    <br />\n    <small style=\"opacity: 0.7;\">v1 文档请访问 <a href=\"https://xrender.fun/\" style=\"color: #1677ff;\">https://xrender.fun</a></small>\n  actions:\n    - text: 立即使用\n      link: /form-render\n    - text: GitHub\n      link: https://github.com/alibaba/x-render\nfeatures:\n  - emoji: 🚀\n    title: FormRender\n    description: 像写一个 input 一样写表单\n  - emoji: 🎨\n    title: TableRender\n    description: 协议生成 & 高度灵活的搜索列表\n  - emoji: 💎\n    title: FormGenerator\n    description: 中后台表单可视化搭建生成利器\n  - emoji: 🚁\n    title: XFlow\n    description: 画布流程编排解决方案\nfooter: false\n---\n\n<embed src=\"../README.md#L33-L163\"></embed>\n\n## 贡献者们\n\n<a href=\"https://github.com/alibaba/x-render/graphs/contributors\">\n  <img style=\"width: 60%\" src=\"https://contrib.rocks/image?repo=alibaba/form-render\" />\n</a>\n\n\n<style>\n.dumi-default-features {\n  display: flex;\n  flex-wrap: nowrap;\n  justify-content: space-evenly;\n  max-width: 1200px !important;\n  margin: 0 auto;\n}\n\n.dumi-default-features-item {\n  flex: 0 1 auto;\n  width: 25%;\n  text-align: center;\n}\n\n.dumi-default-features[data-cols='2'] > .dumi-default-features-item:nth-child(odd) {\n    margin-inline-end: 0;\n}\n</style>"
  },
  {
    "path": "docs/index.zh-CN.md",
    "content": "---\ntitle: 首页\nhero:\n  title: XRender 2.0\n  description: |\n    中后台「表单/表格/图表/流程编排」开箱即用解决方案\n    <br />\n  actions:\n    - text: 立即使用\n      link: /form-render\n    - text: GitHub\n      link: https://github.com/alibaba/x-render\nfeatures:\n  - emoji: 🚀\n    title: FormRender\n    description: 像写一个 input 一样写表单\n  - emoji: 🎨\n    title: TableRender\n    description: 协议生成 & 高度灵活的搜索列表\n  - emoji: 💎\n    title: FormGenerator\n    description: 中后台表单可视化搭建生成利器\n  - emoji: 🚁\n    title: XFlow\n    description: 画布流程编排解决方案\nfooter: false\n---\n<!-- <TypeSchema></TypeSchema> -->\n<embed src=\"../README.md#L33-L163\"></embed>\n\n## 贡献者们\n\n<a href=\"https://github.com/alibaba/x-render/graphs/contributors\">\n  <img style=\"height: 500px; margin-bottom: 50px\" src=\"https://contrib.rocks/image?repo=alibaba/form-render\" />\n</a>\n\n<style>\n.dumi-default-features {\n  display: flex;\n  flex-wrap: nowrap;\n  justify-content: space-evenly;\n  max-width: 1200px !important;\n  margin: 0 auto;\n}\n\n.dumi-default-features-item {\n  flex: 0 1 auto;\n  width: 25%;\n  text-align: center;\n}\n\n.dumi-default-features[data-cols='2'] > .dumi-default-features-item:nth-child(odd) {\n    margin-inline-end: 0;\n}\n</style>"
  },
  {
    "path": "docs/playground/controller/index.css",
    "content": ".playground-controller .fr-field {\n  margin-bottom: 4px;\n}\n\n.playground-controller {\n  margin-bottom: 1px;\n  background-color: #fff;\n  padding: 12px 30px 6px 30px;\n  border-radius: 3px;\n}\n\n.playground-controller .fr-inline-field {\n  margin-right: 60px !important;\n}"
  },
  {
    "path": "docs/playground/controller/index.tsx",
    "content": "import React from 'react';\nimport FormRender, { useForm } from 'form-render';\nimport './index.css';\n\nconst schema = {\n  type: 'object',\n  displayType: 'inline',\n  properties: {\n    displayType: {\n      title: '标签布局',\n      type: 'string',\n      widget: 'radio',\n      enum: ['column', 'row'],\n      enumNames: ['垂直', '水平'],\n      default: 'column',\n    },\n    column: {\n      title: '列数',\n      type: 'number',\n      widget: 'radio',\n      enum: [1, 2, 3],\n      enumNames: ['一列', '两列', '三列'],\n      default: 1,\n    },\n    // readonly: {\n    //   title: '只读',\n    //   type: 'boolean',\n    //   widget: 'switch'\n    // },\n    labelWidth: {\n      title: '标签宽度',\n      type: 'number',\n      widget: 'slider',\n      default: 100,\n      props: {\n        min: 20,\n        max: 200,\n        style: {\n          width: '220px'\n        }\n      }\n    },\n    schema: {\n      title: '示例',\n      type: 'string',\n      widget: 'radio',\n      enum: ['simplest', 'basic', 'format', 'dynamic-function', 'new-feature', 'function', 'input', 'select', 'demo'],\n      enumNames: ['最简样例', '基础控件', '校验', '动态函数', '新功能', '复杂联动', '个性输入框', '个性选择框', '完整例子'],\n      default: 'simplest'\n    }\n  }\n}\n\nconst Controller: React.FC<{ onChange: (val: Record<string, any>) => void }> = ({ onChange }) => {\n  const form = useForm();\n\n  const watch = {\n    '#': (values: Record<string, any>) => {\n      onChange(values);\n    }\n  };\n\n  return (\n    <FormRender\n      className='playground-controller'\n      schema={schema}\n      form={form}\n      watch={watch}\n    />\n  )\n};\n\nexport default Controller;"
  },
  {
    "path": "docs/playground/customized/AsyncSelect.js",
    "content": "import React from 'react';\nimport { Select } from 'antd';\nimport jsonp from 'fetch-jsonp';\nimport querystring from 'querystring';\nconst { Option } = Select;\n\nlet timeout;\nlet currentValue;\n\nfunction fetch(value, callback) {\n  if (timeout) {\n    clearTimeout(timeout);\n    timeout = null;\n  }\n  currentValue = value;\n\n  function fake() {\n    const str = querystring.encode({\n      code: 'utf-8',\n      q: value,\n    });\n    jsonp(`https://suggest.taobao.com/sug?${str}`)\n      .then(response => response.json())\n      .then(d => {\n        if (currentValue === value) {\n          const { result } = d;\n          const data = [];\n          result.forEach(r => {\n            data.push({\n              value: r[0],\n              text: r[0],\n            });\n          });\n          callback(data);\n        }\n      });\n  }\n\n  timeout = setTimeout(fake, 300);\n}\n\nclass SearchInput extends React.Component {\n  state = {\n    data: [],\n  };\n\n  handleSearch = value => {\n    if (value) {\n      fetch(value, data => this.setState({ data }));\n    } else {\n      this.setState({ data: [] });\n    }\n  };\n\n  handleChange = value => {\n    const { onChange } = this.props;\n    onChange(value);\n  };\n\n  render() {\n    const { value, ...rest } = this.props;\n    const options = this.state.data.map(d => (\n      <Option key={d.value}>{d.text}</Option>\n    ));\n    return (\n      <Select\n        {...rest}\n        style={{ width: '100%' }}\n        showSearch\n        value={value || undefined}\n        defaultActiveFirstOption={false}\n        showArrow={false}\n        filterOption={false}\n        onSearch={this.handleSearch}\n        onChange={this.handleChange}\n        notFoundContent={null}\n      >\n        {options}\n      </Select>\n    );\n  }\n}\n\nexport default SearchInput;\n"
  },
  {
    "path": "docs/playground/example/expression.ts",
    "content": "\nconst case1 = {\n\t\"type\": \"object\",\n  \"properties\": {\n    \"input\": {\n      \"title\": \"输入框\",\n      \"type\": \"string\",\n      \"widget\": \"input\",\n      \"hidden\": \"{{ formData.hidden }}\",\n      \"disabled\": \"{{ formData.disabled }}\",\n      \"placeholder\": \"{{ formData.placeholder || '请输入' }}\"\n    },\n    \"placeholder\": {\n      \"title\": \"修改提示信息\",\n      \"type\": \"string\",\n      \"widget\": \"input\"\n    },\n    \"hidden\": {\n      \"title\": \"隐藏\",\n      \"type\": \"boolan\",\n      \"widget\": \"switch\"\n    },\n    \"disabled\": {\n      \"title\": \"禁用\",\n      \"type\": \"boolan\",\n      \"widget\": \"switch\"\n    }\n  }\n}\n\n\n\nconst case2 = {\n\t\"type\": \"object\",\n  \"properties\": {\n    \"input1\": {\n      \"title\": \"当输入框的值为 2 时，下拉单选第二个选项隐藏，如果已选中并清空选中值\",\n      \"type\": \"string\",\n      \"widget\": \"input\"\n    },\n    \"select1\": {\n      \"title\": \"下拉单选\",\n      \"type\": \"string\",\n      \"widget\": \"select\",\n      \"defaultValue\": \"{{ (formData.input1 === '2' && formData.select1 === 'b') ? undefined : formData.select1 }}\",\n      \"props\": {\n        \"options\": [\n          {\n            \"label\": \"早\",\n            \"value\": \"a\"\n          },\n          {\n            \"label\": \"中\",\n            \"value\": \"b\",\n            \"hidden\": \"{{ formData.input1 === '2' }}\"\n          },\n          {\n            \"label\": \"晚\",\n            \"value\": \"c\"\n          }\n        ]\n      }\n    }\n  }\n}"
  },
  {
    "path": "docs/playground/index.less",
    "content": ".debug * {\n  outline: 1px solid gold;\n}\n\n.markdown p {\n  color: #454d64 !important;\n  font-size: 15px !important;\n  line-height: 1.60625 !important;\n}\n\n.markdown ol,\n.markdown ul {\n  list-style: initial !important;\n}\n\n.hidden {\n  display: none;\n}\n\n.npm__react-simple-code-editor__textarea {\n  outline: none;\n}\n\n.b {\n  font-weight: bold;\n}\n\n.pa0 {\n  padding: 0;\n}\n\n.pa1 {\n  padding: 0.25rem;\n}\n\n.pa2 {\n  padding: 0.5rem;\n}\n\n.pa3 {\n  padding: 1rem;\n}\n\n.pa4 {\n  padding: 2rem;\n}\n\n.pa5 {\n  padding: 4rem;\n}\n\n.pl0 {\n  padding-left: 0;\n}\n\n.pt1 {\n  padding-top: 0.25rem;\n}\n\n.pt2 {\n  padding-top: 0.5rem;\n}\n\n.pt3 {\n  padding-top: 1rem;\n}\n\n.pt4 {\n  padding-top: 2rem;\n}\n\n.pt5 {\n  padding-top: 4rem;\n}\n\n.pl2 {\n  padding-left: 0.5rem;\n}\n\n.pb2 {\n  padding-bottom: 0.5rem;\n}\n\n.overflow-auto {\n  overflow: auto;\n}\n\n.h-100 {\n  height: 100%;\n}\n\n.vh-100 {\n  height: 100vh;\n}\n\n.relative {\n  position: relative;\n}\n\n.absolute {\n  position: absolute;\n}\n\n.top-0 {\n  top: 0;\n}\n\n.right-0 {\n  right: 0;\n}\n\n.w-50 {\n  width: 50%;\n}\n\n.w-100 {\n  width: 100%;\n}\n\n.flex {\n  display: flex;\n}\n\n.flex-50 {\n  flex-basis: 50%;\n}\n\n.flex-auto {\n  flex: 1 1 auto;\n  min-width: 0;\n  /* 1 */\n  min-height: 0;\n  /* 1 */\n}\n\n.flex-column {\n  flex-direction: column;\n}\n\n.items-center {\n  align-items: center;\n}\n\n.flex-wrap {\n  flex-wrap: wrap;\n}\n\n.z-999 {\n  z-index: 999;\n}\n\n.fr-playground {\n  overflow: hidden;\n  display: flex;\n  flex-direction: column;\n  z-index: 0;\n  height: calc(100vh - 82px);\n  background-color: #f3f3f4;\n\n  .ant-tabs, .ant-tabs-content, .ant-tabs-tabpane {\n    height: 100%;\n  }\n\n  .ant-tabs-nav {\n    margin-bottom: 0;\n    background-color: #fff;\n    padding: 0 20px;\n    .ant-tabs-tab-btn {\n      font-size: 16px;\n    }\n  }\n}\n\n.__dumi-default-previewer-demo {\n  padding: 24px;\n}\n\n.fr-obj-collapse {\n  border-radius: 4px !important;\n  border: 1px solid #f1f1f1 !important;\n  margin-bottom: 24px !important;\n}"
  },
  {
    "path": "docs/playground/index.md",
    "content": "---\norder: 0\nmobile: false\ntitle: ''\nsidebar: false\n---\n\n<code src='./index.tsx' inline={true}></code>\n"
  },
  {
    "path": "docs/playground/index.tsx",
    "content": "import React, { useCallback, useMemo, useState } from \"react\";\nimport FormRender, { useForm } from 'form-render';\nimport debounce from 'lodash/debounce';\nimport Editor from \"@monaco-editor/react\";\nimport Controller from \"./controller\";\nimport { Row, Col, Tabs } from 'antd';\n// @ts-ignore\nimport { serializeToDraft, deserialize } from './serialize';\nimport AsyncSelect from \"./customized/AsyncSelect\";\nimport './index.less';\n\nconst Playground = () => {\n  const form = useForm();\n  const [value, setValue] = useState(\"\");\n  const [displayType, setDisplayType] = useState('column');\n  const [column, setColumn] = useState(1);\n  const [readonly, setReadonly] = useState(false);\n  const [labelWidth, setLabelWidth] = useState(100);\n  const [lang, setLang] = useState('json');\n  const [formData, setFormData] = useState({});\n\n  const onEditorChange = (val?: string) => {\n    if (val) {\n      setValue(val);\n    }\n  }\n  \n  const parseSchema = (str: string) => {\n    try {\n      if (lang === 'javascript') {\n        return deserialize(str);\n      } else {\n        return JSON.parse(str)\n      }\n    } catch (e) {\n      return {}\n    }\n  }\n\n  const onControllerChange = (val) => {\n    if (val.schema) {\n      try {\n        const schema = require(`./json/${val.schema}.json`);\n        setValue(JSON.stringify(schema.schema, null, '\\t'))\n        setLang('json');\n      } catch {\n        const schema = require(`./json/${val.schema}.js`);\n        setLang('javascript')\n        setValue(serializeToDraft(schema.schema))\n      }\n    }\n\n    if (val.displayType) {\n      setDisplayType(val.displayType);\n    }\n\n    if (val.column) {\n      setColumn(val.column)\n    }\n\n    if (typeof val.readonly === 'boolean') {\n      setReadonly(val.readonly);\n    }\n\n    if (val.labelWidth) {\n      setLabelWidth(val.labelWidth);\n    }\n  }\n\n  const debounceEditorChange = useCallback(debounce(onEditorChange, 200), [])\n  const schema = parseSchema(value);\n\n  const onValuesChange = () => {\n    setTimeout(() => {\n      form.submit();\n    })\n  }\n\n  return (\n    <div className=\"fr-playground\">\n        <Controller onChange={onControllerChange} />\n        <Row gutter={20} style={{ flex: 1, overflow: 'hidden' }}>\n          <Col span={12}>\n            <Tabs\n              items={[\n                {\n                  label: 'Schema',\n                  key: 'schema',\n                  children: \n                    <Editor\n                      theme='vs-dark'\n                      value={value}\n                      language={lang}\n                      options={{\n                        lineNumbers: 'off',\n                        fontFamily: \"'JetBrains Mono', monospace\",\n                        fontSize: 14,\n                        minimap: {\n                          enabled: false\n                        }\n                      }}\n                      onChange={debounceEditorChange}\n                      onMount={() => {\n                        const schema = require(`./json/simplest.json`);\n                        setValue(JSON.stringify(schema.schema, null, '\\t'))\n                      }}\n                    />\n                  \n                },\n                {\n                  label: 'FormData',\n                  key: 'data',\n                  children: \n                    <Editor\n                      theme='vs-dark'\n                      value={JSON.stringify(formData, null, '\\t')}\n                      language={lang}\n                      options={{\n                        lineNumbers: 'off',\n                        readOnly: true,\n                        fontFamily: \"'JetBrains Mono', monospace\",\n                        fontSize: 14,\n                        minimap: {\n                          enabled: false\n                        }\n                      }}\n                    />\n                }\n              ]}\n            />\n          </Col>\n          <Col span={12} style={{ overflowY: 'auto', overflowX: 'hidden', height: '100%',  background: '#fff', padding: '24px 32px 24px 24px', boxShadow: '0 2px 12px 0 rgba(0,0,0,.1)' }}>\n            <FormRender\n              form={form}\n              schema={schema}\n              displayType={displayType}\n              column={column}\n              readOnly={readonly}\n              labelWidth={labelWidth}\n              widgets={{ asyncSelect: AsyncSelect }}\n              watch={{ '#' : onValuesChange }}\n              onFinish={(values) => {\n                setFormData(values);\n              }}\n            />\n          </Col>\n        </Row>\n     \n    </div>\n  );\n};\n\nexport default Playground;"
  },
  {
    "path": "docs/playground/json/basic.json",
    "content": "{\n  \"schema\": {\n    \"type\": \"object\",\n    \"properties\": {\n      \"AllString\": {\n        \"title\": \"string类\",\n        \"type\": \"object\",\n        \"properties\": {\n          \"input\": {\n            \"title\": \"简单输入框\",\n            \"type\": \"string\",\n            \"placeholder\": \"昵称\"\n          },\n          \"textarea\": {\n            \"title\": \"文本编辑框\",\n            \"type\": \"string\",\n            \"format\": \"textarea\"\n          },\n          \"color\": {\n            \"title\": \"颜色选择\",\n            \"type\": \"string\",\n            \"format\": \"color\"\n          },\n          \"image\": {\n            \"title\": \"图片展示\",\n            \"type\": \"string\",\n            \"format\": \"image\",\n            \"default\": \"http://placekitten.com/200/300\"\n          },\n          \"uploader\": {\n            \"title\": \"上传文件\",\n            \"type\": \"string\",\n            \"format\": \"upload\",\n            \"props\": {\n              \"action\": \"https://www.mocky.io/v2/5cc8019d300000980a055e76\"\n            }\n          },\n          \"treeSelect\": {\n            \"title\": \"树形选择\",\n            \"type\": \"string\",\n            \"widget\": \"treeSelect\",\n            \"props\": {\n              \"treeDefaultExpandAll\": true,\n              \"treeData\": [\n                {\n                  \"title\": \"Node1\",\n                  \"value\": \"0-0\",\n                  \"key\": \"0-0\",\n                  \"children\": [\n                    {\n                      \"title\": \"Child Node1\",\n                      \"value\": \"0-0-1\",\n                      \"key\": \"0-0-1\"\n                    },\n                    {\n                      \"title\": \"Child Node2\",\n                      \"value\": \"0-0-2\",\n                      \"key\": \"0-0-2\"\n                    }\n                  ]\n                },\n                {\n                  \"title\": \"Node2\",\n                  \"value\": \"0-1\",\n                  \"key\": \"0-1\"\n                }\n              ]\n            }\n          }\n        }\n      },\n      \"allDate\": {\n        \"title\": \"时间类\",\n        \"type\": \"object\",\n        \"properties\": {\n          \"date\": {\n            \"title\": \"日期选择\",\n            \"type\": \"string\",\n            \"format\": \"date\"\n          },\n          \"dateTime\": {\n            \"title\": \"日期时间选择\",\n            \"type\": \"string\",\n            \"format\": \"dateTime\"\n          },\n          \"time\": {\n            \"title\": \"时间选择\",\n            \"type\": \"string\",\n            \"format\": \"time\"\n          },\n          \"dateRange\": {\n            \"title\": \"日期范围\",\n            \"type\": \"range\",\n            \"format\": \"dateTime\",\n            \"placeholder\": [\"开始时间\", \"结束时间\"]\n          },\n          \"timeRange\": {\n            \"title\": \"时间范围\",\n            \"type\": \"range\",\n            \"format\": \"time\"\n          },\n          \"year\": {\n            \"title\": \"年份选择\",\n            \"type\": \"string\",\n            \"format\": \"year\"\n          },\n          \"month\": {\n            \"title\": \"月份选择\",\n            \"type\": \"string\",\n            \"format\": \"month\"\n          },\n          \"week\": {\n            \"title\": \"周选择\",\n            \"type\": \"string\",\n            \"format\": \"week\"\n          },\n          \"quarter\": {\n            \"title\": \"季度选择\",\n            \"type\": \"string\",\n            \"format\": \"quarter\"\n          }\n        }\n      },\n      \"allNumber\": {\n        \"title\": \"number类\",\n        \"type\": \"object\",\n        \"properties\": {\n          \"number1\": {\n            \"title\": \"数字输入框\",\n            \"description\": \"1 - 1000\",\n            \"type\": \"number\",\n            \"min\": 1,\n            \"max\": 1000\n          },\n          \"number2\": {\n            \"title\": \"带滑动条\",\n            \"type\": \"number\",\n            \"widget\": \"slider\"\n          },\n          \"rate\": {\n            \"title\": \"评价\",\n            \"type\": \"number\",\n            \"widget\": \"rate\"\n          }\n        }\n      },\n      \"allBoolean\": {\n        \"title\": \"boolean类\",\n        \"type\": \"object\",\n        \"properties\": {\n          \"radio\": {\n            \"title\": \"是否通过\",\n            \"type\": \"boolean\"\n          },\n          \"switch\": {\n            \"title\": \"开关控制\",\n            \"type\": \"boolean\",\n            \"widget\": \"switch\"\n          }\n        }\n      },\n      \"allEnum\": {\n        \"title\": \"选择类\",\n        \"type\": \"object\",\n        \"properties\": {\n          \"select\": {\n            \"title\": \"单选\",\n            \"type\": \"string\",\n            \"enum\": [\"a\", \"b\", \"c\"],\n            \"enumNames\": [\"早\", \"中\", \"晚\"],\n            \"default\": \"a\",\n            \"widget\": \"select\"\n          },\n          \"radio\": {\n            \"title\": \"单选\",\n            \"type\": \"string\",\n            \"enum\": [\"a\", \"b\", \"c\"],\n            \"enumNames\": [\"早\", \"中\", \"晚\"],\n            \"widget\": \"radio\"\n          },\n          \"multiSelect\": {\n            \"title\": \"多选\",\n            \"tooltip\": \"下拉多选\",\n            \"type\": \"array\",\n            \"items\": {\n              \"type\": \"string\"\n            },\n            \"enum\": [\"A\", \"B\", \"C\", \"D\"],\n            \"enumNames\": [\"杭州\", \"武汉\", \"湖州\", \"贵阳\"],\n            \"widget\": \"multiSelect\"\n          },\n          \"boxes\": {\n            \"title\": \"多选\",\n            \"tooltip\": \"checkbox\",\n            \"type\": \"array\",\n            \"items\": {\n              \"type\": \"string\"\n            },\n            \"enum\": [\"A\", \"B\", \"C\", \"D\"],\n            \"enumNames\": [\"杭州\", \"武汉\", \"湖州\", \"贵阳\"],\n            \"widget\": \"checkboxes\"\n          }\n        }\n      },\n      \"listName2\": {\n        \"title\": \"对象数组\",\n        \"description\": \"对象数组嵌套功能\",\n        \"type\": \"array\",\n        \"min\": 1,\n        \"max\": 3,\n        \"items\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"input1\": {\n              \"title\": \"简单输入框\",\n              \"type\": \"string\"\n            },\n            \"select1\": {\n              \"title\": \"单选\",\n              \"type\": \"string\",\n              \"enum\": [\"a\", \"b\", \"c\"],\n              \"enumNames\": [\"早\", \"中\", \"晚\"]\n            }\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "docs/playground/json/demo.json",
    "content": "{\n  \"schema\": {\n    \"type\": \"object\",\n    \"properties\": {\n      \"inputDemo\": {\n        \"title\": \"长度\",\n        \"type\": \"string\",\n        \"default\": \"750\",\n        \"rules\": [{ \"pattern\": \"^[A-Za-z0-9]+$\", \"message\": \"请输入正确格式\" }],\n        \"className\": \"input-with-px\",\n        \"props\": {\n          \"addonAfter\": \"px\"\n        }\n      },\n      \"numberDemo\": {\n        \"title\": \"数字\",\n        \"description\": \"数字输入框\",\n        \"type\": \"number\",\n        \"min\": 10,\n        \"max\": 100,\n        \"step\": 10\n      },\n      \"textareaDemo\": {\n        \"title\": \"输入框\",\n        \"type\": \"string\",\n        \"widget\": \"textarea\",\n        \"default\": \"FormRender\\nHello World!\",\n        \"required\": true\n      },\n\n      \"imgDemo\": {\n        \"title\": \"图片\",\n        \"type\": \"string\",\n        \"format\": \"image\",\n        \"default\": \"https://img.alicdn.com/tfs/TB1P8p2uQyWBuNjy0FpXXassXXa-750-1334.png\"\n      },\n      \"uploadDemo\": {\n        \"title\": \"文件上传\",\n        \"type\": \"string\",\n        \"default\": \"https://img.alicdn.com/tfs/TB1P8p2uQyWBuNjy0FpXXassXXa-750-1334.png\",\n        \"widget\": \"upload\",\n        \"props\": {\n          \"action\": \"https://www.mocky.io/v2/5cc8019d300000980a055e76\"\n        }\n      },\n      \"disabledDemo\": {\n        \"title\": \"不可用\",\n        \"type\": \"string\",\n        \"default\": \"我是一个被 disabled 的值\",\n        \"disabled\": true\n      },\n      \"enumDemo\": {\n        \"title\": \"枚举\",\n        \"type\": \"string\",\n        \"enum\": [\"A\", \"B\"],\n        \"enumNames\": [\n          \"养成\",\n          \"<span style='background-color: black;display: inline-block;vertical-align: text-top;width: 48px;height: 24px;margin-top:-2px;color:white; border: 1px solid #ddd;'>试试</span>\"\n        ],\n        \"width\": \"50%\"\n      },\n      \"dateDemo\": {\n        \"title\": \"时间\",\n        \"format\": \"dateTime\",\n        \"type\": \"string\",\n        \"widget\": \"date\",\n        \"width\": \"50%\",\n        \"default\": \"2018-11-22\",\n        \"required\": true\n      },\n      \"objDemo\": {\n        \"title\": \"单个对象\",\n        \"description\": \"这是一个对象类型\",\n        \"type\": \"object\",\n        \"properties\": {\n          \"isLike\": {\n            \"title\": \"是否显示颜色选择\",\n            \"type\": \"boolean\",\n            \"default\": true\n          },\n          \"background\": {\n            \"title\": \"颜色选择\",\n            \"description\": \"特殊面板\",\n            \"format\": \"color\",\n            \"type\": \"string\",\n            \"hidden\": \"{{rootValue.isLike === false}}\",\n            \"default\": \"#ffff00\"\n          },\n          \"wayToTravel\": {\n            \"title\": \"旅行方式\",\n            \"type\": \"string\",\n            \"enum\": [\"self\", \"group\"],\n            \"enumNames\": [\"自驾\", \"跟团\"],\n            \"widget\": \"radio\"\n          },\n          \"canDrive\": {\n            \"title\": \"是否拥有驾照\",\n            \"type\": \"boolean\",\n            \"default\": false,\n            \"hidden\": \"{{rootValue.wayToTravel !== 'self'}}\"\n          }\n        },\n        \"required\": [\"background\"]\n      },\n      \"multiSelectDemo\": {\n        \"title\": \"多选组件\",\n        \"tooltip\": \"多选功能\",\n        \"type\": \"array\",\n        \"items\": {\n          \"type\": \"string\"\n        },\n        \"enum\": [\"A\", \"B\", \"C\", \"D\"],\n        \"enumNames\": [\"杭州\", \"武汉\", \"湖州\", \"贵阳\"],\n        \"widget\": \"multiSelect\",\n        \"required\": true\n      },\n      \"custom\": {\n        \"properties\": {\n          \"payType\": {\n            \"title\": \"支付方式\",\n            \"type\": \"array\",\n            \"items\": { \"type\": \"string\" },\n            \"enum\": [\"1\", \"5\", \"6\"],\n            \"enumNames\": [\"预付\", \"面付\", \"信用住\"]\n          }\n        },\n        \"type\": \"object\",\n        \"required\": [\"payType\"],\n        \"title\": \"酒店行业限制\",\n        \"name\": \"custom\"\n      },\n      \"arrDemo\": {\n        \"title\": \"对象数组\",\n        \"description\": \"对象数组嵌套功能\",\n        \"type\": \"array\",\n        \"min\": 1,\n        \"max\": 3,\n        \"items\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"num\": {\n              \"title\": \"数字参数\",\n              \"description\": \"number类型\",\n              \"type\": \"number\"\n            },\n            \"name\": {\n              \"title\": \"字符名称\",\n              \"description\": \"string类型\",\n              \"type\": \"string\",\n              \"rules\": [{ \"pattern\": \"^[A-Za-z0-9]+$\" }],\n              \"disabled\": \"{{rootValue.num === 3}}\"\n            }\n          }\n        },\n        \"props\": {\n          \"foldable\": true,\n          \"hideDelete\": \"{{rootValue.arrDemo.length === 1}}\",\n          \"buttons\": [\n            {\n              \"text\": \"复制\",\n              \"icon\": \"CopyOutlined\",\n              \"callback\": \"copyLast\"\n            }\n          ]\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "docs/playground/json/dynamic-function.js",
    "content": "module.exports = {\n  schema: {\n    type: 'object',\n    properties: {\n      input: {\n        title: '动态函数检验',\n        tooltip: '动态函数检验，存储的是纯函数',\n        type: 'string',\n        labelWidth: 120,\n        rules: [\n          {\n            validator: (rule, value) => value === 'muji',\n            message: '输入的值不等于muji,请重新输入！',\n          },\n        ],\n      },\n    },\n  },\n};\n"
  },
  {
    "path": "docs/playground/json/format.json",
    "content": "{\n  \"schema\": {\n    \"type\": \"object\",\n    \"properties\": {\n      \"inputName\": {\n        \"title\": \"url输入框\",\n        \"type\": \"string\",\n        \"format\": \"url\"\n      },\n      \"imageName\": {\n        \"title\": \"图片展示\",\n        \"type\": \"string\",\n        \"format\": \"image\"\n      },\n      \"inputName2\": {\n        \"title\": \"email输入框\",\n        \"type\": \"string\",\n        \"format\": \"email\"\n      },\n      \"string\": {\n        \"title\": \"正则校验字符串\",\n        \"tooltip\": \"a-z\",\n        \"type\": \"string\",\n        \"rules\": [\n          {\n            \"pattern\": \"^[a-z]+$\"\n          }\n        ]\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "docs/playground/json/function.json",
    "content": "{\n  \"schema\": {\n    \"type\": \"object\",\n    \"properties\": {\n      \"case1\": {\n        \"title\": \"整体隐藏\",\n        \"type\": \"object\",\n        \"properties\": {\n          \"showMore\": {\n            \"title\": \"显示更多\",\n            \"type\": \"boolean\"\n          },\n          \"x1\": {\n            \"title\": \"输入框1\",\n            \"type\": \"string\",\n            \"hidden\": \"{{!formData.case1.showMore}}\"\n          },\n          \"x2\": {\n            \"title\": \"输入框2\",\n            \"type\": \"string\",\n            \"hidden\": \"{{!formData.case1.showMore}}\"\n          }\n        }\n      },\n      \"case2\": {\n        \"title\": \"选项联动\",\n        \"type\": \"object\",\n        \"properties\": {\n          \"bi\": {\n            \"title\": \"汇款币种\",\n            \"type\": \"string\",\n            \"enum\": [\n              \"rmb\",\n              \"dollar\"\n            ],\n            \"enumNames\": [\n              \"人民币\",\n              \"美元\"\n            ]\n          },\n          \"inputName\": {\n            \"title\": \"金额\",\n            \"tooltip\": \"{{formData.case2.bi === 'dollar' ? '一次汇款不超过150美元':'一次汇款不超过1000元'}}\",\n            \"type\": \"string\",\n            \"props\": {\n              \"addonBefore\": \"{{formData.case2.bi === 'dollar'? '$':'￥'}}\",\n              \"addonAfter\": \"{{formData.case2.bi === 'dollar'? '美元':'元'}}\"\n            }\n          }\n        }\n      },\n      \"case3\": {\n        \"title\": \"列表/显示不同组件\",\n        \"type\": \"object\",\n        \"properties\": {\n          \"ruleList\": {\n            \"title\": \"球员筛选\",\n            \"type\": \"array\",\n            \"items\": {\n              \"type\": \"object\",\n              \"properties\": {\n                \"attr\": {\n                  \"title\": \"标准\",\n                  \"type\": \"string\",\n                  \"widget\": \"select\",\n                  \"enum\": [\n                    \"goal\",\n                    \"league\"\n                  ],\n                  \"enumNames\": [\n                    \"入球数\",\n                    \"所在联盟\"\n                  ],\n                  \"width\": \"40%\"\n                },\n                \"relation\": {\n                  \"title\": \"关系\",\n                  \"type\": \"string\",\n                  \"enum\": [\n                    \">\",\n                    \"<\",\n                    \"=\"\n                  ],\n                  \"widget\": \"select\",\n                  \"hidden\": \"{{rootValue.attr === 'league'}}\",\n                  \"width\": \"20%\"\n                },\n                \"goal\": {\n                  \"title\": \"入球数\",\n                  \"type\": \"string\",\n                  \"rules\": [\n                    {\n                      \"pattern\": \"^[0-9]+$\",\n                      \"message\": \"输入正确得分\"\n                    }\n                  ],\n                  \"hidden\": \"{{rootValue.attr === 'league'}}\",\n                  \"width\": \"40%\"\n                },\n                \"league\": {\n                  \"title\": \"名称\",\n                  \"type\": \"string\",\n                  \"enum\": [\n                    \"a\",\n                    \"b\",\n                    \"c\"\n                  ],\n                  \"enumNames\": [\n                    \"西甲\",\n                    \"英超\",\n                    \"中超\"\n                  ],\n                  \"widget\": \"select\",\n                  \"hidden\": \"{{rootValue.attr !== 'league'}}\",\n                  \"width\": \"40%\"\n                }\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n}"
  },
  {
    "path": "docs/playground/json/input.json",
    "content": "{\n  \"schema\": {\n    \"type\": \"object\",\n    \"properties\": {\n      \"string\": {\n        \"title\": \"字符串\",\n        \"description\": \"带清空x按钮\",\n        \"type\": \"string\",\n        \"default\": \"hello world\",\n        \"props\": {\n          \"allowClear\": true\n        },\n        \"width\": \"50%\"\n      },\n      \"string2\": {\n        \"title\": \"复杂校验\",\n        \"description\": \"pattern和message的用法\",\n        \"type\": \"string\",\n        \"rules\": [\n          { \"pattern\": \"^[A-Za-z0-9]+$\", \"message\": \"请输入数字或英文字母\" }\n        ],\n        \"placeholder\": \"请输入数字或英文\",\n        \"width\": \"50%\"\n      },\n      \"string3\": {\n        \"title\": \"长度控制\",\n        \"description\": \"长度在5-15个字之间\",\n        \"type\": \"string\",\n        \"minLength\": 5,\n        \"maxLength\": 15,\n        \"width\": \"50%\"\n      },\n      \"string4\": {\n        \"title\": \"前置/后置标签\",\n        \"type\": \"string\",\n        \"props\": {\n          \"addonBefore\": \"长度\",\n          \"addonAfter\": \"px\"\n        },\n        \"width\": \"50%\"\n      },\n      \"string5\": {\n        \"title\": \"前后缀\",\n        \"type\": \"string\",\n        \"rules\": [\n          {\n            \"pattern\": \"^[0-9]+$\",\n            \"message\": \"请输入数字\"\n          }\n        ],\n        \"props\": {\n          \"prefix\": \"￥\",\n          \"suffix\": \"RMB\"\n        },\n        \"width\": \"50%\"\n      },\n      \"string6\": {\n        \"title\": \"置灰的输入框\",\n        \"type\": \"string\",\n        \"disabled\": true,\n        \"default\": \"hello world\",\n        \"width\": \"50%\"\n      },\n      \"string7\": {\n        \"title\": \"文本框\",\n        \"description\": \"固定高度\",\n        \"type\": \"string\",\n        \"format\": \"textarea\",\n        \"props\": {\n          \"row\": 4\n        }\n      },\n      \"string8\": {\n        \"title\": \"文本框\",\n        \"description\": \"自动高度\",\n        \"type\": \"string\",\n        \"format\": \"textarea\",\n        \"placeholder\": \"根据内容缩放\",\n        \"props\": {\n          \"autoSize\": {\n            \"minRows\": 3,\n            \"maxRows\": 5\n          }\n        }\n      }\n    },\n    \"required\": [\"string4\", \"string5\"]\n  },\n  \"formData\": {}\n}\n"
  },
  {
    "path": "docs/playground/json/new-feature.json",
    "content": "{\n  \"schema\": {\n    \"type\": \"object\",\n    \"properties\": {\n      \"html\": {\n        \"title\": \"html元素的使用\",\n        \"type\": \"object\",\n        \"properties\": {\n          \"html1\": {\n            \"title\": \"纯字符串\",\n            \"type\": \"html\",\n            \"default\": \"hello world\"\n          },\n          \"input\": {\n            \"title\": \"放在尾部\",\n            \"type\": \"string\",\n            \"width\": \"70%\"\n          },\n          \"html3\": {\n            \"title\": \"html\",\n            \"type\": \"html\",\n            \"default\": \"<a>注意事项</a>\",\n            \"width\": \"30%\"\n          }\n        }\n      },\n      \"customItemButtons\": {\n        \"title\": \"数组item自定义button\",\n        \"type\": \"array\",\n        \"items\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"a\": {\n              \"title\": \"简单输入框\",\n              \"type\": \"string\"\n            },\n            \"b\": {\n              \"title\": \"是否判断\",\n              \"type\": \"boolean\"\n            },\n            \"c\": {\n              \"title\": \"开关\",\n              \"type\": \"boolean\",\n              \"widget\": \"switch\"\n            }\n          }\n        },\n        \"default\": [{ \"a\": \"happy\", \"b\": false, \"c\": false }, {}],\n        \"props\": {\n          \"foldable\": true,\n          \"itemButtons\": [\n            {\n              \"html\": \"复制\",\n              \"callback\": \"copyMe\"\n            }\n          ]\n        }\n      },\n      \"labelDemo\": {\n        \"title\": \"标签长度自定义\",\n        \"description\": \"在object上定义labelWidth会影响所有子元素\",\n        \"type\": \"object\",\n        \"labelWidth\": 200,\n        \"properties\": {\n          \"inputName\": {\n            \"title\": \"简单输入框\",\n            \"type\": \"string\"\n          },\n          \"dateName\": {\n            \"title\": \"时间选择\",\n            \"type\": \"string\",\n            \"format\": \"date\"\n          },\n          \"colorName\": {\n            \"title\": \"颜色选择\",\n            \"type\": \"string\",\n            \"format\": \"color\",\n            \"labelWidth\": 80\n          }\n        }\n      },\n      \"objectName\": {\n        \"title\": \"对象\",\n        \"description\": \"这是一个对象类型\",\n        \"type\": \"object\",\n        \"properties\": {\n          \"inputDemo\": {\n            \"title\": \"前后缀\",\n            \"type\": \"string\",\n            \"rules\": [\n              { \"pattern\": \"^[A-Za-z0-9]+$\", \"message\": \"请输入数字或英文字母\" }\n            ],\n            \"width\": \"50%\",\n            \"props\": {\n              \"addonBefore\": \"画布长度\",\n              \"addonAfter\": \"px\"\n            }\n          },\n          \"numberDemo\": {\n            \"title\": \"数字\",\n            \"description\": \"数字输入框\",\n            \"type\": \"number\",\n            \"min\": 10,\n            \"max\": 100,\n            \"step\": 10,\n            \"width\": \"50%\",\n            \"widget\": \"slider\"\n          },\n          \"dateRange\": {\n            \"title\": \"日期范围\",\n            \"type\": \"range\",\n            \"format\": \"dateTime\",\n            \"placeholder\": [\"开始\", \"结束\"]\n          }\n        }\n      },\n      \"customizedWidgets\": {\n        \"title\": \"自定义组件\",\n        \"type\": \"object\",\n        \"properties\": {\n          \"asyncSelect\": {\n            \"title\": \"异步加载的下拉框\",\n            \"type\": \"string\",\n            \"labelWidth\": 130,\n            \"widget\": \"asyncSelect\",\n            \"placeholder\": \"搜索淘宝商品\"\n          }\n        }\n      },\n      \"complexArray\": {\n        \"title\": \"复杂结构数组\",\n        \"description\": \"数组item中含有数组等\",\n        \"type\": \"array\",\n        \"props\": {\n          \"foldable\": true,\n          \"hideIndex\": true\n        },\n        \"items\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"age\": {\n              \"title\": \"填写年龄\",\n              \"type\": \"number\",\n              \"hidden\": \"{{rootValue.movieType === 'a' || rootValue.movieType === 'c'}}\"\n            },\n            \"movieType\": {\n              \"title\": \"短片类型\",\n              \"type\": \"string\",\n              \"enum\": [\"a\", \"b\", \"c\"],\n              \"enumNames\": [\"教育\", \"恐怖\", \"故事\"]\n            },\n            \"movieList\": {\n              \"title\": \"电影列表\",\n              \"type\": \"array\",\n              \"items\": {\n                \"type\": \"object\",\n                \"properties\": {\n                  \"movieName\": {\n                    \"title\": \"电影名\",\n                    \"type\": \"string\"\n                  }\n                }\n              }\n            },\n            \"when\": {\n              \"title\": \"何时观看\",\n              \"description\": \"多选\",\n              \"type\": \"array\",\n              \"items\": {\n                \"type\": \"string\"\n              },\n              \"enum\": [\"A\", \"B\", \"C\", \"D\"],\n              \"enumNames\": [\"早上\", \"中午\", \"下午\", \"晚上\"]\n            }\n          },\n          \"required\": [\"age\"]\n        }\n      }\n    }\n  },\n  \"formData\": {\n    \"html\": {\n      \"html2\": \"hello from <span style='color: red'>formData</span>!\"\n    },\n    \"complexArray\": [\n      {\n        \"age\": \"\",\n        \"movieType\": \"b\",\n        \"movieList\": []\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "docs/playground/json/select.json",
    "content": "{\n  \"schema\": {\n    \"type\": \"object\",\n    \"properties\": {\n      \"select\": {\n        \"title\": \"带搜索的单选框\",\n        \"type\": \"string\",\n        \"enum\": [\"a\", \"b\", \"c\"],\n        \"enumNames\": [\"jack\", \"steve\", \"david\"],\n        \"widget\": \"select\",\n        \"props\": {\n          \"filterOption\": true,\n          \"showSearch\": true,\n          \"optionFilterProp\": \"label\"\n        }\n      },\n      \"multiSelect\": {\n        \"title\": \"标签模式\",\n        \"description\": \"除了可选的标签，还可输入自定义的标签\",\n        \"type\": \"array\",\n        \"enum\": [\"旅行达人\", \"工作狂\", \"老司机\", \"小资\"],\n        \"widget\": \"select\",\n        \"props\": {\n          \"mode\": \"tags\"\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "docs/playground/json/simplest.json",
    "content": "{\n  \"schema\": {\n    \"type\": \"object\",\n    \"properties\": {\n      \"input\": {\n        \"title\": \"简单输入框\",\n        \"type\": \"string\",\n        \"min\": 6,\n        \"rules\": [\n          {\n            \"pattern\": \"^[A-Za-z0-9]+$\",\n            \"message\": \"只允许填写英文字母和数字\"\n          }\n        ]\n      },\n      \"select\": {\n        \"title\": \"单选\",\n        \"type\": \"string\",\n        \"tooltip\": \"单项选择\",\n        \"enum\": [\n          \"a\",\n          \"b\",\n          \"c\"\n        ],\n        \"enumNames\": [\n          \"早\",\n          \"中\",\n          \"晚\"\n        ]\n      },\n      \"date\": {\n        \"title\": \"日期\",\n        \"type\": \"string\",\n        \"widget\": \"dateRange\"\n      },\n      \"listName2\": {\n        \"title\": \"对象数组\",\n        \"description\": \"对象数组嵌套功能\",\n        \"type\": \"array\",\n        \"min\": 1,\n        \"max\": 3,\n        \"items\": {\n          \"type\": \"object\",\n          \"properties\": {\n            \"input1\": {\n              \"title\": \"简单输入框\",\n              \"type\": \"string\"\n            },\n            \"select1\": {\n              \"title\": \"单选\",\n              \"type\": \"string\",\n              \"enum\": [\n                \"a\",\n                \"b\",\n                \"c\"\n              ],\n              \"enumNames\": [\n                \"早\",\n                \"中\",\n                \"晚\"\n              ]\n            }\n          }\n        }\n      }\n    }\n  }\n}"
  },
  {
    "path": "docs/playground/serialize.js",
    "content": "/*\nCopyright (c) 2014, Yahoo! Inc. All rights reserved.\nCopyrights licensed under the New BSD License.\nSee the accompanying LICENSE file for terms.\n*/\n\n// fork自https://github.com/yahoo/serialize-javascript.git，新增 ignoreUndefined 参数的支持\n\nimport { customAlphabet } from 'nanoid';\nconst nanoid = customAlphabet('1234567890abcdef');\n\n// Generate an internal UID to make the regexp pattern harder to guess.\nvar UID = nanoid(32);\nvar PLACE_HOLDER_REGEXP = new RegExp(\n  '(\\\\\\\\)?\"@__(F|R|D|M|S|A|U|I|B|L)-' + UID + '-(\\\\d+)__@\"',\n  'g'\n);\n\nvar IS_NATIVE_CODE_REGEXP = /\\{\\s*\\[native code\\]\\s*\\}/g;\nvar IS_PURE_FUNCTION = /function.*?\\(/;\nvar IS_ARROW_FUNCTION = /.*?=>.*?/;\nvar UNSAFE_CHARS_REGEXP = /[<>\\/\\u2028\\u2029]/g;\n\nvar RESERVED_SYMBOLS = ['*', 'async'];\n\n// Mapping of unsafe HTML and invalid JavaScript line terminator chars to their\n// Unicode char counterparts which are safe to use in JavaScript strings.\nvar ESCAPED_CHARS = {\n  '<': '\\\\u003C',\n  '>': '\\\\u003E',\n  '/': '\\\\u002F',\n  '\\u2028': '\\\\u2028',\n  '\\u2029': '\\\\u2029',\n};\n\nfunction escapeUnsafeChars(unsafeChar) {\n  return ESCAPED_CHARS[unsafeChar];\n}\n\nfunction deleteFunctions(obj) {\n  var functionKeys = [];\n  for (var key in obj) {\n    if (typeof obj[key] === 'function') {\n      functionKeys.push(key);\n    }\n  }\n  for (var i = 0; i < functionKeys.length; i++) {\n    delete obj[functionKeys[i]];\n  }\n}\n\nfunction deleteUndefined(obj) {\n  var undefinedKeys = [];\n  for (var key in obj) {\n    if (typeof obj[key] === 'undefined') {\n      undefinedKeys.push(key);\n    }\n  }\n  for (var i = 0; i < undefinedKeys.length; i++) {\n    delete obj[undefinedKeys[i]];\n  }\n}\n\n\nfunction serializeUtil(obj, options) {\n  options || (options = {});\n\n  // Backwards-compatibility for `space` as the second argument.\n  if (typeof options === 'number' || typeof options === 'string') {\n    options = { space: options };\n  }\n\n  var functions = [];\n  var regexps = [];\n  var dates = [];\n  var maps = [];\n  var sets = [];\n  var arrays = [];\n  var undefs = [];\n  var infinities = [];\n  var bigInts = [];\n  var urls = [];\n\n  // Returns placeholders for functions and regexps (identified by index)\n  // which are later replaced by their string representation.\n  function replacer(key, value) {\n    // For nested function\n    if (options.ignoreFunction) {\n      deleteFunctions(value);\n    }\n\n    if (options.ignoreUndefined) {\n      deleteUndefined(value);\n    }\n\n    if (!value && value !== undefined) {\n      return value;\n    }\n\n    // If the value is an object w/ a toJSON method, toJSON is called before\n    // the replacer runs, so we use this[key] to get the non-toJSONed value.\n    var origValue = this[key];\n    var type = typeof origValue;\n\n    if (type === 'object') {\n      if (origValue instanceof RegExp) {\n        return '@__R-' + UID + '-' + (regexps.push(origValue) - 1) + '__@';\n      }\n\n      if (origValue instanceof Date) {\n        return '@__D-' + UID + '-' + (dates.push(origValue) - 1) + '__@';\n      }\n\n      if (origValue instanceof Map) {\n        return '@__M-' + UID + '-' + (maps.push(origValue) - 1) + '__@';\n      }\n\n      if (origValue instanceof Set) {\n        return '@__S-' + UID + '-' + (sets.push(origValue) - 1) + '__@';\n      }\n\n      if (origValue instanceof Array) {\n        var isSparse =\n          origValue.filter(function () {\n            return true;\n          }).length !== origValue.length;\n        if (isSparse) {\n          return '@__A-' + UID + '-' + (arrays.push(origValue) - 1) + '__@';\n        }\n      }\n\n      if (origValue instanceof URL) {\n        return '@__L-' + UID + '-' + (urls.push(origValue) - 1) + '__@';\n      }\n    }\n\n    if (type === 'function') {\n      return '@__F-' + UID + '-' + (functions.push(origValue) - 1) + '__@';\n    }\n\n    if (type === 'undefined') {\n      return '@__U-' + UID + '-' + (undefs.push(origValue) - 1) + '__@';\n    }\n\n    if (type === 'number' && !isNaN(origValue) && !isFinite(origValue)) {\n      return '@__I-' + UID + '-' + (infinities.push(origValue) - 1) + '__@';\n    }\n\n    if (type === 'bigint') {\n      return '@__B-' + UID + '-' + (bigInts.push(origValue) - 1) + '__@';\n    }\n\n    return value;\n  }\n\n  function serializeFunc(fn) {\n    var serializedFn = fn.toString();\n    if (IS_NATIVE_CODE_REGEXP.test(serializedFn)) {\n      throw new TypeError('Serializing native function: ' + fn.name);\n    }\n\n    // pure functions, example: {key: function() {}}\n    if (IS_PURE_FUNCTION.test(serializedFn)) {\n      return serializedFn;\n    }\n\n    // arrow functions, example: arg1 => arg1+5\n    if (IS_ARROW_FUNCTION.test(serializedFn)) {\n      return serializedFn;\n    }\n\n    var argsStartsAt = serializedFn.indexOf('(');\n    var def = serializedFn\n      .substr(0, argsStartsAt)\n      .trim()\n      .split(' ')\n      .filter(function (val) {\n        return val.length > 0;\n      });\n\n    var nonReservedSymbols = def.filter(function (val) {\n      return RESERVED_SYMBOLS.indexOf(val) === -1;\n    });\n\n    // enhanced literal objects, example: {key() {}}\n    if (nonReservedSymbols.length > 0) {\n      return (\n        (def.indexOf('async') > -1 ? 'async ' : '') +\n        'function' +\n        (def.join('').indexOf('*') > -1 ? '*' : '') +\n        serializedFn.substr(argsStartsAt)\n      );\n    }\n\n    // arrow functions\n    return serializedFn;\n  }\n\n  // Check if the parameter is function\n  if (options.ignoreFunction && typeof obj === 'function') {\n    obj = undefined;\n  }\n  // Protects against `JSON.stringify()` returning `undefined`, by serializing\n  // to the literal string: \"undefined\".\n  if (obj === undefined) {\n    return String(obj);\n  }\n\n  var str;\n\n  // Creates a JSON string representation of the value.\n  // NOTE: Node 0.12 goes into slow mode with extra JSON.stringify() args.\n  if (options.isJSON && !options.space) {\n    str = JSON.stringify(obj);\n  } else {\n    str = JSON.stringify(obj, options.isJSON ? null : replacer, options.space);\n  }\n\n  // Protects against `JSON.stringify()` returning `undefined`, by serializing\n  // to the literal string: \"undefined\".\n  if (typeof str !== 'string') {\n    return String(str);\n  }\n\n  // Replace unsafe HTML and invalid JavaScript line terminator chars with\n  // their safe Unicode char counterpart. This _must_ happen before the\n  // regexps and functions are serialized and added back to the string.\n  if (options.unsafe !== true) {\n    str = str.replace(UNSAFE_CHARS_REGEXP, escapeUnsafeChars);\n  }\n\n  if (\n    functions.length === 0 &&\n    regexps.length === 0 &&\n    dates.length === 0 &&\n    maps.length === 0 &&\n    sets.length === 0 &&\n    arrays.length === 0 &&\n    undefs.length === 0 &&\n    infinities.length === 0 &&\n    bigInts.length === 0 &&\n    urls.length === 0\n  ) {\n    return str;\n  }\n\n  // Replaces all occurrences of function, regexp, date, map and set placeholders in the\n  // JSON string with their string representations. If the original value can\n  // not be found, then `undefined` is used.\n  return str.replace(\n    PLACE_HOLDER_REGEXP,\n    function (match, backSlash, type, valueIndex) {\n      // The placeholder may not be preceded by a backslash. This is to prevent\n      // replacing things like `\"a\\\"@__R-<UID>-0__@\"` and thus outputting\n      // invalid JS.\n      if (backSlash) {\n        return match;\n      }\n\n      if (type === 'D') {\n        return 'new Date(\"' + dates[valueIndex].toISOString() + '\")';\n      }\n\n      if (type === 'R') {\n        return (\n          'new RegExp(' +\n          serialize(regexps[valueIndex].source) +\n          ', \"' +\n          regexps[valueIndex].flags +\n          '\")'\n        );\n      }\n\n      if (type === 'M') {\n        return (\n          'new Map(' +\n          serialize(Array.from(maps[valueIndex].entries()), options) +\n          ')'\n        );\n      }\n\n      if (type === 'S') {\n        return (\n          'new Set(' +\n          serialize(Array.from(sets[valueIndex].values()), options) +\n          ')'\n        );\n      }\n\n      if (type === 'A') {\n        return (\n          'Array.prototype.slice.call(' +\n          serialize(\n            Object.assign(\n              { length: arrays[valueIndex].length },\n              arrays[valueIndex]\n            ),\n            options\n          ) +\n          ')'\n        );\n      }\n\n      if (type === 'U') {\n        return 'undefined';\n      }\n\n      if (type === 'I') {\n        return infinities[valueIndex];\n      }\n\n      if (type === 'B') {\n        return 'BigInt(\"' + bigInts[valueIndex] + '\")';\n      }\n\n      if (type === 'L') {\n        return 'new URL(\"' + urls[valueIndex].toString() + '\")';\n      }\n\n      var fn = functions[valueIndex];\n\n      return serializeFunc(fn);\n    }\n  );\n}\n\n/**\n * @description 使用serialize-javascript进行序列化，替代JSON.stringify()\n * @description 组件rules里面会存储一些validator函数，如果用JSON.stringify提交给接口会丢失\n * @param {*} obj 传入需要序列化的值\n * @returns 序列化后的值\n */\nfunction serialize(obj) {\n  return serializeUtil(obj, {\n    ignoreUndefined: true,\n  });\n}\n\n/**\n * @description 序列化成编辑器需要的展示格式\n * @param {*} obj  传入需要序列化的值\n * @return {*}  序列化后的值\n */\nfunction serializeToDraft(obj) {\n  return serializeUtil(obj, {\n    space: 2,\n    ignoreUndefined: true,\n  });\n}\n\n/**\n * @description 对serialize-javascript序列化后的值进行反序列化化\n * @param {*} serializedJavascript 反序列化后的值\n * @returns\n */\nfunction deserialize(serializedJavascript) {\n  return new Function('return ' + serializedJavascript)();\n}\n\nexport { deserialize, serialize, serializeToDraft, serializeUtil };\n\n\n\n\n\n\n\n\n"
  },
  {
    "path": "docs/schema-builder/api.md",
    "content": "---\norder: 0\nmobile: false\n---\n# API\n\n## Props\n\n| 属性                  | 描述                                                  | 类型                      | 默认值     |\n| ---------------------| ----------------------------------------------------- | -------------------      | --------- |\n| defaultValue         | 默认 schema                                            |    `schema`              |     -     |\n| onMount              | 首次加载完成                                            |    `Function`            |     -     |\n| logo                 | logo 模块设置                                          | [Logo](#Logo)| -         | -         |\n| importBtn            | 导入按钮，用于 schema 导入                               | `boolean`                |   false   |\n| exportBtn            | 导出按钮，用于 schema 导出                               | `boolean`                |   false   |\n| clearBtn             | 清除按钮  用于清除设计器 schema                           | `false` ｜ [Btn](#Btn)   |     -     |\n| saveBtn              | 保存按钮，用于保存 schema                                | `false` ｜ [Btn](#Btn)   |     -     |\n| pubBtn               | 发布按钮，用于发布 schema                                | `false` ｜ [Btn](#Btn)   |     -     |\n| extraBtns            | 自定义更多按钮                                          | [Btn](#Btn) []           |     -     |\n\n## Logo\n\n我们将搜索相关的能力放到 `<Search />` 上面配置，包括对应的搜索筛选表单的渲染\n\n| 属性                  | 描述                                                                      | 类型                 | 默认值     |\n| --------------------- | ------------------------------------------------------------------------ | ------------------- | --------- |\n| image         |   图片地址                                                                        | `string`      |   -     |\n| href          |   图片点击跳转地址                                                                  | `string`    |   -     |\n| title         |   标题                                                                            | `string`     |   -     |\n\n\n## Btn\n| 属性                  | 描述                                                                      | 类型                 | 默认值     |\n| -------------------- | ------------------------------------------------------------------------  | ------------------- | --------- |\n| key                 |   按钮 key                                                                  | `string`           |   必填      |\n| text                 |   按钮文案                                                                  | `string`           |   -       |\n| order                |   按钮顺序                                                                  | `number`           |   -       |\n| onClick              |   按钮点击回调函数                                                           | `(schema) => void`  |   -       |\n\n\n## Methods\n通过配置 ref，获取组件实例访问，由于中间隔了一层 iframe，不要在 React useEffect 中调用方法，请使用 `onMount`\n| 属性                  | 描述                                                                      | 类型                 | 默认值     |\n| -------------------- | ------------------------------------------------------------------------  | ------------------- | --------- |\n| getValue                 |   获取 schema                                                               | `schema`           |   -       |\n| setValue                |    设置 schema                                                                  | `schema`           |   -       |"
  },
  {
    "path": "docs/schema-builder/index.md",
    "content": "---\norder: 0\ntitle: 开始使用\nmobile: false\n---\n\n<div style=\"display:flex;align-items:center;margin-bottom:24px\">\n  <img src=\"https://img.alicdn.com/tfs/TB17UtINiLaK1RjSZFxXXamPFXa-606-643.png\" alt=\"logo\" width=\"48px\"/>\n  <span style=\"font-size:30px;font-weight:600;display:inline-block;margin-left:12px\">SchemaBuilder</span>\n</div>\n<p style=\"display:flex;justify-content:space-between;width:440px\">\n  <a href=\"https://www.npmjs.com/package/@xrenders/schema-builder\" target=\"_blank\">\n    <img alt=\"npm\" src=\"https://img.shields.io/npm/v/@xrenders/schema-builder.svg?maxAge=3600&style=flat-square\">\n  </a>\n  <a href=\"https://npmjs.org/package/@xrenders/schema-builder\" target=\"_blank\">\n    <img alt=\"NPM downloads\" src=\"https://img.shields.io/npm/dm/@xrenders/schema-builder.svg?style=flat-square\">\n  </a>\n  <a href=\"https://npmjs.org/package/@xrenders/schema-builder\" target=\"_blank\">\n    <img alt=\"NPM all downloads\" src=\"https://img.shields.io/npm/dt/@xrenders/schema-builder.svg?style=flat-square\">\n  </a>\n  <a>\n    <img alt=\"PRs Welcome\" src=\"https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square\">\n  </a>\n</p>\n\n## 简介\nJsonSchema 生成器，快速生成 formRender 2.0 表单协议，1.0 版本表单设计器 <a href=\"https://1.xrender.fun/generator\" target=\"_blank\">请访问此链接</a>\n\n注意：\n- 目前只有 alpha 版本，已实现内置组件可视化编排，快速生成 JsonSchema，预计 6月底会发布正式包。\n- 暂时不支持自定义组件接入，这块功能正在研发中\n- 关于设计器定制化的需求，可以在讨论组中 <a href=\"https://github.com/alibaba/x-render/discussions/1110\" target=\"_blank\">留言</a>\n\n## 安装\n```shell\nnpm i @xrenders/schema-builder --save\n```\n\n项目需要配置\n```json\nexternals: {\n  \"react\": \"React\",\n  \"react-dom\": \"ReactDOM\",\n}\n```\n- 注意：externals 之后需要在 html 里面引入 React、ReactDOM cdn 资源\n\n## 使用方式\n\n<a href=\"/schema-builder-online\" target=\"_blank\">全屏体验</a>\n\n```jsx\n/**\n* transform: true\n* defaultShowCode: true\n* background: 'rgb(204,204,204, .33)'\n* padding: 20px\n*/\nimport React from 'react';\nimport SchemaBuilder from '@xrenders/schema-builder';\n\nconst Demo = () => {\n  return (\n    <div style={{ height: '80vh' }}>\n      <SchemaBuilder importBtn={true} exportBtn={true} pubBtn={false} />\n    </div>\n  );\n};\n\nexport default Demo;\n```"
  },
  {
    "path": "docs/schema-builder-online/index.md",
    "content": "---\nsidebar: false\n---\n\n```jsx\n/**\n * transform: true\n * compact: true\n * inline: true\n */\nimport React from 'react';\nimport SchemaBuilder from '@xrenders/schema-builder';\n\nimport ReactDOM from 'react-dom';\n\n// 将 React 和 ReactDOM 绑定到全局作用域中\nwindow.React = React;\nwindow.ReactDOM = ReactDOM;\n\nconst Demo = () => {\n  return (\n    <div style={{ height: '100vh' }}>\n      <SchemaBuilder importBtn={true} exportBtn={true} pubBtn={false} />\n    </div>\n  );\n};\n\nexport default Demo;\n```"
  },
  {
    "path": "docs/table-render/.test/test.md",
    "content": "<!-- <code src='./test1.jsx'></code> -->\n"
  },
  {
    "path": "docs/table-render/api.md",
    "content": "---\norder: 0\nmobile: false\ngroup: \n  title: 其他\n  order: 5\n---\n# API\n\n## TableRender\n\n对 Antd Table 的 [props](https://ant-design.antgroup.com/components/table-cn/#Table) 完全兼容，但 `dataSource`, `loading` 这两个参数是内部状态，不允许配置\n\n| 属性                  | 描述                                                                   | 类型                             | 默认值 |\n| --------------------- | ---------------------------------------------------------------------- | -------------------------------- | ------ |\n| search                | 搜索表单，不传就是无查询列表                                           | [Search](#search)                | -      | - |\n| request               | **必填**，初始化&点击查询时执行的函数                                  | [Request](#request-1)            | -      | - |\n| title                 | 表格标题                                                               | `string \\| ReactNode`            | -      | - |\n| toolbarRender         | 表格主体右上方的控件，例如“添加”按钮                                   | `() => ReactNode`                | false  |\n| toolbarAction         | 显示在表格主体右上方的 Icon 列表，内置了刷新、调整密度、全屏显示等功能 | `boolean \\| ToolbarActionConfig` | false  |\n| pageChangeWithRequest | 切换分页时是否需要请求接口                                             | `boolean`                        | true   |\n| columns               | 列定义                                                                 | [ProColumnsType](#columns)       | -      |\n\n### ToolbarActionConfig\n\n工具栏的具体配置\n\n| 属性    | 描述       | 类型                                                                          | 默认值    | 版本 |\n| ------- | ---------- | ----------------------------------------------------------------------------- | --------- | ---- |\n| enabled | 启用的功能 | ` Array<'refresh' \\| 'columnsSetting' \\| 'fullScreen' \\| 'density'>` | `['refresh', 'columnsSetting', 'fullScreen', 'density']` |      |\n\n## Search\n\n我们将搜索相关的能力放到 `<Search />` 上面配置，包括对应的搜索筛选表单的渲染\n\n| 属性               | 描述                                                                                  | 类型                                   | 默认值  |\n| ------------------ | ------------------------------------------------------------------------------------- | -------------------------------------- | ------- | \n| schema             | **必填**，用于渲染查询表单，详见[form-render 文档](/form-render/api-schema) | <a target=\"_blank\" href=\"https://github.com/alibaba/x-render/blob/e2feff8fdb3bef5537b92a2157dbbf40b9d4eb17/packages/form-render/src/type.ts#L32\">SchemaBase</a>                               | -       | \n| collapsed           | 是否可折叠                             | `boolean`          |  -    |\n| defaultCollapsed    | 折叠收起                               | `boolean`          |  true   |\n| loading             | 查询按钮加载中                          | `boolean`          |  -    |\n| column              | 一行多列                               | `number`           |  4    |\n| layoutAuto            | 自适应布局，可设置为 true / false 或对象：{ fieldMinWidth: `number` }， 当设置fieldMinWidth 会根据最小宽度动态自适应                         | `boolean` or `object`         |  `false `   |\n| onSearch           | 在表格查询时执行一些额外的操作                                                        | `(params) => void`                             | -       |\n| afterSearch        | 在表格查询结束后执行一些额外的操作                                                    | `(params) => void`                             | -       |\n| searchOnMount      | 组件初次挂载时，是否默认执行查询动作                                                  | `boolean`                              | true  | \n| hidden             | 是否隐藏 `<Search />` 组件                                                              | `boolean`                              | false |\n| searchBtnRender    | 自定义表单查询按钮                                                                    | `(refresh,clearSearch, { loadding }) => ReactNode[]` | -       | \n| searchBtnStyle     | 自定义表单操作按钮组的样式                                                            | `CSSProperties`                  | -      | \n| searchBtnClassName | 自定义表单操作按钮组的 ClassName                                                      | `string`                               | -      | \n| searchWithError    | 表单校验失败时，是否继续执行查询操作                                                  | `boolean`                              | true    |\n| searchText         | 自定义查询按钮的文本                                                                  | `string`                               | 查询  | \n| resetText          | 自定义重置按钮的文本                                                                  | `string`                               | 重置  | \n\n## Request\n入参：`params`、`sorter`，分别是表单筛选项的值、排序参数。\n出参： 需要返回一个对象，此对象中必须要有 `data` 和 `total`。\n \n```jsx | pure\nconst request = async (params, sorter) => {\n  const result = await getTableData(params, sorter);\n\n  return {\n    data: result.list,    // request 对应表格的 dataSource，必须返回\n    total: result.total,  // total 对应数据的总数，用于分页，必须返回\n  }\n}\n\n```\n**多个请求**： 用于 TableRender 多个 Tab 的情况\n```jsx | pure\n\nconst getHotel = async (params) => {\n  const result = await getHotelData(params);\n  return {\n    data: result.list,\n    total: result.total,\n  }\n};\n\nconst getPeople = async (params) => {\n  const result = await getPeopleData(params);\n  return {\n    data: result.list,\n    total: result.total,\n  }\n};\n\nconst request = [\n  { name: '酒店数据', api: getHotel },\n  { name: '人员数据', api: getPeople },\n];\n\n```\n## Columns\n\ncolumns 为 antd 已有的 props，支持 antd 所有的 <a href=\"https://ant.design/components/table-cn/#Column\" target=\"_blank\">columns</a> 配置，同时也提供了一些更方便的 api，加快书写\n\n| 属性      | 描述                                   | 类型                                        | 默认值 |\n| --------- | -------------------------------------- | ------------------------------------------- | ------ |\n| ellipsis  | 是否自动缩略                           | `boolean`                                   | -      |\n| copyable  | 是否支持复制                           | `boolean`                                   | -      |\n| valueType | 值的类型，详见 [ValueType](#valuetype) | `'text' \\| 'money' \\| 'date' \\| 'dateTime'` | text   |\n| enum      | 当前列值的枚举，详见[Enum](#enum)      | `object`                                    | -      |\n\n#### ValueType\n\nTableRender 封装了一些常用的值类型来减少重复的 render 操作，配置一个 valueType 即可展示格式化响应的数据\n\n| 属性          | 描述                                             |\n| ------------- |------------------------------------------------|\n| text          | 普通的文本类型                                        |\n| date          | 当数据是日期类型的返回时，会自动将格式转换为 `YYYY-MM-DD`            |\n| dateTime      | 当数据是日期类型的返回时，会自动将格式转换为 `YYYY-MM-DD HH:mm:ss`   |\n| dateRange     | 当数据是日期区间类型的返回时，会自动将格式转换为 `YYYY-MM-DD`          |\n| money         | 当数据是金额时，会自动将格式转换为 `¥0,0.00`                    |\n| `...`          | 详见 tableRender [Props](/table-render/valueType) |\n\n  ```js\n  const columns = [\n    {\n      title: '酒店GMV',\n      dataIndex: 'money',\n      valueType: 'money', // 自动将格式转换为 '¥0,0.00'  \n    },\n    {\n      title: '成立时间',\n      dataIndex: 'created_at',\n      valueType: 'date', // 自动将格式转换为 'YYYY-MM-DD' \n    },\n    // ...\n  ]\n  ```\n\n#### Enum\n\n当前列值的枚举，方便处理表格值的映射\n\n```js\nconst columns = [\n  {\n    title: '酒店状态'\n    dataIndex: 'state',\n    enum: {\n      open: '营业中',   // 自动将 open 转换为 营业中\n      closed: '已打烊', // 自动将 closed 转换为 已打烊\n    },\n  },\n  // ...\n]\n```\n\n## Ref\n\n可通过 `Ref` 获取如下 `table-render` 的 context\n\n| 属性      | 描述                                                                                                                         | 类型                                |\n| --------- | ---------------------------------------------------------------------------------------------------------------------------- | ----------------------------------- |\n| refresh   | 刷新表格数据，详见[Refresh](#refresh)                                                                                        | `(config, search) => Promise<void>` |\n| changeTab | 手动切换 tab 的函数，例如目前两个搜索 tab： “我的活动”，“全部活动” （分别对应 tab 值为 0 和 1），详见[ChangeTab](#changetab) | `(tab) => void`                     |\n| form      | Search 组件是 form-render 生成的，可以取到搜索表单的 form 实例以及挂在上面的方法，例如 `form.resetFields` 清空搜索项         | `object`                            |\n| getState  | 这些是全局的状态，根据需要使用                                                                                               | [TableStateType](#tablestate)       |\n| setState  | 用于修改全局状态的工具函数，setTable 之于 tableState，等同 setState 之于 state                                               | `(tableState) => void`              |\n\n#### Refresh\n\n主动触发表单刷新的方法\n\n```ts\ntype Refresh = (\n  config?: { \n    stay: boolean, // 刷新之后是否停留在目前的页码上，默认 false，回到第一页\n    tab: number    // searchApi 有多个时，用于强制搜索某个 tab 对应的 searchApi\n  },\n  search?: any     // 额外传递给 searchApi 的参数\n) => Promise<void>;\n\n\nconst onClick = () => {\n  tableRef.current.refresh({ stay: true }); // 刷新数据，但停留在现有的页码\n}\n\n```\n\n#### ChangeTab\n\n手动切换当前 tab 的方法\n\n  ```ts\n  type ChangeTab = (\n    tab: number\n  ) => Promise<void>;\n\n  const onClick = () => {\n    tableRef.current.changeTab(1);   // 手动切换到对应tab\n  };\n  ```\n"
  },
  {
    "path": "docs/table-render/basic.md",
    "content": "---\norder: 2\ntitle: '基础交互'\nmobile: false\ngroup: \n  title: 最佳展示\n  order: 2\n---\n# 基础交互\n\n<code src=\"./demo/basic/index.tsx\"></code>\n"
  },
  {
    "path": "docs/table-render/collapsed.md",
    "content": "---\norder: 2\nmobile: false\ntitle: '搜索栏折叠'\ngroup: \n  title: 最佳展示\n  order: 2\n---\n# 搜索栏折叠\n\n## 默认收起\n```jsx\n/**\n * transform: true\n * defaultShowCode: true\n * background: 'rgb(245,245,245)'\n */\nimport React, { useRef } from 'react';\nimport TableRender, { TableContext } from 'table-render';\n\nimport { schema2 } from './static/search';\nimport { columns, toolbarRender } from './static/table';\nimport { searchApi, searchApi2 } from './static/request';\n\nconst Demo = () => {\n  const tableRef = useRef(null);\n\n  return (\n    <TableRender\n      ref={tableRef}\n      search={{\n        schema: schema2,\n        collapsed: true,\n      }}\n      request={searchApi}\n      columns={columns}\n      toolbarRender={toolbarRender}\n    />\n  )\n};\n\nexport default Demo;\n```\n\n\n## 默认展开\n```jsx\n/**\n * transform: true\n * defaultShowCode: true\n * background: 'rgb(245,245,245)'\n */\nimport React, { useRef } from 'react';\nimport TableRender from 'table-render';\n\nimport { schema2 } from './static/search';\nimport { columns, toolbarRender } from './static/table';\nimport { searchApi, searchApi2 } from './static/request';\n\n\nconst Demo = () => {\n  const tableRef = useRef();\n\n  return (\n    <TableRender\n      ref={tableRef}\n      search={{\n        schema: schema2,\n        collapsed: true,\n        defaultCollapsed: false\n      }}\n      request={searchApi}\n      columns={columns}\n      toolbarRender={toolbarRender}\n    />\n  )\n};\n\nexport default Demo;\n```\n\n\n"
  },
  {
    "path": "docs/table-render/custom-table.md",
    "content": "---\norder: 6\nmobile: false\ntitle: 'Table 包裹容器'\ngroup: \n  title: 最佳展示\n  order: 0\n---\n\n# tableWrapper 包裹容器\n\n有些情况下，你会希望在搜索栏和表格之间增加一些内容。这时可以通过 `tableWrapper` 实现你的需求。\n\n<code src=\"./demo/display/custom-table.tsx\" background=\"rgb(245,245,245)\"></code>\n"
  },
  {
    "path": "docs/table-render/demo/basic/index.tsx",
    "content": "/**\n * transform: true\n * defaultShowCode: true\n * background: 'rgb(245,245,245)'\n */\nimport React, { useRef } from 'react';\nimport TableRender, { TableContext } from 'table-render';\nimport { Button } from 'antd';\nimport { schema } from '../../static/search';\nimport { columns } from '../../static/table';\nimport { searchApi } from '../../static/request';\n\n\nconst Demo = () => {\n  const tableRef = useRef<TableContext>(null);\n\n  const handleClick = () => {\n    tableRef.current?.refresh();\n  }\n\n  const handleClick2 = () => {\n    tableRef.current?.refresh({ stay: true });\n  }\n\n  const handleClick3 = () => {\n    tableRef.current?.form.setValues({ pageId: 'xxx' })\n  }\n\n  return (\n    <TableRender\n      ref={tableRef}\n      search={{ schema }}\n      request={searchApi}\n      columns={columns}\n      pagination={{ pageSize: 2 }}\n      toolbarRender={\n        <>\n          <Button onClick={handleClick}>刷新列表</Button>\n          <Button onClick={handleClick2}>保持当前页刷新</Button>\n          <Button onClick={handleClick3}>将url参数同步到查询条件里面</Button>\n        </>\n      }\n    />\n  )\n};\n\nexport default Demo;\n"
  },
  {
    "path": "docs/table-render/demo/display/custom-table.tsx",
    "content": "/**\n * defaultShowCode: true\n */\nimport React, { useRef } from 'react';\nimport TableRender, { TableContext } from 'table-render';\nimport { Alert } from 'antd';\n\nimport { schema } from '../../static/search';\nimport { columns, toolbarRender } from '../../static/table';\nimport { searchApi } from '../../static/request';\n\nconst Demo = () => {\n  const tableRef = useRef<TableContext>(null);\n\n  return (\n    <TableRender\n      ref={tableRef}\n      search={{\n        schema: schema\n      }}\n      request={searchApi}\n      columns={columns}\n      toolbarRender={toolbarRender}\n      tableWrapper={(table) => (\n        <div>\n          <Alert\n            message=\"Warning Text Warning Text Warning TextW arning Text Warning Text Warning TextWarning Text\"\n            type=\"warning\"\n            closable\n            style={{ marginBottom: 16 }}\n          />\n          {table}\n        </div>\n      )}\n    />\n  )\n};\n\nexport default Demo;\n"
  },
  {
    "path": "docs/table-render/demo/toolbar/basic.tsx",
    "content": "/**\n * background: 'rgb(245,245,245)'\n */\nimport React, { useRef } from 'react';\nimport TableRender, { ProColumnsType } from 'table-render';\nimport { schema } from '../../static/search';\nimport { searchApi } from '../../static/request';\n\nconst Demo = () => {\n  const tableRef: any = useRef();\n\n  const columns: ProColumnsType<any> = [\n    {\n      title: '酒店名称',\n      // dataIndex 唯一时，可以省略 key\n      dataIndex: 'title',\n      valueType: 'text',\n      width: '20%'\n    },\n    {\n      title: '酒店地址',\n      dataIndex: 'address',\n      ellipsis: true,\n      copyable: true,\n      valueType: 'text',\n      width: '25%',\n    },\n    {\n      title: '酒店状态',\n      enum: {\n        open: '营业中',\n        closed: '已打烊',\n      },\n      dataIndex: 'state',\n    },\n    {\n      title: '酒店星级',\n      dataIndex: 'labels',\n      width: 90,\n      valueType: 'tags'\n    },\n    {\n      title: '酒店GMV',\n      sorter: true,\n      dataIndex: 'money',\n      valueType: 'money',\n    },\n    {\n      title: '成立时间',\n      dataIndex: 'created_at',\n      valueType: 'date',\n    },\n  ];\n\n  return (\n    <TableRender\n      ref={tableRef}\n      search={{ schema }}\n      scroll={{ x: 1500 }}\n      request={searchApi}\n      columns={columns}\n      pagination={{ pageSize: 2 }}\n      toolbarAction\n    />\n  )\n};\n\nexport default Demo;"
  },
  {
    "path": "docs/table-render/demo/toolbar/selection-tool.tsx",
    "content": "/**\n * transform: true\n * defaultShowCode: false\n * background: 'rgb(245,245,245)'\n */\nimport React, { useRef } from 'react';\nimport TableRender, { ProColumnsType } from 'table-render';\nimport { schema } from '../../static/search';\nimport { searchApi } from '../../static/request';\n\nconst Demo = () => {\n  const tableRef: any = useRef();\n\n  const columns: ProColumnsType<any> = [\n    {\n      title: '酒店名称',\n      // dataIndex 唯一时，可以省略 key\n      dataIndex: 'title',\n      valueType: 'text',\n      width: '20%'\n    },\n    {\n      title: '酒店地址',\n      dataIndex: 'address',\n      ellipsis: true,\n      copyable: true,\n      valueType: 'text',\n      width: '25%',\n    },\n    {\n      title: '酒店状态',\n      enum: {\n        open: '营业中',\n        closed: '已打烊',\n      },\n      dataIndex: 'state',\n    },\n    {\n      title: '酒店星级',\n      dataIndex: 'labels',\n      width: 90,\n      valueType: 'tags'\n    },\n    {\n      title: '酒店GMV',\n      sorter: true,\n      dataIndex: 'money',\n      valueType: 'money',\n    },\n    {\n      title: '成立时间',\n      dataIndex: 'created_at',\n      valueType: 'date',\n    },\n  ];\n\n  return (\n    <TableRender\n      ref={tableRef}\n      search={{ schema }}\n      scroll={{ x: 1500 }}\n      request={searchApi}\n      columns={columns}\n      pagination={{ pageSize: 2 }}\n      toolbarAction={{\n        enabled: ['columnsSetting']\n      }}\n    />\n  )\n};\n\nexport default Demo;"
  },
  {
    "path": "docs/table-render/index.md",
    "content": "---\norder: 0\nmobile: false\ntitle: '使用教程'\ngroup: \n  order: 1\n---\n\n<div style=\"display:flex;align-items:center;margin-bottom:24px\">\n  <img src=\"https://img.alicdn.com/tfs/TB17UtINiLaK1RjSZFxXXamPFXa-606-643.png\" alt=\"logo\" width=\"48px\"/>\n  <span style=\"font-size:30px;font-weight:600;display:inline-block;margin-left:12px\">TableRender</span>\n</div>\n\n<p style=\"display:flex;justify-content:space-between;width:440px\">\n  <a href=\"https://www.npmjs.com/package/table-render?_blank\">\n    <img alt=\"npm\" src=\"https://img.shields.io/npm/v/table-render.svg?maxAge=3600&style=flat-square\">\n  </a>\n  <a href=\"https://npmjs.org/package/table-render\">\n    <img alt=\"NPM downloads\" src=\"https://img.shields.io/npm/dm/table-render.svg?style=flat-square\">\n  </a>\n  <a href=\"https://npmjs.org/package/table-render\">\n    <img alt=\"NPM all downloads\" src=\"https://img.shields.io/npm/dt/table-render.svg?style=flat-square\">\n  </a>\n  <a href=\"https://github.com/alibaba/x-render\" >\n    <img alt=\"PRs Welcome\" src=\"https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square\">\n  </a>\n</p>\n中后台列表开箱即用解决方案，常用于搜索列表快速生成\n\n## 简介\n\n* **开箱即用**：以最简单的方式配置 API 请求和表头字段，就能生成一个好用的搜索列表。\n* **XRender 生态**：搜索部分集成 FormRender，以最小成本快速生成搜索面板。\n* **无缝对接**：表格部分沿用 Ant Design Table， API 无缝对接，降低用户使用成本。\n* **数据模版**：表格列内置多种数据展示模版，减少自定义 Render 函数配置。\n* **多种形态**：支持搜索栏、工具栏、表格内容，根据业务需求相互组合展示多种形态。\n\n## 安装\n\ntable-render 依赖 ant design，单独使用不要忘记安装～\n```sh\nnpm i table-render --save\n```\n\n## 最简 Demo\n\n```jsx\n/**\n * transform: true\n * defaultShowCode: true\n * background: 'rgb(245,245,245)'\n */\nimport React from 'react';\nimport TableRender from 'table-render';\nimport { Button } from 'antd';\nimport { InfoCircleOutlined, PlusOutlined } from '@ant-design/icons';\n\nconst dataSource = [];\nfor (let i = 0; i < 6; i++) {\n  dataSource.push({\n    id: i.toString(),\n    title: `标题${i + 1}`,\n    created_at: new Date().getTime(),\n  });\n}\n\nconst schema = {\n  type: 'object',\n  labelWidth: 70,\n  properties: {\n    title: {\n      title: '标题',\n      type: 'string'\n    },\n    created_at: {\n      title: '创建时间',\n      type: 'string',\n      format: 'date'\n    }\n  }\n};\n\nconst columns = [\n  {\n    title: '标题',\n    dataIndex: 'title',\n  },\n  {\n    title: '创建时间',\n    key: 'since',\n    dataIndex: 'created_at',\n    valueType: 'date',\n  },\n  {\n    title: '操作',\n    render: (row, record) => <a onClick={() => alert(row.title)}>编辑</a>,\n  }\n];\n\nconst Demo = () => {\n  \n  const api = () => {\n    return {\n      data: dataSource,\n      total: dataSource.length\n    };\n  };\n\n  return (\n    <TableRender\n      search={{ schema }}\n      request={api}\n      columns={columns}\n      title='最简表格'\n      toolbarRender={ \n        <>\n          <Button>查看日志</Button>\n          <Button>导出数据</Button>\n          <Button type='primary'>\n            <PlusOutlined />\n            新增\n          </Button>\n        </>\n      }\n    />\n  );\n}\n\nexport default Demo;\n```\n\n"
  },
  {
    "path": "docs/table-render/migrate.md",
    "content": "---\norder: 0\nmobile: false\ngroup: \n  title: 其他\n  order: 5\n---\n# V2 升级方案\n\n本文档将帮助你从 1.x 升级到 2.x 版本\n\n## 特性\n\n全新的 table-render 2.0 主要具备以下特性：\n\n- 🚀 **更好的搜索性能**：解决了单页列表数据过多，表格搜索严重卡顿的问题\n- 🎨 **更简单的使用方式**：使用时不在需要导出  Search, Table, useTable, withTable 对象，统一导出默认对象 default(TableRender)即可\n- 🚥 **国际化**：国际化多语言支持，内置中英文语言包，英文版 locale: 'en-US'\n- 💎 **Antd V5**：兼容 antd V5 版本，无需配置\n\n## 有哪些不兼容的变化\n\n#### 导出实例变更\n```diff\n- import { Search, Table, useTable, withTable } from 'table-render';\n\n- const Demo = () => {\n-   return (\n-     <div>\n-      <Search {...searchProps} />\n-      <Table {...tableProps}/>\n-     </div>\n-   );\n- }\n- export default withTable(Demo);\n\n\n+ import TableRender form 'table-render';\n+ const Demo = () => {\n+   return (\n+     <TableRender \n+      search={{\n+        ...searchProps\n+      }}\n+      {...tableProps}\n+     />\n+   )\n+ }\n+ export default Demo;\n\n```\n\n#### 废弃 useTable，改用 ref 获取\ntableState、setTable 移除，改用 getState()、setState()\n```diff\n- import { Search, Table, useTable, withTable } from 'table-render';\n- const { refresh } = useTable();\n\n+ import React, { useRef } from 'react';\n+ const tableRef = useRef(); // tableRef.current = { refresh, changeTab, form, getState }\n+ <TableRender\n+   ref={tableRef}\n+ />\n```\n\n#### api 移入到 tableProps 里面 变成 request\n返回参数 rows 改成 data\n```diff\nconst api = (params, sorter) => {\n  return {\n    data: [],\n    total: 10\n  }\n};\n-  <div>\n-   <Search api={api} />\n-   <Table {...tableProps}/>\n-  </div>\n\n\n+  <TableRender \n+    search={{\n+        ...searchProps\n+    }}\n+    request={api}\n+    {...tableProps}\n+  />\n```\n\n#### headTitle 变更成 title"
  },
  {
    "path": "docs/table-render/noSearch.md",
    "content": "---\norder: 3\nmobile: false\ngroup: \n  title: 最佳展示\n  order: 2\n---\n\n\n# 无搜索栏\n\n```jsx\n/**\n * transform: true\n * defaultShowCode: true\n * background: 'rgb(245,245,245)'\n */\nimport React, { useRef } from 'react';\nimport TableRender from 'table-render';\n\nimport { schema } from './static/search';\nimport { columns, toolbarRender } from './static/table';\nimport { searchApi, searchApi2 } from './static/request';\n\nconst Demo = () => {\n  const tableRef = useRef();\n\n  return (\n    <TableRender\n      ref={tableRef}\n      columns={columns}\n      request={searchApi}\n      toolbarRender={toolbarRender}\n    />\n  )\n};\n\nexport default Demo;\n```\n"
  },
  {
    "path": "docs/table-render/promptly-search.md",
    "content": "---\norder: 3\nmobile: false\ntitle: '查询实时响应'\ngroup: \n  title: 最佳展示\n  order: 2\n---\n# 实时响应\n有些情况下，我们需要当查询条件值改变时，立即触发查询，这个时候可以配置 `search: { mode : 'simple' }`\n\n## 按钮保留\n```jsx\n/**\n * transform: true\n * defaultShowCode: true\n * background: 'rgb(245,245,245)'\n */\nimport React, { useRef } from 'react';\nimport TableRender, { TableContext } from 'table-render';\n\nimport { schema } from './static/search';\nimport { columns, toolbarRender } from './static/table';\nimport { searchApi, searchApi2 } from './static/request';\n\nconst Demo = () => {\n  const tableRef = useRef(null);\n\n  return (\n    <TableRender\n      ref={tableRef}\n      search={{\n        schema: schema,\n        mode: 'simple',\n        retainBtn: true\n      }}\n      request={searchApi}\n      columns={columns}\n      toolbarRender={toolbarRender}\n    />\n  )\n};\n\nexport default Demo;\n```\n\n## 保留某一个\n```jsx\n/**\n * transform: true\n * defaultShowCode: true\n * background: 'rgb(245,245,245)'\n */\nimport React, { useRef } from 'react';\nimport TableRender from 'table-render';\n\nimport { schema } from './static/search';\nimport { columns, toolbarRender } from './static/table';\nimport { searchApi, searchApi2 } from './static/request';\n\nconst Demo = () => {\n  const tableRef = useRef();\n\n  return (\n    <TableRender\n      ref={tableRef}\n      search={{\n        schema: schema,\n        mode: 'simple',\n        retainBtn: ['reset'] // ['rest', 'submit']\n      }}\n      request={searchApi}\n      columns={columns}\n      toolbarRender={toolbarRender}\n    />\n  )\n};\n\nexport default Demo;\n```\n\n## 按钮不保留\n\n```jsx\n/**\n * transform: true\n * defaultShowCode: true\n * background: 'rgb(245,245,245)'\n */\nimport React, { useRef } from 'react';\nimport TableRender from 'table-render';\n\nimport { schema } from './static/search';\nimport { columns, toolbarRender } from './static/table';\nimport { searchApi, searchApi2 } from './static/request';\n\nconst Demo = () => {\n  const tableRef = useRef();\n\n  return (\n    <TableRender\n      ref={tableRef}\n      search={{\n        schema: schema,\n        mode: 'simple'\n      }}\n      request={searchApi}\n      columns={columns}\n      toolbarRender={toolbarRender}\n    />\n  )\n};\n\nexport default Demo;\n```\n"
  },
  {
    "path": "docs/table-render/static/request.ts",
    "content": "import request from 'umi-request';\n\nconst requestData = (params: any) => {\n  return request\n    .get(\n      'https://www.fastmock.site/mock/62ab96ff94bc013592db1f67667e9c76/getTableList/api/basic',\n      { params }\n    )\n    .then(res => ({ success: true, data: res.data }))\n    .catch(() => ({ success: false, data: {} }))\n}\n\n\nexport const searchApi = async (params) => {\n  const { success, data } = await requestData(params);\n  if (success) {\n    return {\n      data: data,\n      total: data.length,\n    }\n  } else {\n    // 必须返回 data 和 total\n    return {\n      data: [],\n      total: 0,\n    }\n  }\n};\n\nexport const searchApi2 = async (params) => {\n  const { success, data } = await requestData(params);\n  if (success) {\n    return {\n      data: data.slice(1),\n      total: data.length - 1,\n    }\n  } else {\n    // 必须返回 data 和 total\n    return {\n      data: [],\n      total: 0,\n    }\n  }\n};"
  },
  {
    "path": "docs/table-render/static/search.ts",
    "content": "export const schema = {\n  type: 'object',\n  labelWidth: 80,\n  properties: {\n    state: {\n      title: '酒店状态',\n      type: 'string',\n      widget: 'select',\n      props: {\n        options: [\n          { label: '营业中', value: 'open' },\n          { label: '已打烊', value: 'closed' },\n        ],\n      },\n    },\n    labels: {\n      title: '酒店星级',\n      type: 'string',\n    },\n    created_at: {\n      title: '成立时间',\n      type: 'string',\n      format: 'date',\n    },\n  },\n};\n\nexport const schema2 = {\n  type: 'object',\n  labelWidth: 80,\n  properties: {\n    state: {\n      title: '酒店状态',\n      type: 'string',\n      widget: 'select',\n      props: {\n        options: [\n          { label: '营业中', value: 'open' },\n          { label: '已打烊', value: 'closed' },\n          { label: '暂停营业', value: 'stop' },\n        ],\n      },\n    },\n    labels: {\n      title: '酒店星级',\n      type: 'string',\n    },\n    created_at: {\n      title: '成立时间',\n      type: 'string',\n      format: 'date',\n    },\n    labels1: {\n      title: '酒店星级',\n      type: 'string',\n    },\n    labels2: {\n      title: '酒店地址',\n      type: 'string',\n    },\n    labels3: {\n      title: '酒店状态',\n      type: 'string',\n    },\n    labels4: {\n      title: '酒店区域',\n      type: 'string',\n    },\n\n    labels5: {\n      title: '酒店区域',\n      type: 'string',\n    },\n\n\n    created_at1: {\n      title: '成立时间',\n      type: 'string',\n      format: 'date',\n    },\n    labels11: {\n      title: '酒店星级',\n      type: 'string',\n    },\n    labels21: {\n      title: '酒店地址',\n      type: 'string',\n    },\n    labels31: {\n      title: '酒店状态',\n      type: 'string',\n    },\n    labels41: {\n      title: '酒店区域',\n      type: 'string',\n    },\n\n    labels51: {\n      title: '酒店区域',\n      type: 'string',\n    },\n    \n  },\n};\n"
  },
  {
    "path": "docs/table-render/static/table.tsx",
    "content": "import React from 'react';\nimport { Button } from 'antd';\nimport { PlusOutlined } from '@ant-design/icons';\nimport { ProColumnsType } from 'table-render';\n\nexport const columns: ProColumnsType = [\n  {\n    title: '酒店名称',\n    dataIndex: 'title',\n    valueType: 'text',\n    width: '20%'\n  },\n  {\n    title: '酒店地址',\n    dataIndex: 'address',\n    ellipsis: true,\n    copyable: true,\n    valueType: 'text',\n    width: '25%',\n  },\n  {\n    title: '酒店状态',\n    enum: {\n      open: '营业中',\n      closed: '已打烊',\n    },\n    dataIndex: 'state',\n  },\n  {\n    title: '酒店星级',\n    dataIndex: 'labels',\n    width: 90,\n    valueType: 'tags'\n  },\n  {\n    title: '酒店GMV',\n    sorter: true,\n    dataIndex: 'money',\n    valueType: 'money',\n  },\n  {\n    title: '成立时间',\n    dataIndex: 'created_at',\n    valueType: 'date',\n  },\n];\n\nexport const toolbarRender = (\n  <>\n    <Button>查看日志</Button>\n    <Button>导出数据</Button>\n    <Button\n      type='primary'\n      onClick={() => alert('table-render！')}\n    >\n      <PlusOutlined />\n      创建\n    </Button>\n  </>\n);"
  },
  {
    "path": "docs/table-render/tabs.md",
    "content": "---\norder: 6\nmobile: false\ntitle: 'Tab 数据分类'\ngroup: \n  title: 最佳展示\n  order: 2\n---\n\n# 数据分类\n```jsx\n/**\n * transform: true\n * defaultShowCode: true\n * background: 'rgb(245,245,245)'\n */\nimport React, { useRef } from 'react';\nimport TableRender, { TableContext } from 'table-render';\n\nimport { schema } from './static/search';\nimport { columns, toolbarRender } from './static/table';\nimport { searchApi, searchApi2 } from './static/request';\n\nconst Demo = () => {\n  const tableRef = useRef(null);\n\n  return (\n    <TableRender\n      ref={tableRef}\n      search={{\n        schema: schema\n      }}\n       onTabChange={(a) => {\n        console.log('onTabChange');\n      }}\n      request={[\n        { name: '我的', api: searchApi },\n        { name: '全部', api: searchApi2 }\n      ]}\n      columns={columns}\n      toolbarRender={toolbarRender}\n    />\n  )\n};\n\nexport default Demo;\n```\n\n\n"
  },
  {
    "path": "docs/table-render/toolbar.md",
    "content": "---\norder: 7\ntitle: '工具栏'\nmobile: false\ngroup: \n  title: 最佳展示\n---\n\n# 工具栏\n\nTable Render 内置工具栏，通过 `toolbarAction` 开启。默认四种功能，刷新表格、更改表格 `size`、全屏显示表格、表格列设置。\n\n:::warning\n使用表格列设置功能，必须为每个 `column` 指定 `key` 或 `dataIndex`。大多数场景下使用 `dataIndex` 就足够了，但前提是必须保证其是唯一的，如果不是，需要另外指定 `key` 的值。\n:::\n\n<code src=\"./demo/toolbar/basic.tsx\"></code>\n\n可以通过传入一个对象控制具体使用哪些工具，例如只显示列设置功能。\n\n<code src=\"./demo/toolbar/selection-tool.tsx\"></code>"
  },
  {
    "path": "docs/table-render/valueType.md",
    "content": "---\norder: 5\nmobile: false\ntitle: '数据展示模版'\ngroup: \n  title: 最佳展示\n  order: 0\n---\n\n# 数据展示类型\n数据模版：`image`、`money`、`tag`、`tags`、`progress`、`date`、`dateTime`、`dateRange`、`dateTimeRange`，通过配置 `valueType`\n列值转换：通过配置 `enum`\n列头气泡提示：通过配置 `tooltip`\n复制：通过配置 `copyable`\n省略：通过配置 `ellipsis`\n\n```jsx\n/**\n * transform: true\n * defaultShowCode: true\n * background: 'rgb(245,245,245)'\n */\n\nimport React, { useRef } from 'react';\nimport TableRender, { TableContext } from 'table-render';\nimport { schema } from './static/search';\nimport { toolbarRender } from './static/table';\n\nconst Demo = () => {\n  const tableRef = useRef(null);\n\n  const columns = [\n    {\n      title: '图片',\n      dataIndex: 'imgSrc',\n      valueType: 'image',\n      // valueTypeProps: { 配置图片大小 antd iamge props\n\n      // }\n    },\n    {\n      title: 'link',\n      dataIndex: 'link',\n      valueType: 'link',\n      width: '200px',\n      // valueTypeProps: { 配置图片大小 antd iamge props\n      //   type: '_self', // 默认是外跳，内部跳转需配置 self\n      //   onClick: (value, record, index) => void //也可以自定义点击事件\n      //   href: '跳转链接' // 默认显示 value 值，也可以重新配置， string ||  (value, record, index) => string\n      // }\n    },\n    {\n      title: '复制、省略',\n      dataIndex: 'address',\n      valueType: 'text',\n      ellipsis: true,\n      copyable: true,\n      width: '120px',\n    },\n    {\n      title: '标题气泡',\n      tooltip: { title: '气泡提示'},\n      enum: {\n        open: '营业中',\n        closed: '已打烊',\n      },\n      dataIndex: 'state',\n      width: '120px',\n    },\n    {\n      title: '金额',\n      sorter: true,\n      dataIndex: 'money',\n      valueType: 'money',\n    },\n    {\n      title: '标签',\n      dataIndex: 'tagText',\n      valueType: 'tag',\n      valueTypeProps: (value, record) => ({\n        color: value === '1' ? 'red' :  'blue'\n      }),\n      enum: {\n        1: '失败'\n      }\n    },\n    {\n      title: '多标签',\n      dataIndex: 'tags', \n      valueType: 'tags',\n      width: 160,\n      valueTypeProps: (value) => { // [ { name : '', color: '' }] => 命中默认格式无需配置\n        return {\n          name: value?.text,\n          color: 'cyan'\n        }\n      },\n    },\n    {\n      title: '进度条',\n      dataIndex: 'progressNum',\n      valueType: 'progress',\n      // valueTypeProps: (value, record) => ({\n      //   status: 'exception'\n      // })\n      width: '140px',\n    },\n    {\n      title: '枚举转换',\n      dataIndex: 'status',\n      valueType: 'text',\n      enum: {\n        success: '成功',\n        default: '失败' // 默认值\n      }\n    },\n    {\n      title: '时间-切换格式',\n      dataIndex: 'created_at',\n      width: '140px',\n\n      valueType: 'date',\n      valueTypeProps: {\n        format: 'YYYY/MM/DD' // 默认是 'YYYY-MM-DD'\n      }\n    },\n    {\n      title: '时间区间',\n      width: '220px',\n\n      dataIndex: 'dateRange', // 默认值 [start, end]\n      valueType: 'dateRange'\n    },\n    {\n      title: '时间区间（两个字段）',\n      width: '220px',\n\n      valueType: 'dateRange', \n      valueTypeProps: {\n        bind: ['stateDate', 'endDate'] // 时间聚合成数组 [start, end]\n      }\n    }\n  ];\n\n  const searchApi = () => {\n    const list = [{\n      id: 1,\n      address: '余杭区聚橙路和文昌路交叉口',\n      status: 'success',\n      money: 9999999,\n      imgSrc: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',\n      created_at: '2020-05-26T09:42:56Z',\n      progressNum: '10',\n      tagText: '1',\n      tags: [\n        {\n          text: '三星1',\n          color: 'cyan'\n        },\n        {\n          text: '三星2',\n          color: 'cyan'\n        }\n      ],\n      state: 'open',\n      dateRange: ['2020-05-26T09:42:56Z', '2020-05-26T09:42:56Z'],\n      timeRange: ['2020-05-26T09:42:56Z', '2020-05-26T09:42:56Z'],\n      stateDate: '2020-05-26T09:42:56Z',\n      endDate: '2020-05-26T09:42:56Z',\n      link: 'https://xrender.fun/'\n    }];\n\n    return {\n      data: list,\n      total: list.length\n    };\n  };\n\n  return (\n    <TableRender \n      ref={tableRef}\n      search={{ schema }}\n      request={searchApi}\n      columns={columns}\n      toolbarRender={toolbarRender}\n      scroll={{\n        x: 2000\n      }}\n    />\n  )\n};\n\nexport default Demo;\n\n\n\n\n\n```\n"
  },
  {
    "path": "docs/tools/proptypes/index.md",
    "content": "---\ntitle: PropToSchema\ngroup:\n  title: PropToSchema\ntoc: content\n---\n\n# PropToSchema &nbsp;&nbsp;[![npm](https://img.shields.io/npm/v/proptypes-to-json-schema.svg?maxAge=3600&style=flat-square)](https://www.npmjs.com/package/proptypes-to-json-schema?_blank)\n\n> 将 React PropTypes 转换生成 [JSON Schema](https://spacetelescope.github.io/understanding-json-schema/index.html)，常用于 React 模块的可视化配置参数。\n\n## 安装\n\n```sh\nnpm i proptypes-to-json-schema\n```\n\n## 使用\n\n```js\nconst fs = require('fs');\nconst path = require('path');\nconst propToSchema = require('proptypes-to-json-schema');\n\n// 参数为目标文件地址\nconst apiInfo = propToSchema(`${__dirname}/index.jsx`);\nfs.writeFileSync(\n  path.join(__dirname, 'schema.json'),\n  JSON.stringify(apiInfo, null, 2)\n);\n```\n\n## 说明\n\n| 名称    | 类型   | 默认值 | 备注                                                        |\n| ------- | ------ | ------ | ----------------------------------------------------------- |\n| path    | string | 必填   | 需要转换的 jsx 目录地址                                     |\n| options | object | `{}`   | 目前支持参数 shouldAddUi,true 会自动生成带 ui 配置的 schema |\n\n### 目前支持\n\n| 类型   | PropTypes | prop schema type | 默认 ui setting  | 可选 ui setting                                   |\n| ------ | --------- | ---------------- | ---------------- | ------------------------------------------------- |\n| 布尔值 | bool      | boolean          | checkbox         | 无                                                |\n| 字符串 | string    | string           | input[type=text] | textarea、password、color、date、date-time、image |\n| 数字   | number    | number           | input[type=text] | updown(步进器，搭配 min、max、step 使用)          |\n| 单选项 | oneOf     | enum             | select box       | 无                                                |\n| 数组项 | arrayOf   | array            | 组展示(\\*1)      | 同左                                              |\n| 对象   | shape     | object           | 对象展示(\\*2)    | 同左                                              |\n\n1. 子元素展示依赖于 arrayOf 的设置，类似这样设置 `PropTypes.arrayOf(PropTypes.xxx)`,其实 xxx 为前 3 种基本类型,不设置默认是字符串形式\n\n2. 目前对象形式必须通过 PropTypes.shape 来指明类型，目前不支持嵌套对象，只支持嵌套基础类型！\n\n### 使用规范\n\n1. 只有添加了注释的属性，才会去提出对应信息\n2. 从 defaultProps 中获取默认值\n3. 从 propTypes 中获取属性类型转换成 schema 属性名\n4. 注释的填写规范\n   - @title 参数的标题,对应 schema 中的 title 字段，用于可视化配置中的 label\n   - @description 参数的描述，对应 schema 中的 title 字段 description 字段，用于可视化中的输入提示\n   - @format 特殊类型(不必须)，一般不填入，表单默认通过 input 进行输入，一般只有 string 类型才可以设置 format，目前支持 format 字段如下可见上表\n   - @pattern 正则校验表达式(不必须)，主要用于字符串和数字类型的校验，假如长度限制不满足时候，可以使用和这个\n   - @enumNames oneOf 场景中会渲染成一个选择的场景，可以通过 enumNames 数组字段来描述里 key 对应的 title\n5. 顶部可以设置 column 字段用来表示表单展示形式为 1 排 N\n\n   <img src=\"https://img.alicdn.com/tfs/TB1ZoBaPNjaK1RjSZFAXXbdLFXa-1472-622.png\" width=\"300\"/>\n\n## 效果\n\n<table>\n  <tr>\n    <td>React 输入</td>\n    <td>标准Json schema 输出</td>\n    <td>带 Ui 配置的 schema 输出</td>\n  </tr>\n  <tr>\n    <td>\n      <img src=\"https://img.alicdn.com/tfs/TB1jVQFtuuSBuNjy1XcXXcYjFXa-1004-1310.png\" width=\"420\">\n    </td>\n    <td>\n      <img src=\"https://gw.alicdn.com/tfs/TB14I0Rzx1YBuNjy1zcXXbNcXXa-862-1538.png\" width=\"310\">\n    </td>\n    <td>\n      <img src=\"https://gw.alicdn.com/tfs/TB1r9QFwXmWBuNjSspdXXbugXXa-898-1460.png\" width=\"340\">\n    </td>\n  </tr>\n</table>\n\n可以参考 [Demo](https://github.com/alibaba/form-render/tree/master/tools/proptypes-to-json-schema/demo) 中的使用\n"
  },
  {
    "path": "docs/tools/vscode/index.md",
    "content": "---\ntitle: VSCode 插件\ngroup:\n  title: VSCode 插件\ntoc: content\n---\n\n# vscode-plugin-fr-schema\n\n> 可视化编辑器的 VSCode 插件版本，便于维护本地 Schema\n\n## 何时使用\n\n- 表单 Schema 在项目本地维护而非保存在服务端\n- 项目使用 ts 开发，需要编写表单数据类型定义\n\n## 如何使用\n\n### 安装\n\n插件商店搜索 `FormRender` 或访问 [vscode 网上商店](https://marketplace.visualstudio.com/items?itemName=F-loat.vscode-plugin-fr-schema) 进行安装\n\n### 演示\n\n1. 打开任意 `.json` 文件，右上角出现进入编辑模式的 icon，表明已安装成功。可通过以下两种方式进入可视化编辑\n\n   - 点击右上角的 icon (推荐) <img alt=\"icon\" height=\"30px\" src=\"https://img.alicdn.com/imgextra/i1/O1CN01I90n9s1l5KiSj3UOb_!!6000000004767-0-tps-216-104.jpg\" />\n   - 右键 `.json` 文件，选择 `可视化编辑表单配置` 或 `open with formRender schema editor`（英文编辑器）\n\n2. 在表单设计器里完成编辑点击 “保存”。全流程如下\n\n   <img alt=\"overview\" width=\"80%\" src=\"https://img.alicdn.com/tfs/TB1b53cmGNj0u4jSZFyXXXgMVXa-2740-1748.gif\" />\n\n## 更多能力\n\n### 代码片段\n\n<img width=\"80%\" src=\"https://img.alicdn.com/tfs/TB12vYkq1T2gK0jSZFvXXXnFXXa-1862-976.jpg\" />\n\n如图，在 .json 文件中输入关键词 `fr` 即可查阅所有的快捷 snippets。其中 `fr-init` 会生成如下的骨架：\n\n```json\n{\n  \"type\": \"object\",\n  \"properties\": {},\n  \"required\": []\n}\n```\n\n然后在骨架中使用元素 snippets 撰写所有的表单项吧，例如需要一个列表则使用 `fr-list` ，如果需要下拉单选框则使用 `fr-select`，等等。\n\nsnippets 基本分为三类：\n\n- 生成一个表单元素的 schema（例如输入框，多选框，列表，对象）\n- 生成部分常用选项的 schema（例如 props, width, format 等）\n- 生成骨架和 demo。fr-init 生成骨架，fr-demo 生成一个包含所有可用组件以及一个联动显隐的样例\n\n注意不需要输入完整的 `fr-number-complex` 来唤起一个 snippet 哦，只需要输入 `number` 或者甚至 `fnc` 就能智能联想了。\n\n### 生成 Interface\n\n- 选中 js/ts 文件内的 schema 代码，右键选择 `解析所选对象为接口`\n  <img alt=\"code2interface\" width=\"80%\" src=\"https://img.alicdn.com/tfs/TB1sv2xlCR26e4jSZFEXXbwuXXa-1440-900.gif\" />\n\n- 右键任意 `.json` 文件，选择 `解析表单配置为接口`\n  <img alt=\"json2interface\" width=\"80%\" src=\"https://img.alicdn.com/tfs/TB1nI.NWrY1gK0jSZTEXXXDQVXa-2736-1744.png\" />\n\n### 生成 Schema\n\n- 将 React PropTypes 解析为 schema\n\n右键任意 `.jsx` 文件，选择 `解析组件为表单配置`，具体配置方式可查看 [proptypes-to-json-schema](./proptypes) 文档\n\n- 将表单数据转换为 schema\n\n右键任意 `.json` 文件，选择 `转换数据为表单配置`\n\n### 预览模式\n\n可实时展示 `json` 文件改动效果\n\n   <img alt=\"preview\" width=\"80%\" src=\"https://img.alicdn.com/imgextra/i3/O1CN01BbcGi71vDFhN4cwcA_!!6000000006138-2-tps-2295-1224.png\" />\n\n## 配置\n\n> Settings -> Extensions -> FormRender generator\n\n- 启用主题\n\n是否适配 vscode 主题，关闭后显示默认样式\n\n## 插件开发\n\n- 克隆项目\n\n```sh\ngit clone https://github.com/alibaba/form-render.git\ncd form-render/tools/vscode-plugin-fr-schema\n```\n\n- 安装依赖\n\n```sh\nnpm install\n```\n\n- 调试插件\n\n使用 VSCode 打开项目，执行 `npm run dev:web`，然后按下 F5 开始调试\n"
  },
  {
    "path": "docs/xflow/FlowProvider.md",
    "content": "---\norder: 4\ntitle: 'FlowProvider'\nmobile: false\ngroup: \n  title: 高级用法\n  order: 2\n---\n\n# FlowProvider\n\n`<FlowProvider/>` 组件是一个 Context Provider，它使在 `<XFlow/>` 组件之外访问流的内部状态成为可能。我们提供的 `useFlow()`、`useNodes()` 和 `useEdges()` 钩子依赖于这个组件才能工作。\n\n\n## 基础用法\n\n```js\nimport { FlowProvider } from '@xrenders/xflow';\n\nexport default () => {\n  return (\n    <FlowProvider>\n         <XFlow\n          initialValues={{ nodes: initialNodes, edges: initialEdges }}\n          settings={settings as any[]}\n          nodeSelector={{\n            showSearch: true,\n          }}\n        />\n    </FlowProvider>\n  );\n};\n```\n\n## 完整示例\n<code src=\"./demo/flow-provider/index.tsx\"></code>\n\n## useFlow\n\n`useFlow()` 钩子返回 XFlow 实例，包含了一些实用的内部方法。\n\n### 节点操作\n- `setNodes`: 设置节点\n- `addNodes`: 添加一个或多个节点\n- `getNodes`: 获取节点数据\n<!-- - `onNodesChange`: 节点变化时的回调 -->\n- `deleteNode`: 删除单个节点\n- `copyNode`: 复制单个节点\n- `pasteNode`: 粘贴单个节点\n\n### 边操作\n- `setEdges`: 设置边\n- `addEdges`: 添加一个或多个边\n- `getEdges`: 获取边数据\n<!-- - `onEdgesChange`: 边变化时的回调 -->\n<!-- - `onConnect`: 连接边时的回调 -->\n\n### 视图控制\n- `zoomIn`: 放大画布\n- `zoomOut`: 缩小画布\n- `zoomTo(level)`: 缩放画布到指定比例\n- `getZoom`: 获取缩放比例\n- `setViewport`: 设置视口\n- `getViewport`: 获取视口\n- `fitView`: 适应画布\n- `setCenter`: 设置画布中心\n- `fitBounds`: 适应边界\n\n### 坐标转换\n- `screenToFlowPosition`: 将屏幕坐标转换为画布坐标\n- `flowToScreenPosition`: 将画布坐标转换为屏幕坐标\n\n### 布局\n- `runAutoLayout`: 自动布局节点\n<!-- - `layout`: 获取当前布局配置 -->\n\n### 数据转换\n- `toObject`: 将画布数据转换为对象返回\n\n## useNodes\n\n`useNodes()` 钩子用于实时监听节点状态变化。与 `useFlow` 的 `getNodes` 不同，`getNodes` 是瞬时值，`useNodes` 返回实时的nodes状态。\n \n## useEdges\n\n `useFlow` 的 `getEdges` 是瞬时值。想要监听节点状态，请使用 `useEdges` 钩子来返回实时 edges 状态。\n\n\n## 注意事项\n\n- 如果你正在使用路由器并且希望流程的状态在不同路由之间保持持久，那么将 `<FlowProvider/>` 组件放置在路由器外部是至关重要的。\n- 如果在同一页面上有多个 `<XFlow/>`，则需要为每个 `<XFlow/>` 使用单独的 `<FlowProvider/>`\n"
  },
  {
    "path": "docs/xflow/api.md",
    "content": "---\norder: 1\ntitle: 'API'\nmobile: false\ngroup: \n  title: 'API文档'\n  order: 3\n---\n# API\n\n## XFlow\n\n| 属性          | 描述                                 | 类型                                                                                        | 默认值  |\n| --------------------------- | ------------------------------------ | ------------------------------------------------------------------------------------------- | ------- |\n| initialValues | 初始的节点和边数据                   | `{nodes:Array<{id:string;type:string;data:Record<string,any>;position:{x:number;y:number}}>,edges:Array<{id:string;source:string;target:string}>}` | -       | - |\n| layout        | 节点布局的方向                       | `LR \\| TB`                                                                                  | LR      | - |\n| widgets       | 自定义组件                           | `Record<string, ReactNode>`                                                                 | -       | - |\n| settings      | 节点配置，定义页面中可拖动的节点配置 | ( [TNodeGroup](#tnodegroup) \\| [TNodeItem](#tnodeitem) )[ ]                                 |         |\n| nodeSelector  | 节点选择器配置，可控制节点的可搜索性 | `TNodeSelector`                                                                             |\n| iconFontUrl   | iconfont url，用于配置图标渲染       | `String`                                                                                    |         |\n| globalConfig  | 全局的面板和节点配置                 | {nodePanel:[TNodePanel](#tnodepanel),nodeView:[TNodeView](#tnodeview),edge:[TEdge](#tedge),controls:[TControl](#tcontrol),handle:[THandle](#thandle),deleteKeyCode:[TdeleteKeyCode](#tdeletekeycode)  } |         |\n| logPanel      | 日志面板配置                         | [TLogPanel](#tlogpanel)                                                                     |         |\n| onNodeClick   | 节点点击事件                         | `NodeMouseHandler`                                                                          |         |\n| onEdgeClick   | 边点击事件                         | `EdgeMouseHandler`                                                                          |         |\n| antdVersion   | antd 的版本                          | `V4 \\| V5`                                                                                  | `V5`    |\n| readOnly      | 只读模式                             | `boolean`                                                                                   | `false` |\n| onTesting      | 单点调试方法                             | `(node,nodes)=>void`                                                                                   |  |\n| onMenuItemClick | 点击节点右上角复制粘贴删除功能， 支持自定义和默认行为 | (itemInfo: [ItemInfo](#iteminfo), defaultAction: () => void) => void | - |\n| clickAddNode | 自定义添加节点逻辑 | `(type: string, nodeItem: TNodeItem, addNode: (initData?: Record<string,any>) => void) => void` | - |\n| zoomOnScroll      | 是否通过滚动鼠标滚轮来缩放画布                           | `boolean`                                                                                   | `true` |\n| panOnScroll      | 是否通过滚动鼠标滚轮来平移画布                           | `boolean`                                                                                   | `false` |\n| preventScrolling      |   是否阻止浏览器在画布上滚动时的默认行为                         | `boolean`                                                                                   | `true` |\n| openColorfulMode      |   是否开启多彩模式                         | `boolean`                                                                                   | `false` |\n\n\n## TNodePanel\n\n面板相关配置\n\n| 属性     | 描述                               | 类型               | 默认值 |\n| -------- | ---------------------------------- | ------------------ | ------ |\n| showPanel   |   是否展示配置面板                         | `boolean`                                                                                   | `true` |\n| width    | 设置配置面板宽度                   | `string \\| number` | 400    |\n| hideDesc | 是否在配置面板中显示节点的描述信息 | `boolean`          | false  |\n| onClose | 配置面板关闭事件 | `(nodeID:string)=>void`          |   |\n\n\n\n\n## TNodeView\n\n节点相关配置\n\n| 属性          | 描述                                                                           | 类型                                | 默认值 |\n| ------------- | ------------------------------------------------------------------------------ | ----------------------------------- | ------ |\n| hideTitleTips | 是否隐藏节点标题的tooltip描述信息提示                                          | `boolean`                           | false  |\n| status        | 节点状态自定义配置,自定义节点不同状态的颜色展示。name:状态名称，color:颜色值。 | `Array<{name:string;color:string}>` | []     |\n\n## TEdge\n\n边的全局配置\n\n| 属性           | 描述                                       | 类型      | 默认值 |\n| -------------- | ------------------------------------------ | --------- | ------ |\n| hideEdgeAddBtn | 是否隐藏两个节点之间，连线上的增加节点按钮 | `boolean` | false  |\n| hideEdgeDelBtn | 是否隐藏两个节点之间，连线上的删除按钮, 需要配合deletable一起使用 | `boolean` | false  |\n| deletable | 配置边是否可用快捷键删除，如果需要同时隐藏删除按钮，则需要设置deletable为true | `boolean` | true  |\n\n## TControl\n\n工具栏控制面板功能的配置。\n\n| 属性           | 描述                                       | 类型      | 默认值 |\n| -------------- | ------------------------------------------ | --------- | ------ |\n| hideAddNode | 是否隐藏增加节点功能 | `boolean` | false  |\n| hideAnnotate | 是否隐藏注释节点功能 | `boolean` | false  |\n| hideUndoRedoBtns | 是否隐藏撤销和重做按钮 | `boolean` | false  |\n| hideZoomInOutBtns | 是否隐藏缩放按钮 | `boolean` | false  |\n| hideControlBtns | 是否隐藏所有控制按钮 | `boolean` | false  |\n| hideAutoLayout | 是否隐藏整理画布功能 | `boolean` | false  |\n| hideFullscreen | 是否隐藏全屏功能 | `boolean` | false  |\n| hideInteractionMode | 是否隐藏指针和手形工具切换功能 | `boolean` | false  |\n| onAutoLayoutCompleted | 整理画布完成后的回调函数，接收整理后的节点数组作为参数，支持异步函数 | `(nodes: node[]) => void \\| Promise<void>` | - |\n\n\n\n## THandle\n\nHandle 配置继承自 React Flow 的 Handle 配置，用于控制节点连接点的行为。更多 Handle 相关的配置可以参考 [React Flow Handle API](https://reactflow.dev/docs/api/nodes/handle/)。\n\n| 属性               | 描述                                       | 类型      | 默认值 |\n| -------------- | ------------------------------------------ | --------- | ------ |\n| isValidConnection | 验证连接是否有效，返回 true 则允许连接 | `(connection: Connection) => boolean` | - |\n| onConnect | 连接建立时的回调 | `(params: Connection) => void` | - |\n\n\n## TDeleteKeyCode\n\n删除键配置，用于控制删除节点和边的快捷键。\n\n| 属性           | 描述                                       | 类型      | 默认值 |\n| -------------- | ------------------------------------------ | --------- | ------ |\n| deleteKeyCode | 设置删除节点和边的快捷键，可以是单个键或多个键的数组，设置为 null 则禁用快捷键删除功能 | `string \\| string[] \\| null` | 'Backspace' |\n\n## TNodeGroup\n\n节点分组配置\n\n| 属性  | 描述         | 类型          | 默认值 |\n| ----- | ------------ | ------------- | ------ |\n| title | 分组名称     | `string`      |        |\n| type  | 分组类型     | `_group`      | _group |\n| items | 节点配置信息 | `TNodeItem[]` |        |\n\n## TLogPanel\n\n日志面板配置\n\n| 属性      | 描述                | 类型                          | 默认值 |\n| --------- | ------------------- | ----------------------------- | ------ |\n| enable | 是否启用日志面板，设置为`false`时全局禁用日志面板 | `boolean` | true |\n| logList   | 日志面板数据        |  Array<[TLogListItem](#tloglistitem)>|        |\n| loading   | 日志面板loading效果 | `boolean`                     | false  |\n| logWidget | 自定义日志面板展示  | `string`                      |        |\n| width | 日志面板宽度 | `number`                      | 400       |\n| tabsProps | 内置日志面板,详情和追踪选项卡切换组件,antd的tabs配置透传 |  [TabsProps](https://ant.design/components/tabs-cn#tabs)                        |        |\n| detailLogWidget | 内置日志面板详情tab，自定义组件会渲染在code面板下方。组件接收参数为：`data`(当前日志详情数据)、`logList`(日志面板数据)、`currentStatus`(当前节点状态)、`logPanel`(日志面板配置) |  `string`                    |        |\n\n\n\n\n## TLogListItem\n\n日志面板数据格式\n\n| 属性        | 描述                                                                                          | 类型                                                                                                | 默认值 |\n| ------------------- | --------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- | ------ |\n| statusPanel     | 日志面板数据,isBadge是否以badge形式展示状态。非必填，不传statusPanel字段不渲染状态面板。      | `{status: Array<{ label: string; value?: string; isBadge?: boolean }>;extra?: string \\| ReactNode}` |        |\n| codePanel      | 代码面板数据渲染，如果没有 codePanel，则不渲染代码面板。title:代码面板标题，code:代码面板数据 | `Array<{ title: string; code: string }>`                                                            |        |\n| nodeId         | 节点ID，必填。如果`logList`中有多个相同的nodeId,会自动合并相同nodeId的statusPanel、codePanel数据，并以数组的形式渲染     | `string`                                                                                            |        |\n| groupTitle           |当有多个相同的nodeId，每个板块的分组名称，不传不渲染。可以传入字符串，或者传入`widgets`自定义组件名称，自定义渲染组件   | `string`                                                                                            |        |\n| showDetailLogWidget           |是否展示`detailLogWidget`组件   | `boolean`                                                                                            |   true      |  \n| _status           |当前log的状态，如果没有，则以`data._status`为准 | `string\\|number`                                                                                            |         |  \n\n\n\n\n## TNodeItem\n\n\n单个节点配置\n\n| 属性                                          | 描述                | 类型      | 默认值 |\n| -------------------------------------------- | ---------------------------- | ------------------------------- | ------ |\n| title              | 节点名称                                                                                                                                         | `string`                                                                                                                                                        |        |\n| type               | 节点类型                                                                                                                                         | `string`                                                                                                                                                        |        |\n| description              | 节点描述                                                                                                                                         | `string`                                                                                                                                                        |        |\n| hidden             | 是否在配置面板中显示节点                                                                                                                         | `boolean`                                                                                                                                                       | false  |\n| targetHandleHidden | 是否隐藏左侧输入连接头                                                                                                                           | `boolean`                                                                                                                                                       | false  |\n| sourceHandleHidden | 是否隐藏右侧输出连接头                                                                                                                           | `boolean`                                                                                                                                                       | false  |\n| icon               | 节点的图标配置                                                                                                                                   | `{type:string;bgColor:string}`                                                                                                                                  |        |\n| iconSvg            | 自定义节点图标组件，可支持图标的SVG形式，IMG形式，从全局 `widgets` 中导入组件                                                                                                                            | `string`                                                                                                                                                 |        |\n| className               | 节点className                                                                                                                                   | `string`                                                                                                                                  |        |\n| settingSchema      | 节点的业务配置信息，详见[form-render 文档](/form-render/api-schema)。同时设置`settingSchema`和`settingWidget`只生效`settingWidget`               | <a target=\"_blank\" href=\"https://github.com/alibaba/x-render/blob/e2feff8fdb3bef5537b92a2157dbbf40b9d4eb17/packages/form-render/src/type.ts#L32\">SchemaBase</a> |        |\n| settingWidget      | 自定义节点的业务配置组件，在弹窗中展示。同时设置`settingSchema`和`settingWidget`只生效`settingWidget`。定义之后需要在`widgets`中引入自定义组件。 | `string`                                                                                                                                                        |        |\n| settingWidgetProps | 用于向`settingWidget`自定义组件中传递自定义参数                                                                                                  | `object`                                                                                                                                                        |        |\n| nodeWidget         | 自定义节点的业务配置信息展示组件，在节点内部展示业务配置信息。定义之后需要在`widgets`中引入自定义组件。                                          | `string`                                                                                                                                                        |        |\n| nodePanel          | 自定义节点的面板配置信息                                                                                                                         | [TNodePanel](#tnodepanel)                                                                                                                                       |        |\n| switchExtra        | 条件节点属性配置                                                                                                                               | [TSwitchExtra](#tswitchextra)                                                                                                                                   |        |\n| parallelExtra        | 并行节点属性配置                                                                                                                               | [TParallelExtra](#tparallelextra)                                                                                                                                   |        |\n| showTestingBtn      |  是否展示节点的单点调试按钮                      | `boolean`                                                                                   | `false` |\n| getSettingSchema | 动态获取节点的业务配置信息，返回值同settingSchema。同时设置`settingSchema`和`getSettingSchema`只生效`getSettingSchema` | `(nodeId: string, nodeType: string, nodeItem: TNodeItem, nodeData: any, form: ReturnType<typeof useForm>) => Promise<Schema>` |        |\n| renderHandle | 自定义渲染节点的handle，以实现某个节点自定义的出口数量.`sourceHandle`是原handle组件 |  `(sourceHandle: SourceHandleType,sourceHandleProps:ComponentProps<SourceHandleType>,nodeProps: {id: string;type: string;data: any;layout: 'LR';isConnectable: boolean;readOnly: boolean;}) => React.JSX.Element`|   |\n| disabledShortcutDelete | 是否禁用该节点的快捷键删除功能 | `boolean` | false |\n| disabledShortcutCopy | 是否禁用该节点的快捷键复制功能 | `boolean` | false |\n\n\n## TNodeSelector\n\n| 属性       | 描述             | 类型                                                        | 默认值 |\n| ---------- | ---------------- | ----------------------------------------------------------- | ------ |\n| showSearch | 节点是否可被搜索 | `boolean`                                                   | false  |\n| items      | 节点配置         | ( [TNodeGroup](#tnodegroup) \\| [TNodeItem](#tnodeitem) )[ ] |        |\n\n## TSwitchExtra\n\n| 属性     | 描述                             | 类型      | 默认值 |\n| -------- | -------------------------------- | --------- | ------ |\n| hideElse | 隐藏条件节点ELSE分支（默认分支） | `boolean` | false  |\n| valueKey | 自定义节点 value 的字段          | `string`  |        |\n| titleKey | 自定义节点 title 的字段          | `string`  |        |\n\n## TParallelExtra\n\n| 属性     | 描述                             | 类型      | 默认值 |\n| -------- | -------------------------------- | --------- | ------ |\n| valueKey | 自定义节点 value 的字段          | `string`  |        |\n| titleKey | 自定义节点 title 的字段          | `string`  |        |\n\n\n## ItemInfo\n\n| 属性     | 描述                             | 类型      | 默认值 |\n| -------- | -------------------------------- | --------- | ------ |\n| key | 节点菜单项的key | `string`  |        |\n| nodeId | 节点ID | `string`  |        |\n| sourceHandle | 连接头ID | `string`  |        |\n\n\n## 画布快捷键\n\n| 快捷键                | 功能描述         |\n|----------------------|------------------|\n| Ctrl/Cmd + C         | 复制选中节点      |\n| Ctrl/Cmd + V         | 粘贴节点          |\n| Delete/Backspace     | 删除选中节点或边  |\n| Ctrl/Cmd + Z         | 撤销              |\n| Ctrl/Cmd + Y         | 重做              |\n"
  },
  {
    "path": "docs/xflow/best-practices.md",
    "content": "---\norder: 1\ntitle: '最佳实践'\nmobile: false\ngroup: \n  title: 最佳实践\n  order: 3\n---\n\n# 最佳实践\n    \n\n## 案例1\n<code src=\"./demo/best/basic/index.tsx\"></code>\n\n## 案例2\n<code src=\"./demo/best/demo2/index.tsx\"></code>\n\n\n"
  },
  {
    "path": "docs/xflow/custom-node-setting.md",
    "content": "---\norder: 3\ntitle: '自定义节点配置面板'\nmobile: false\ngroup: \n  title: 高级用法\n  order: 2\n---\n\n# 自定义节点配置面板\n节点配置面板支持以下两种渲染方式：\n\n- **Schema 方式**: 适用于节点配置较为简单的场景。通过 FormRender 配置 schema 来实现快速渲染。\n- **Widget 方式**: 针对复杂的配置需求，schema 无法满足时，可以通过配置`settingWidget`自定义组件进行灵活渲染。\n\n## Schema 方式\n通过配置节点的 settingSchema 属性，实现节点数据配置项的自定义渲染。Schema方式适合快速实现简单的配置面板。\n\n:::info\n注意：使用 FormRender 的 schema方式配置，只能用`{{ }}` 函数表达式和`dependencies`方式实现简单联动，不能使用`watch`监听的形式实现复杂联动，如果需要使用`watch`监听的形式请使用[`settingWidget`自定义组件](#widget-方式)  \n:::\n \n### 基础示例\n如果schema中设置了`required:true`必填项，则无法关闭和切换节点配置面板。如下方：开始节点中的必填项，试下点击开始节点后直接关闭配置面板的效果。\n\n```jsx\nimport { Input } from 'antd';\nimport React from 'react';\nimport XFlow from '@xrenders/xflow';\nimport settings from './schema/custom-settings.ts';\n\nconst customWidget = ({ value, onChange }) => {\n  return <Input value={value} onChange={e => onChange(e.target.value)} />\n}\n\nexport default () => {\n  const nodes = [\n    {\n      id: '1',\n      type: 'Start',\n      data: {\n        inputVal: '我是自定义组件'\n      },\n      position: {\n        x: 40,\n        y: 240,\n      }\n    },\n    {\n      id: '2',\n      type: 'End',\n      data: {},\n      position: {\n        x: 500,\n        y: 240,\n      }\n    }\n  ];\n\n  const edges = [\n    { source: '1', target: '2', id: '234123' }\n  ]\n\n  return (\n    <div style={{ height: '600px' }}>\n      <XFlow\n        initialValues={{ nodes, edges }}\n        settings={settings}\n        widgets={{ customWidget }}\n      />\n    </div>\n  );\n}\n```\n\n## Widget 方式\n通过配置节点的 `settingWidget` 属性，实现节点数据配置项的自定义渲染。Widget 方式提供了更大的灵活性，可以实现复杂的交互和样式需求。\n\n### 使用方法\n\n1. 创建自定义配置组件，组件参数见[自定义组件接收到的props](#自定义组件接收到的props)\n```js\nconst customWidget = ({ value, onChange,...rest }) => {\n  return (\n    <Input\n      value={value?.inputVal}\n      onChange={e => onChange({ inputVal: e.target.value })}\n    />\n  );\n};\n```\n1. 在`widgets`中注册自定义组件\n```js\n       <XFlow\n        initialValues={{ nodes, edges }}\n        settings={settings}\n        widgets={{ customWidget }}\n       />\n\n```\n3. 在`settings`属性使用\n```js\n{\n    title: '开始',\n    type: 'Start',\n    hidden: true,\n    targetHandleHidden: true,\n    icon: {\n      type: 'icon-start',\n      bgColor: '#17B26A',\n    },\n    settingWidget: \"customWidget\",  // 使用自定义组件customWidget\n    settingWidgetProps: {  // 给自定义组件传递参数\n      params: \"test\"\n    }\n}\n\n```\n\n### 自定义组件接收到的props\n默认情况下 Widget 会接收到如下的 props：\n\n#### value\n- 类型：`any`\n- 描述：当前组件的值，用于 settingWidget 的受控\n\n#### onChange\n- 类型：`(value: any) => void`\n- 描述：当前 组件的值变化时的回调用于 settingWidget 的受控\n\n#### readOnly\n- 类型：`boolean`\n- 描述：当前组件是否为只读状态\n\n#### others\n接收`settingWidgetProps`传入的所有值\n\n### 基础示例\n<code src=\"./demo/custom-flow/index.tsx\"></code>\n\n### 复杂联动示例\n以下示例了如何在`settingWidget`中使用复杂联动，以及如何在弹窗关闭时增加必填校验。\n\n需要暴露`validateForm`方法，以便关闭弹窗时进行必填校验。\n```js\nexport const AdvancedSettingWidget = forwardRef<\n  AdvancedSettingWidgetRef,\n  AdvancedSettingWidgetProps\n>(({ value, onChange, readOnly }, ref) => {\n  // ...组件实现\n  useImperativeHandle(ref, () => ({\n  validateForm: async () => {\n    return await form\n      .validateFields()\n      .then(() => true)\n      .catch(() => false);\n  },\n}));\n});\n```\n\n<code src=\"./demo/custom-flow/advancedLinkageCase\"></code>\n\n###  完整案例\n\n<code src=\"./demo/custom-flow/fullCase/index.tsx\"></code>\n\n这个示例适用于以下场景：\n\n- 需要根据不同配置类型显示不同表单项的场景\n- 表单项之间存在复杂联动关系的场景\n- 需要动态添加表单项的场景\n- 需要实现严格表单验证的场景\n\n通过这个示例，你可以了解如何使用 settingWidget 方式实现复杂的表单交互，以及如何处理表单验证、数据联动等高级需求。这种方式比简单的 Schema 方式更灵活，能够满足更复杂的业务需求。\n"
  },
  {
    "path": "docs/xflow/custom-node-view.md",
    "content": "---\norder: 1\ntitle: '自定义节点展示'\nmobile: false\ngroup: \n  title: 高级用法\n  order: 2\n---\n\n# 自定义节点展示\n\n当默认的节点内容展示不满足要求时，可以通过 `nodeWidget` 进行自定义渲染。\n\n1. 定义自定义组件\n2. 在 XFlow 组件中通过 `widgets` 属性注册组件\n3. 在节点配置中通过 `nodeWidget` 指定使用的组件\n   \n```js\n// 1.自定义节点\nconst LLMNodeWidget = ({ data }) => {\n  const { model, temperature, maxTokens, systemPrompt } = data; // data为配置面板数据\n  return (\n    <Card\n      size=\"small\"\n      bodyStyle={{ padding: '12px' }}\n    >\n      //  自定义渲染节点内容\n    </Card>\n  );\n};\n```\n注册自定义组件\n```js\n<XFlow\n  initialValues={{ nodes, edges }}\n  settings={settings}\n  widgets={{\n    LLMNodeWidget  // 2.注册自定义组件\n  }}\n/>\n\n```\n在settings中使用自定义组件\n```js\n{\n    type: 'LLM',\n    title: 'LLM 处理',\n    icon: {\n      type: 'icon-model',\n      bgColor: '#6172F3',\n    },\n    nodeWidget: 'LLMNodeWidget' // 3.使用自定义组件\n}\n```\n<code src=\"./demo/nodeWidget\"></code>\n"
  },
  {
    "path": "docs/xflow/data/basic.ts",
    "content": "export default {\n  \"creator\": \"清风徐来\",\n  \"relevanceCode\": \"421421\",\n  \"desc\": \"浙江省杭州市工专路\",\n  \"create-time\": \"2019-10-10\",\n  \"effective-date\": \"2019-10-10 ～ 2020-10-31\",\n  \"safety\": {\n    \"name\": \"Test demo 001\",\n    \"app\": \"中后台详情页面\",\n    \"mode\": \"代码包\",\n    \"yum\": \"592342323904823489\",\n    \"fore\": \"23\"\n  },\n  \"operLog\": [{\n    \"type\": \"创建测试\",\n    \"creator\": \"清风徐来\",\n    \"time\": \"2019-10-30 12:23:45\",\n    \"result\": \"1\",\n    \"desc\": \"这是备注\"\n  }, {\n    \"type\": \"创建测试\",\n    \"creator\": \"清风徐来\",\n    \"time\": \"2019-10-30 12:23:45\",\n    \"result\": \"1\",\n    \"desc\": \"这是备注\"\n  }, {\n    \"type\": \"创建测试\",\n    \"creator\": \"清风徐来\",\n    \"time\": \"2019-10-30 12:23:45\",\n    \"result\": \"1\",\n    \"desc\": \"这是备注\"\n  }, {\n    \"type\": \"创建测试\",\n    \"creator\": \"清风徐来\",\n    \"time\": \"2019-10-30 12:23:45\",\n    \"result\": \"1\",\n    \"desc\": \"这是备注\"\n  }, {\n    \"type\": \"创建测试\",\n    \"creator\": \"清风徐来\",\n    \"time\": \"2019-10-30 12:23:45\",\n    \"result\": \"1\",\n    \"desc\": \"这是备注\"\n  }, {\n    \"type\": \"创建测试\",\n    \"creator\": \"清风徐来\",\n    \"time\": \"2019-10-30 12:23:45\",\n    \"result\": \"1\",\n    \"desc\": \"这是备注\"\n  }]\n}"
  },
  {
    "path": "docs/xflow/demo/basic/index.tsx",
    "content": "import React from 'react';\nimport XFlow from '@xrenders/xflow';\nimport settings from './setting';\n\nexport default () => {\n  const nodes = [\n    {\n      id: '1',\n      type: 'Start',\n      data: {},\n      position: {\n        x: 40,\n        y: 240,\n      }\n    },\n    {\n      id: '2',\n      type: 'End',\n      data: {},\n      position: {\n        x: 500,\n        y: 240,\n      }\n    }\n  ];\n\n  const edges = [\n    { source: '1', target: '2', id: '234123' }\n  ]\n\n  return (\n    <div style={{ height: '600px' }}>\n      <XFlow\n        initialValues={{ nodes, edges }}\n        settings={settings}\n        nodeSelector={{\n          showSearch: true,\n        }}\n      />\n    </div>\n  );\n}\n"
  },
  {
    "path": "docs/xflow/demo/basic/setting.tsx",
    "content": "export default [\n  {\n    title: '开始',\n    type: 'Start',\n    hidden: true,\n    targetHandleHidden: true,\n    icon: {\n      type: 'icon-start',\n      bgColor: '#17B26A',\n    },\n    settingSchema: {\n      type: 'object',\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n        select: {\n          title: '变量二',\n          type: 'string',\n          widget: 'select',\n          props: {\n            options: [\n              { label: 'a', value: 'a' },\n              { label: 'b', value: 'b' },\n              { label: 'c', value: 'c' },\n            ],\n          },\n        },\n        radio1: {\n          title: '点击单选',\n          type: 'string',\n          widget: 'radio',\n          props: {\n            options: [\n              { label: '早', value: 'a' },\n              { label: '中', value: 'b' },\n              { label: '晚', value: 'c' }\n            ]\n          }\n        },\n        textarea1: {\n          title: '长文本',\n          type: 'string',\n          widget: 'textArea'\n        },\n        date1: {\n          title: '日期选择',\n          type: 'string',\n          widget: 'datePicker'\n        },\n        dateRange1: {\n          title: '日期范围',\n          type: 'range',\n          widget: 'dateRange'\n        },\n        time1: {\n          title: '时间选择',\n          type: 'string',\n          widget: 'timePicker'\n        },\n        timeRange1: {\n          title: '时间范围',\n          type: 'range',\n          widget: 'timeRange'\n        },\n      },\n    },\n  },\n  {\n    title: '结束',\n    type: 'End',\n    hidden: true,\n    sourceHandleHidden: true,\n    icon: {\n      type: 'icon-end',\n      bgColor: '#F79009',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n        select: {\n          title: '变量二',\n          type: 'string',\n          widget: 'select',\n          props: {\n            options: [\n              { label: 'a', value: 'a' },\n              { label: 'b', value: 'b' },\n              { label: 'c', value: 'c' },\n            ],\n          },\n        },\n      }\n    }\n  },\n  {\n    title: 'LLM',\n    type: 'LLM',\n    description: '调用大语言模型回答问题或者对自然语言进行处理',\n    icon: {\n      type: 'icon-model',\n      bgColor: '#6172F3',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n      }\n    }\n  },\n  {\n    title: 'Prompt',\n    type: 'Prompt',\n    description: '通过精心设计提示词，提升大语言模型回答效果',\n    icon: {\n      type: 'icon-prompt',\n      bgColor: '#17B26A',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n      }\n    }\n  },\n  {\n    title: '知识库',\n    type: 'knowledge',\n    description: '允许你从知识库中查询与用户问题相关的文本内容',\n    icon: {\n      type: 'icon-knowledge',\n      bgColor: '#6172F3',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n      }\n    }\n  },\n  {\n    title: 'Switch',\n    type: 'Switch',\n    description: '允许你根据 if/else 条件将 workflow 拆分成两个分支',\n    icon: {\n      type: 'icon-fenzhi',\n      bgColor: '#06AED4',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n      }\n    }\n  },\n  {\n    title: 'HSF',\n    type: 'hsf',\n    description: '允许通过 HSF 协议发送服务器请求',\n    icon: {\n      type: 'icon-hsf',\n      bgColor: '#875BF7',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n      }\n    }\n  },\n  {\n    title: 'Http',\n    type: 'http',\n    description: '允许通过 HTTP 协议发送服务器请求',\n    icon: {\n      type: 'icon-http',\n      bgColor: '#875BF7',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n      }\n    }\n  },\n  {\n    title: '代码执行',\n    type: 'Code',\n    description: '执行一段 Groovy 或 Python 或 NodeJS 代码实现自定义逻辑',\n    icon: {\n      type: 'icon-code',\n      bgColor: '#2E90FA',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n      }\n    }\n  },\n  {\n    title: '工具',\n    type: 'tool',\n    description: '允许使用工具能力',\n    icon: {\n      type: 'icon-gongju',\n      bgColor: '#2E90FA',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n      }\n    }\n  },\n  {\n    title: '工具',\n    type: '_group',\n    items: [\n      {\n        title: '代码执行',\n        type: 'Code',\n        description: '执行一段 Groovy 或 Python 或 NodeJS 代码实现自定义逻辑',\n        icon: {\n          type: 'icon-code',\n          bgColor: '#2E90FA',\n        },\n        settingSchema: {\n          type: \"object\",\n          properties: {\n            input: {\n              title: '变量一',\n              type: 'string',\n              widget: 'input',\n            },\n          }\n        }\n      },\n      {\n        title: '工具',\n        type: 'tool',\n        description: '允许使用工具能力',\n        icon: {\n          type: 'icon-gongju',\n          bgColor: '#2E90FA',\n        },\n        settingSchema: {\n          type: \"object\",\n          properties: {\n            input: {\n              title: '变量一',\n              type: 'string',\n              widget: 'input',\n            },\n          }\n        }\n      },\n    ],\n  },\n];\n"
  },
  {
    "path": "docs/xflow/demo/best/basic/TextEllipsis/index.less",
    "content": ".text-ellipsis {\n  display: inline-block;\n  max-width: 100%;\n  overflow: hidden;\n  white-space: nowrap;\n  text-overflow: ellipsis;\n}\n\n.paragraph-ellipsis {\n  display: block;\n  width: 100%;\n  word-break: break-all;\n}\n"
  },
  {
    "path": "docs/xflow/demo/best/basic/TextEllipsis/index.tsx",
    "content": "import { Tooltip, TooltipProps } from 'antd';\nimport React, { FC, memo, useEffect, useState } from 'react';\nimport './index.less';\n\ninterface ITextEllipsisProps {\n  text: string;\n  style?: object;\n  className?: string;\n  toolTipProps?: TooltipProps;\n  type?: 'text' | 'paragraph';\n  rows?: number;\n}\nconst TextEllipsis: FC<ITextEllipsisProps> = ({\n  text,\n  style,\n  toolTipProps,\n  type = 'text',\n  rows = 1,\n  className,\n}) => {\n  const typographyRef = React.useRef<HTMLElement>(null);\n  const [isEllipse, setIsEllipse] = useState(false);\n\n  const component =\n    type === 'paragraph' ? (\n      <span\n        ref={typographyRef}\n        className={`paragraph-ellipsis ${className}`}\n        style={{\n          ...style,\n          display: '-webkit-box',\n          overflow: 'hidden',\n          WebkitBoxOrient: 'vertical',\n          WebkitLineClamp: rows,\n        }}\n      >\n        {text}\n      </span>\n    ) : (\n      <span\n        ref={typographyRef}\n        className={`text-ellipsis ${className}`}\n        style={style}\n      >\n        {text}\n      </span>\n    );\n\n  useEffect(() => {\n    if (text) {\n      if (type === 'paragraph') {\n        const { offsetHeight, scrollHeight, clientHeight } =\n          typographyRef.current;\n        setIsEllipse(scrollHeight > clientHeight);\n      } else {\n        const isEllipse = isEleEllipsis(typographyRef?.current);\n        setIsEllipse(isEllipse);\n      }\n    }\n  }, [text]);\n\n  const isEleEllipsis = (ele: HTMLElement): boolean => {\n    const childDiv = document.createElement('em');\n    ele.appendChild(childDiv);\n\n    const rect = ele.getBoundingClientRect();\n    const childRect = childDiv.getBoundingClientRect();\n\n    ele.removeChild(childDiv);\n\n    return (\n      rect.left > childRect.left ||\n      childRect.right > rect.right ||\n      rect.top > childRect.top ||\n      childRect.bottom > rect.bottom\n    );\n  };\n  if (isEllipse) {\n    return (\n      <Tooltip\n        title={text}\n        getPopupContainer={() =>\n          document.getElementById('xflow-container') as HTMLElement\n        }\n        color=\"#ffff\"\n        overlayInnerStyle={{\n          color: '#354052',\n          fontSize: '12px',\n          borderRadius: '8px',\n        }}\n        placement=\"bottomRight\"\n        {...toolTipProps}\n      >\n        {component}\n      </Tooltip>\n    );\n  }\n  return component;\n};\n\nexport default memo(TextEllipsis);\n"
  },
  {
    "path": "docs/xflow/demo/best/basic/const.tsx",
    "content": "export const nodes = [\n  {\n    id: 'y01s4993gdvyknzf',\n    type: 'startEvent',\n    position: {\n      x: 0,\n      y: 120,\n    },\n    measured: {\n      width: 268,\n      height: 54,\n    },\n    selected: false,\n    dragging: false,\n    data: {\n      _isCandidate: false,\n      title: '开始',\n      string: 'hello',\n      string2: 'test',\n      string3: '16',\n      string4: '17',\n      string5: '19',\n      string6: 'hello world',\n      string7: 'test',\n    },\n  },\n  {\n    id: 'bzydz67rlmv1n871',\n    type: 'Switch',\n    position: {\n      x: 354,\n      y: 120,\n    },\n    measured: {\n      width: 268,\n      height: 222,\n    },\n    selected: false,\n    dragging: false,\n    data: {\n      _isCandidate: false,\n      title: '条件分支',\n      list: [\n        {\n          _id: 'yjrqixxjwfdn47of',\n          name: '条件一',\n          type: '类型一',\n          value: 'a==12',\n        },\n        {\n          name: '条件二',\n          type: '类型一',\n          value: 'a!=12',\n          _id: 'id_wmu3t8gkanwg271a',\n        },\n      ],\n    },\n  },\n  {\n    id: 'mjw4se36aadccnbt',\n    type: 'serviceTask',\n    position: {\n      x: 708,\n      y: 22.5,\n    },\n    measured: {\n      width: 268,\n      height: 54,\n    },\n    selected: false,\n    dragging: false,\n    data: {\n      _isCandidate: false,\n      title: '知识检索_ncjg',\n      properties: [\n        {\n          name: 'name1',\n          value: 'value1',\n        },\n      ],\n    },\n  },\n  {\n    id: '6w52vnb5kwwq7pdj',\n    type: 'receiveTask',\n    position: {\n      x: 708,\n      y: 217.5,\n    },\n    measured: {\n      width: 268,\n      height: 73,\n    },\n    selected: false,\n    data: {\n      _isCandidate: false,\n      title: '问题分类器_ysa6',\n      desc: '问题分类描述',\n      properties: [\n        {\n          name: '问题1',\n          value: '值1',\n        },\n      ],\n    },\n  },\n  {\n    id: 'dm6eebudu8tmb4v3',\n    type: 'Parallel',\n    position: {\n      x: 1062,\n      y: 120,\n    },\n    measured: {\n      width: 268,\n      height: 196,\n    },\n    selected: false,\n    dragging: false,\n    data: {\n      _isCandidate: false,\n      title: '并行事件_49pn',\n      properties: [\n        {\n          name: '属性',\n          value: 'value',\n        },\n      ],\n      list: [\n        {\n          _id: 'id_7m3vitc4qy476axa',\n          name: '事件1',\n          value: '描述1',\n        },\n        {\n          _id: 'id_m26wo9298g3imnes',\n          name: '事件2',\n          value: '描述2',\n        },\n      ],\n    },\n  },\n  {\n    id: 'walnrnko6rjn56bx',\n    type: 'userTask',\n    position: {\n      x: 1416,\n      y: 22.5,\n    },\n    measured: {\n      width: 268,\n      height: 54,\n    },\n    selected: false,\n    data: {\n      _isCandidate: false,\n      title: '代码执行_flcv',\n      properties: [\n        {\n          name: 'name',\n          value: 'value',\n        },\n      ],\n    },\n  },\n  {\n    id: 'rxoar6yr0whfr7ym',\n    type: 'callActivity',\n    position: {\n      x: 1416,\n      y: 217.5,\n    },\n    measured: {\n      width: 268,\n      height: 54,\n    },\n    selected: false,\n    data: {\n      _isCandidate: false,\n      title: 'HTTP请求_pmkf',\n      properties: [\n        {\n          name: 'name',\n          value: 'value',\n        },\n      ],\n    },\n  },\n  {\n    id: 'qjmv9gxija93gxkb',\n    type: 'endEvent',\n    position: {\n      x: 1770,\n      y: 120,\n    },\n    measured: {\n      width: 268,\n      height: 54,\n    },\n    selected: true,\n    dragging: false,\n    data: {\n      _isCandidate: false,\n      title: '结束_af4u',\n      nodeDesc: '流程结束',\n    },\n  },\n];\n\nexport const edges = [\n  {\n    type: 'buttonedge',\n    style: {\n      strokeWidth: 1.5,\n      stroke: '#c9c9c9',\n    },\n    markerEnd: {\n      type: 'arrowclosed',\n      color: '#c9c9c9',\n    },\n    deletable: true,\n    source: 'y01s4993gdvyknzf',\n    target: 'bzydz67rlmv1n871',\n    id: 'xy-edge__y01s4993gdvyknzf-bzydz67rlmv1n871',\n  },\n  {\n    type: 'buttonedge',\n    style: {\n      strokeWidth: 1.5,\n      stroke: '#c9c9c9',\n    },\n    markerEnd: {\n      type: 'arrowclosed',\n      color: '#c9c9c9',\n    },\n    deletable: true,\n    source: 'bzydz67rlmv1n871',\n    sourceHandle: 'yjrqixxjwfdn47of',\n    target: 'mjw4se36aadccnbt',\n    id: 'xy-edge__bzydz67rlmv1n871yjrqixxjwfdn47of-mjw4se36aadccnbt',\n  },\n  {\n    type: 'buttonedge',\n    style: {\n      strokeWidth: 1.5,\n      stroke: '#c9c9c9',\n    },\n    markerEnd: {\n      type: 'arrowclosed',\n      color: '#c9c9c9',\n    },\n    deletable: true,\n    source: 'bzydz67rlmv1n871',\n    sourceHandle: 'id_wmu3t8gkanwg271a',\n    target: '6w52vnb5kwwq7pdj',\n    id: 'xy-edge__bzydz67rlmv1n871id_wmu3t8gkanwg271a-6w52vnb5kwwq7pdj',\n  },\n  {\n    type: 'buttonedge',\n    style: {\n      strokeWidth: 1.5,\n    },\n    markerEnd: {\n      type: 'arrowclosed',\n    },\n    deletable: true,\n    source: 'mjw4se36aadccnbt',\n    target: 'dm6eebudu8tmb4v3',\n    id: 'xy-edge__mjw4se36aadccnbt-dm6eebudu8tmb4v3',\n  },\n  {\n    type: 'buttonedge',\n    style: {\n      strokeWidth: 1.5,\n      stroke: '#c9c9c9',\n    },\n    markerEnd: {\n      type: 'arrowclosed',\n      color: '#c9c9c9',\n    },\n    deletable: true,\n    source: '6w52vnb5kwwq7pdj',\n    target: 'dm6eebudu8tmb4v3',\n    id: 'xy-edge__6w52vnb5kwwq7pdj-dm6eebudu8tmb4v3',\n  },\n  {\n    type: 'buttonedge',\n    style: {\n      strokeWidth: 1.5,\n      stroke: '#c9c9c9',\n    },\n    markerEnd: {\n      type: 'arrowclosed',\n      color: '#c9c9c9',\n    },\n    deletable: true,\n    source: 'dm6eebudu8tmb4v3',\n    sourceHandle: 'id_7m3vitc4qy476axa',\n    target: 'walnrnko6rjn56bx',\n    id: 'xy-edge__dm6eebudu8tmb4v3id_7m3vitc4qy476axa-walnrnko6rjn56bx',\n  },\n  {\n    type: 'buttonedge',\n    style: {\n      strokeWidth: 1.5,\n    },\n    markerEnd: {\n      type: 'arrowclosed',\n    },\n    deletable: true,\n    source: 'dm6eebudu8tmb4v3',\n    sourceHandle: 'id_m26wo9298g3imnes',\n    target: 'rxoar6yr0whfr7ym',\n    id: 'xy-edge__dm6eebudu8tmb4v3id_m26wo9298g3imnes-rxoar6yr0whfr7ym',\n  },\n  {\n    type: 'buttonedge',\n    style: {\n      strokeWidth: 1.5,\n    },\n    markerEnd: {\n      type: 'arrowclosed',\n    },\n    deletable: true,\n    source: 'walnrnko6rjn56bx',\n    target: 'qjmv9gxija93gxkb',\n    id: 'xy-edge__walnrnko6rjn56bx-qjmv9gxija93gxkb',\n  },\n  {\n    type: 'buttonedge',\n    style: {\n      strokeWidth: 1.5,\n    },\n    markerEnd: {\n      type: 'arrowclosed',\n    },\n    deletable: true,\n    source: 'rxoar6yr0whfr7ym',\n    target: 'qjmv9gxija93gxkb',\n    id: 'xy-edge__rxoar6yr0whfr7ym-qjmv9gxija93gxkb',\n  },\n];\n"
  },
  {
    "path": "docs/xflow/demo/best/basic/header.tsx",
    "content": "import React ,{ FC } from 'react';\nimport './index.less';\n\nconst Header: FC<{ data: any }> = ({ data }) => {\n  //  const container = (document.getElementById('xflow-container') as HTMLElement);\n  return (\n    <div>\n      <div className=\"process-header-card\">\n        <img\n          src=\"https://img.alicdn.com/tfs/TB17UtINiLaK1RjSZFxXXamPFXa-606-643.png\"\n          style={{ width: '22px' }}\n        />\n        <span style={{ fontSize: '23px', fontWeight: 600, marginLeft: '5px' }}>\n          XFlow\n        </span>\n      </div>\n    </div>\n  );\n};\n\nexport default Header;\n"
  },
  {
    "path": "docs/xflow/demo/best/basic/index.less",
    "content": ".settingSchemaStyle {\n  .ant-col {\n    width: 100% !important;\n\n    .ant-form-item-label label {\n      font-weight: 500;\n    }\n\n    .ant-form-item-explain-error {\n      font-size: 12px;\n    }\n  }\n\n  .child-title {\n    .ant-form-item-label label {\n      font-size: 12px;\n    }\n  }\n\n  .fr-list-simple {\n    display: block;\n  }\n\n  .parallel-wrap {\n    .fr-list-simple .fr-inline-field {\n      min-width: 200px;\n    }\n  }\n\n  .switch-list {\n    .fr-list-simple .fr-inline-field {\n      width: 140px;\n      min-width: 140px;\n    }\n  }\n\n  .ant-form-item-label>label {\n    color: #096dd9;\n  }\n}\n\n\n.switch-custom-node {\n  width: 100%;\n  min-height: 20px;\n  padding: 0.25rem;\n  text-align: justify;\n  word-wrap: break-word;\n  background-color: #f2f4f7;\n  border-radius: 0.375rem;\n\n  .condition-label {\n    display: flex;\n  }\n}\n\n\n.process-header-card {\n  position: absolute;\n  top: 14px;\n  left: 10px;\n  z-index: 1;\n  padding: 6px;\n  font-size: 12px;\n}\n\n\n.tools {\n  position: absolute;\n  top: 14px;\n  right: 10px;\n  z-index: 1;\n\n  .tools-btn {\n    height: 29px;\n    font-size: 12px;\n    border-radius: 6px;\n  }\n}\n"
  },
  {
    "path": "docs/xflow/demo/best/basic/index.tsx",
    "content": "import XFlow from '@xrenders/xflow';\nimport { settings } from './setting';\nimport { nodes,edges } from './const';\nimport React from 'react';\nimport './index.less';\nimport showSwitchNode from './showSwitchNode';\nimport Header from './header';\nimport { Tools } from './tools';\n\nexport default () => {\n  return (\n    <div style={{ height: '600px',position:'relative' }}>\n      {/* <Header data={ {}} /> */}\n      <XFlow\n        initialValues={{ nodes, edges }}\n        settings={settings}\n        nodeSelector={{\n          showSearch: true,\n        }}\n        widgets={{ showSwitchNode }}\n      />\n      <Tools />\n    </div>\n  );\n};\n"
  },
  {
    "path": "docs/xflow/demo/best/basic/setting.tsx",
    "content": "export const settingSchema = {\n  properties: {\n    type: 'array',\n    widget: 'simpleList',\n    props: {\n      hideCopy: true,\n      hideMove: true,\n    },\n    className: 'parallel-wrap',\n    items: {\n      type: 'object',\n      properties: {\n        name: {\n          title: '名称',\n          type: 'string',\n          props: {\n            allowClear: true,\n          },\n          className: 'child-title',\n          readOnlyWidget: 'ReadOnlyPanel',\n        },\n        value: {\n          title: 'value',\n          type: 'string',\n          props: {\n            allowClear: true,\n          },\n          className: 'child-title',\n          readOnlyWidget: 'ReadOnlyPanel',\n        },\n      },\n    },\n  },\n};\n\nexport const activitySchema = {\n  title: '工具',\n  type: '_group',\n  items: [\n    {\n      title: '知识检索',\n      type: 'serviceTask',\n      description: '允许你从知识库中查询与用户问题相关的文本内容',\n      icon: {\n        type: 'icon-knowledge',\n        bgColor: '#fa541c',\n      },\n      hideDesc: true,\n      nodePanel: {\n        width: 510,\n      },\n      settingSchema: {\n        type: 'object',\n        className: 'settingSchemaStyle',\n        properties: {\n          ...settingSchema,\n        },\n      },\n    },\n    {\n      title: '问题分类器',\n      type: 'receiveTask',\n      description: '定义问题的分类条件',\n      icon: {\n        type: 'icon-prompt',\n        bgColor: '#875BF7',\n      },\n      hideDesc: true,\n      nodePanel: {\n        width: 510,\n      },\n      settingSchema: {\n        type: 'object',\n        className: 'settingSchemaStyle',\n        properties: {\n          ...settingSchema,\n        },\n      },\n    },\n    {\n      title: '代码执行',\n      type: 'userTask', // exclusiveGateway\n      description: '执行一段代码实现自定义逻辑',\n      icon: {\n        type: 'icon-code',\n        bgColor: 'pink',\n      },\n      hideDesc: true,\n      nodePanel: {\n        width: 510,\n      },\n      settingSchema: {\n        type: 'object',\n        className: 'settingSchemaStyle',\n        properties: {\n          ...settingSchema,\n        },\n      },\n    },\n    {\n      title: 'HTTP请求',\n      type: 'callActivity',\n      description: '允许通过 HTTP 协议发送服务器请求',\n      icon: {\n        type: 'icon-http',\n        bgColor: '#2E90FA',\n      },\n      hideDesc: true,\n      nodePanel: {\n        width: 510,\n      },\n      settingSchema: {\n        type: 'object',\n        className: 'settingSchemaStyle',\n        properties: {\n          ...settingSchema,\n        },\n      },\n    },\n  ],\n};\n\nexport const settings = [\n  {\n    title: '事件',\n    type: '_group',\n    items: [\n      {\n        title: '开始',\n        type: 'startEvent',\n        description: '流程开始的节点，一个流程只允许有一个开始节点',\n        icon: {\n          type: 'icon-start',\n          bgColor: '#17B26A',\n        },\n        disabledShortcutDelete: true,\n        targetHandleHidden: true,\n        settingSchema: {\n          type: 'object',\n          className: 'settingSchemaStyle',\n          properties: {\n            string: {\n              title: '字符串',\n              description: '带清空x按钮',\n              type: 'string',\n              default: 'hello world',\n              props: {\n                allowClear: true,\n              },\n            },\n            string2: {\n              title: '复杂校验',\n              description: 'pattern和message的用法',\n              type: 'string',\n              rules: [\n                {\n                  pattern: '^[A-Za-z0-9]+$',\n                  message: '请输入数字或英文字母',\n                },\n              ],\n              placeholder: '请输入数字或英文',\n            },\n            string3: {\n              title: '长度控制',\n              description: '长度在5-15个字之间',\n              type: 'string',\n              minLength: 5,\n              maxLength: 15,\n            },\n            string4: {\n              title: '前置/后置标签',\n              type: 'string',\n              props: {\n                addonBefore: '长度',\n                addonAfter: 'px',\n              },\n            },\n            string5: {\n              title: '前后缀',\n              type: 'string',\n              rules: [\n                {\n                  pattern: '^[0-9]+$',\n                  message: '请输入数字',\n                },\n              ],\n              props: {\n                prefix: '￥',\n                suffix: 'RMB',\n              },\n            },\n            string6: {\n              title: '置灰的输入框',\n              type: 'string',\n              disabled: true,\n              default: 'hello world',\n            },\n            string7: {\n              title: '文本框',\n              description: '固定高度',\n              type: 'string',\n              format: 'textarea',\n              props: {\n                row: 4,\n              },\n            },\n          },\n          required: ['string4', 'string5'],\n        },\n      },\n      {\n        title: '结束',\n        type: 'endEvent',\n        description: '表示流程结束节点，可以有多个结束节点',\n        icon: {\n          type: 'icon-end',\n          bgColor: '#F79009',\n        },\n        disabledShortcutDelete: true,\n        sourceHandleHidden: true,\n        settingSchema: {\n          type: 'object',\n          className: 'settingSchemaStyle',\n          properties: {\n           nodeDesc: {\n              title: '结束描述',\n              type: 'string',\n              format: 'textarea',\n              placeholder: '根据内容缩放',\n              props: {\n                autoSize: {\n                  minRows: 3,\n                  maxRows: 5,\n                },\n             },\n             readOnlyWidget: 'ReadOnlyPanel',\n            },\n          },\n        },\n      },\n    ],\n  },\n  {\n    title: '逻辑',\n    type: '_group',\n    items: [\n      {\n        title: '条件分支',\n        type: 'Switch',\n        description: '允许你根据 if/else 条件将 workflow 拆分成两个分支',\n        icon: {\n          type: 'icon-fenzhi',\n          bgColor: '#6172F3',\n        },\n        nodePanel: {\n          width: 550,\n        },\n        hideDesc: true,\n        switchExtra: {\n          hideElse: true,\n          titleKey: 'name',\n        },\n        settingSchema: {\n          type: 'object',\n          className: 'settingSchemaStyle',\n          properties: {\n            list: {\n              // title: '高级属性',\n              type: 'array',\n              widget: 'simpleList',\n              props: {\n                hideCopy: true,\n                hideMove: true,\n              },\n              className: 'switch-list',\n              items: {\n                type: 'object',\n                properties: {\n                  name: {\n                    title: '条件名称', // 条件描述\n                    type: 'string',\n                    props: {\n                      allowClear: true,\n                    },\n                    className: 'child-title',\n                    readOnlyWidget: 'ReadOnlyPanel',\n                  },\n                  type: {\n                    title: '条件类型',\n                    type: 'string',\n                    widget: 'select',\n                    props: {\n                      allowClear: true,\n                    },\n                    enum: ['类型一'],\n                    enumNames: ['类型一'],\n                    className: 'child-title',\n                    readOnlyWidget: 'ReadOnlyPanel',\n                  },\n                  value: {\n                    title: '条件语句',\n                    type: 'string',\n                    props: {\n                      allowClear: true,\n                    },\n                    className: 'child-title',\n                    readOnlyWidget: 'ReadOnlyPanel',\n                  },\n                },\n              },\n            },\n          },\n        },\n        nodeWidget: 'showSwitchNode',\n      },\n      {\n        title: '并行事件',\n        type: 'Parallel',\n        description: '支持多个分支同时执行',\n        icon: {\n          type: 'icon-parallel',\n          bgColor: '#06aed4',\n        },\n        hideDesc: true,\n        nodePanel: {\n          width: 510,\n        },\n        parallelExtra: {\n          titleKey: 'name',\n        },\n        settingSchema: {\n          type: 'object',\n          className: 'settingSchemaStyle',\n          properties: {\n            properties: {\n              title: 'Properties',\n              type: 'array',\n              widget: 'simpleList',\n              props: {\n                hideCopy: true,\n                hideMove: true,\n              },\n              className: 'parallel-wrap',\n              items: {\n                type: 'object',\n                properties: {\n                  name: {\n                    title: '属性名称',\n                    type: 'string',\n                    props: {\n                      allowClear: true,\n                    },\n                    className: 'child-title',\n                    readOnlyWidget: 'ReadOnlyPanel',\n                  },\n                  value: {\n                    title: 'value',\n                    type: 'string',\n                    props: {\n                      allowClear: true,\n                    },\n                    className: 'child-title',\n                    readOnlyWidget: 'ReadOnlyPanel',\n                  },\n                },\n              },\n            },\n            list: {\n              title: '并行事件',\n              type: 'array',\n              widget: 'simpleList',\n              props: {\n                hideCopy: true,\n                hideMove: true,\n              },\n              className: 'parallel-wrap',\n              items: {\n                type: 'object',\n                properties: {\n                  name: {\n                    title: '事件名称',\n                    type: 'string',\n                    props: {\n                      allowClear: true,\n                    },\n                    className: 'child-title',\n                    readOnlyWidget: 'ReadOnlyPanel',\n                  },\n                  value: {\n                    title: '事件描述',\n                    type: 'string',\n                    props: {\n                      allowClear: true,\n                    },\n                    className: 'child-title',\n                    readOnlyWidget: 'ReadOnlyPanel',\n                  },\n                },\n              },\n            },\n          },\n        },\n      },\n    ],\n  },\n  { ...activitySchema },\n];\n"
  },
  {
    "path": "docs/xflow/demo/best/basic/showSwitchNode.tsx",
    "content": "import { Space } from 'antd';\nimport React from 'react';\nimport './index.less';\nimport TextEllipsis from './TextEllipsis';\n\nconst showSwitchNode = ({ data, index }) => {\n  const { type, value } = data;\n  if (!type && !value) {\n    return <div style={{ minHeight: '40px' }}></div>;\n  }\n\n  return (\n    <Space style={{ width: '100%' }} direction=\"vertical\" className=\"switch-custom-node\">\n      {type && (\n        <div className=\"condition-label\">\n          <div style={{ fontWeight: 600, minWidth: '70px' }}>条件类型：</div>\n          <TextEllipsis text={type} />\n        </div>\n      )}\n      {value && (\n        <div className=\"condition-label\">\n          <div style={{ fontWeight: 600, minWidth: '70px' }}>条件语句：</div>\n          <TextEllipsis text={value} />\n        </div>\n      )}\n    </Space>\n  );\n};\n\nexport default showSwitchNode;\n"
  },
  {
    "path": "docs/xflow/demo/best/basic/tools.tsx",
    "content": "import { Button, Space } from 'antd';\nimport React from 'react'\n\n\nexport const Tools = () => {\n  return (\n    <Space className=\"tools\">\n      <Button size=\"small\" className=\"tools-btn\" >\n        预览\n      </Button>\n      <Button size=\"small\" className=\"tools-btn\">\n        保存\n      </Button>\n      <Button type=\"primary\" size=\"small\" className=\"tools-btn\">\n        立即发布\n      </Button>\n    </Space>\n  );\n};\n"
  },
  {
    "path": "docs/xflow/demo/best/demo2/CustomSvg.tsx",
    "content": "const CustomSvg = () => (\n  <svg viewBox=\"0 0 1024 1024\" width=\"1em\" height=\"1em\" fill=\"currentColor\">\n    <title>Panda icon</title>\n    <path\n      d=\"M99.096 315.634s-82.58-64.032-82.58-132.13c0-66.064 33.032-165.162 148.646-148.646 83.37 11.91 99.096 165.162 99.096 165.162l-165.162 115.614zM924.906 315.634s82.58-64.032 82.58-132.13c0-66.064-33.032-165.162-148.646-148.646-83.37 11.91-99.096 165.162-99.096 165.162l165.162 115.614z\"\n      fill=\"#6B676E\"\n    />\n    <path\n      d=\"M1024 561.548c0 264.526-229.23 429.42-512.002 429.42S0 826.076 0 561.548 283.96 66.064 512.002 66.064 1024 297.022 1024 561.548z\"\n      fill=\"#FFEBD2\"\n    />\n    <path\n      d=\"M330.324 842.126c0 82.096 81.34 148.646 181.678 148.646s181.678-66.55 181.678-148.646H330.324z\"\n      fill=\"#E9D7C3\"\n    />\n    <path\n      d=\"M644.13 611.098C594.582 528.516 561.55 512 512.002 512c-49.548 0-82.58 16.516-132.13 99.096-42.488 70.814-78.73 211.264-49.548 247.742 66.064 82.58 165.162 33.032 181.678 33.032 16.516 0 115.614 49.548 181.678-33.032 29.18-36.476-7.064-176.93-49.55-247.74z\"\n      fill=\"#FFFFFF\"\n    />\n    <path\n      d=\"M611.098 495.484c0-45.608 36.974-82.58 82.58-82.58 49.548 0 198.194 99.098 198.194 165.162s-79.934 144.904-148.646 99.096c-49.548-33.032-132.128-148.646-132.128-181.678zM412.904 495.484c0-45.608-36.974-82.58-82.58-82.58-49.548 0-198.194 99.098-198.194 165.162s79.934 144.904 148.646 99.096c49.548-33.032 132.128-148.646 132.128-181.678z\"\n      fill=\"#6B676E\"\n    />\n    <path\n      d=\"M512.002 726.622c-30.06 0-115.614 5.668-115.614 33.032 0 49.638 105.484 85.24 115.614 82.58 10.128 2.66 115.614-32.944 115.614-82.58-0.002-27.366-85.556-33.032-115.614-33.032z\"\n      fill=\"#464655\"\n    />\n    <path\n      d=\"M330.324 495.484m-33.032 0a33.032 33.032 0 1 0 66.064 0 33.032 33.032 0 1 0-66.064 0Z\"\n      fill=\"#464655\"\n    />\n    <path\n      d=\"M693.678 495.484m-33.032 0a33.032 33.032 0 1 0 66.064 0 33.032 33.032 0 1 0-66.064 0Z\"\n      fill=\"#464655\"\n    />\n  </svg>\n);\n\nexport default CustomSvg;"
  },
  {
    "path": "docs/xflow/demo/best/demo2/TextEllipsis/index.less",
    "content": ".text-ellipsis {\n  display: inline-block;\n  max-width: 100%;\n  overflow: hidden;\n  white-space: nowrap;\n  text-overflow: ellipsis;\n}\n\n.paragraph-ellipsis {\n  display: block;\n  width: 100%;\n  word-break: break-all;\n}\n"
  },
  {
    "path": "docs/xflow/demo/best/demo2/TextEllipsis/index.tsx",
    "content": "import { Tooltip, TooltipProps } from 'antd';\nimport React, { FC, memo, useEffect, useState } from 'react';\nimport './index.less';\n\ninterface ITextEllipsisProps {\n  text: string;\n  style?: object;\n  className?: string;\n  toolTipProps?: TooltipProps;\n  type?: 'text' | 'paragraph';\n  rows?: number;\n}\nconst TextEllipsis: FC<ITextEllipsisProps> = ({\n  text,\n  style,\n  toolTipProps,\n  type = 'text',\n  rows = 1,\n  className,\n}) => {\n  const typographyRef = React.useRef<HTMLElement>(null);\n  const [isEllipse, setIsEllipse] = useState(false);\n\n  const component =\n    type === 'paragraph' ? (\n      <span\n        ref={typographyRef}\n        className={`paragraph-ellipsis ${className}`}\n        style={{\n          ...style,\n          display: '-webkit-box',\n          overflow: 'hidden',\n          WebkitBoxOrient: 'vertical',\n          WebkitLineClamp: rows,\n        }}\n      >\n        {text}\n      </span>\n    ) : (\n      <span\n        ref={typographyRef}\n        className={`text-ellipsis ${className}`}\n        style={style}\n      >\n        {text}\n      </span>\n    );\n\n  useEffect(() => {\n    if (text) {\n      if (type === 'paragraph') {\n        const { offsetHeight, scrollHeight, clientHeight } =\n          typographyRef.current;\n        setIsEllipse(scrollHeight > clientHeight);\n      } else {\n        const isEllipse = isEleEllipsis(typographyRef?.current);\n        setIsEllipse(isEllipse);\n      }\n    }\n  }, [text]);\n\n  const isEleEllipsis = (ele: HTMLElement): boolean => {\n    const childDiv = document.createElement('em');\n    ele.appendChild(childDiv);\n\n    const rect = ele.getBoundingClientRect();\n    const childRect = childDiv.getBoundingClientRect();\n\n    ele.removeChild(childDiv);\n\n    return (\n      rect.left > childRect.left ||\n      childRect.right > rect.right ||\n      rect.top > childRect.top ||\n      childRect.bottom > rect.bottom\n    );\n  };\n  if (isEllipse) {\n    return (\n      <Tooltip\n        title={text}\n        getPopupContainer={() =>\n          document.getElementById('xflow-container') as HTMLElement\n        }\n        color=\"#ffff\"\n        overlayInnerStyle={{\n          color: '#354052',\n          fontSize: '12px',\n          borderRadius: '8px',\n        }}\n        placement=\"bottomRight\"\n        {...toolTipProps}\n      >\n        {component}\n      </Tooltip>\n    );\n  }\n  return component;\n};\n\nexport default memo(TextEllipsis);\n"
  },
  {
    "path": "docs/xflow/demo/best/demo2/const.tsx",
    "content": "export const nodes = [\n  {\n    id: 'y01s4993gdvyknzf',\n    type: 'startEvent',\n    position: {\n      x: 0,\n      y: 120,\n    },\n    measured: {\n      width: 268,\n      height: 54,\n    },\n    selected: false,\n    dragging: false,\n    data: {\n      _isCandidate: false,\n      title: '开始',\n      string: 'hello',\n      string2: 'test',\n      string3: '16',\n      string4: '17',\n      string5: '19',\n      string6: 'hello world',\n      string7: 'test',\n    },\n  },\n  {\n    id: 'bzydz67rlmv1n871',\n    type: 'Switch',\n    position: {\n      x: 354,\n      y: 120,\n    },\n    measured: {\n      width: 268,\n      height: 222,\n    },\n    selected: false,\n    dragging: false,\n    data: {\n      _isCandidate: false,\n      title: '条件分支',\n      list: [\n        {\n          _id: 'yjrqixxjwfdn47of',\n          name: '条件一',\n          type: '类型一',\n          value: 'a==12',\n        },\n        {\n          name: '条件二',\n          type: '类型一',\n          value: 'a!=12',\n          _id: 'id_wmu3t8gkanwg271a',\n        },\n      ],\n    },\n  },\n  {\n    id: 'mjw4se36aadccnbt',\n    type: 'serviceTask',\n    position: {\n      x: 708,\n      y: 22.5,\n    },\n    measured: {\n      width: 268,\n      height: 54,\n    },\n    selected: false,\n    dragging: false,\n    data: {\n      _isCandidate: false,\n      title: '知识检索_ncjg',\n      properties: [\n        {\n          name: 'name1',\n          value: 'value1',\n        },\n      ],\n    },\n  },\n  {\n    id: '6w52vnb5kwwq7pdj',\n    type: 'receiveTask',\n    position: {\n      x: 708,\n      y: 217.5,\n    },\n    measured: {\n      width: 268,\n      height: 73,\n    },\n    selected: false,\n    data: {\n      _isCandidate: false,\n      title: '问题分类器_ysa6',\n      desc: '问题分类描述',\n      properties: [\n        {\n          name: '问题1',\n          value: '值1',\n        },\n      ],\n    },\n  },\n  {\n    id: 'dm6eebudu8tmb4v3',\n    type: 'Parallel',\n    position: {\n      x: 1062,\n      y: 120,\n    },\n    measured: {\n      width: 268,\n      height: 196,\n    },\n    selected: false,\n    dragging: false,\n    data: {\n      _isCandidate: false,\n      title: '并行事件_49pn',\n      properties: [\n        {\n          name: '属性',\n          value: 'value',\n        },\n      ],\n      list: [\n        {\n          _id: 'id_7m3vitc4qy476axa',\n          name: '事件1',\n          value: '描述1',\n        },\n        {\n          _id: 'id_m26wo9298g3imnes',\n          name: '事件2',\n          value: '描述2',\n        },\n      ],\n    },\n  },\n  {\n    id: 'walnrnko6rjn56bx',\n    type: 'userTask',\n    position: {\n      x: 1416,\n      y: 22.5,\n    },\n    measured: {\n      width: 268,\n      height: 54,\n    },\n    selected: false,\n    data: {\n      _isCandidate: false,\n      title: '代码执行_flcv',\n      properties: [\n        {\n          name: 'name',\n          value: 'value',\n        },\n      ],\n    },\n  },\n  {\n    id: 'rxoar6yr0whfr7ym',\n    type: 'callActivity',\n    position: {\n      x: 1416,\n      y: 217.5,\n    },\n    measured: {\n      width: 268,\n      height: 54,\n    },\n    selected: false,\n    data: {\n      _isCandidate: false,\n      title: 'HTTP请求_pmkf',\n      properties: [\n        {\n          name: 'name',\n          value: 'value',\n        },\n      ],\n    },\n  },\n  {\n    id: 'qjmv9gxija93gxkb',\n    type: 'endEvent',\n    position: {\n      x: 1770,\n      y: 120,\n    },\n    measured: {\n      width: 268,\n      height: 54,\n    },\n    selected: true,\n    dragging: false,\n    data: {\n      _isCandidate: false,\n      title: '结束_af4u',\n      nodeDesc: '流程结束',\n    },\n  },\n];\n\nexport const edges = [\n  {\n    type: 'buttonedge',\n    style: {\n      strokeWidth: 1.5,\n      stroke: '#c9c9c9',\n    },\n    markerEnd: {\n      type: 'arrowclosed',\n      color: '#c9c9c9',\n    },\n    deletable: true,\n    source: 'y01s4993gdvyknzf',\n    target: 'bzydz67rlmv1n871',\n    id: 'xy-edge__y01s4993gdvyknzf-bzydz67rlmv1n871',\n  },\n  {\n    type: 'buttonedge',\n    style: {\n      strokeWidth: 1.5,\n      stroke: '#c9c9c9',\n    },\n    markerEnd: {\n      type: 'arrowclosed',\n      color: '#c9c9c9',\n    },\n    deletable: true,\n    source: 'bzydz67rlmv1n871',\n    sourceHandle: 'yjrqixxjwfdn47of',\n    target: 'mjw4se36aadccnbt',\n    id: 'xy-edge__bzydz67rlmv1n871yjrqixxjwfdn47of-mjw4se36aadccnbt',\n  },\n  {\n    type: 'buttonedge',\n    style: {\n      strokeWidth: 1.5,\n      stroke: '#c9c9c9',\n    },\n    markerEnd: {\n      type: 'arrowclosed',\n      color: '#c9c9c9',\n    },\n    deletable: true,\n    source: 'bzydz67rlmv1n871',\n    sourceHandle: 'id_wmu3t8gkanwg271a',\n    target: '6w52vnb5kwwq7pdj',\n    id: 'xy-edge__bzydz67rlmv1n871id_wmu3t8gkanwg271a-6w52vnb5kwwq7pdj',\n  },\n  {\n    type: 'buttonedge',\n    style: {\n      strokeWidth: 1.5,\n    },\n    markerEnd: {\n      type: 'arrowclosed',\n    },\n    deletable: true,\n    source: 'mjw4se36aadccnbt',\n    target: 'dm6eebudu8tmb4v3',\n    id: 'xy-edge__mjw4se36aadccnbt-dm6eebudu8tmb4v3',\n  },\n  {\n    type: 'buttonedge',\n    style: {\n      strokeWidth: 1.5,\n      stroke: '#c9c9c9',\n    },\n    markerEnd: {\n      type: 'arrowclosed',\n      color: '#c9c9c9',\n    },\n    deletable: true,\n    source: '6w52vnb5kwwq7pdj',\n    target: 'dm6eebudu8tmb4v3',\n    id: 'xy-edge__6w52vnb5kwwq7pdj-dm6eebudu8tmb4v3',\n  },\n  {\n    type: 'buttonedge',\n    style: {\n      strokeWidth: 1.5,\n      stroke: '#c9c9c9',\n    },\n    markerEnd: {\n      type: 'arrowclosed',\n      color: '#c9c9c9',\n    },\n    deletable: true,\n    source: 'dm6eebudu8tmb4v3',\n    sourceHandle: 'id_7m3vitc4qy476axa',\n    target: 'walnrnko6rjn56bx',\n    id: 'xy-edge__dm6eebudu8tmb4v3id_7m3vitc4qy476axa-walnrnko6rjn56bx',\n  },\n  {\n    type: 'buttonedge',\n    style: {\n      strokeWidth: 1.5,\n    },\n    markerEnd: {\n      type: 'arrowclosed',\n    },\n    deletable: true,\n    source: 'dm6eebudu8tmb4v3',\n    sourceHandle: 'id_m26wo9298g3imnes',\n    target: 'rxoar6yr0whfr7ym',\n    id: 'xy-edge__dm6eebudu8tmb4v3id_m26wo9298g3imnes-rxoar6yr0whfr7ym',\n  },\n  {\n    type: 'buttonedge',\n    style: {\n      strokeWidth: 1.5,\n    },\n    markerEnd: {\n      type: 'arrowclosed',\n    },\n    deletable: true,\n    source: 'walnrnko6rjn56bx',\n    target: 'qjmv9gxija93gxkb',\n    id: 'xy-edge__walnrnko6rjn56bx-qjmv9gxija93gxkb',\n  },\n  {\n    type: 'buttonedge',\n    style: {\n      strokeWidth: 1.5,\n    },\n    markerEnd: {\n      type: 'arrowclosed',\n    },\n    deletable: true,\n    source: 'rxoar6yr0whfr7ym',\n    target: 'qjmv9gxija93gxkb',\n    id: 'xy-edge__rxoar6yr0whfr7ym-qjmv9gxija93gxkb',\n  },\n];\n"
  },
  {
    "path": "docs/xflow/demo/best/demo2/header.tsx",
    "content": "import React ,{ FC } from 'react';\nimport './index.less';\n\nconst Header: FC<{ data: any }> = ({ data }) => {\n  //  const container = (document.getElementById('xflow-container') as HTMLElement);\n  return (\n    <div>\n      <div className=\"process-header-card\">\n        <img\n          src=\"https://img.alicdn.com/tfs/TB17UtINiLaK1RjSZFxXXamPFXa-606-643.png\"\n          style={{ width: '22px' }}\n        />\n        <span style={{ fontSize: '23px', fontWeight: 600, marginLeft: '5px' }}>\n          XFlow\n        </span>\n      </div>\n    </div>\n  );\n};\n\nexport default Header;\n"
  },
  {
    "path": "docs/xflow/demo/best/demo2/index.less",
    "content": ".settingSchemaStyle {\n  .ant-col {\n    width: 100% !important;\n\n    .ant-form-item-label label {\n      font-weight: 500;\n    }\n\n    .ant-form-item-explain-error {\n      font-size: 12px;\n    }\n  }\n\n  .child-title {\n    .ant-form-item-label label {\n      font-size: 12px;\n    }\n  }\n\n  .fr-list-simple {\n    display: block;\n  }\n\n  .parallel-wrap {\n    .fr-list-simple .fr-inline-field {\n      min-width: 200px;\n    }\n  }\n\n  .switch-list {\n    .fr-list-simple .fr-inline-field {\n      width: 140px;\n      min-width: 140px;\n    }\n  }\n\n  .ant-form-item-label>label {\n    color: #096dd9;\n  }\n}\n\n\n.switch-custom-node {\n  width: 100%;\n  min-height: 20px;\n  padding: 0.25rem;\n  text-align: justify;\n  word-wrap: break-word;\n  background-color: #f2f4f7;\n  border-radius: 0.375rem;\n\n  .condition-label {\n    display: flex;\n  }\n}\n\n\n.process-header-card {\n  position: absolute;\n  top: 14px;\n  left: 10px;\n  z-index: 1;\n  padding: 6px;\n  font-size: 12px;\n}\n\n\n.tools {\n  position: absolute;\n  top: 14px;\n  right: 10px;\n  z-index: 1;\n\n  .tools-btn {\n    height: 29px;\n    font-size: 12px;\n    border-radius: 6px;\n  }\n}\n\n.ant-card-head {\n  background: #fff;\n  border-bottom: 1px solid #f0f0f0;\n  padding: 0 24px;\n  height: 56px;\n  line-height: 56px;\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n}\n\n.ant-card-body {\n  padding: 0;\n  height: calc(100% - 56px);\n}\n"
  },
  {
    "path": "docs/xflow/demo/best/demo2/index.tsx",
    "content": "import React, { useState } from 'react';\nimport XFlow from '@xrenders/xflow';\nimport { Button, Space, message } from 'antd';\nimport { PlayCircleOutlined, SaveOutlined } from '@ant-design/icons';\nimport { settings } from './setting';\nimport './index.less';\nimport CustomSvg from './CustomSvg';\n\nexport default () => {\n  const [loading, setLoading] = useState(false);\n  const [logList, setLogList] = useState<any[]>([\n    {\n      nodeId: 'llm',\n      statusPanel: {\n        status: [\n          { label: '状态', value: 'success', isBadge: true },\n          { label: '耗时', value: '2.3s' },\n          { label: 'Token', value: '856' },\n        ],\n        extra: `执行时间: ${new Date().toLocaleString()}`,\n      },\n      codePanel: [\n        {\n          title: '输入',\n          code: JSON.stringify({\n            name: 'GPT-4调用',\n            model: 'gpt-4',\n            temperature: 0.7,\n            prompt: '请生成一段产品介绍文案',\n          }, null, 2),\n        },\n        {\n          title: '输出',\n          code: `XFlow是一个强大的流程编排组件，它提供了直观的可视化界面，支持节点拖拽、连线、配置等功能。用户可以通过简单的操作快速构建复杂的业务流程。\\n\\n特点：\\n1. 可视化编排\\n2. 丰富的节点类型\\n3. 灵活的配置选项\\n4. 完善的状态管理`,\n        },\n      ],\n    },\n  ]);\n\n  // 初始节点配置\n  const nodes = [\n    {\n      id: 'start',\n      type: 'Start',\n      data: {\n        title: '开始',\n      },\n      position: {\n        x: 10,\n        y: 60,\n      },\n    },\n    {\n      id: 'llm',\n      type: 'LLM',\n      data: {\n        name: 'GPT-4调用',\n        model: 'gpt-4',\n        temperature: 0.7,\n        _status: 'success', // 初始状态改为success，匹配初始日志\n      },\n      position: {\n        x: 250,\n        y: 150,\n      },\n    },\n    {\n      id: 'review',\n      type: 'userTask',\n      data: {\n        name: '人工审核',\n        assignee: 'admin',\n        description: '请审核AI生成的内容',\n        _status: 'warning',\n      },\n      position: {\n        x: 450,\n        y: 250,\n      },\n    },\n    {\n      id: 'format',\n      type: 'Format',\n      data: {\n        name: '结果格式化',\n        format: 'markdown',\n        _status: 'success',\n      },\n      position: {\n        x: 650,\n        y: 150,\n      },\n    },\n    {\n      id: 'end',\n      type: 'End',\n      data: {\n        title: '结束',\n      },\n      position: {\n        x: 850,\n        y: 230,\n      },\n    },\n  ];\n\n  const edges = [\n    { source: 'start', target: 'llm', id: 'e1' },\n    { source: 'llm', target: 'review', id: 'e2' },\n    { source: 'review', target: 'format', id: 'e3' },\n    { source: 'format', target: 'end', id: 'e4' },\n  ];\n\n  // 模拟节点测试\n  const handleNodeTest = async (node) => {\n    setLoading(true);\n    try {\n      await new Promise(resolve => setTimeout(resolve, 1500));\n\n      const newLog = {\n        nodeId: node.id,\n        statusPanel: {\n          status: [\n            { label: '状态', value: 'success', isBadge: true },\n            { label: '耗时', value: '1.5s' },\n            { label: 'Token', value: '1024' },\n          ],\n          extra: `执行时间: ${new Date().toLocaleString()}`,\n        },\n        codePanel: [\n          {\n            title: '输入',\n            code: JSON.stringify(node.data, null, 2),\n          },\n          {\n            title: '输出',\n            code: `节点 ${node.data.name} 执行成功\\n处理完成时间: ${new Date().toLocaleString()}`,\n          },\n        ],\n      };\n\n      setLogList(prev => [...prev, newLog]);\n      message.success(`${node.data.name}测试成功`);\n    } catch (error) {\n      message.error(`${node.data.name}测试失败`);\n    } finally {\n      setLoading(false);\n    }\n  };\n\n  // 运行整个流程\n  const runFlow = async () => {\n    setLoading(true);\n    try {\n      setLogList([]);\n      for (const node of nodes) {\n        if (node.type !== 'Start' && node.type !== 'End') {\n          await handleNodeTest(node);\n        }\n      }\n      message.success('流程执行完成');\n    } catch (error) {\n      message.error('流程执行失败');\n    } finally {\n      setLoading(false);\n    }\n  };\n\n  return (\n    <div style={{ height: '600px', position: 'relative' }}>\n        <div style={{ height: '600px' }}>\n          <XFlow\n            initialValues={{ nodes, edges }}\n            settings={settings}\n            onTesting={handleNodeTest}\n            logPanel={{\n              logList,\n              loading,\n            }}\n            globalConfig={{\n              nodeView: {\n                status: [\n                  { value: 'processing', color: '#1890FF', name: '处理中' },\n                  { value: 'success', color: '#52c41a', name: '成功' },\n                  { value: 'error', color: '#ff4d4f', name: '失败' },\n                  { value: 'warning', color: '#faad14', name: '警告' },\n                ],\n              },\n            }}\n          widgets={{ CustomSvg }}\n          />\n        </div>\n        <Space className=\"tools\">\n          <Button\n            type=\"primary\"\n            icon={<PlayCircleOutlined />}\n            loading={loading}\n          onClick={runFlow}\n          size=\"small\" className=\"tools-btn\"\n          >\n            运行流程\n          </Button>\n        <Button icon={<SaveOutlined />} size=\"small\" className=\"tools-btn\" >保存流程</Button>\n        </Space>\n    </div>\n  );\n};\n"
  },
  {
    "path": "docs/xflow/demo/best/demo2/setting.tsx",
    "content": "export const settingSchema = {\n  properties: {\n    type: 'array',\n    widget: 'simpleList',\n    props: {\n      hideCopy: true,\n      hideMove: true,\n    },\n    className: 'parallel-wrap',\n    items: {\n      type: 'object',\n      properties: {\n        name: {\n          title: '名称',\n          type: 'string',\n          props: {\n            allowClear: true,\n          },\n          className: 'child-title',\n          readOnlyWidget: 'ReadOnlyPanel',\n        },\n        value: {\n          title: 'value',\n          type: 'string',\n          props: {\n            allowClear: true,\n          },\n          className: 'child-title',\n          readOnlyWidget: 'ReadOnlyPanel',\n        },\n      },\n    },\n  },\n};\n\nexport const activitySchema = {\n  title: '工具',\n  type: '_group',\n  items: [\n    {\n      title: '知识检索',\n      type: 'LLM',\n      description: '允许你从知识库中查询与用户问题相关的文本内容',\n      icon: {\n        type: 'icon-knowledge',\n        bgColor: '#fa541c',\n      },\n      hideDesc: true,\n      nodePanel: {\n        width: 510,\n      },\n      settingSchema: {\n        type: 'object',\n        className: 'settingSchemaStyle',\n        properties: {\n          ...settingSchema,\n        },\n      },\n    },\n    {\n      title: '问题分类器',\n      type: 'Prompt',\n      description: '定义问题的分类条件',\n      icon: {\n        type: 'icon-prompt',\n        bgColor: '#875BF7',\n      },\n      hideDesc: true,\n      nodePanel: {\n        width: 510,\n      },\n      settingSchema: {\n        type: 'object',\n        className: 'settingSchemaStyle',\n        properties: {\n          ...settingSchema,\n        },\n      },\n    },\n    {\n      title: '代码执行',\n      type: 'userTask', // exclusiveGateway\n      description: '执行一段代码实现自定义逻辑',\n      icon: {\n        type: 'icon-code',\n        bgColor: 'pink',\n      },\n      hideDesc: true,\n      nodePanel: {\n        width: 510,\n      },\n      settingSchema: {\n        type: 'object',\n        className: 'settingSchemaStyle',\n        properties: {\n          ...settingSchema,\n        },\n      },\n    },\n    {\n      title: 'HTTP请求',\n      type: 'callActivity',\n      description: '允许通过 HTTP 协议发送服务器请求',\n      icon: {\n        type: 'icon-http',\n        bgColor: '#2E90FA',\n      },\n      hideDesc: true,\n      nodePanel: {\n        width: 510,\n      },\n      settingSchema: {\n        type: 'object',\n        className: 'settingSchemaStyle',\n        properties: {\n          ...settingSchema,\n        },\n      },\n    },\n  ],\n};\n\nexport const settings = [\n  {\n    title: '开始',\n    type: 'Start',\n    icon: {\n      type: 'icon-start',\n      bgColor: '#17B26A',\n    },\n  },\n  {\n    title: '条件判断',\n    type: 'Switch',\n    description:'条件判断',\n    showTestingBtn: true,\n    icon: {\n      type: 'icon-switch',\n      bgColor: '#FF8800',\n    },\n    settingSchema: {\n      type: 'object',\n      properties: {\n        name: {\n          title: '节点名称',\n          type: 'string',\n          required: true,\n        },\n        template: {\n          title: '提示词模板',\n          type: 'string',\n          widget: 'textarea',\n        },\n      },\n    },\n  },\n  {\n    title: 'LLM',\n    type: 'LLM',\n    showTestingBtn: true,\n    description:'LLM',\n    icon: {\n      // type: 'icon-model',\n      bgColor: '#1890FF',\n    },\n    iconSvg:'CustomSvg',\n    settingSchema: {\n      type: 'object',\n      properties: {\n        name: {\n          title: '节点名称',\n          type: 'string',\n          required: true,\n        },\n        model: {\n          title: '模型选择',\n          type: 'string',\n          enum: ['gpt-3.5-turbo', 'gpt-4'],\n          enumNames: ['GPT-3.5', 'GPT-4'],\n          widget: 'select',\n        },\n        temperature: {\n          title: '温度',\n          type: 'number',\n          min: 0,\n          max: 1,\n          widget: 'slider',\n        },\n      },\n    },\n  },\n  {\n    title: '人工任务',\n    type: 'userTask',\n    showTestingBtn: true,\n    icon: {\n      type: 'icon-knowledge',\n      bgColor: '#6172F3',\n    },\n    settingSchema: {\n      type: 'object',\n      properties: {\n        name: {\n          title: '节点名称',\n          type: 'string',\n          required: true,\n        },\n        assignee: {\n          title: '处理人',\n          type: 'string',\n        },\n        description: {\n          title: '任务描述',\n          type: 'string',\n          widget: 'textarea',\n        },\n      },\n    },\n  },\n  {\n    title: '合并节点',\n    type: 'Merge',\n    showTestingBtn: true,\n    icon: {\n      type: 'icon-gongju',\n      bgColor: '#9E6BE6',\n    },\n    settingSchema: {\n      type: 'object',\n      properties: {\n        name: {\n          title: '节点名称',\n          type: 'string',\n          required: true,\n        },\n        strategy: {\n          title: '合并策略',\n          type: 'string',\n          enum: ['compare', 'concat', 'custom'],\n          enumNames: ['对比合并', '拼接合并', '自定义合并'],\n          widget: 'select',\n        },\n      },\n    },\n  },\n  {\n    title: '格式化',\n    type: 'Format',\n    showTestingBtn: true,\n    icon: {\n      type: 'icon-code',\n      bgColor: '#F759AB',\n    },\n    settingSchema: {\n      type: 'object',\n      properties: {\n        name: {\n          title: '节点名称',\n          type: 'string',\n          required: true,\n        },\n        format: {\n          title: '格式类型',\n          type: 'string',\n          enum: ['markdown', 'html', 'text'],\n          enumNames: ['Markdown', 'HTML', '纯文本'],\n          widget: 'select',\n        },\n      },\n    },\n  },\n  {\n    title: '结束',\n    type: 'End',\n    icon: {\n      type: 'icon-end',\n      bgColor: '#FF4D4F',\n    },\n  },\n];\n"
  },
  {
    "path": "docs/xflow/demo/best/demo2/showSwitchNode.tsx",
    "content": "import { Space } from 'antd';\nimport React from 'react';\nimport './index.less';\nimport TextEllipsis from './TextEllipsis';\n\nconst showSwitchNode = ({ data, index }) => {\n  const { type, value } = data;\n  if (!type && !value) {\n    return <div style={{ minHeight: '40px' }}></div>;\n  }\n\n  return (\n    <Space style={{ width: '100%' }} direction=\"vertical\" className=\"switch-custom-node\">\n      {type && (\n        <div className=\"condition-label\">\n          <div style={{ fontWeight: 600, minWidth: '70px' }}>条件类型：</div>\n          <TextEllipsis text={type} />\n        </div>\n      )}\n      {value && (\n        <div className=\"condition-label\">\n          <div style={{ fontWeight: 600, minWidth: '70px' }}>条件语句：</div>\n          <TextEllipsis text={value} />\n        </div>\n      )}\n    </Space>\n  );\n};\n\nexport default showSwitchNode;\n"
  },
  {
    "path": "docs/xflow/demo/best/demo2/tools.tsx",
    "content": "import { Button, Space } from 'antd';\nimport React from 'react'\n\n\nexport const Tools = () => {\n  return (\n    <Space className=\"tools\">\n      <Button size=\"small\" className=\"tools-btn\" >\n        预览\n      </Button>\n      <Button size=\"small\" className=\"tools-btn\">\n        保存\n      </Button>\n      <Button type=\"primary\" size=\"small\" className=\"tools-btn\">\n        立即发布\n      </Button>\n    </Space>\n  );\n};\n"
  },
  {
    "path": "docs/xflow/demo/custom-flow/advancedLinkageCase/customWidget.tsx",
    "content": "import { Button, Space } from 'antd';\nimport FormRender, { useForm } from 'form-render';\nimport React, { forwardRef, useImperativeHandle } from 'react';\n\nexport interface AdvancedSettingWidgetProps {\n  value: any;\n  onChange: (value: any) => void;\n  readOnly?: boolean;\n}\n\nexport interface AdvancedSettingWidgetRef {\n  validateForm: () => Promise<boolean>;\n}\n\nexport const AdvancedSettingWidget = forwardRef<\n  AdvancedSettingWidgetRef,\n  AdvancedSettingWidgetProps\n>(({ value, onChange, readOnly }, ref) => {\n  const form = useForm();\n\n  // 暴露验证方法\n  useImperativeHandle(ref, () => ({\n    validateForm: async () => {\n      return await form\n        .validateFields()\n        .then(() => {\n          return true;\n        })\n        .catch(err => {\n          return false;\n        });\n    },\n  }));\n\n  const schema = {\n    type: 'object',\n    properties: {\n      name: {\n        title: '节点名称',\n        type: 'string',\n        required: true,\n      },\n      formType: {\n        title: '配置类型',\n        type: 'string',\n        enum: ['simple', 'complex'],\n        enumNames: ['简单配置', '复杂配置'],\n        widget: 'radio',\n        required: true,\n      },\n      simpleInput: {\n        title: '简单输入',\n        type: 'string',\n        hidden: '{{formData.formType !== \"simple\"}}',\n        required: true,\n      },\n      complexConfig: {\n        title: '复杂配置',\n        type: 'object',\n        hidden: '{{formData.formType !== \"complex\"}}',\n        properties: {\n          field1: {\n            title: '字段1',\n            type: 'string',\n          },\n          field2: {\n            title: '字段2',\n            type: 'string',\n            description: '字段1有值时才能输入',\n            disabled: '{{!formData.complexConfig?.field1}}',\n          },\n        },\n      },\n      conditions: {\n        title: '条件配置',\n        type: 'array',\n        widget: 'cardList',\n        items: {\n          type: 'object',\n          properties: {\n            name: {\n              title: '条件名称',\n              type: 'string',\n              required: true,\n            },\n            operator: {\n              title: '操作符',\n              type: 'string',\n              enum: ['equals', 'contains', 'greater', 'less'],\n              enumNames: ['等于', '包含', '大于', '小于'],\n              required: true,\n            },\n            value: {\n              title: '条件值',\n              type: 'string',\n              required: true,\n            },\n          },\n        },\n      },\n    },\n  };\n\n  // 监听表单变化\n  const watch = {\n    '#': formData => {\n      onChange(formData);\n    },\n    name: val => {\n      console.log('监听节点名称的变化', val);\n    },\n    'complexConfig.field1': value => {\n      // 当field1变化时，如果为空则清空field2\n      if (!value) {\n        const values = form.getValues();\n        form.setValues({\n          ...values,\n          complexConfig: {\n            ...values.complexConfig,\n            field2: '',\n          },\n        });\n      }\n    },\n  };\n\n  // 添加条件按钮\n  const addCondition = () => {\n    const values = form.getValues();\n    const conditions = values.conditions || [];\n    form.setValues({\n      ...values,\n      conditions: [\n        ...conditions,\n        {\n          name: '',\n          operator: 'equals',\n          value: '',\n        },\n      ],\n    });\n  };\n\n  React.useEffect(() => {\n    if (value) {\n      form.setValues(value);\n    }\n  }, [value]);\n\n  return (\n    <div>\n      <FormRender\n        form={form}\n        schema={schema}\n        watch={watch}\n        readOnly={readOnly}\n      />\n      {!readOnly && (\n        <div style={{ marginTop: '16px' }}>\n          <Space>\n            <Button type=\"primary\" onClick={addCondition}>\n              添加条件\n            </Button>\n          </Space>\n        </div>\n      )}\n    </div>\n  );\n});\n"
  },
  {
    "path": "docs/xflow/demo/custom-flow/advancedLinkageCase/index.tsx",
    "content": "import React, { useState } from 'react';\nimport XFlow from '@xrenders/xflow';\nimport { AdvancedSettingWidget } from './customWidget';\nimport setting from './setting';\nimport { Button, Switch } from 'antd';\n\nexport default () => {\n  const [readOnly, setReadonly] = useState(false);\n\n  const nodes = [\n    {\n      id: 'start',\n      type: 'Start',\n      data: {\n        title: '开始',\n      },\n      position: {\n        x: 200,\n        y: 200,\n      }\n    },\n    {\n      id: '1',\n      type: 'LLM',\n      data: {\n        name: '高级节点',\n        formType: 'simple',\n        simpleInput: '',\n        complexConfig: {\n          field1: '',\n          field2: '',\n        },\n        conditions: []\n      },\n      position: {\n        x: 500,\n        y: 200,\n      }\n    },\n    {\n      id: '2',\n      type: 'End',\n      data: {},\n      position: {\n        x: 800,\n        y: 200,\n      }\n    }\n  ];\n\n  const edges = [\n    { source: 'start', target: '1', id: 'e0' },\n    { source: '1', target: '2', id: 'e1' }\n  ];\n\n  return (\n    <div style={{ height: '600px' }}>\n      <div style={{ marginBottom: '16px' }}>\n        <span> 查看只读状态 <Switch checked={readOnly} onChange={setReadonly} /></span>\n      </div>\n      <XFlow\n        initialValues={{ nodes, edges }}\n        settings={setting}\n        widgets={{ AdvancedSettingWidget }}\n        readOnly={readOnly}\n        style={{ marginTop: '20px' }}\n        globalConfig={{\n          nodePanel: {\n            onClose: (nodeID) => {\n              console.log(\"关闭节点\",nodeID)\n            }\n          }\n        }}\n      />\n    </div>\n  );\n};\n"
  },
  {
    "path": "docs/xflow/demo/custom-flow/advancedLinkageCase/setting.tsx",
    "content": "export default [\n  {\n    title: '开始',\n    type: 'Start',\n    hidden: true,\n    targetHandleHidden: true,\n    icon: {\n      type: 'icon-start',\n      bgColor: '#17B26A',\n    },\n    settingWidget: \"customWidget\",\n    settingWidgetProps: {\n      params: \"test\"\n    }\n  },\n  {\n    title: '结束',\n    type: 'End',\n    hidden: true,\n    sourceHandleHidden: true,\n    icon: {\n      type: 'icon-end',\n      bgColor: '#F79009',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n        select: {\n          title: '变量二',\n          type: 'string',\n          widget: 'select',\n          props: {\n            options: [\n              { label: 'a', value: 'a' },\n              { label: 'b', value: 'b' },\n              { label: 'c', value: 'c' },\n            ],\n          },\n        },\n      }\n    }\n  },\n  {\n    title: '自定义组件节点',\n    type: 'LLM',\n    icon: {\n      type: 'icon-model',\n      bgColor: '#6172F3',\n    },\n    settingWidget: 'AdvancedSettingWidget',\n  },\n  {\n    title: 'Prompt',\n    type: 'Prompt',\n    description: '通过精心设计提示词，提升大语言模型回答效果',\n    icon: {\n      type: 'icon-prompt',\n      bgColor: '#17B26A',\n    },\n  },\n  {\n    title: '知识库',\n    type: 'knowledge',\n    description: '允许你从知识库中查询与用户问题相关的文本内容',\n    icon: {\n      type: 'icon-knowledge',\n      bgColor: '#6172F3',\n    },\n  },\n  {\n    title: 'Switch',\n    type: 'Switch',\n    description: '允许你根据 if/else 条件将 workflow 拆分成两个分支',\n    icon: {\n      type: 'icon-fenzhi',\n      bgColor: '#06AED4',\n    },\n  },\n  {\n    title: 'HSF',\n    type: 'hsf',\n    description: '允许通过 HSF 协议发送服务器请求',\n    icon: {\n      type: 'icon-hsf',\n      bgColor: '#875BF7',\n    },\n  },\n  {\n    title: 'Http',\n    type: 'http',\n    description: '允许通过 HTTP 协议发送服务器请求',\n    icon: {\n      type: 'icon-http',\n      bgColor: '#875BF7',\n    },\n  },\n  {\n    title: '代码执行',\n    type: 'Code',\n    description: '执行一段 Groovy 或 Python 或 NodeJS 代码实现自定义逻辑',\n    icon: {\n      type: 'icon-code',\n      bgColor: '#2E90FA',\n    },\n  },\n  {\n    title: '工具',\n    type: 'tool',\n    description: '允许使用工具能力',\n    icon: {\n      type: 'icon-gongju',\n      bgColor: '#2E90FA',\n    },\n  },\n  {\n    title: '工具',\n    type: '_group',\n    items: [\n      {\n        title: '代码执行',\n        type: 'Code',\n        description: '执行一段 Groovy 或 Python 或 NodeJS 代码实现自定义逻辑',\n        icon: {\n          type: 'icon-code',\n          bgColor: '#2E90FA',\n        },\n      },\n      {\n        title: '工具',\n        type: 'tool',\n        description: '允许使用工具能力',\n        icon: {\n          type: 'icon-gongju',\n          bgColor: '#2E90FA',\n        },\n      },\n    ],\n  },\n];\n"
  },
  {
    "path": "docs/xflow/demo/custom-flow/customWidget.tsx",
    "content": "import { Input } from 'antd';\n\nconst customWidget = ({ value, onChange,...rest }) => {\n  console.log(\"参数\", { value, onChange, ...rest })\n  return (\n    <Input\n      value={value?.inputVal}\n      onChange={e => onChange({ inputVal: e.target.value })}\n    />\n  );\n};\n\nexport default customWidget;\n"
  },
  {
    "path": "docs/xflow/demo/custom-flow/fullCase/components/ReadOnlyPanel.tsx",
    "content": "import '../index.less';\n\nconst ReadOnlyPanel = (props) => {\n  const { value } = props;\n\n  return <div className=\"smart-read-only-panel\">{value?? '-'}</div>;\n};\n\nexport default ReadOnlyPanel;\n"
  },
  {
    "path": "docs/xflow/demo/custom-flow/fullCase/components/TagWidget.tsx",
    "content": "import React from 'react';\nimport { Tag, Popover, Typography, Space } from 'antd';\nimport '../index.less';\n\nconst { Text } = Typography;\n\ninterface TagItem {\n  name: string;\n  code: string;\n  description: string;\n}\n\ninterface TagWidgetProps {\n  tags?: TagItem[];\n  style?: React.CSSProperties;\n  className?: string;\n}\n\nconst getTagData = (data: any, values: any) => {\n  const tagData =\n    (values || [])\n      ?.map((val) => data?.tagData?.find((item) => item.code === val))\n      .filter(Boolean) || [];\n  return tagData;\n};\n\nconst TagWidget: React.FC<TagWidgetProps> = ({ data }) => {\n  const dataKeys = Object.keys(data);\n  const nodeType = dataKeys?.includes('firstScene')\n    ? 'firstScene'\n    : 'secondScene';\n  const firstSceneData = data?.displayName\n    ? [\n        {\n          name: data.displayName,\n          code: data.firstMeasure,\n          description: data.description,\n          color: 'blue',\n        },\n      ]\n    : getTagData(data, [data?.firstMeasure] || []);\n  const nodeData =\n    nodeType === 'firstScene'\n      ? firstSceneData\n      : data?.tagWidgetData || getTagData(data, data?.secondMeasures || []);\n\n  const renderContent = (tag: TagItem) => (\n    <Space direction=\"vertical\" size=\"small\" style={{ padding: '8px' }}>\n      <div>\n        <Text strong style={{ fontSize: '12px' }}>\n          指标名称:\n        </Text>{' '}\n        {tag.name}\n      </div>\n      <div>\n        <Text strong style={{ fontSize: '12px' }}>\n          指标代码:\n        </Text>{' '}\n        {tag.code}\n      </div>\n      <div>\n        <Text strong style={{ fontSize: '12px' }}>\n          指标描述:\n        </Text>{' '}\n        {tag.description}\n      </div>\n    </Space>\n  );\n\n  return (\n    <div>\n      {Boolean(data?.firstScene || data?.secondScene) && (\n        <div style={{ marginBottom: '8px' }}>\n          <Text strong style={{ fontSize: '12px' }}>\n            分类：\n          </Text>\n          <Text style={{ color: '#1890ff' }}>\n            {nodeType === 'firstScene' ? data?.firstScene : data?.secondScene}\n          </Text>\n        </div>\n      )}\n      <Space size=\"small\" style={{ width: '100%' }} wrap>\n        {(nodeData || []).map((tag: any, index: number) => (\n          <Popover\n            key={index}\n            content={renderContent(tag)}\n            trigger=\"hover\"\n            getPopupContainer={() =>\n              document.getElementById('xflow-container') as HTMLElement\n            }\n            overlayClassName=\"tag-popover\"\n          >\n            <Tag color={tag.color} key={index}>\n              {tag.name}\n            </Tag>\n          </Popover>\n        ))}\n      </Space>\n    </div>\n  );\n};\n\nexport default TagWidget;\n"
  },
  {
    "path": "docs/xflow/demo/custom-flow/fullCase/components/customWidget.tsx",
    "content": "import FormRender, { useForm } from 'form-render';\nimport React, { forwardRef, useEffect, useImperativeHandle } from 'react';\nimport { firstTagSchema } from '../setting';\nimport '../index.less';\nimport { getColorByIndex } from '..';\nimport ReadOnlyPanel from './ReadOnlyPanel';\n\nexport interface AdvancedSettingWidgetProps {\n  value: any;\n  onChange: (value: any) => void;\n  readOnly?: boolean;\n}\n\nexport interface AdvancedSettingWidgetRef {\n  validateForm: () => Promise<boolean>;\n}\n\nconst CustomWidget = forwardRef<\n  AdvancedSettingWidgetRef,\n  AdvancedSettingWidgetProps\n>(({ value, onChange, readOnly }, ref) => {\n  const form = useForm();\n\n  // 暴露验证方法\n  useImperativeHandle(ref, () => ({\n    validateForm: async () => {\n      return await form\n        .validateFields()\n        .then(() => {\n          return true;\n        })\n        .catch((err) => {\n          return false;\n        });\n    },\n  }));\n\n  // 监听表单变化\n  const watch = {\n    '#': (formData) => {\n      onChange(formData);\n    },\n    'firstScene':(value)=>{\n      if (value) {\n        form.setValueByPath('title',`一级指标_${value}`)\n      }\n    }\n  };\n\n  useEffect(() => {\n    if (value) {\n      form.setValues(value);\n    }\n  }, []);\n\n  return (\n    <div>\n      <FormRender\n        form={form}\n        schema={firstTagSchema}\n        watch={watch}\n        readOnly={readOnly}\n        onMount={async () => {\n          const res = await new Promise((resolve) => {\n            setTimeout(() => {\n              resolve({\n                data: [\n                  {\n                    measureName: 'perform_jys_1d_all',\n                    displayName: '用户活跃度指标',\n                    description:\n                      '用户活跃度指标反映了平台用户的参与程度和互动频率。该指标通过分析用户登录次数、浏览时长、功能使用频率等维度综合计算得出。活跃度分为高、中、低三个等级，分别对应不同的用户行为特征和参与度水平。',\n                  },\n                  {\n                    displayName: '用户转化率',\n                    measureName: 'perform_amt_1d',\n                    description: '用户转化率指标衡量了从浏览到实际完成交易的用户比例。该指标通过分析用户行为路径，计算各环节的转化漏斗，识别用户流失的关键节点。转化率受多种因素影响，包括产品展示、价格策略、用户体验等。',\n                  },\n                  {\n                    displayName: '平均停留时长',\n                    measureName: 'perform_adr_1d',\n                    description: '用户平均在平台上的停留时长，反映用户粘性和内容吸引力'\n                  }\n                ]\n              });\n            }, 100);\n          });\n          const enumData = (res?.data || []).map(\n            (item: any, index: number) => ({\n              name: item.displayName,\n              code: item.measureName,\n              description: item.description,\n              color: getColorByIndex(index),\n            }),\n          );\n          form.setValueByPath('tagData', enumData);\n          form.setSchemaByPath('firstMeasure', {\n            enum: (res?.data || [])?.map((v) => v.measureName) || [],\n            enumNames:\n              (res?.data || [])?.map((item) => `${item.displayName}(${item.measureName})`) || [],\n          });\n        }}\n        // size=\"small\"\n        removeHiddenData={false}\n        widgets={{ ReadOnlyPanel }}\n        configProvider={{\n          getPopupContainer: triggerNode => triggerNode.parentElement\n        }}\n      />\n    </div>\n  );\n});\n\nexport default CustomWidget;\n"
  },
  {
    "path": "docs/xflow/demo/custom-flow/fullCase/components/secondTagWidget.tsx",
    "content": "import FormRender, { useForm } from 'form-render';\nimport React, {\n  forwardRef,\n  useEffect,\n  useImperativeHandle,\n} from 'react';\nimport { secondTagSchema } from '../setting';\nimport '../index.less';\nimport { getColorByIndex } from '..';\nimport ReadOnlyPanel from './ReadOnlyPanel';\n\nexport interface AdvancedSettingWidgetProps {\n  value: any;\n  onChange: (value: any) => void;\n  readOnly?: boolean;\n}\n\nexport interface AdvancedSettingWidgetRef {\n  validateForm: () => Promise<boolean>;\n}\n\ninterface MeasureData {\n  measureName: string;\n  displayName: string;\n}\n\nconst secondTagWidget = forwardRef<\n  AdvancedSettingWidgetRef,\n  AdvancedSettingWidgetProps\n>(({ value, onChange, readOnly }, ref) => {\n  const form = useForm();\n\n  // 暴露验证方法\n  useImperativeHandle(ref, () => ({\n    validateForm: async () => {\n      return await form\n        .validateFields()\n        .then(() => {\n          return true;\n        })\n        .catch((err) => {\n          return false;\n        });\n    },\n  }));\n\n\n  // 监听表单变化\n  const watch = {\n    '#': (formData: any) => {\n      onChange(formData);\n    },\n    secondScene: (value: string) => {\n      if (value) {\n        form.setValueByPath('title', `二级指标_${value}`);\n      }\n    }\n  };\n\n  useEffect(() => {\n    if (value) {\n      form.setValues(value);\n    }\n  }, []);\n\n  return (\n    <div>\n      <FormRender\n        form={form}\n        schema={secondTagSchema}\n        watch={watch}\n        readOnly={readOnly}\n        onMount={async () => {\n          const res = await new Promise((resolve) => {\n            setTimeout(() => {\n              resolve({\n                data: [\n                  {\n                    displayName: '用户转化率',\n                    measureName: 'perform_amt_1d',\n                    description: '用户转化率指标衡量了从浏览到实际完成交易的用户比例。该指标通过分析用户行为路径，计算各环节的转化漏斗，识别用户流失的关键节点。转化率受多种因素影响，包括产品展示、价格策略、用户体验等。'\n                  },\n                  {\n                    displayName: '平均停留时长',\n                    measureName: 'perform_adr_1d',\n                    description: '用户平均在平台上的停留时长，反映用户粘性和内容吸引力'\n                  },\n                  {\n                    displayName: '多端访问量',\n                    measureName: 'xz_ipvuv_daycnt_4client',\n                    description: '多端访问量统计了用户在不同设备（PC、移动端、平板等）上的访问情况，用于分析用户使用习惯和平台适配性'\n                  },\n                  {\n                    displayName: '用户留存率',\n                    measureName: 'perform_jys_1d',\n                    description: '用户留存率反映了用户持续使用平台的比率。该指标通过跟踪用户首次使用后的持续活跃情况，计算不同时间窗口（次日、7日、30日等）的留存率，评估产品的用户粘性和长期价值。'\n                  }\n                ]\n              });\n            }, 100);\n          });\n          const enumData = (res?.data || []).map(\n            (item: any, index: number) => ({\n              name: item.displayName,\n              code: item.measureName,\n              description: item.description,\n              color: getColorByIndex(index),\n            }),\n          );\n          console.log('11hmy', res, enumData)\n          form.setValueByPath('tagData', enumData);\n          form.setSchemaByPath('secondMeasures', {\n            enum:\n              (res?.data || []).map((v: MeasureData) => v.measureName) || [],\n            enumNames:\n              (res?.data || []).map(\n                (item: MeasureData) =>\n                  `${item.displayName}(${item.measureName})`,\n              ) || [],\n          });\n        }}\n        // size=\"small\"\n        widgets={{ ReadOnlyPanel }}\n        configProvider={{  // 全屏时挂载到父节点\n          getPopupContainer: triggerNode => triggerNode.parentElement\n        }}\n      />\n    </div>\n  );\n});\n\nexport default secondTagWidget;\n"
  },
  {
    "path": "docs/xflow/demo/custom-flow/fullCase/const.ts",
    "content": "export const initNodes = [\n  {\n    id: 'n5lj2vtbm_1744364868686',\n    type: 'primaryMetrics',\n    data: {\n      firstScene: '交易结果3',\n      firstMeasure: 'perform_jys_1d_all',\n      displayName: '用户活跃度指标',\n      description:\n        '用户活跃度指标反映了平台用户的参与程度和互动频率。该指标通过分析用户登录次数、浏览时长、功能使用频率等维度综合计算得出。活跃度分为高、中、低三个等级，分别对应不同的用户行为特征和参与度水平。',\n    },\n    position: {\n      x: 0,\n      y: 0,\n    },\n  },\n  {\n    id: 'w83nw5uz0_1744364868686',\n    type: 'secondaryMetrics',\n    data: {\n      secondScene: '项目视角2',\n      secondMeasures: [\n        'perform_amt_1d',\n        'perform_adr_1d',\n        'xz_ipvuv_daycnt_4client',\n        'perform_jys_1d',\n      ],\n      tagWidgetData: [\n        {\n          name: '用户转化率',\n          code: 'perform_amt_1d',\n          description:\n            '用户转化率指标衡量了从浏览到实际完成交易的用户比例。该指标通过分析用户行为路径，计算各环节的转化漏斗，识别用户流失的关键节点。转化率受多种因素影响，包括产品展示、价格策略、用户体验等。',\n          color: 'blue',\n        },\n        {\n          name: '平均停留时长',\n          code: 'perform_adr_1d',\n          description: '用户平均在平台上的停留时长，反映用户粘性和内容吸引力',\n          color: 'green',\n        },\n        {\n          name: '多端访问量',\n          code: 'xz_ipvuv_daycnt_4client',\n          description:\n            '多端访问量统计了用户在不同设备（PC、移动端、平板等）上的访问情况，用于分析用户使用习惯和平台适配性',\n          color: 'red',\n        },\n        {\n          name: '用户留存率',\n          code: 'perform_jys_1d',\n          description:\n            '用户留存率反映了用户持续使用平台的比率。该指标通过跟踪用户首次使用后的持续活跃情况，计算不同时间窗口（次日、7日、30日等）的留存率，评估产品的用户粘性和长期价值。',\n          color: 'orange',\n        },\n      ],\n    },\n    position: {\n      x: 0,\n      y: 0,\n    },\n  },\n  {\n    id: 'vl9hmneno_1744364868686',\n    type: 'secondaryMetrics',\n    data: {\n      secondScene: '视角3',\n      secondMeasures: ['perform_adr_1d', 'perform_jys_1d'],\n      tagWidgetData: [\n        {\n          name: '平均停留时长',\n          code: 'perform_adr_1d',\n          description: '用户平均在平台上的停留时长，反映用户粘性和内容吸引力',\n          color: 'blue',\n        },\n        {\n          name: '用户留存率',\n          code: 'perform_jys_1d',\n          description:\n            '用户留存率反映了用户持续使用平台的比率。该指标通过跟踪用户首次使用后的持续活跃情况，计算不同时间窗口（次日、7日、30日等）的留存率，评估产品的用户粘性和长期价值。',\n          color: 'green',\n        },\n      ],\n    },\n    position: {\n      x: 0,\n      y: 0,\n    },\n  },\n  {\n    id: 'r6ctw58w1_1744364868686',\n    type: 'secondaryMetrics',\n    data: {\n      secondScene: '342424',\n      secondMeasures: ['perform_amt_1d'],\n      tagWidgetData: [\n        {\n          name: '用户转化率',\n          code: 'perform_amt_1d',\n          description:\n            '用户转化率指标衡量了从浏览到实际完成交易的用户比例。该指标通过分析用户行为路径，计算各环节的转化漏斗，识别用户流失的关键节点。转化率受多种因素影响，包括产品展示、价格策略、用户体验等。',\n          color: 'blue',\n        },\n      ],\n    },\n    position: {\n      x: 0,\n      y: 0,\n    },\n  },\n];\n\nexport const initEdges = [\n  {\n    source: 'n5lj2vtbm_1744364868686',\n    target: 'w83nw5uz0_1744364868686',\n    id: 'b1ye449b1',\n  },\n  {\n    source: 'n5lj2vtbm_1744364868686',\n    target: 'vl9hmneno_1744364868686',\n    id: '51u53vc90',\n  },\n  {\n    source: 'n5lj2vtbm_1744364868686',\n    target: 'r6ctw58w1_1744364868686',\n    id: '5um40pak9',\n  },\n];\n"
  },
  {
    "path": "docs/xflow/demo/custom-flow/fullCase/index.less",
    "content": ".indicator-flow-container {\n  position: relative;\n}\n\n.indicator-flow-back-btn-container {\n  position: absolute;\n  top: 10px;\n  right: 10px;\n  z-index: 1000;\n\n  .tools-btn {\n    height: 29px;\n    font-size: 12px;\n    border-radius: 6px;\n  }\n}\n\n.indicator-flow-header {\n  position: absolute;\n  top: 10px;\n  left: 10px;\n  z-index: 1000;\n}\n\n.indicator-flow-header-title {\n  font-weight: bold;\n  color: #1890ff;\n}\n\n.hidden-form-item {\n  display: none !important;\n}\n\n.tag-popover {\n  max-width: 300px !important;\n\n  .ant-popover-inner {\n    border-radius: 8px;\n  }\n\n  .ant-popover-inner-content {\n    padding: 6px;\n  }\n}\n\n.custom-node-panel .ant-drawer-content-wrapper {\n  top: 54px !important;\n  bottom: 14px !important;\n  right: 12px !important;\n}\n\n.settingSchemaStyle {\n  .ant-col {\n    width: 100% !important;\n\n    .ant-form-item-label label {\n      font-weight: 500;\n    }\n\n    .ant-form-item-explain-error {\n      font-size: 12px;\n    }\n  }\n\n  .ant-form-item-label > label {\n    color: #5050e6;\n  }\n\n  .tag-select-style {\n    .ant-form-item-control .ant-form-item-control-input-content > div {\n      width: 100%;\n      min-height: 20px;\n      padding: 10px;\n      font-size: 12px;\n      text-align: justify;\n      word-wrap: break-word;\n      background-color: #f2f4f7;\n      border-radius: 8px;\n    }\n    .ant-form-item-control .ant-form-item-control-input-content .ant-select {\n      padding: 0;\n      background-color: none;\n    }\n  }\n}\n\n.smart-read-only-panel {\n  width: 100%;\n  min-height: 20px;\n  padding: 10px;\n  font-size: 12px;\n  text-align: justify;\n  word-wrap: break-word;\n  background-color: #f2f4f7;\n  border-radius: 8px;\n}\n"
  },
  {
    "path": "docs/xflow/demo/custom-flow/fullCase/index.tsx",
    "content": "import XFlow, {\n  FlowProvider,\n  useEdges,\n  useFlow,\n  useNodes,\n} from '@xrenders/xflow';\nimport { Button, Card, Space, Spin } from 'antd';\nimport * as React from 'react';\nimport { useEffect, useState } from 'react';\nimport firstTagWidget from './components/customWidget';\nimport secondTagWidget from './components/secondTagWidget';\nimport TagWidget from './components/TagWidget';\nimport { initEdges, initNodes } from './const';\nimport './index.less';\nimport { settings } from './setting';\nexport const tagColors = [\n  'blue',\n  'green',\n  'red',\n  'orange',\n  'purple',\n  'cyan',\n  'magenta',\n  'gold',\n  'lime',\n  'volcano',\n];\n\nexport const getColorByIndex = (index: number) => {\n  return tagColors[index % tagColors.length];\n};\n\nconst XFlowContent = () => {\n  const [loading, setLoading] = useState(false);\n  const nodes = useNodes();\n  const edges = useEdges();\n  const [initData, setInitData] = useState<any>({ nodes: [], edges: [] });\n  const { setEdges, setNodes, runAutoLayout, getNodes, fitView } = useFlow();\n  const [saveLoading, setSaveLoading] = useState(false);\n\n  useEffect(() => {\n    runAutoLayout();\n  }, []);\n\n  return (\n    <Spin spinning={loading}>\n      <div style={{ height: '88vh' }} className=\"indicator-flow-container\">\n        <div className=\"indicator-flow-header\">\n          <div className=\"indicator-flow-header-title\">指标关系图谱</div>\n        </div>\n        <div className=\"indicator-flow-back-btn-container\">\n          <Space>\n            <Button\n              onClick={() => {\n                setLoading(true);\n                setNodes(initData?.nodes || []);\n                setEdges(initData?.edges || []);\n                runAutoLayout();\n                setLoading(false);\n              }}\n              className=\"tools-btn\"\n            >\n              重置\n            </Button>\n\n            <Button type=\"primary\" className=\"tools-btn\" loading={saveLoading}>\n              保存\n            </Button>\n          </Space>\n        </div>\n        <XFlow\n          initialValues={{\n            nodes: initNodes,\n            edges: initEdges,\n          }}\n          settings={settings}\n          widgets={{\n            firstTagWidget,\n            secondTagWidget,\n            TagWidget,\n          }}\n          globalConfig={{\n            edge: {\n              hideEdgeAddBtn: true,\n              deletable: false,\n            },\n            controls: {\n              hideAnnotate: true,\n            },\n          }}\n        />\n      </div>\n    </Spin>\n  );\n};\n\nexport default React.memo(() => {\n  return (\n    <FlowProvider>\n      <Card size=\"small\">\n        <XFlowContent />\n      </Card>\n    </FlowProvider>\n  );\n});\n"
  },
  {
    "path": "docs/xflow/demo/custom-flow/fullCase/setting.ts",
    "content": "export const settings = [\n  {\n    title: '一级指标',\n    type: 'primaryMetrics',\n    targetHandleHidden: true,\n    icon: {\n      type: 'icon-start',\n      bgColor: '#17B26A',\n    },\n    nodePanel: {\n      hideDesc: true,\n    },\n    settingWidget: 'firstTagWidget',\n    nodeWidget:'TagWidget'\n  },\n  {\n    title: '二级指标',\n    type: 'secondaryMetrics',\n    sourceHandleHidden: true,\n    icon: {\n      type: 'icon-model',\n      bgColor: '#6172F3',\n    },\n    nodePanel: {\n      hideDesc: true,\n    },\n    settingWidget: 'secondTagWidget',\n    nodeWidget: 'TagWidget'\n  },\n];\n\nexport const firstTagSchema = {\n  type: 'object',\n  // displayType: 'row',\n  // labelCol: 6,\n  // fieldCol: 18,\n  className: 'settingSchemaStyle',\n  properties: {\n    firstScene: {\n      title: '分类',\n      type: 'string',\n      widget: 'input',\n      required: true,\n      props: {\n        allowClear: true,\n      },\n      readOnlyWidget: 'ReadOnlyPanel',\n    },\n    firstMeasure: {\n      title: '指标',\n      type: 'string',\n      widget: 'select',\n      required: true,\n      props: {\n        allowClear: true,\n      },\n      className: 'tag-select-style',\n      // readOnlyWidget: 'ReadOnlyPanel',\n    },\n    tagData: {\n      title: '标签',\n      type: 'array',\n      widget: 'simpleList',\n      className: 'hidden-form-item',\n      items: {\n        type: 'object',\n        properties: {\n          name: {\n            type: 'string'\n          },\n          code: {\n            type: 'string'\n          },\n          description: {\n            type: 'string'\n          },\n          color: {\n            type: 'string'\n          }\n        }\n      }\n    }\n  },\n};\n\nexport const secondTagSchema = {\n  type: 'object',\n  className: 'settingSchemaStyle',\n  properties: {\n    secondScene: {\n      title: '分类',\n      type: 'string',\n      widget: 'input',\n      required: true,\n      props: {\n        allowClear: true,\n      },\n      readOnlyWidget: 'ReadOnlyPanel',\n    },\n\n    secondMeasures: {\n      title: '指标',\n      type: 'array',\n      widget: 'multiSelect',\n      required: true,\n      props: {\n        maxTagCount: 1,\n        mode: 'multiple',\n        allowClear: true,\n      },\n      className: 'tag-select-style',\n    },\n    tagData: {\n      title: '标签',\n      type: 'array',\n      widget: 'simpleList',\n      className: 'hidden-form-item',\n      items: {\n        type: 'object',\n        properties: {\n          name: {\n            type: 'string'\n          },\n          code: {\n            type: 'string'\n          },\n          description: {\n            type: 'string'\n          },\n          color: {\n            type: 'string'\n          }\n        }\n      }\n    }\n  },\n};\n"
  },
  {
    "path": "docs/xflow/demo/custom-flow/index.tsx",
    "content": "import React from 'react';\nimport XFlow from '@xrenders/xflow';\nimport settings from './setting';\nimport customWidget from './customWidget';\n\nexport default () => {\n  const nodes = [\n    {\n      id: '1',\n      type: 'Start',\n      data: {\n        inputVal:'我是自定义组件'\n      },\n      position: {\n        x: 40,\n        y: 240,\n      }\n    },\n    {\n      id: '2',\n      type: 'End',\n      data: {},\n      position: {\n        x: 500,\n        y: 240,\n      }\n    }\n  ];\n\n  const edges = [\n    { source: '1', target: '2', id: '234123' }\n  ]\n\n  return (\n    <div style={{ height: '600px' }}>\n      <XFlow\n        initialValues={{ nodes, edges }}\n        settings={settings}\n        nodeSelector={{\n          showSearch: true,\n        }}\n        widgets={{ customWidget }}\n      />\n    </div>\n  );\n}\n"
  },
  {
    "path": "docs/xflow/demo/custom-flow/setting.tsx",
    "content": "export default [\n  {\n    title: '开始',\n    type: 'Start',\n    hidden: true,\n    targetHandleHidden: true,\n    icon: {\n      type: 'icon-start',\n      bgColor: '#17B26A',\n    },\n    settingWidget: \"customWidget\",\n    settingWidgetProps: {\n      params: \"test\"\n    }\n  },\n  {\n    title: '结束',\n    type: 'End',\n    hidden: true,\n    sourceHandleHidden: true,\n    icon: {\n      type: 'icon-end',\n      bgColor: '#F79009',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n        select: {\n          title: '变量二',\n          type: 'string',\n          widget: 'select',\n          props: {\n            options: [\n              { label: 'a', value: 'a' },\n              { label: 'b', value: 'b' },\n              { label: 'c', value: 'c' },\n            ],\n          },\n        },\n      }\n    }\n  },\n  {\n    title: 'LLM',\n    type: 'LLM',\n    description: '调用大语言模型回答问题或者对自然语言进行处理',\n    icon: {\n      type: 'icon-model',\n      bgColor: '#6172F3',\n    },\n  },\n  {\n    title: 'Prompt',\n    type: 'Prompt',\n    description: '通过精心设计提示词，提升大语言模型回答效果',\n    icon: {\n      type: 'icon-prompt',\n      bgColor: '#17B26A',\n    },\n  },\n  {\n    title: '知识库',\n    type: 'knowledge',\n    description: '允许你从知识库中查询与用户问题相关的文本内容',\n    icon: {\n      type: 'icon-knowledge',\n      bgColor: '#6172F3',\n    },\n  },\n  {\n    title: 'Switch',\n    type: 'Switch',\n    description: '允许你根据 if/else 条件将 workflow 拆分成两个分支',\n    icon: {\n      type: 'icon-fenzhi',\n      bgColor: '#06AED4',\n    },\n  },\n  {\n    title: 'HSF',\n    type: 'hsf',\n    description: '允许通过 HSF 协议发送服务器请求',\n    icon: {\n      type: 'icon-hsf',\n      bgColor: '#875BF7',\n    },\n  },\n  {\n    title: 'Http',\n    type: 'http',\n    description: '允许通过 HTTP 协议发送服务器请求',\n    icon: {\n      type: 'icon-http',\n      bgColor: '#875BF7',\n    },\n  },\n  {\n    title: '代码执行',\n    type: 'Code',\n    description: '执行一段 Groovy 或 Python 或 NodeJS 代码实现自定义逻辑',\n    icon: {\n      type: 'icon-code',\n      bgColor: '#2E90FA',\n    },\n  },\n  {\n    title: '工具',\n    type: 'tool',\n    description: '允许使用工具能力',\n    icon: {\n      type: 'icon-gongju',\n      bgColor: '#2E90FA',\n    },\n  },\n  {\n    title: '工具',\n    type: '_group',\n    items: [\n      {\n        title: '代码执行',\n        type: 'Code',\n        description: '执行一段 Groovy 或 Python 或 NodeJS 代码实现自定义逻辑',\n        icon: {\n          type: 'icon-code',\n          bgColor: '#2E90FA',\n        },\n      },\n      {\n        title: '工具',\n        type: 'tool',\n        description: '允许使用工具能力',\n        icon: {\n          type: 'icon-gongju',\n          bgColor: '#2E90FA',\n        },\n      },\n    ],\n  },\n];\n"
  },
  {
    "path": "docs/xflow/demo/flow-provider/edges.ts",
    "content": "export const edges = [{ source: '1', target: '2', id: '234123' }];\n"
  },
  {
    "path": "docs/xflow/demo/flow-provider/index.tsx",
    "content": "import * as React from \"react\";\n\nimport XFlow, { FlowProvider, useNodes } from '@xrenders/xflow';\nimport { edges as initialEdges } from './edges';\nimport { nodes as initialNodes } from './nodes';\nimport settings from './setting';\n\nconst App = () => {\n  return (\n    <FlowProvider>\n      <div style={{ height: '600px' }}>\n        <XFlow\n          initialValues={{ nodes: initialNodes, edges: initialEdges }}\n          settings={settings as any[]}\n          nodeSelector={{\n            showSearch: true,\n          }}\n        />\n      </div>\n      <Sidebar />\n    </FlowProvider>\n  );\n};\n\nfunction Sidebar() {\n  // This hook will only work if the component it's used in is a child of a\n  // <FlowProvider />.\n  const nodes = useNodes();\n  console.log(\"nodes\", nodes);\n\n  return (\n    <aside>\n      nodes数据格式：\n      <pre style={{ fontSize: '12px', margin: '4px 0' }}>\n        {JSON.stringify(nodes, null, 2)}\n      </pre>\n    </aside>\n  );\n}\n\nexport default () => {\n  return (\n    <FlowProvider>\n      <App />\n    </FlowProvider>\n  );\n};\n"
  },
  {
    "path": "docs/xflow/demo/flow-provider/nodes.ts",
    "content": "export const nodes = [\n  {\n    id: '1',\n    type: 'Start',\n    data: {},\n    position: {\n      x: 40,\n      y: 240,\n    },\n  },\n  {\n    id: '2',\n    type: 'End',\n    data: {},\n    position: {\n      x: 500,\n      y: 240,\n    },\n  },\n];\n"
  },
  {
    "path": "docs/xflow/demo/flow-provider/setting.tsx",
    "content": "export default [\n  {\n    title: '开始',\n    type: 'Start',\n    hidden: true,\n    targetHandleHidden: true,\n    icon: {\n      type: 'icon-start',\n      bgColor: '#17B26A',\n    },\n    settingSchema: {\n      type: 'object',\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n        select: {\n          title: '变量二',\n          type: 'string',\n          widget: 'select',\n          props: {\n            options: [\n              { label: 'a', value: 'a' },\n              { label: 'b', value: 'b' },\n              { label: 'c', value: 'c' },\n            ],\n          },\n        },\n        radio1: {\n          title: '点击单选',\n          type: 'string',\n          widget: 'radio',\n          props: {\n            options: [\n              { label: '早', value: 'a' },\n              { label: '中', value: 'b' },\n              { label: '晚', value: 'c' }\n            ]\n          }\n        },\n        textarea1: {\n          title: '长文本',\n          type: 'string',\n          widget: 'textArea'\n        },\n        date1: {\n          title: '日期选择',\n          type: 'string',\n          widget: 'datePicker'\n        },\n        dateRange1: {\n          title: '日期范围',\n          type: 'range',\n          widget: 'dateRange'\n        },\n        time1: {\n          title: '时间选择',\n          type: 'string',\n          widget: 'timePicker'\n        },\n        timeRange1: {\n          title: '时间范围',\n          type: 'range',\n          widget: 'timeRange'\n        },\n      },\n    },\n  },\n  {\n    title: '结束',\n    type: 'End',\n    hidden: true,\n    sourceHandleHidden: true,\n    icon: {\n      type: 'icon-end',\n      bgColor: '#F79009',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n        select: {\n          title: '变量二',\n          type: 'string',\n          widget: 'select',\n          props: {\n            options: [\n              { label: 'a', value: 'a' },\n              { label: 'b', value: 'b' },\n              { label: 'c', value: 'c' },\n            ],\n          },\n        },\n      }\n    }\n  },\n  {\n    title: 'LLM',\n    type: 'LLM',\n    description: '调用大语言模型回答问题或者对自然语言进行处理',\n    icon: {\n      type: 'icon-model',\n      bgColor: '#6172F3',\n    },\n  },\n  {\n    title: 'Prompt',\n    type: 'Prompt',\n    description: '通过精心设计提示词，提升大语言模型回答效果',\n    icon: {\n      type: 'icon-prompt',\n      bgColor: '#17B26A',\n    },\n  },\n  {\n    title: '知识库',\n    type: 'knowledge',\n    description: '允许你从知识库中查询与用户问题相关的文本内容',\n    icon: {\n      type: 'icon-knowledge',\n      bgColor: '#6172F3',\n    },\n  },\n  {\n    title: 'Switch',\n    type: 'Switch',\n    description: '允许你根据 if/else 条件将 workflow 拆分成两个分支',\n    icon: {\n      type: 'icon-fenzhi',\n      bgColor: '#06AED4',\n    },\n  },\n  {\n    title: 'HSF',\n    type: 'hsf',\n    description: '允许通过 HSF 协议发送服务器请求',\n    icon: {\n      type: 'icon-hsf',\n      bgColor: '#875BF7',\n    },\n  },\n  {\n    title: 'Http',\n    type: 'http',\n    description: '允许通过 HTTP 协议发送服务器请求',\n    icon: {\n      type: 'icon-http',\n      bgColor: '#875BF7',\n    },\n  },\n  {\n    title: '代码执行',\n    type: 'Code',\n    description: '执行一段 Groovy 或 Python 或 NodeJS 代码实现自定义逻辑',\n    icon: {\n      type: 'icon-code',\n      bgColor: '#2E90FA',\n    },\n  },\n  {\n    title: '工具',\n    type: 'tool',\n    description: '允许使用工具能力',\n    icon: {\n      type: 'icon-gongju',\n      bgColor: '#2E90FA',\n    },\n  },\n  {\n    title: '工具',\n    type: '_group',\n    items: [\n      {\n        title: '代码执行',\n        type: 'Code',\n        description: '执行一段 Groovy 或 Python 或 NodeJS 代码实现自定义逻辑',\n        icon: {\n          type: 'icon-code',\n          bgColor: '#2E90FA',\n        },\n      },\n      {\n        title: '工具',\n        type: 'tool',\n        description: '允许使用工具能力',\n        icon: {\n          type: 'icon-gongju',\n          bgColor: '#2E90FA',\n        },\n      },\n    ],\n  },\n];\n"
  },
  {
    "path": "docs/xflow/demo/layout/LR/index.tsx",
    "content": "import React from 'react';\nimport XFlow from '@xrenders/xflow';\n\nconst settings = [\n  {\n    title: '流程控制',\n    type: '_group',\n    items: [\n      {\n        title: '开始',\n        type: 'Start',\n        targetHandleHidden: true,\n        hidden: true,\n        description: '流程开始节点',\n        icon: {\n          type: 'icon-start',\n          bgColor: '#17B26A',\n        },\n        settingSchema: {\n          type: 'object',\n          properties: {\n            name: {\n              title: '流程名称',\n              type: 'string',\n              widget: 'input',\n              required: true,\n            },\n            description: {\n              title: '流程描述',\n              type: 'string',\n              widget: 'textarea',\n            },\n            variables: {\n              title: '流程变量',\n              type: 'array',\n              items: {\n                type: 'object',\n                properties: {\n                  name: {\n                    title: '变量名',\n                    type: 'string',\n                    required: true,\n                  },\n                  type: {\n                    title: '变量类型',\n                    type: 'string',\n                    enum: ['string', 'number', 'boolean'],\n                    default: 'string',\n                  },\n                  defaultValue: {\n                    title: '默认值',\n                    type: 'string',\n                  },\n                },\n              },\n            },\n          },\n        },\n      },\n      {\n        title: '结束',\n        type: 'End',\n        description: '流程结束节点',\n        hidden: true,\n        sourceHandleHidden: true,\n        icon: {\n          type: 'icon-end',\n          bgColor: '#F79009',\n        },\n        settingSchema: {\n          type: 'object',\n          properties: {\n            output: {\n              title: '输出结果',\n              type: 'string',\n              widget: 'textarea',\n            },\n            status: {\n              title: '结束状态',\n              type: 'string',\n              enum: ['success', 'error', 'warning'],\n              default: 'success',\n            },\n            message: {\n              title: '状态信息',\n              type: 'string',\n              widget: 'textarea',\n            },\n          },\n        },\n      },\n    ],\n  },\n  {\n    title: 'AI 能力',\n    type: '_group',\n    items: [\n      {\n        title: 'LLM',\n        type: 'LLM',\n        description: '调用大语言模型',\n        icon: {\n          type: 'icon-model',\n          bgColor: '#6172F3',\n        },\n        settingSchema: {\n          type: 'object',\n          properties: {\n            model: {\n              title: '模型选择',\n              type: 'string',\n              enum: ['gpt-3.5', 'gpt-4', 'claude-2', 'claude-3'],\n              default: 'gpt-3.5',\n            },\n            temperature: {\n              title: '温度',\n              type: 'number',\n              default: 0.7,\n              minimum: 0,\n              maximum: 1,\n              description: '控制输出的随机性，值越高输出越随机',\n            },\n            maxTokens: {\n              title: '最大Token数',\n              type: 'number',\n              default: 2000,\n              minimum: 1,\n              maximum: 4000,\n            },\n            systemPrompt: {\n              title: '系统提示词',\n              type: 'string',\n              widget: 'textarea',\n              description: '设置AI助手的角色和行为',\n            },\n            stopSequences: {\n              title: '停止序列',\n              type: 'array',\n              items: {\n                type: 'string',\n              },\n              description: '设置停止生成的关键词',\n            },\n          },\n        },\n      },\n      {\n        title: 'Prompt',\n        type: 'Prompt',\n        description: '提示词模板',\n        icon: {\n          type: 'icon-prompt',\n          bgColor: '#17B26A',\n        },\n        settingSchema: {\n          type: 'object',\n          properties: {\n            prompt: {\n              title: '提示词',\n              type: 'string',\n              widget: 'textarea',\n              default: '请帮我完成以下任务：',\n            },\n            variables: {\n              title: '变量替换',\n              type: 'array',\n              items: {\n                type: 'object',\n                properties: {\n                  name: {\n                    title: '变量名',\n                    type: 'string',\n                    required: true,\n                  },\n                  value: {\n                    title: '变量值',\n                    type: 'string',\n                  },\n                },\n              },\n            },\n            examples: {\n              title: '示例',\n              type: 'array',\n              items: {\n                type: 'object',\n                properties: {\n                  input: {\n                    title: '输入',\n                    type: 'string',\n                    widget: 'textarea',\n                  },\n                  output: {\n                    title: '输出',\n                    type: 'string',\n                    widget: 'textarea',\n                  },\n                },\n              },\n            },\n          },\n        },\n      },\n    ],\n  },\n  {\n    title: '数据处理',\n    type: '_group',\n    items: [\n      {\n        title: '知识库',\n        type: 'knowledge',\n        description: '知识库检索',\n        icon: {\n          type: 'icon-knowledge',\n          bgColor: '#6172F3',\n        },\n        settingSchema: {\n          type: 'object',\n          properties: {\n            knowledgeBase: {\n              title: '知识库选择',\n              type: 'string',\n              enum: ['产品文档', '技术文档', '用户手册', 'API文档'],\n              default: '产品文档',\n            },\n            searchLimit: {\n              title: '检索限制',\n              type: 'number',\n              default: 5,\n              minimum: 1,\n              maximum: 10,\n            },\n            similarity: {\n              title: '相似度阈值',\n              type: 'number',\n              default: 0.7,\n              minimum: 0,\n              maximum: 1,\n              description: '设置检索结果的相似度阈值',\n            },\n            filters: {\n              title: '过滤条件',\n              type: 'array',\n              items: {\n                type: 'object',\n                properties: {\n                  field: {\n                    title: '字段',\n                    type: 'string',\n                    required: true,\n                  },\n                  operator: {\n                    title: '操作符',\n                    type: 'string',\n                    enum: ['equals', 'contains', 'greaterThan', 'lessThan'],\n                    default: 'equals',\n                  },\n                  value: {\n                    title: '值',\n                    type: 'string',\n                  },\n                },\n              },\n            },\n          },\n        },\n      },\n      {\n        title: '数据转换',\n        type: 'transform',\n        description: '数据格式转换',\n        icon: {\n          type: 'icon-prompt',\n          bgColor: '#F79009',\n        },\n        settingSchema: {\n          type: 'object',\n          properties: {\n            inputType: {\n              title: '输入类型',\n              type: 'string',\n              enum: ['json', 'text', 'csv', 'xml'],\n              default: 'json',\n            },\n            outputType: {\n              title: '输出类型',\n              type: 'string',\n              enum: ['json', 'text', 'csv', 'xml'],\n              default: 'json',\n            },\n            mapping: {\n              title: '字段映射',\n              type: 'array',\n              items: {\n                type: 'object',\n                properties: {\n                  source: {\n                    title: '源字段',\n                    type: 'string',\n                    required: true,\n                  },\n                  target: {\n                    title: '目标字段',\n                    type: 'string',\n                    required: true,\n                  },\n                  transform: {\n                    title: '转换规则',\n                    type: 'string',\n                    widget: 'textarea',\n                  },\n                },\n              },\n            },\n          },\n        },\n      },\n    ],\n  },\n];\n\nconst initialValues = {\n  nodes: [\n    {\n      id: 'start',\n      type: 'Start',\n      data: {\n        name: '智能客服流程',\n        description: '基于LLM的智能客服系统',\n        variables: [\n          {\n            name: 'userInput',\n            type: 'string',\n            defaultValue: '',\n          },\n        ],\n      },\n      position: {\n        x: 100,\n        y: 200,\n      },\n    },\n    {\n      id: 'llm1',\n      type: 'LLM',\n      data: {\n        model: 'gpt-3.5',\n        temperature: 0.7,\n        maxTokens: 2000,\n        systemPrompt: '你是一个专业的客服代表，请用简洁专业的语言回答问题。',\n      },\n      position: {\n        x: 400,\n        y: 100,\n      },\n    },\n    {\n      id: 'prompt1',\n      type: 'Prompt',\n      data: {\n        prompt: '请分析用户问题类型：',\n        variables: [\n          {\n            name: 'userInput',\n            value: '${userInput}',\n          },\n        ],\n      },\n      position: {\n        x: 400,\n        y: 300,\n      },\n    },\n    {\n      id: 'knowledge1',\n      type: 'knowledge',\n      data: {\n        knowledgeBase: '产品文档',\n        searchLimit: 5,\n        similarity: 0.7,\n        filters: [\n          {\n            field: 'category',\n            operator: 'equals',\n            value: 'FAQ',\n          },\n        ],\n      },\n      position: {\n        x: 700,\n        y: 100,\n      },\n    },\n    {\n      id: 'transform1',\n      type: 'transform',\n      data: {\n        inputType: 'json',\n        outputType: 'text',\n        mapping: [\n          {\n            source: 'answer',\n            target: 'response',\n            transform: '${answer}',\n          },\n        ],\n      },\n      position: {\n        x: 700,\n        y: 300,\n      },\n    },\n    {\n      id: 'llm2',\n      type: 'LLM',\n      data: {\n        model: 'gpt-3.5',\n        temperature: 0.7,\n        maxTokens: 2000,\n        systemPrompt: '你是一个专业的客服代表，请根据知识库内容回答问题。',\n      },\n      position: {\n        x: 1000,\n        y: 200,\n      },\n    },\n    {\n      id: 'end',\n      type: 'End',\n      data: {\n        output: '处理完成',\n        status: 'success',\n        message: '客服问题已解决',\n      },\n      position: {\n        x: 1300,\n        y: 200,\n      },\n    },\n  ],\n  edges: [\n    {\n      id: 'start-llm1',\n      source: 'start',\n      target: 'llm1',\n    },\n    {\n      id: 'start-prompt1',\n      source: 'start',\n      target: 'prompt1',\n    },\n    {\n      id: 'llm1-knowledge1',\n      source: 'llm1',\n      target: 'knowledge1',\n    },\n    {\n      id: 'prompt1-transform1',\n      source: 'prompt1',\n      target: 'transform1',\n    },\n    {\n      id: 'knowledge1-llm2',\n      source: 'knowledge1',\n      target: 'llm2',\n    },\n    {\n      id: 'transform1-llm2',\n      source: 'transform1',\n      target: 'llm2',\n    },\n    {\n      id: 'llm2-end',\n      source: 'llm2',\n      target: 'end',\n    },\n  ],\n};\n\nconst Demo = () => {\n  return (\n    <div style={{ width: '100%', height: '600px' }}>\n      <XFlow\n        settings={settings}\n        initialValues={initialValues}\n        layout=\"LR\"\n      />\n    </div>\n  );\n};\n\nexport default Demo;\n"
  },
  {
    "path": "docs/xflow/demo/layout/LR/setting.tsx",
    "content": "export  const settings=[\n  {\n    title: '开始',\n    type: 'Start',\n    hidden: true,\n    targetHandleHidden: true,\n    icon: {\n      type: 'icon-start',\n      bgColor: '#17B26A',\n    },\n    settingSchema: {\n      type: 'object',\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n        select: {\n          title: '变量二',\n          type: 'string',\n          widget: 'select',\n          props: {\n            options: [\n              { label: 'a', value: 'a' },\n              { label: 'b', value: 'b' },\n              { label: 'c', value: 'c' },\n            ],\n          },\n        },\n        radio1: {\n          title: '点击单选',\n          type: 'string',\n          widget: 'radio',\n          props: {\n            options: [\n              { label: '早', value: 'a' },\n              { label: '中', value: 'b' },\n              { label: '晚', value: 'c' }\n            ]\n          }\n        },\n        textarea1: {\n          title: '长文本',\n          type: 'string',\n          widget: 'textArea'\n        },\n        date1: {\n          title: '日期选择',\n          type: 'string',\n          widget: 'datePicker'\n        },\n        dateRange1: {\n          title: '日期范围',\n          type: 'range',\n          widget: 'dateRange'\n        },\n        time1: {\n          title: '时间选择',\n          type: 'string',\n          widget: 'timePicker'\n        },\n        timeRange1: {\n          title: '时间范围',\n          type: 'range',\n          widget: 'timeRange'\n        },\n      },\n    },\n  },\n  {\n    title: '结束',\n    type: 'End',\n    hidden: true,\n    sourceHandleHidden: true,\n    icon: {\n      type: 'icon-end',\n      bgColor: '#F79009',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n        select: {\n          title: '变量二',\n          type: 'string',\n          widget: 'select',\n          props: {\n            options: [\n              { label: 'a', value: 'a' },\n              { label: 'b', value: 'b' },\n              { label: 'c', value: 'c' },\n            ],\n          },\n        },\n      }\n    }\n  },\n  {\n    title: 'LLM',\n    type: 'LLM',\n    description: '调用大语言模型回答问题或者对自然语言进行处理',\n    icon: {\n      type: 'icon-model',\n      bgColor: '#6172F3',\n    },\n  },\n  {\n    title: 'Prompt',\n    type: 'Prompt',\n    description: '通过精心设计提示词，提升大语言模型回答效果',\n    icon: {\n      type: 'icon-prompt',\n      bgColor: '#17B26A',\n    },\n  },\n  {\n    title: '知识库',\n    type: 'knowledge',\n    description: '允许你从知识库中查询与用户问题相关的文本内容',\n    icon: {\n      type: 'icon-knowledge',\n      bgColor: '#6172F3',\n    },\n  },\n  {\n    title: 'Switch',\n    type: 'Switch',\n    description: '允许你根据 if/else 条件将 workflow 拆分成两个分支',\n    icon: {\n      type: 'icon-fenzhi',\n      bgColor: '#06AED4',\n    },\n  },\n  {\n    title: 'HSF',\n    type: 'hsf',\n    description: '允许通过 HSF 协议发送服务器请求',\n    icon: {\n      type: 'icon-hsf',\n      bgColor: '#875BF7',\n    },\n  },\n  {\n    title: 'Http',\n    type: 'http',\n    description: '允许通过 HTTP 协议发送服务器请求',\n    icon: {\n      type: 'icon-http',\n      bgColor: '#875BF7',\n    },\n  },\n  {\n    title: '代码执行',\n    type: 'Code',\n    description: '执行一段 Groovy 或 Python 或 NodeJS 代码实现自定义逻辑',\n    icon: {\n      type: 'icon-code',\n      bgColor: '#2E90FA',\n    },\n  },\n  {\n    title: '工具',\n    type: 'tool',\n    description: '允许使用工具能力',\n    icon: {\n      type: 'icon-gongju',\n      bgColor: '#2E90FA',\n    },\n  },\n  {\n    title: '工具',\n    type: '_group',\n    items: [\n      {\n        title: '代码执行',\n        type: 'Code',\n        description: '执行一段 Groovy 或 Python 或 NodeJS 代码实现自定义逻辑',\n        icon: {\n          type: 'icon-code',\n          bgColor: '#2E90FA',\n        },\n      },\n      {\n        title: '工具',\n        type: 'tool',\n        description: '允许使用工具能力',\n        icon: {\n          type: 'icon-gongju',\n          bgColor: '#2E90FA',\n        },\n      },\n    ],\n  },\n];\n\n\nexport const nodes = [\n  {\n    type: 'Start',\n    id: '1',\n    position: { x: -35, y: 268 },\n  },\n  {\n    type: 'Switch',\n    id: 'b6zsd6w5ah2b209t',\n    position: { x: 277.5, y: 268 },\n    data: {\n      list: [\n        {\n          _id: 'iawoyh5niyi6zjob',\n        },\n      ],\n    },\n  },\n  {\n    type: 'Code',\n    id: '3',\n    position: { x: 675, y: 123.75 },\n  },\n  {\n    type: 'tool',\n    id: '4',\n    position: { x: 686.25, y: 495 },\n  },\n  {\n    type: 'End',\n    id: '5',\n    position: { x: 1176.2499999999998, y: 281.25 },\n  },\n];\nexport const edges = [\n  {\n    source: '3',\n    target: '5',\n    id: 'e3-5',\n  },\n  {\n    source: '4',\n    target: '5',\n    id: 'e4-5',\n  },\n  {\n    id: 'px7fsmha99pju315',\n    source: '1',\n    target: 'b6zsd6w5ah2b209t',\n  },\n  {\n    type: 'buttonedge',\n    source: 'b6zsd6w5ah2b209t',\n    sourceHandle: 'iawoyh5niyi6zjob',\n    target: '3',\n    id: 'xy-edge__b6zsd6w5ah2b209tiawoyh5niyi6zjob-3',\n  },\n  {\n    type: 'buttonedge',\n    source: 'b6zsd6w5ah2b209t',\n    sourceHandle: 'id_else',\n    target: '4',\n    id: 'xy-edge__b6zsd6w5ah2b209tid_else-4',\n  },\n];\n"
  },
  {
    "path": "docs/xflow/demo/layout/TB/index.tsx",
    "content": "import React from 'react';\nimport XFlow from '@xrenders/xflow';\n\nconst settings = [\n  {\n    title: '流程控制',\n    type: '_group',\n    items: [\n      {\n        title: '开始',\n        type: 'Start',\n        description: '流程开始节点',\n        targetHandleHidden: true,\n        hidden:true,\n        icon: {\n          type: 'icon-start',\n          bgColor: '#17B26A',\n        },\n        settingSchema: {\n          type: 'object',\n          properties: {\n            name: {\n              title: '流程名称',\n              type: 'string',\n              widget: 'input',\n              required: true,\n            },\n          },\n        },\n      },\n      {\n        title: '结束',\n        type: 'End',\n        description: '流程结束节点',\n        hidden: true,\n        sourceHandleHidden:true,\n        icon: {\n          type: 'icon-end',\n          bgColor: '#F79009',\n        },\n        settingSchema: {\n          type: 'object',\n          properties: {\n            output: {\n              title: '输出结果',\n              type: 'string',\n              widget: 'textarea',\n            },\n          },\n        },\n      },\n    ],\n  },\n  {\n    title: 'AI 能力',\n    type: '_group',\n    items: [\n      {\n        title: 'LLM',\n        type: 'LLM',\n        description: '调用大语言模型',\n        icon: {\n          type: 'icon-model',\n          bgColor: '#6172F3',\n        },\n        settingSchema: {\n          type: 'object',\n          properties: {\n            model: {\n              title: '模型选择',\n              type: 'string',\n              enum: ['gpt-3.5', 'gpt-4', 'claude-2', 'claude-3'],\n              default: 'gpt-3.5',\n            },\n            temperature: {\n              title: '温度',\n              type: 'number',\n              default: 0.7,\n              minimum: 0,\n              maximum: 1,\n            },\n          },\n        },\n      },\n      {\n        title: 'Prompt',\n        type: 'Prompt',\n        description: '提示词模板',\n        icon: {\n          type: 'icon-prompt',\n          bgColor: '#17B26A',\n        },\n        settingSchema: {\n          type: 'object',\n          properties: {\n            prompt: {\n              title: '提示词',\n              type: 'string',\n              widget: 'textarea',\n              default: '请帮我完成以下任务：',\n            },\n          },\n        },\n      },\n    ],\n  },\n  {\n    title: '数据处理',\n    type: '_group',\n    items: [\n      {\n        title: '知识库',\n        type: 'knowledge',\n        description: '知识库检索',\n        icon: {\n          type: 'icon-knowledge',\n          bgColor: '#6172F3',\n        },\n        settingSchema: {\n          type: 'object',\n          properties: {\n            knowledgeBase: {\n              title: '知识库选择',\n              type: 'string',\n              enum: ['产品文档', '技术文档', '用户手册', 'API文档'],\n              default: '产品文档',\n            },\n            searchLimit: {\n              title: '检索限制',\n              type: 'number',\n              default: 5,\n              minimum: 1,\n              maximum: 10,\n            },\n          },\n        },\n      },\n      {\n        title: '数据转换',\n        type: 'transform',\n        description: '数据格式转换',\n        icon: {\n          type: 'icon-prompt',\n          bgColor: '#F79009',\n        },\n        settingSchema: {\n          type: 'object',\n          properties: {\n            inputType: {\n              title: '输入类型',\n              type: 'string',\n              enum: ['json', 'text', 'csv', 'xml'],\n              default: 'json',\n            },\n            outputType: {\n              title: '输出类型',\n              type: 'string',\n              enum: ['json', 'text', 'csv', 'xml'],\n              default: 'json',\n            },\n          },\n        },\n      },\n    ],\n  },\n];\n\nconst initialValues = {\n  nodes: [\n    {\n      id: 'start',\n      type: 'Start',\n      data: {\n        name: '数据分析流程',\n        description: '基于LLM的数据分析系统',\n        variables: [\n          {\n            name: 'dataSource',\n            type: 'string',\n            defaultValue: 'csv',\n          },\n          {\n            name: 'analysisType',\n            type: 'string',\n            defaultValue: 'trend',\n          },\n        ],\n      },\n      position: {\n        x: 400,\n        y: 50,\n      },\n    },\n    {\n      id: 'transform1',\n      type: 'transform',\n      data: {\n        inputType: 'csv',\n        outputType: 'json',\n        mapping: [\n          {\n            source: 'date',\n            target: 'timestamp',\n            transform: 'new Date(${date}).getTime()',\n          },\n          {\n            source: 'value',\n            target: 'amount',\n            transform: 'parseFloat(${value})',\n          },\n        ],\n      },\n      position: {\n        x: 200,\n        y: 200,\n      },\n    },\n    {\n      id: 'transform2',\n      type: 'transform',\n      data: {\n        inputType: 'json',\n        outputType: 'text',\n        mapping: [\n          {\n            source: 'summary',\n            target: 'description',\n            transform: '${summary}',\n          },\n        ],\n      },\n      position: {\n        x: 600,\n        y: 200,\n      },\n    },\n    {\n      id: 'llm1',\n      type: 'LLM',\n      data: {\n        model: 'gpt-3.5',\n        temperature: 0.7,\n        maxTokens: 2000,\n        systemPrompt: '你是一个专业的数据分析师，请分析数据趋势。',\n        stopSequences: ['\\n\\n', '。'],\n      },\n      position: {\n        x: 200,\n        y: 350,\n      },\n    },\n    {\n      id: 'knowledge1',\n      type: 'knowledge',\n      data: {\n        knowledgeBase: '技术文档',\n        searchLimit: 5,\n        similarity: 0.7,\n        filters: [\n          {\n            field: 'type',\n            operator: 'equals',\n            value: 'analysis',\n          },\n        ],\n      },\n      position: {\n        x: 200,\n        y: 450,\n      },\n    },\n    {\n      id: 'prompt1',\n      type: 'Prompt',\n      data: {\n        prompt: '请分析数据趋势：',\n        variables: [\n          {\n            name: 'dataType',\n            value: '${analysisType}',\n          },\n        ],\n        examples: [\n          {\n            input: '销售数据',\n            output: '销售额呈现上升趋势，环比增长15%',\n          },\n        ],\n      },\n      position: {\n        x: 600,\n        y: 450,\n      },\n    },\n    {\n      id: 'llm2',\n      type: 'LLM',\n      data: {\n        model: 'gpt-3.5',\n        temperature: 0.7,\n        maxTokens: 2000,\n        systemPrompt: '你是一个专业的数据分析师，请生成分析报告。',\n      },\n      position: {\n        x: 400,\n        y: 700,\n      },\n    },\n    {\n      id: 'end',\n      type: 'End',\n      data: {\n        output: '分析完成',\n        status: 'success',\n        message: '数据分析报告已生成',\n      },\n      position: {\n        x: 400,\n        y: 800,\n      },\n    },\n  ],\n  edges: [\n    {\n      id: 'start-transform1',\n      source: 'start',\n      target: 'transform1',\n    },\n    {\n      id: 'start-transform2',\n      source: 'start',\n      target: 'transform2',\n    },\n    {\n      id: 'transform1-llm1',\n      source: 'transform1',\n      target: 'llm1',\n    },\n    {\n      id: 'transform2-prompt1',\n      source: 'transform2',\n      target: 'prompt1',\n    },\n    {\n      id: 'llm1-knowledge1',\n      source: 'llm1',\n      target: 'knowledge1',\n    },\n    {\n      id: 'knowledge1-llm2',\n      source: 'knowledge1',\n      target: 'llm2',\n    },\n    {\n      id: 'prompt1-llm2',\n      source: 'prompt1',\n      target: 'llm2',\n    },\n    {\n      id: 'llm2-end',\n      source: 'llm2',\n      target: 'end',\n    },\n  ],\n};\n\nconst Demo = () => {\n  return (\n    <div style={{ width: '100%', height: '800px' }}>\n      <XFlow\n        settings={settings}\n        initialValues={initialValues}\n        layout=\"TB\"\n      />\n    </div>\n  );\n};\n\nexport default Demo;\n"
  },
  {
    "path": "docs/xflow/demo/layout/TB/setting.tsx",
    "content": "export const settings = [\n  {\n    title: '开始',\n    type: 'Start',\n    hidden: true,\n    targetHandleHidden: true,\n    icon: {\n      type: 'icon-start',\n      bgColor: '#17B26A',\n    },\n    settingSchema: {\n      type: 'object',\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n        select: {\n          title: '变量二',\n          type: 'string',\n          widget: 'select',\n          props: {\n            options: [\n              { label: 'a', value: 'a' },\n              { label: 'b', value: 'b' },\n              { label: 'c', value: 'c' },\n            ],\n          },\n        },\n        radio1: {\n          title: '点击单选',\n          type: 'string',\n          widget: 'radio',\n          props: {\n            options: [\n              { label: '早', value: 'a' },\n              { label: '中', value: 'b' },\n              { label: '晚', value: 'c' },\n            ],\n          },\n        },\n        textarea1: {\n          title: '长文本',\n          type: 'string',\n          widget: 'textArea',\n        },\n        date1: {\n          title: '日期选择',\n          type: 'string',\n          widget: 'datePicker',\n        },\n        dateRange1: {\n          title: '日期范围',\n          type: 'range',\n          widget: 'dateRange',\n        },\n        time1: {\n          title: '时间选择',\n          type: 'string',\n          widget: 'timePicker',\n        },\n        timeRange1: {\n          title: '时间范围',\n          type: 'range',\n          widget: 'timeRange',\n        },\n      },\n    },\n  },\n  {\n    title: '结束',\n    type: 'End',\n    hidden: true,\n    sourceHandleHidden: true,\n    icon: {\n      type: 'icon-end',\n      bgColor: '#F79009',\n    },\n    settingSchema: {\n      type: 'object',\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n        select: {\n          title: '变量二',\n          type: 'string',\n          widget: 'select',\n          props: {\n            options: [\n              { label: 'a', value: 'a' },\n              { label: 'b', value: 'b' },\n              { label: 'c', value: 'c' },\n            ],\n          },\n        },\n      },\n    },\n  },\n  {\n    title: 'LLM',\n    type: 'LLM',\n    description: '调用大语言模型回答问题或者对自然语言进行处理',\n    icon: {\n      type: 'icon-model',\n      bgColor: '#6172F3',\n    },\n  },\n  {\n    title: 'Prompt',\n    type: 'Prompt',\n    description: '通过精心设计提示词，提升大语言模型回答效果',\n    icon: {\n      type: 'icon-prompt',\n      bgColor: '#17B26A',\n    },\n  },\n  {\n    title: '知识库',\n    type: 'knowledge',\n    description: '允许你从知识库中查询与用户问题相关的文本内容',\n    icon: {\n      type: 'icon-knowledge',\n      bgColor: '#6172F3',\n    },\n  },\n  {\n    title: 'Switch',\n    type: 'Switch',\n    description: '允许你根据 if/else 条件将 workflow 拆分成两个分支',\n    icon: {\n      type: 'icon-fenzhi',\n      bgColor: '#06AED4',\n    },\n  },\n  {\n    title: 'HSF',\n    type: 'hsf',\n    description: '允许通过 HSF 协议发送服务器请求',\n    icon: {\n      type: 'icon-hsf',\n      bgColor: '#875BF7',\n    },\n  },\n  {\n    title: 'Http',\n    type: 'http',\n    description: '允许通过 HTTP 协议发送服务器请求',\n    icon: {\n      type: 'icon-http',\n      bgColor: '#875BF7',\n    },\n  },\n  {\n    title: '代码执行',\n    type: 'Code',\n    description: '执行一段 Groovy 或 Python 或 NodeJS 代码实现自定义逻辑',\n    icon: {\n      type: 'icon-code',\n      bgColor: '#2E90FA',\n    },\n  },\n  {\n    title: '工具',\n    type: 'tool',\n    description: '允许使用工具能力',\n    icon: {\n      type: 'icon-gongju',\n      bgColor: '#2E90FA',\n    },\n  },\n  {\n    title: '工具',\n    type: '_group',\n    items: [\n      {\n        title: '代码执行',\n        type: 'Code',\n        description: '执行一段 Groovy 或 Python 或 NodeJS 代码实现自定义逻辑',\n        icon: {\n          type: 'icon-code',\n          bgColor: '#2E90FA',\n        },\n      },\n      {\n        title: '工具',\n        type: 'tool',\n        description: '允许使用工具能力',\n        icon: {\n          type: 'icon-gongju',\n          bgColor: '#2E90FA',\n        },\n      },\n    ],\n  },\n];\n\nexport const nodes = [\n  {\n    type: 'Start',\n    id: '1',\n    position: {\n      x: 346.25,\n      y: -75.54414939880371,\n    },\n  },\n  {\n    type: 'Switch',\n    id: 'b6zsd6w5ah2b209t',\n    position: {\n      x: 346.25,\n      y: 41.75,\n    },\n    data: {\n      list: [\n        {\n          _id: 'iawoyh5niyi6zjob',\n        },\n      ],\n    },\n  },\n  {\n    type: 'Code',\n    id: '3',\n    position: {\n      x: 53,\n      y: 207.5,\n    },\n  },\n  {\n    type: 'tool',\n    id: '4',\n    position: {\n      x: 676,\n      y: 220,\n    },\n  },\n  {\n    type: 'End',\n    id: '5',\n    position: {\n      x: 388.7499999999998,\n      y: 497.5,\n    },\n  },\n];\nexport const edges = [\n  {\n    source: '3',\n    target: '5',\n    id: 'e3-5',\n  },\n  {\n    source: '4',\n    target: '5',\n    id: 'e4-5',\n  },\n  {\n    id: 'px7fsmha99pju315',\n    source: '1',\n    target: 'b6zsd6w5ah2b209t',\n  },\n  {\n    type: 'buttonedge',\n    source: 'b6zsd6w5ah2b209t',\n    sourceHandle: 'iawoyh5niyi6zjob',\n    target: '3',\n    id: 'xy-edge__b6zsd6w5ah2b209tiawoyh5niyi6zjob-3',\n  },\n  {\n    type: 'buttonedge',\n    source: 'b6zsd6w5ah2b209t',\n    sourceHandle: 'id_else',\n    target: '4',\n    id: 'xy-edge__b6zsd6w5ah2b209tid_else-4',\n  },\n];\n"
  },
  {
    "path": "docs/xflow/demo/log/buildIn-log/CustomGroupTittle.tsx",
    "content": "import React from 'react';\nimport { Tag } from 'antd';\n\nconst CustomGroupTitle = ({ logData }) => {\n  return (\n    <div className=\"log-detail-panel-title\" style={{ justifyContent: 'space-between' }}>\n      <div style={{ display: 'flex', alignItems: 'center' }}>\n        <span className=\"log-detail-panel-title-text\">\n          {logData?.groupTitle}\n        </span>\n        <Tag color=\"blue\" style={{ marginLeft: 8 }}>自定义分组标题</Tag>\n      </div>\n      <div className=\"log-detail-panel-title-line\" />\n    </div>\n  );\n};\n\nexport default CustomGroupTitle;\n"
  },
  {
    "path": "docs/xflow/demo/log/buildIn-log/DetailLogWidget.tsx",
    "content": "import React from 'react';\nimport { Card, Typography, Space, Tag } from 'antd';\n\nconst { Text } = Typography;\n\ninterface DetailLogWidgetProps {\n  data: any;\n  logList: any[];\n  currentStatus: string;\n  logPanel: any;\n}\n\nconst DetailLogWidget: React.FC<DetailLogWidgetProps> = ({\n  data,\n  logList,\n  currentStatus,\n  logPanel\n}) => {\n  return (\n    <Card size=\"small\" title=\"自定义组件部分\" style={{ marginTop: 16 }}>\n      <Space direction=\"vertical\" style={{ width: '100%' }}>\n        <div>\n          <Text type=\"secondary\">当前节点状态: </Text>\n          <Tag color={currentStatus === 'success' ? 'green' : 'orange'}>\n            {currentStatus}\n          </Tag>\n        </div>\n\n        <div>\n          <Text type=\"secondary\">节点ID: </Text>\n          <Text>{data.nodeId}</Text>\n        </div>\n\n        {data.statusPanel?.extra && (\n          <div>\n            <Text type=\"secondary\">额外信息: </Text>\n            <Text>{data.statusPanel.extra}</Text>\n          </div>\n        )}\n      </Space>\n    </Card>\n  );\n};\n\nexport default DetailLogWidget;\n"
  },
  {
    "path": "docs/xflow/demo/log/buildIn-log/index.tsx",
    "content": "import XFlow from '@xrenders/xflow';\nimport React,{ useState } from 'react';\nimport settings from './setting';\nimport { Button } from 'antd';\nimport CustomGroupTitle from './CustomGroupTittle';\nimport DetailLogWidget from './DetailLogWidget';\n\nexport default () => {\n  const [logList, setLogList] = useState<any>([\n    {\n      nodeId: 'mcelcsg6pinydoy7',\n      groupTitle: \"日志分组1\",\n      // _status: 'custom-succsess',\n      statusPanel: {\n        status: [\n          {\n            label: '状态',\n            isBadge: true,\n            // value:\"失败\"\n          },\n          {\n            label: '执行时间',\n            value: '0.01s',\n          },\n          {\n            label: '总token数',\n            value: '0tokens',\n          },\n        ],\n        // extra: <span>哈哈哈哈哈哈哈哈</span>,\n        extra: '测试测试',\n      },\n      codePanel: [\n        {\n          title: '输出数据',\n          code: \"{'target_language':'english','query':'string'}\",\n        },\n        {\n          title: '报错信息',\n          code: \"{'target_language':'english','query':'string'}\",\n        },\n      ],\n    },\n    {\n      nodeId: 'mcelcsg6pinydoy7',\n      groupTitle: \"customGroupTitle\",// 自定义分组标题组件\n      // _status: 'custom-error',\n      statusPanel: {\n        status: [\n          {\n            label: '状态',\n            isBadge: true,\n            // value:\"失败\"\n          },\n          {\n            label: '执行时间',\n            value: '0.01s',\n          },\n          {\n            label: '总token数',\n            value: '0tokens',\n          },\n        ],\n        // extra: <span>哈哈哈哈哈哈哈哈</span>,\n        extra: '测试测试',\n      },\n      codePanel: [\n        {\n          title: '输出数据',\n          code: \"{'target_language':'english','query':'string'}\",\n        },\n        {\n          title: '报错信息',\n          code: \"{'target_language':'english','query':'string'}\",\n        },\n      ],\n    },\n    {\n      nodeId: 'w4be9edh4bhdlokm',\n      statusPanel: {\n        status: [\n          {\n            label: '状态',\n            isBadge: true,\n            // value:\"失败\"\n          },\n          {\n            label: '执行时间',\n            value: '0.01s',\n          },\n          {\n            label: '总token数',\n            value: '0tokens',\n          },\n        ],\n        // extra: <span>哈哈\n        // 哈哈哈哈哈哈</span>,\n        extra: '测试测试',\n      },\n      codePanel: [\n        {\n          title: '输出数据',\n          code: \"{'target_language':'english','query':'string'}\",\n        },\n        {\n          title: '报错信息',\n          code: \"{'target_language':'english','query':'string'}\",\n        },\n      ],\n      showDetailLogWidget:false,// 是否展示自定义详情tab组件\n    },\n     {\n       nodeId: '4m9tee00n819nyyy',\n      statusPanel: {\n        status: [\n          {\n            label: '状态',\n            isBadge: true,\n            // value:\"失败\"\n          },\n          {\n            label: '执行时间',\n            value: '0.01s',\n          },\n          {\n            label: '总token数',\n            value: '0tokens',\n          },\n        ],\n        extra: '测试测试',\n      },\n      showDetailLogWidget: false,// 是否展示自定义详情tab组件\n    },\n    {\n      nodeId: '3qloq2p1x3wcwbzg',\n      statusPanel: {\n        status: [\n          {\n            label: '状态',\n            isBadge: true,\n            // value:\"失败\"\n          },\n          {\n            label: '执行时间',\n            value: '0.01s',\n          },\n          {\n            label: '总token数',\n            value: '0tokens',\n          },\n        ],\n        extra: '测试测试',\n      },\n      showDetailLogWidget: false,// 是否展示自定义详情tab组件\n    }\n  ]);\n  const [loading, setLoading] = useState<boolean>(false);\n  const [activeKey, setActiveKey] = useState<string>('detail');\n  const nodes = [\n    {\n      id: 'mcelcsg6pinydoy7',\n      type: 'Parallel',\n      data: {\n        _status: 'warning',\n        list: [\n          {\n            _id: 'id_30ds0x3evus7ogo2',\n            value: '事件1',\n          },\n          {\n            _id: 'id_m1l276eelcgn7s1p',\n            value: '事件2',\n          },\n        ],\n        logTrackList: [\n          {\n            title: '输出数据1',\n            code: \"{'target_language':'english','query':'string'}\",\n          },\n          {\n            title: '报错信息2',\n            code: \"{'target_language':'english','query':'string'}\",\n          },\n        ],\n      },\n      position: {\n        x: -40,\n        y: 281.25,\n      },\n    },\n    {\n      id: '4m9tee00n819nyyy',\n      type: 'tool',\n      position: {\n        x: 400,\n        y: 227.5,\n      },\n      data: {\n        _status: 'custom-succsess',\n      },\n    },\n    {\n      id: 'j0kufl0o4fca4ee9',\n      type: 'knowledge',\n      // data: {\n      //   _status: \"custom-error\"\n      // },\n      position: {\n        x: 312.5,\n        y: 438.75,\n      },\n    },\n    {\n      id: 'w4be9edh4bhdlokm',\n      type: 'Start',\n      position: {\n        x: -379.21875,\n        y: 348.75,\n      },\n      data: {\n        _status: 'success',\n      },\n    },\n    {\n      id: '3qloq2p1x3wcwbzg',\n      type: 'End',\n      position: {\n        x: 675,\n        y: 360,\n      },\n      data: {\n        _status: 'warning',\n      },\n    },\n  ];\n\n  const edges = [\n    {\n      id: 'ky0eedrd6t2hqq81',\n      source: 'mcelcsg6pinydoy7',\n      target: '4m9tee00n819nyyy',\n      sourceHandle: 'id_30ds0x3evus7ogo2',\n    },\n    {\n      id: '7tm5a339lj94ugtn',\n      source: 'mcelcsg6pinydoy7',\n      target: 'j0kufl0o4fca4ee9',\n      sourceHandle: 'id_m1l276eelcgn7s1p',\n    },\n    {\n      type: 'buttonedge',\n      source: 'w4be9edh4bhdlokm',\n      target: 'mcelcsg6pinydoy7',\n      id: 'xy-edge__w4be9edh4bhdlokm-mcelcsg6pinydoy7',\n    },\n    {\n      type: 'buttonedge',\n      source: '4m9tee00n819nyyy',\n      target: '3qloq2p1x3wcwbzg',\n      id: 'xy-edge__4m9tee00n819nyyy-3qloq2p1x3wcwbzg',\n    },\n    {\n      type: 'buttonedge',\n      source: 'j0kufl0o4fca4ee9',\n      target: '3qloq2p1x3wcwbzg',\n      id: 'xy-edge__j0kufl0o4fca4ee9-3qloq2p1x3wcwbzg',\n    },\n  ];\n  return (\n    <div style={{ height: '600px' }}>\n      <XFlow\n        initialValues={{ nodes, edges }}\n        settings={settings}\n        nodeSelector={{\n          showSearch: true,\n        }}\n        widgets={{\n          customGroupTitle: CustomGroupTitle,\n          DetailLogWidget\n        }}\n        logPanel={{\n          // 日志面板\n          logList, // 日志面板接受的数据\n          loading, // 日志面板loading\n          tabsProps: {\n            tabBarExtraContent:activeKey === 'detail' ? <Button type='primary' size='small'>自定义按钮</Button> : null,\n            onChange: (activeKey)=>{\n              setActiveKey(activeKey);\n            },\n          },\n          detailLogWidget: 'DetailLogWidget',//自定义详情tab组件\n        }}\n        globalConfig={{\n          nodeView: {\n            status: [\n              {\n                value: 'custom-error',\n                color: 'red',\n                name: '失败状态',\n              },\n              {\n                value: 'custom-succsess',\n                color: 'green',\n                name: '成功状态',\n              },\n            ],\n          },\n        }}\n      />\n    </div>\n  );\n};\n"
  },
  {
    "path": "docs/xflow/demo/log/buildIn-log/setting.tsx",
    "content": "export default [\n  {\n    title: '开始',\n    type: 'Start',\n    // hidden: true,\n    targetHandleHidden: true,\n    icon: {\n      type: 'icon-start',\n      bgColor: '#17B26A',\n    },\n    settingSchema: {\n      type: 'object',\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n        select: {\n          title: '变量二',\n          type: 'string',\n          widget: 'select',\n          props: {\n            options: [\n              { label: 'a', value: 'a' },\n              { label: 'b', value: 'b' },\n              { label: 'c', value: 'c' },\n            ],\n          },\n        },\n        radio1: {\n          title: '点击单选',\n          type: 'string',\n          widget: 'radio',\n          props: {\n            options: [\n              { label: '早', value: 'a' },\n              { label: '中', value: 'b' },\n              { label: '晚', value: 'c' }\n            ]\n          }\n        },\n        textarea1: {\n          title: '长文本',\n          type: 'string',\n          widget: 'textArea'\n        },\n        date1: {\n          title: '日期选择',\n          type: 'string',\n          widget: 'datePicker'\n        },\n        dateRange1: {\n          title: '日期范围',\n          type: 'range',\n          widget: 'dateRange'\n        },\n        time1: {\n          title: '时间选择',\n          type: 'string',\n          widget: 'timePicker'\n        },\n        timeRange1: {\n          title: '时间范围',\n          type: 'range',\n          widget: 'timeRange'\n        },\n      },\n    },\n  },\n  {\n    title: '结束',\n    type: 'End',\n    // hidden: true,\n    sourceHandleHidden: true,\n    icon: {\n      type: 'icon-end',\n      bgColor: '#F79009',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n        select: {\n          title: '变量二',\n          type: 'string',\n          widget: 'select',\n          props: {\n            options: [\n              { label: 'a', value: 'a' },\n              { label: 'b', value: 'b' },\n              { label: 'c', value: 'c' },\n            ],\n          },\n        },\n      }\n    }\n  },\n  {\n    title: 'Parallel',\n    type: 'Parallel',\n    description: '并行节点',\n    icon: {\n      type: 'icon-parallel',\n      bgColor: '#06AED4',\n    },\n  },\n  {\n    title: 'LLM',\n    type: 'LLM',\n    description: '调用大语言模型回答问题或者对自然语言进行处理',\n    icon: {\n      type: 'icon-model',\n      bgColor: '#6172F3',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n      }\n    }\n  },\n  {\n    title: 'Prompt',\n    type: 'Prompt',\n    description: '通过精心设计提示词，提升大语言模型回答效果',\n    icon: {\n      type: 'icon-prompt',\n      bgColor: '#17B26A',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n      }\n    }\n  },\n  {\n    title: '知识库',\n    type: 'knowledge',\n    description: '允许你从知识库中查询与用户问题相关的文本内容',\n    icon: {\n      type: 'icon-knowledge',\n      bgColor: '#6172F3',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n      }\n    }\n  },\n  {\n    title: 'Switch',\n    type: 'Switch',\n    description: '允许你根据 if/else 条件将 workflow 拆分成两个分支',\n    icon: {\n      type: 'icon-fenzhi',\n      bgColor: '#06AED4',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n      }\n    }\n  },\n  {\n    title: 'HSF',\n    type: 'hsf',\n    description: '允许通过 HSF 协议发送服务器请求',\n    icon: {\n      type: 'icon-hsf',\n      bgColor: '#875BF7',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n      }\n    }\n  },\n  {\n    title: 'Http',\n    type: 'http',\n    description: '允许通过 HTTP 协议发送服务器请求',\n    icon: {\n      type: 'icon-http',\n      bgColor: '#875BF7',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n      }\n    }\n  },\n  {\n    title: '代码执行',\n    type: 'Code',\n    description: '执行一段 Groovy 或 Python 或 NodeJS 代码实现自定义逻辑',\n    icon: {\n      type: 'icon-code',\n      bgColor: '#2E90FA',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n      }\n    }\n  },\n  {\n    title: '工具',\n    type: 'tool',\n    description: '允许使用工具能力',\n    icon: {\n      type: 'icon-gongju',\n      bgColor: '#2E90FA',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n      }\n    }\n  },\n  {\n    title: '工具',\n    type: '_group',\n    items: [\n      {\n        title: '代码执行',\n        type: 'Code',\n        description: '执行一段 Groovy 或 Python 或 NodeJS 代码实现自定义逻辑',\n        icon: {\n          type: 'icon-code',\n          bgColor: '#2E90FA',\n        },\n        settingSchema: {\n          type: \"object\",\n          properties: {\n            input: {\n              title: '变量一',\n              type: 'string',\n              widget: 'input',\n            },\n          }\n        }\n      },\n      {\n        title: '工具',\n        type: 'tool',\n        description: '允许使用工具能力',\n        icon: {\n          type: 'icon-gongju',\n          bgColor: '#2E90FA',\n        },\n        settingSchema: {\n          type: \"object\",\n          properties: {\n            input: {\n              title: '变量一',\n              type: 'string',\n              widget: 'input',\n            },\n          }\n        }\n      },\n    ],\n  },\n];\n"
  },
  {
    "path": "docs/xflow/demo/log/buildIn-log/utils.tsx",
    "content": "export const delay = ms => new Promise(res => setTimeout(res, ms));\n\nexport const fakeApi = (url, data) => {\n  console.group('request:', url);\n  console.log('params:', data);\n  console.groupEnd();\n  return delay(500).then(res => ({\n    success: true,\n    message: 'Data fetched successfully',\n    data: {\n      id: data.id || 'default-id',\n      name: 'Sample Node',\n      logs: [\n        { timestamp: '2023-10-01T10:00:00Z', message: 'Log entry 1' },\n        { timestamp: '2023-10-01T11:00:00Z', message: 'Log entry 2' },\n      ],\n    },\n  }));\n};\n"
  },
  {
    "path": "docs/xflow/demo/log/custom-log/index.tsx",
    "content": "import XFlow from '@xrenders/xflow';\nimport { useState } from 'react';\nimport settings from './setting';\nimport { fakeApi } from './utils';\n\nconst CustomLogPanel = ({ logList, node }) => {\n  console.log('自定义组件', logList, node);\n  return <p>自定义组件:{node?.id}</p>;\n};\n\nexport default () => {\n  const [logList, setLogList] = useState<any>();\n  const [loading, setLoading] = useState<boolean>(false);\n  const nodes = [\n    {\n      id: 'mcelcsg6pinydoy7',\n      type: 'Parallel',\n      data: {\n        _status: 'warning',\n        list: [\n          {\n            _id: 'id_30ds0x3evus7ogo2',\n            value: '事件1',\n          },\n          {\n            _id: 'id_m1l276eelcgn7s1p',\n            value: '事件2',\n          },\n        ],\n      },\n      position: {\n        x: -40,\n        y: 281.25,\n      },\n    },\n    {\n      id: '4m9tee00n819nyyy',\n      type: 'tool',\n      position: {\n        x: 400,\n        y: 227.5,\n      },\n      data: {\n        _status: 'custom-success',\n      },\n    },\n    {\n      id: 'j0kufl0o4fca4ee9',\n      type: 'knowledge',\n      position: {\n        x: 312.5,\n        y: 438.75,\n      },\n    },\n    {\n      id: 'w4be9edh4bhdlokm',\n      type: 'Start',\n      position: {\n        x: -379.21875,\n        y: 348.75,\n      },\n      data: {\n        _status: 'success',\n      },\n    },\n    {\n      id: '3qloq2p1x3wcwbzg',\n      type: 'End',\n      position: {\n        x: 675,\n        y: 360,\n      },\n      data: {\n        _status: 'warning',\n      },\n    },\n  ];\n\n  const edges = [\n    {\n      id: 'ky0eedrd6t2hqq81',\n      source: 'mcelcsg6pinydoy7',\n      target: '4m9tee00n819nyyy',\n      sourceHandle: 'id_30ds0x3evus7ogo2',\n    },\n    {\n      id: '7tm5a339lj94ugtn',\n      source: 'mcelcsg6pinydoy7',\n      target: 'j0kufl0o4fca4ee9',\n      sourceHandle: 'id_m1l276eelcgn7s1p',\n    },\n    {\n      type: 'buttonedge',\n      source: 'w4be9edh4bhdlokm',\n      target: 'mcelcsg6pinydoy7',\n      id: 'xy-edge__w4be9edh4bhdlokm-mcelcsg6pinydoy7',\n    },\n    {\n      type: 'buttonedge',\n      source: '4m9tee00n819nyyy',\n      target: '3qloq2p1x3wcwbzg',\n      id: 'xy-edge__4m9tee00n819nyyy-3qloq2p1x3wcwbzg',\n    },\n    {\n      type: 'buttonedge',\n      source: 'j0kufl0o4fca4ee9',\n      target: '3qloq2p1x3wcwbzg',\n      id: 'xy-edge__j0kufl0o4fca4ee9-3qloq2p1x3wcwbzg',\n    },\n  ];\n  return (\n    <div style={{ height: '600px' }}>\n      <XFlow\n        initialValues={{ nodes, edges }}\n        settings={settings}\n        nodeSelector={{\n          showSearch: true,\n        }}\n        widgets={{ CustomLogPanel }}\n        logPanel={{\n          // 日志面板\n          logList, // 日志面板接受的数据\n          loading, // 日志面板loading\n          logWidget: 'CustomLogPanel',\n        }}\n        globalConfig={{\n          nodeView: {\n            status: [\n              {\n                value: 'custom-success',\n                color: 'green',\n                name: '自定义成功状态',\n              },\n            ],\n          },\n        }}\n        onNodeClick={async (e, node) => {\n          setLoading(true);\n          const logList = await fakeApi('xx', {});\n          setLogList([logList]);\n          setLoading(false);\n        }}\n      />\n    </div>\n  );\n};\n"
  },
  {
    "path": "docs/xflow/demo/log/custom-log/setting.tsx",
    "content": "export default [\n  {\n    title: '开始',\n    type: 'Start',\n    // hidden: true,\n    targetHandleHidden: true,\n    icon: {\n      type: 'icon-start',\n      bgColor: '#17B26A',\n    },\n    settingSchema: {\n      type: 'object',\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n        select: {\n          title: '变量二',\n          type: 'string',\n          widget: 'select',\n          props: {\n            options: [\n              { label: 'a', value: 'a' },\n              { label: 'b', value: 'b' },\n              { label: 'c', value: 'c' },\n            ],\n          },\n        },\n        radio1: {\n          title: '点击单选',\n          type: 'string',\n          widget: 'radio',\n          props: {\n            options: [\n              { label: '早', value: 'a' },\n              { label: '中', value: 'b' },\n              { label: '晚', value: 'c' }\n            ]\n          }\n        },\n        textarea1: {\n          title: '长文本',\n          type: 'string',\n          widget: 'textArea'\n        },\n        date1: {\n          title: '日期选择',\n          type: 'string',\n          widget: 'datePicker'\n        },\n        dateRange1: {\n          title: '日期范围',\n          type: 'range',\n          widget: 'dateRange'\n        },\n        time1: {\n          title: '时间选择',\n          type: 'string',\n          widget: 'timePicker'\n        },\n        timeRange1: {\n          title: '时间范围',\n          type: 'range',\n          widget: 'timeRange'\n        },\n      },\n    },\n  },\n  {\n    title: '结束',\n    type: 'End',\n    // hidden: true,\n    sourceHandleHidden: true,\n    icon: {\n      type: 'icon-end',\n      bgColor: '#F79009',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n        select: {\n          title: '变量二',\n          type: 'string',\n          widget: 'select',\n          props: {\n            options: [\n              { label: 'a', value: 'a' },\n              { label: 'b', value: 'b' },\n              { label: 'c', value: 'c' },\n            ],\n          },\n        },\n      }\n    }\n  },\n  {\n    title: 'Parallel',\n    type: 'Parallel',\n    description: '并行节点',\n    icon: {\n      type: 'icon-parallel',\n      bgColor: '#06AED4',\n    },\n  },\n  {\n    title: 'LLM',\n    type: 'LLM',\n    description: '调用大语言模型回答问题或者对自然语言进行处理',\n    icon: {\n      type: 'icon-model',\n      bgColor: '#6172F3',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n      }\n    }\n  },\n  {\n    title: 'Prompt',\n    type: 'Prompt',\n    description: '通过精心设计提示词，提升大语言模型回答效果',\n    icon: {\n      type: 'icon-prompt',\n      bgColor: '#17B26A',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n      }\n    }\n  },\n  {\n    title: '知识库',\n    type: 'knowledge',\n    description: '允许你从知识库中查询与用户问题相关的文本内容',\n    icon: {\n      type: 'icon-knowledge',\n      bgColor: '#6172F3',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n      }\n    }\n  },\n  {\n    title: 'Switch',\n    type: 'Switch',\n    description: '允许你根据 if/else 条件将 workflow 拆分成两个分支',\n    icon: {\n      type: 'icon-fenzhi',\n      bgColor: '#06AED4',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n      }\n    }\n  },\n  {\n    title: 'HSF',\n    type: 'hsf',\n    description: '允许通过 HSF 协议发送服务器请求',\n    icon: {\n      type: 'icon-hsf',\n      bgColor: '#875BF7',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n      }\n    }\n  },\n  {\n    title: 'Http',\n    type: 'http',\n    description: '允许通过 HTTP 协议发送服务器请求',\n    icon: {\n      type: 'icon-http',\n      bgColor: '#875BF7',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n      }\n    }\n  },\n  {\n    title: '代码执行',\n    type: 'Code',\n    description: '执行一段 Groovy 或 Python 或 NodeJS 代码实现自定义逻辑',\n    icon: {\n      type: 'icon-code',\n      bgColor: '#2E90FA',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n      }\n    }\n  },\n  {\n    title: '工具',\n    type: 'tool',\n    description: '允许使用工具能力',\n    icon: {\n      type: 'icon-gongju',\n      bgColor: '#2E90FA',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n      }\n    }\n  },\n  {\n    title: '工具',\n    type: '_group',\n    items: [\n      {\n        title: '代码执行',\n        type: 'Code',\n        description: '执行一段 Groovy 或 Python 或 NodeJS 代码实现自定义逻辑',\n        icon: {\n          type: 'icon-code',\n          bgColor: '#2E90FA',\n        },\n        settingSchema: {\n          type: \"object\",\n          properties: {\n            input: {\n              title: '变量一',\n              type: 'string',\n              widget: 'input',\n            },\n          }\n        }\n      },\n      {\n        title: '工具',\n        type: 'tool',\n        description: '允许使用工具能力',\n        icon: {\n          type: 'icon-gongju',\n          bgColor: '#2E90FA',\n        },\n        settingSchema: {\n          type: \"object\",\n          properties: {\n            input: {\n              title: '变量一',\n              type: 'string',\n              widget: 'input',\n            },\n          }\n        }\n      },\n    ],\n  },\n];\n"
  },
  {
    "path": "docs/xflow/demo/log/custom-log/utils.tsx",
    "content": "export const delay = ms => new Promise(res => setTimeout(res, ms));\n\nexport const fakeApi = (url, data) => {\n  console.group('request:', url);\n  console.log('params:', data);\n  console.groupEnd();\n  return delay(500).then(res => ({\n    success: true,\n    message: 'Data fetched successfully',\n    data: {\n      id: data.id || 'default-id',\n      name: 'Sample Node',\n      logs: [\n        { timestamp: '2023-10-01T10:00:00Z', message: 'Log entry 1' },\n        { timestamp: '2023-10-01T11:00:00Z', message: 'Log entry 2' },\n      ],\n    },\n  }));\n};\n"
  },
  {
    "path": "docs/xflow/demo/log/index.tsx",
    "content": "import XFlow from '@xrenders/xflow';\nimport React,{ useState } from 'react';\nimport settings from './setting';\n\nexport default () => {\n  const [logList, setLogList] = useState<any>([\n    {\n      nodeId:'mcelcsg6pinydoy7',\n      statusPanel: {\n        status: [\n          {\n            label: '状态',\n            isBadge: true,\n            // value:\"失败\"\n          },\n          {\n            label: '执行时间',\n            value: '0.01s',\n          },\n          {\n            label: '总token数',\n            value: '0tokens',\n          },\n        ],\n        // extra: <span>哈哈哈哈哈哈哈哈</span>,\n        extra: '测试测试',\n      },\n      codePanel: [\n        {\n          title: '输出数据',\n          code: \"{'target_language':'english','query':'string'}\",\n        },\n        {\n          title: '报错信息',\n          code: \"{'target_language':'english','query':'string'}\",\n        },\n      ],\n    },\n  ]);\n  const [loading, setLoading] = useState<boolean>(false);\n  const nodes = [\n    {\n      id: 'mcelcsg6pinydoy7',\n      type: 'Parallel',\n      data: {\n        _status: 'warning',\n        list: [\n          {\n            _id: 'id_30ds0x3evus7ogo2',\n            value: '事件1',\n          },\n          {\n            _id: 'id_m1l276eelcgn7s1p',\n            value: '事件2',\n          },\n        ],\n        logTrackList: [\n          {\n            title: '输出数据1',\n            code: \"{'target_language':'english','query':'string'}\",\n          },\n          {\n            title: '报错信息2',\n            code: \"{'target_language':'english','query':'string'}\",\n          },\n        ],\n      },\n      position: {\n        x: -40,\n        y: 281.25,\n      },\n    },\n    {\n      id: '4m9tee00n819nyyy',\n      type: 'tool',\n      position: {\n        x: 400,\n        y: 227.5,\n      },\n      data: {\n        _status: 'custom-succsess',\n      },\n    },\n    {\n      id: 'j0kufl0o4fca4ee9',\n      type: 'knowledge',\n      // data: {\n      //   _status: \"custom-error\"\n      // },\n      position: {\n        x: 312.5,\n        y: 438.75,\n      },\n    },\n    {\n      id: 'w4be9edh4bhdlokm',\n      type: 'Start',\n      position: {\n        x: -379.21875,\n        y: 348.75,\n      },\n      data: {\n        _status: 'success',\n      },\n    },\n    {\n      id: '3qloq2p1x3wcwbzg',\n      type: 'End',\n      position: {\n        x: 675,\n        y: 360,\n      },\n      data: {\n        _status: 'warning',\n      },\n    },\n  ];\n\n  const edges = [\n    {\n      id: 'ky0eedrd6t2hqq81',\n      source: 'mcelcsg6pinydoy7',\n      target: '4m9tee00n819nyyy',\n      sourceHandle: 'id_30ds0x3evus7ogo2',\n    },\n    {\n      id: '7tm5a339lj94ugtn',\n      source: 'mcelcsg6pinydoy7',\n      target: 'j0kufl0o4fca4ee9',\n      sourceHandle: 'id_m1l276eelcgn7s1p',\n    },\n    {\n      type: 'buttonedge',\n      source: 'w4be9edh4bhdlokm',\n      target: 'mcelcsg6pinydoy7',\n      id: 'xy-edge__w4be9edh4bhdlokm-mcelcsg6pinydoy7',\n    },\n    {\n      type: 'buttonedge',\n      source: '4m9tee00n819nyyy',\n      target: '3qloq2p1x3wcwbzg',\n      id: 'xy-edge__4m9tee00n819nyyy-3qloq2p1x3wcwbzg',\n    },\n    {\n      type: 'buttonedge',\n      source: 'j0kufl0o4fca4ee9',\n      target: '3qloq2p1x3wcwbzg',\n      id: 'xy-edge__j0kufl0o4fca4ee9-3qloq2p1x3wcwbzg',\n    },\n  ];\n  return (\n    <div style={{ height: '600px' }}>\n      <XFlow\n        initialValues={{ nodes, edges }}\n        settings={settings}\n        nodeSelector={{\n          showSearch: true,\n        }}\n        logPanel={{\n          // 日志面板\n          logList, // 日志面板接受的数据\n          loading, // 日志面板loading\n        }}\n        globalConfig={{\n          nodeView: {\n            status: [\n              {\n                value: 'custom-error',\n                color: 'red',\n                name: '失败状态',\n              },\n              {\n                value: 'custom-succsess',\n                color: 'green',\n                name: '成功状态',\n              },\n            ],\n          },\n        }}\n      />\n    </div>\n  );\n};\n"
  },
  {
    "path": "docs/xflow/demo/log/runNode/index.tsx",
    "content": "import XFlow from '@xrenders/xflow';\nimport { message } from 'antd';\nimport { useState } from 'react';\n\nexport default () => {\n  const [loading, setLoading] = useState<boolean>(false);\n\n  const nodes = [\n    {\n      id: 'node1',\n      type: 'LLM',\n      position: {\n        x: 300,\n        y: 200,\n      },\n    },\n  ];\n\n  const settings = [\n    {\n      title: 'LLM',\n      type: 'LLM',\n      description: '调用大语言模型回答问题或者对自然语言进行处理',\n      showTestingBtn: true,\n      icon: {\n        type: 'icon-model',\n        bgColor: '#6172F3',\n      },\n      gradientHeight: '100%',\n      settingSchema: {\n        type: 'object',\n        properties: {\n          input: {\n            title: '变量一',\n            type: 'string',\n            widget: 'input',\n          },\n        },\n      },\n    },\n  ];\n\n  return (\n    <div style={{ height: '400px' }}>\n      <XFlow\n        initialValues={{ nodes, edges: [] }}\n        settings={settings}\n        onTesting={(node, nodes) => {\n          setLoading(true);\n          // 模拟调试过程\n          setTimeout(() => {\n            message.success('节点调试完成');\n            setLoading(false);\n          }, 1000);\n          console.log('单点调试', node, nodes);\n        }}\n      />\n    </div>\n  );\n};\n"
  },
  {
    "path": "docs/xflow/demo/log/runNode/setting.tsx",
    "content": "export default [\n  {\n    title: '开始',\n    type: 'Start',\n    // hidden: true,\n    targetHandleHidden: true,\n    showTestingBtn: true,\n    icon: {\n      type: 'icon-start',\n      bgColor: '#17B26A',\n    },\n    settingSchema: {\n      type: 'object',\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n        select: {\n          title: '变量二',\n          type: 'string',\n          widget: 'select',\n          props: {\n            options: [\n              { label: 'a', value: 'a' },\n              { label: 'b', value: 'b' },\n              { label: 'c', value: 'c' },\n            ],\n          },\n        },\n        radio1: {\n          title: '点击单选',\n          type: 'string',\n          widget: 'radio',\n          props: {\n            options: [\n              { label: '早', value: 'a' },\n              { label: '中', value: 'b' },\n              { label: '晚', value: 'c' }\n            ]\n          }\n        },\n        textarea1: {\n          title: '长文本',\n          type: 'string',\n          widget: 'textArea'\n        },\n        date1: {\n          title: '日期选择',\n          type: 'string',\n          widget: 'datePicker'\n        },\n        dateRange1: {\n          title: '日期范围',\n          type: 'range',\n          widget: 'dateRange'\n        },\n        time1: {\n          title: '时间选择',\n          type: 'string',\n          widget: 'timePicker'\n        },\n        timeRange1: {\n          title: '时间范围',\n          type: 'range',\n          widget: 'timeRange'\n        },\n      },\n    },\n  },\n  {\n    title: '结束',\n    type: 'End',\n    // hidden: true,\n    sourceHandleHidden: true,\n    icon: {\n      type: 'icon-end',\n      bgColor: '#F79009',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n        select: {\n          title: '变量二',\n          type: 'string',\n          widget: 'select',\n          props: {\n            options: [\n              { label: 'a', value: 'a' },\n              { label: 'b', value: 'b' },\n              { label: 'c', value: 'c' },\n            ],\n          },\n        },\n      }\n    }\n  },\n  {\n    title: 'Parallel',\n    type: 'Parallel',\n    showTestingBtn: true,\n    description: '并行节点',\n    icon: {\n      type: 'icon-parallel',\n      bgColor: '#06AED4',\n    },\n  },\n  {\n    title: 'LLM',\n    type: 'LLM',\n    description: '调用大语言模型回答问题或者对自然语言进行处理',\n    showTestingBtn:true,\n    icon: {\n      type: 'icon-model',\n      bgColor: '#6172F3',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n      }\n    }\n  },\n  {\n    title: 'Prompt',\n    type: 'Prompt',\n    description: '通过精心设计提示词，提升大语言模型回答效果',\n    icon: {\n      type: 'icon-prompt',\n      bgColor: '#17B26A',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n      }\n    }\n  },\n  {\n    title: '知识库',\n    type: 'knowledge',\n    description: '允许你从知识库中查询与用户问题相关的文本内容',\n    icon: {\n      type: 'icon-knowledge',\n      bgColor: '#6172F3',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n      }\n    }\n  },\n  {\n    title: 'Switch',\n    type: 'Switch',\n    description: '允许你根据 if/else 条件将 workflow 拆分成两个分支',\n    icon: {\n      type: 'icon-fenzhi',\n      bgColor: '#06AED4',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n      }\n    }\n  },\n  {\n    title: 'HSF',\n    type: 'hsf',\n    description: '允许通过 HSF 协议发送服务器请求',\n    icon: {\n      type: 'icon-hsf',\n      bgColor: '#875BF7',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n      }\n    }\n  },\n  {\n    title: 'Http',\n    type: 'http',\n    description: '允许通过 HTTP 协议发送服务器请求',\n    icon: {\n      type: 'icon-http',\n      bgColor: '#875BF7',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n      }\n    }\n  },\n  {\n    title: '代码执行',\n    type: 'Code',\n    description: '执行一段 Groovy 或 Python 或 NodeJS 代码实现自定义逻辑',\n    icon: {\n      type: 'icon-code',\n      bgColor: '#2E90FA',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n      }\n    }\n  },\n  {\n    title: '工具',\n    type: 'tool',\n    description: '允许使用工具能力',\n    icon: {\n      type: 'icon-gongju',\n      bgColor: '#2E90FA',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n      }\n    }\n  },\n  {\n    title: '工具',\n    type: '_group',\n    items: [\n      {\n        title: '代码执行',\n        type: 'Code',\n        description: '执行一段 Groovy 或 Python 或 NodeJS 代码实现自定义逻辑',\n        icon: {\n          type: 'icon-code',\n          bgColor: '#2E90FA',\n        },\n        settingSchema: {\n          type: \"object\",\n          properties: {\n            input: {\n              title: '变量一',\n              type: 'string',\n              widget: 'input',\n            },\n          }\n        }\n      },\n      {\n        title: '工具',\n        type: 'tool',\n        description: '允许使用工具能力',\n        icon: {\n          type: 'icon-gongju',\n          bgColor: '#2E90FA',\n        },\n        settingSchema: {\n          type: \"object\",\n          properties: {\n            input: {\n              title: '变量一',\n              type: 'string',\n              widget: 'input',\n            },\n          }\n        }\n      },\n    ],\n  },\n];\n"
  },
  {
    "path": "docs/xflow/demo/log/runNode/utils.tsx",
    "content": "export const delay = ms => new Promise(res => setTimeout(res, ms));\n\nexport const fakeApi = (url, data) => {\n  console.group('request:', url);\n  console.log('params:', data);\n  console.groupEnd();\n  return delay(500).then(res => ({\n    success: true,\n    message: 'Data fetched successfully',\n    data: {\n      id: data.id || 'default-id',\n      name: 'Sample Node',\n      logs: [\n        { timestamp: '2023-10-01T10:00:00Z', message: 'Log entry 1' },\n        { timestamp: '2023-10-01T11:00:00Z', message: 'Log entry 2' },\n      ],\n    },\n  }));\n};\n"
  },
  {
    "path": "docs/xflow/demo/log/setting.tsx",
    "content": "export default [\n  {\n    title: '开始',\n    type: 'Start',\n    // hidden: true,\n    targetHandleHidden: true,\n    icon: {\n      type: 'icon-start',\n      bgColor: '#17B26A',\n    },\n    settingSchema: {\n      type: 'object',\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n        select: {\n          title: '变量二',\n          type: 'string',\n          widget: 'select',\n          props: {\n            options: [\n              { label: 'a', value: 'a' },\n              { label: 'b', value: 'b' },\n              { label: 'c', value: 'c' },\n            ],\n          },\n        },\n        radio1: {\n          title: '点击单选',\n          type: 'string',\n          widget: 'radio',\n          props: {\n            options: [\n              { label: '早', value: 'a' },\n              { label: '中', value: 'b' },\n              { label: '晚', value: 'c' }\n            ]\n          }\n        },\n        textarea1: {\n          title: '长文本',\n          type: 'string',\n          widget: 'textArea'\n        },\n        date1: {\n          title: '日期选择',\n          type: 'string',\n          widget: 'datePicker'\n        },\n        dateRange1: {\n          title: '日期范围',\n          type: 'range',\n          widget: 'dateRange'\n        },\n        time1: {\n          title: '时间选择',\n          type: 'string',\n          widget: 'timePicker'\n        },\n        timeRange1: {\n          title: '时间范围',\n          type: 'range',\n          widget: 'timeRange'\n        },\n      },\n    },\n  },\n  {\n    title: '结束',\n    type: 'End',\n    // hidden: true,\n    sourceHandleHidden: true,\n    icon: {\n      type: 'icon-end',\n      bgColor: '#F79009',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n        select: {\n          title: '变量二',\n          type: 'string',\n          widget: 'select',\n          props: {\n            options: [\n              { label: 'a', value: 'a' },\n              { label: 'b', value: 'b' },\n              { label: 'c', value: 'c' },\n            ],\n          },\n        },\n      }\n    }\n  },\n  {\n    title: 'Parallel',\n    type: 'Parallel',\n    description: '并行节点',\n    icon: {\n      type: 'icon-parallel',\n      bgColor: '#06AED4',\n    },\n  },\n  {\n    title: 'LLM',\n    type: 'LLM',\n    description: '调用大语言模型回答问题或者对自然语言进行处理',\n    icon: {\n      type: 'icon-model',\n      bgColor: '#6172F3',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n      }\n    }\n  },\n  {\n    title: 'Prompt',\n    type: 'Prompt',\n    description: '通过精心设计提示词，提升大语言模型回答效果',\n    icon: {\n      type: 'icon-prompt',\n      bgColor: '#17B26A',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n      }\n    }\n  },\n  {\n    title: '知识库',\n    type: 'knowledge',\n    description: '允许你从知识库中查询与用户问题相关的文本内容',\n    icon: {\n      type: 'icon-knowledge',\n      bgColor: '#6172F3',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n      }\n    }\n  },\n  {\n    title: 'Switch',\n    type: 'Switch',\n    description: '允许你根据 if/else 条件将 workflow 拆分成两个分支',\n    icon: {\n      type: 'icon-fenzhi',\n      bgColor: '#06AED4',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n      }\n    }\n  },\n  {\n    title: 'HSF',\n    type: 'hsf',\n    description: '允许通过 HSF 协议发送服务器请求',\n    icon: {\n      type: 'icon-hsf',\n      bgColor: '#875BF7',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n      }\n    }\n  },\n  {\n    title: 'Http',\n    type: 'http',\n    description: '允许通过 HTTP 协议发送服务器请求',\n    icon: {\n      type: 'icon-http',\n      bgColor: '#875BF7',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n      }\n    }\n  },\n  {\n    title: '代码执行',\n    type: 'Code',\n    description: '执行一段 Groovy 或 Python 或 NodeJS 代码实现自定义逻辑',\n    icon: {\n      type: 'icon-code',\n      bgColor: '#2E90FA',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n      }\n    }\n  },\n  {\n    title: '工具',\n    type: 'tool',\n    description: '允许使用工具能力',\n    icon: {\n      type: 'icon-gongju',\n      bgColor: '#2E90FA',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n      }\n    }\n  },\n  {\n    title: '工具',\n    type: '_group',\n    items: [\n      {\n        title: '代码执行',\n        type: 'Code',\n        description: '执行一段 Groovy 或 Python 或 NodeJS 代码实现自定义逻辑',\n        icon: {\n          type: 'icon-code',\n          bgColor: '#2E90FA',\n        },\n        settingSchema: {\n          type: \"object\",\n          properties: {\n            input: {\n              title: '变量一',\n              type: 'string',\n              widget: 'input',\n            },\n          }\n        }\n      },\n      {\n        title: '工具',\n        type: 'tool',\n        description: '允许使用工具能力',\n        icon: {\n          type: 'icon-gongju',\n          bgColor: '#2E90FA',\n        },\n        settingSchema: {\n          type: \"object\",\n          properties: {\n            input: {\n              title: '变量一',\n              type: 'string',\n              widget: 'input',\n            },\n          }\n        }\n      },\n    ],\n  },\n];\n"
  },
  {
    "path": "docs/xflow/demo/log/utils.tsx",
    "content": "export const delay = ms => new Promise(res => setTimeout(res, ms));\n\nexport const fakeApi = (url, data) => {\n  console.group('request:', url);\n  console.log('params:', data);\n  console.groupEnd();\n  return delay(500).then(res => ({\n    success: true,\n    message: 'Data fetched successfully',\n    data: {\n      id: data.id || 'default-id',\n      name: 'Sample Node',\n      logs: [\n        { timestamp: '2023-10-01T10:00:00Z', message: 'Log entry 1' },\n        { timestamp: '2023-10-01T11:00:00Z', message: 'Log entry 2' },\n      ],\n    },\n  }));\n};\n"
  },
  {
    "path": "docs/xflow/demo/nodeSetting/data.tsx",
    "content": "export const settings = [\n  {\n    title: '开始', // 节点名称\n    type: 'Start',\n    hidden: true,\n    targetHandleHidden: true,\n    icon: {\n      // 图标描述\n      type: 'icon-start', // icon-font\n      bgColor: '#17B26A', // 图标背景颜色\n    },\n    nodePanel: {\n      width: 600,\n      hideDesc: false,\n    },\n    settingSchema: {\n      // 自定义节点配置\n      type: 'object',\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n        select: {\n          title: '变量二',\n          type: 'string',\n          widget: 'select',\n          props: {\n            options: [\n              { label: 'a', value: 'a' },\n              { label: 'b', value: 'b' },\n              { label: 'c', value: 'c' },\n            ],\n          },\n        },\n        radio1: {\n          title: '点击单选',\n          type: 'string',\n          widget: 'radio',\n          props: {\n            options: [\n              { label: '早', value: 'a' },\n              { label: '中', value: 'b' },\n              { label: '晚', value: 'c' },\n            ],\n          },\n        },\n        textarea1: {\n          title: '长文本',\n          type: 'string',\n          widget: 'textArea',\n        },\n        date1: {\n          title: '日期选择',\n          type: 'string',\n          widget: 'datePicker',\n        },\n        dateRange1: {\n          title: '日期范围',\n          type: 'range',\n          widget: 'dateRange',\n        },\n        time1: {\n          title: '时间选择',\n          type: 'string',\n          widget: 'timePicker',\n        },\n        timeRange1: {\n          title: '时间范围',\n          type: 'range',\n          widget: 'timeRange',\n        },\n      },\n    },\n  },\n  {\n    title: '结束',\n    type: 'End',\n    hidden: true,\n    sourceHandleHidden: true,\n    icon: {\n      type: 'icon-end',\n      bgColor: '#F79009',\n    },\n    settingSchema: {\n      type: 'object',\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n        select: {\n          title: '变量二',\n          type: 'string',\n          widget: 'select',\n          props: {\n            options: [\n              { label: 'a', value: 'a' },\n              { label: 'b', value: 'b' },\n              { label: 'c', value: 'c' },\n            ],\n          },\n        },\n      },\n    },\n  },\n  {\n    title: 'Switch',\n    type: 'Switch',\n    description: '允许你根据 if/else 条件将 workflow 拆分成两个分支',\n    icon: {\n      type: 'icon-switch',\n      bgColor: '#06AED4',\n    },\n    onTesting: (node, nodes) => {\n      console.log(\"单点调试\",node,nodes)\n    },\n  },\n  {\n    title: '工具',\n    type: 'tool',\n    description: '允许使用工具能力',\n    icon: {\n      type: 'icon-gongju',\n      bgColor: '#2E90FA',\n    },\n  },\n  {\n    title: '工具',\n    type: '_group', // 节点分组\n    items: [\n      {\n        title: '代码执行',\n        type: 'Code',\n        description: '执行一段 Groovy 或 Python 或 NodeJS 代码实现自定义逻辑',\n      },\n      {\n        title: '工具',\n        type: 'tool',\n        description: '允许使用工具能力',\n        icon: {\n          type: 'icon-gongju',\n          bgColor: '#2E90FA',\n        },\n      },\n    ],\n  },\n];\n\nexport const nodes = [\n  {\n    type: 'Start',\n    id: '1',\n    position: { x: -35, y: 268 },\n  },\n  {\n    type: 'Switch',\n    id: 'b6zsd6w5ah2b209t',\n    position: { x: 277.5, y: 268 },\n    data: {\n      list: [\n        {\n          _id: 'iawoyh5niyi6zjob',\n        },\n      ],\n    },\n  },\n  {\n    type: 'Code',\n    id: '3',\n    position: { x: 675, y: 123.75 },\n  },\n  {\n    type: 'tool',\n    id: '4',\n    position: { x: 686.25, y: 495 },\n  },\n  {\n    type: 'End',\n    id: '5',\n    position: { x: 1176.2499999999998, y: 281.25 },\n  },\n];\nexport const edges = [\n  {\n    source: '3',\n    target: '5',\n    id: 'e3-5',\n  },\n  {\n    source: '4',\n    target: '5',\n    id: 'e4-5',\n  },\n  {\n    id: 'px7fsmha99pju315',\n    source: '1',\n    target: 'b6zsd6w5ah2b209t',\n  },\n  {\n    type: 'buttonedge',\n    source: 'b6zsd6w5ah2b209t',\n    sourceHandle: 'iawoyh5niyi6zjob',\n    target: '3',\n    id: 'xy-edge__b6zsd6w5ah2b209tiawoyh5niyi6zjob-3',\n  },\n  {\n    type: 'buttonedge',\n    source: 'b6zsd6w5ah2b209t',\n    sourceHandle: 'id_else',\n    target: '4',\n    id: 'xy-edge__b6zsd6w5ah2b209tid_else-4',\n  },\n];\n"
  },
  {
    "path": "docs/xflow/demo/nodeSetting/fullDemo/CustomImg.tsx",
    "content": "import React from 'react';\n\nconst CustomImg = ({ setting }) => {\n  console.log('props', setting);\n  return (\n    <img src={setting?.imgSrc} alt=\"logo\" width=\"14px\" />\n  );\n}\n\nexport default CustomImg;"
  },
  {
    "path": "docs/xflow/demo/nodeSetting/fullDemo/CustomSvg.tsx",
    "content": "const CustomSvg = () => (\n  <svg viewBox=\"0 0 1024 1024\" width=\"1em\" height=\"1em\" fill=\"currentColor\">\n    <title>Panda icon</title>\n    <path\n      d=\"M99.096 315.634s-82.58-64.032-82.58-132.13c0-66.064 33.032-165.162 148.646-148.646 83.37 11.91 99.096 165.162 99.096 165.162l-165.162 115.614zM924.906 315.634s82.58-64.032 82.58-132.13c0-66.064-33.032-165.162-148.646-148.646-83.37 11.91-99.096 165.162-99.096 165.162l165.162 115.614z\"\n      fill=\"#6B676E\"\n    />\n    <path\n      d=\"M1024 561.548c0 264.526-229.23 429.42-512.002 429.42S0 826.076 0 561.548 283.96 66.064 512.002 66.064 1024 297.022 1024 561.548z\"\n      fill=\"#FFEBD2\"\n    />\n    <path\n      d=\"M330.324 842.126c0 82.096 81.34 148.646 181.678 148.646s181.678-66.55 181.678-148.646H330.324z\"\n      fill=\"#E9D7C3\"\n    />\n    <path\n      d=\"M644.13 611.098C594.582 528.516 561.55 512 512.002 512c-49.548 0-82.58 16.516-132.13 99.096-42.488 70.814-78.73 211.264-49.548 247.742 66.064 82.58 165.162 33.032 181.678 33.032 16.516 0 115.614 49.548 181.678-33.032 29.18-36.476-7.064-176.93-49.55-247.74z\"\n      fill=\"#FFFFFF\"\n    />\n    <path\n      d=\"M611.098 495.484c0-45.608 36.974-82.58 82.58-82.58 49.548 0 198.194 99.098 198.194 165.162s-79.934 144.904-148.646 99.096c-49.548-33.032-132.128-148.646-132.128-181.678zM412.904 495.484c0-45.608-36.974-82.58-82.58-82.58-49.548 0-198.194 99.098-198.194 165.162s79.934 144.904 148.646 99.096c49.548-33.032 132.128-148.646 132.128-181.678z\"\n      fill=\"#6B676E\"\n    />\n    <path\n      d=\"M512.002 726.622c-30.06 0-115.614 5.668-115.614 33.032 0 49.638 105.484 85.24 115.614 82.58 10.128 2.66 115.614-32.944 115.614-82.58-0.002-27.366-85.556-33.032-115.614-33.032z\"\n      fill=\"#464655\"\n    />\n    <path\n      d=\"M330.324 495.484m-33.032 0a33.032 33.032 0 1 0 66.064 0 33.032 33.032 0 1 0-66.064 0Z\"\n      fill=\"#464655\"\n    />\n    <path\n      d=\"M693.678 495.484m-33.032 0a33.032 33.032 0 1 0 66.064 0 33.032 33.032 0 1 0-66.064 0Z\"\n      fill=\"#464655\"\n    />\n  </svg>\n);\n\nexport default CustomSvg;"
  },
  {
    "path": "docs/xflow/demo/nodeSetting/fullDemo/index.tsx",
    "content": "import React from 'react';\nimport XFlow from '@xrenders/xflow';\nimport { settings } from './settings';\nimport CustomSvg from './CustomSvg';\nimport CustomImg from './CustomImg';\nimport { message } from 'antd';\n\n\nconst initialValues = {\n  nodes: [\n    {\n      id: 'start',\n      type: 'Start',\n      data: {\n        name: 'AI 助手流程',\n        description: '一个基于 LLM 的智能助手流程',\n        variables: [\n          {\n            name: 'userInput',\n            type: 'string',\n            defaultValue: '',\n          },\n        ],\n      },\n      position: {\n        x: 100,\n        y: 200,\n      },\n    },\n    {\n      id: 'llm',\n      type: 'LLM',\n      data: {\n        model: 'gpt-3.5',\n        temperature: 0.7,\n        maxTokens: 2000,\n        systemPrompt: '你是一个专业的AI助手，请用简洁专业的语言回答问题。',\n      },\n      position: {\n        x: 400,\n        y: 200,\n      },\n    },\n    {\n      id: 'knowledge',\n      type: 'knowledge',\n      data: {\n        knowledgeBase: '产品文档',\n        searchLimit: 5,\n        similarity: 0.7,\n      },\n      position: {\n        x: 700,\n        y: 200,\n      },\n    },\n    {\n      id: 'end',\n      type: 'End',\n      data: {\n        output: '处理完成',\n      },\n      position: {\n        x: 1000,\n        y: 200,\n      },\n    },\n  ],\n  edges: [\n    {\n      id: 'start-llm',\n      source: 'start',\n      target: 'llm',\n    },\n    {\n      id: 'llm-knowledge',\n      source: 'llm',\n      target: 'knowledge',\n    },\n    {\n      id: 'knowledge-end',\n      source: 'knowledge',\n      target: 'end',\n    },\n  ],\n};\n\nconst Demo = () => {\n  return (\n    <div style={{ width: '100%', height: '600px' }}>\n      <XFlow\n        settings={settings}\n        initialValues={initialValues}\n        widgets={{ CustomSvg, CustomImg }}\n        nodeSelector={{\n          showSearch: true,\n        }}\n        globalConfig={{\n          controls: {\n            onAutoLayoutCompleted: async (nodes) => {\n              console.log('整理画布完成，节点数量:', nodes.length);\n              console.log('整理后的节点数据:', nodes);\n              message.success(`画布已整理完成，共 ${nodes.length} 个节点`);\n            }\n          }\n        }}\n      />\n    </div>\n  );\n};\n\nexport default Demo;\n"
  },
  {
    "path": "docs/xflow/demo/nodeSetting/fullDemo/settings.ts",
    "content": "export const settings= [\n  {\n    title: '流程控制',\n    type: '_group',\n    items: [\n      {\n        title: '开始',\n        type: 'Start',\n        description: '自定义SVG形式的图标',\n        icon: {\n          bgColor: '#17B26A', // icon背景颜色\n        },\n        iconSvg: 'CustomSvg',  // 传入字符串形式的 SVG\n        settingSchema: {\n          type: 'object',\n          properties: {\n            name: {\n              title: '流程名称',\n              type: 'string',\n              widget: 'input',\n              required: true,\n            },\n            description: {\n              title: '流程描述',\n              type: 'string',\n              widget: 'textarea',\n            },\n            variables: {\n              title: '流程变量',\n              type: 'array',\n              items: {\n                type: 'object',\n                properties: {\n                  name: {\n                    title: '变量名',\n                    type: 'string',\n                    required: true,\n                  },\n                  type: {\n                    title: '变量类型',\n                    type: 'string',\n                    enum: ['string', 'number', 'boolean'],\n                    default: 'string',\n                  },\n                  defaultValue: {\n                    title: '默认值',\n                    type: 'string',\n                  },\n                },\n              },\n            },\n          },\n        },\n      },\n      {\n        title: '结束',\n        type: 'End',\n        description: '自定义图片形式的图标',\n        icon: {\n          bgColor: '#DACFF3',\n        },\n        iconSvg: 'CustomImg',  // 图标图片组件\n        imgSrc: 'https://img.alicdn.com/tfs/TB17UtINiLaK1RjSZFxXXamPFXa-606-643.png', // 图片地址\n        settingSchema: {\n          type: 'object',\n          properties: {\n            output: {\n              title: '输出结果',\n              type: 'string',\n              widget: 'textarea',\n            },\n          },\n        },\n      },\n    ],\n  },\n  {\n    title: 'AI 能力',\n    type: '_group',\n    items: [\n      {\n        title: 'LLM',\n        type: 'LLM',\n        description: '调用大语言模型回答问题或者对自然语言进行处理',\n        icon: {\n          bgColor:'#FFACF8',\n        },\n        iconSvg: 'CustomImg',  // 图标图片组件\n        imgSrc: 'https://img.alicdn.com/imgextra/i2/O1CN01Lu3elm1XLSPBGqvPi_!!6000000002907-2-tps-200-200.png', // 图片\n        settingSchema: {\n          type: 'object',\n          properties: {\n            model: {\n              title: '模型选择',\n              type: 'string',\n              enum: ['gpt-3.5', 'gpt-4', 'claude-2', 'claude-3'],\n              default: 'gpt-3.5',\n            },\n            temperature: {\n              title: '温度',\n              type: 'number',\n              default: 0.7,\n              minimum: 0,\n              maximum: 1,\n              description: '控制输出的随机性，值越高输出越随机',\n            },\n            maxTokens: {\n              title: '最大Token数',\n              type: 'number',\n              default: 2000,\n              minimum: 1,\n              maximum: 4000,\n            },\n            systemPrompt: {\n              title: '系统提示词',\n              type: 'string',\n              widget: 'textarea',\n              description: '设置AI助手的角色和行为',\n            },\n            stopSequences: {\n              title: '停止序列',\n              type: 'array',\n              items: {\n                type: 'string',\n              },\n              description: '设置停止生成的关键词',\n            },\n          },\n        },\n      },\n      {\n        title: 'Prompt',\n        type: 'Prompt',\n        description: '通过精心设计提示词，提升大语言模型回答效果',\n        icon: {\n          type: 'icon-prompt',\n          bgColor: '#17B26A',\n        },\n        settingSchema: {\n          type: 'object',\n          properties: {\n            prompt: {\n              title: '提示词',\n              type: 'string',\n              widget: 'textarea',\n              default: '请帮我完成以下任务：',\n            },\n            variables: {\n              title: '变量替换',\n              type: 'array',\n              items: {\n                type: 'object',\n                properties: {\n                  name: {\n                    title: '变量名',\n                    type: 'string',\n                    required: true,\n                  },\n                  value: {\n                    title: '变量值',\n                    type: 'string',\n                  },\n                },\n              },\n            },\n            examples: {\n              title: '示例',\n              type: 'array',\n              items: {\n                type: 'object',\n                properties: {\n                  input: {\n                    title: '输入',\n                    type: 'string',\n                    widget: 'textarea',\n                  },\n                  output: {\n                    title: '输出',\n                    type: 'string',\n                    widget: 'textarea',\n                  },\n                },\n              },\n            },\n          },\n        },\n      },\n    ],\n  },\n  {\n    title: '数据处理',\n    type: '_group',\n    items: [\n      {\n        title: '知识库',\n        type: 'knowledge',\n        description: '允许你从知识库中查询与用户问题相关的文本内容',\n        icon: {\n          type: 'icon-knowledge',\n          bgColor: '#6172F3',\n        },\n        settingSchema: {\n          type: 'object',\n          properties: {\n            knowledgeBase: {\n              title: '知识库选择',\n              type: 'string',\n              enum: ['产品文档', '技术文档', '用户手册', 'API文档'],\n              default: '产品文档',\n            },\n            searchLimit: {\n              title: '检索限制',\n              type: 'number',\n              default: 5,\n              minimum: 1,\n              maximum: 10,\n            },\n            similarity: {\n              title: '相似度阈值',\n              type: 'number',\n              default: 0.7,\n              minimum: 0,\n              maximum: 1,\n              description: '设置检索结果的相似度阈值',\n            },\n            filters: {\n              title: '过滤条件',\n              type: 'array',\n              items: {\n                type: 'object',\n                properties: {\n                  field: {\n                    title: '字段',\n                    type: 'string',\n                    required: true,\n                  },\n                  operator: {\n                    title: '操作符',\n                    type: 'string',\n                    enum: ['equals', 'contains', 'greaterThan', 'lessThan'],\n                    default: 'equals',\n                  },\n                  value: {\n                    title: '值',\n                    type: 'string',\n                  },\n                },\n              },\n            },\n          },\n        },\n      },\n      {\n        title: '数据转换',\n        type: 'transform',\n        description: '对数据进行格式转换和处理',\n        icon: {\n          type: 'icon-function-add-line1',\n          bgColor: '#F79009',\n        },\n        settingSchema: {\n          type: 'object',\n          properties: {\n            inputType: {\n              title: '输入类型',\n              type: 'string',\n              enum: ['json', 'text', 'csv', 'xml'],\n              default: 'json',\n            },\n            outputType: {\n              title: '输出类型',\n              type: 'string',\n              enum: ['json', 'text', 'csv', 'xml'],\n              default: 'json',\n            },\n            mapping: {\n              title: '字段映射',\n              type: 'array',\n              items: {\n                type: 'object',\n                properties: {\n                  source: {\n                    title: '源字段',\n                    type: 'string',\n                    required: true,\n                  },\n                  target: {\n                    title: '目标字段',\n                    type: 'string',\n                    required: true,\n                  },\n                  transform: {\n                    title: '转换规则',\n                    type: 'string',\n                    widget: 'textarea',\n                  },\n                },\n              },\n            },\n          },\n        },\n      },\n    ],\n  },\n];"
  },
  {
    "path": "docs/xflow/demo/nodeSetting/index.tsx",
    "content": "import XFlow, { FlowProvider, useFlow } from '@xrenders/xflow';\nimport { Button } from 'antd';\n// import settings from './setting';\nimport React from 'react';\nimport { settings, nodes, edges } from \"./data\";\n\nexport const Flow = () => {\n  const { getNodes, getEdges, getViewport, toObject } = useFlow();\n\n  return (\n    <>\n      <Button type=\"primary\" onClick={() => {\n        console.info(\"打印节点数据\", getNodes())\n        console.info(\"打印边数据\", getEdges())\n        console.info(\"打印视口数据\", getViewport())\n        console.info(\"打印画布数据\", toObject())\n      }} style={{marginBottom:\"10px\"}}>打印画布数据</Button>\n      <XFlow\n        initialValues={{ nodes, edges }}\n        settings={settings as any}  // 节点配置\n        nodeSelector={{\n          showSearch: true,\n        }}\n        // layout=\"TB\"\n        globalConfig={{\n          nodePanel: {\n            width: '400px',\n            hideDesc:true\n          },\n          edge: {\n            hideEdgeAddBtn:true,\n          },\n          nodeView: {\n            hideTitleTips: true\n          },\n        }}\n      />\n    </>\n  )\n}\n\nexport default () => {\n  return (\n    <div style={{ height: '600px' }}>\n      <FlowProvider>\n        <Flow />\n      </FlowProvider>\n    </div>\n  );\n};\n"
  },
  {
    "path": "docs/xflow/demo/nodeSetting/setting.tsx",
    "content": "export default [\n  {\n    title: '开始',\n    type: 'Start',\n    hidden: true,\n    targetHandleHidden: true,\n    icon: {\n      type: 'icon-start',\n      bgColor: '#17B26A',\n    },\n    settingSchema: {\n      type: 'object',\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n        select: {\n          title: '变量二',\n          type: 'string',\n          widget: 'select',\n          props: {\n            options: [\n              { label: 'a', value: 'a' },\n              { label: 'b', value: 'b' },\n              { label: 'c', value: 'c' },\n            ],\n          },\n        },\n        radio1: {\n          title: '点击单选',\n          type: 'string',\n          widget: 'radio',\n          props: {\n            options: [\n              { label: '早', value: 'a' },\n              { label: '中', value: 'b' },\n              { label: '晚', value: 'c' }\n            ]\n          }\n        },\n        textarea1: {\n          title: '长文本',\n          type: 'string',\n          widget: 'textArea'\n        },\n        date1: {\n          title: '日期选择',\n          type: 'string',\n          widget: 'datePicker'\n        },\n        dateRange1: {\n          title: '日期范围',\n          type: 'range',\n          widget: 'dateRange'\n        },\n        time1: {\n          title: '时间选择',\n          type: 'string',\n          widget: 'timePicker'\n        },\n        timeRange1: {\n          title: '时间范围',\n          type: 'range',\n          widget: 'timeRange'\n        },\n      },\n    },\n  },\n  {\n    title: '结束',\n    type: 'End',\n    hidden: true,\n    sourceHandleHidden: true,\n    icon: {\n      type: 'icon-end',\n      bgColor: '#F79009',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n        select: {\n          title: '变量二',\n          type: 'string',\n          widget: 'select',\n          props: {\n            options: [\n              { label: 'a', value: 'a' },\n              { label: 'b', value: 'b' },\n              { label: 'c', value: 'c' },\n            ],\n          },\n        },\n      }\n    }\n  },\n  {\n    title: 'LLM',\n    type: 'LLM',\n    description: '调用大语言模型回答问题或者对自然语言进行处理',\n    icon: {\n      type: 'icon-model',\n      bgColor: '#6172F3',\n    },\n  },\n  {\n    title: 'Prompt',\n    type: 'Prompt',\n    description: '通过精心设计提示词，提升大语言模型回答效果',\n    icon: {\n      type: 'icon-prompt',\n      bgColor: '#17B26A',\n    },\n  },\n  {\n    title: '知识库',\n    type: 'knowledge',\n    description: '允许你从知识库中查询与用户问题相关的文本内容',\n    icon: {\n      type: 'icon-knowledge',\n      bgColor: '#6172F3',\n    },\n  },\n  {\n    title: 'Switch',\n    type: 'Switch',\n    description: '允许你根据 if/else 条件将 workflow 拆分成两个分支',\n    icon: {\n      type: 'icon-fenzhi',\n      bgColor: '#06AED4',\n    },\n  },\n  {\n    title: 'HSF',\n    type: 'hsf',\n    description: '允许通过 HSF 协议发送服务器请求',\n    icon: {\n      type: 'icon-hsf',\n      bgColor: '#875BF7',\n    },\n  },\n  {\n    title: 'Http',\n    type: 'http',\n    description: '允许通过 HTTP 协议发送服务器请求',\n    icon: {\n      type: 'icon-http',\n      bgColor: '#875BF7',\n    },\n  },\n  {\n    title: '代码执行',\n    type: 'Code',\n    description: '执行一段 Groovy 或 Python 或 NodeJS 代码实现自定义逻辑',\n    icon: {\n      type: 'icon-code',\n      bgColor: '#2E90FA',\n    },\n  },\n  {\n    title: '工具',\n    type: 'tool',\n    description: '允许使用工具能力',\n    icon: {\n      type: 'icon-gongju',\n      bgColor: '#2E90FA',\n    },\n  },\n  {\n    title: '工具',\n    type: '_group',\n    items: [\n      {\n        title: '代码执行',\n        type: 'Code',\n        description: '执行一段 Groovy 或 Python 或 NodeJS 代码实现自定义逻辑',\n        icon: {\n          type: 'icon-code',\n          bgColor: '#2E90FA',\n        },\n      },\n      {\n        title: '工具',\n        type: 'tool',\n        description: '允许使用工具能力',\n        icon: {\n          type: 'icon-gongju',\n          bgColor: '#2E90FA',\n        },\n      },\n    ],\n  },\n];\n"
  },
  {
    "path": "docs/xflow/demo/nodeWidget/index.less",
    "content": ".node-llm-style {\n  .custom-node-container{\n    background-color:  rgba(97, 113, 243,0.3);\n  }\n}\n"
  },
  {
    "path": "docs/xflow/demo/nodeWidget/index.tsx",
    "content": "import React from 'react';\nimport XFlow from '@xrenders/xflow';\nimport { Space, Tag, Typography, Card } from 'antd';\nimport settings from './setting';\nimport './index.less';\n\n// LLM 节点\nconst LLMNodeWidget = ({ data }) => {\n  const { model, temperature, maxTokens, systemPrompt } = data;\n  return (\n    <Card\n      size=\"small\"\n      bodyStyle={{ padding: '12px' }}\n    >\n      <Space direction=\"vertical\" size=\"small\" style={{ width: '100%' }}>\n        <div style={{ padding: '8px 12px', background: '#f9fafb', borderRadius: 6 }}>\n          <Typography.Text type=\"secondary\" style={{ fontSize: 12 }}>模型</Typography.Text>\n          <div style={{ marginTop: 4 }}>\n            <Tag color=\"blue\" style={{ margin: 0 }}>{model}</Tag>\n          </div>\n        </div>\n        <div style={{ padding: '8px 12px', background: '#f9fafb', borderRadius: 6 }}>\n          <Typography.Text type=\"secondary\" style={{ fontSize: 12 }}>参数</Typography.Text>\n          <div style={{ marginTop: 4 }}>\n            <Space>\n              <Tag style={{ margin: 0 }}>温度: {temperature}</Tag>\n              <Tag style={{ margin: 0 }}>最大Token: {maxTokens}</Tag>\n            </Space>\n          </div>\n        </div>\n        <div style={{ padding: '8px 12px', background: '#f9fafb', borderRadius: 6 }}>\n          <Typography.Text type=\"secondary\" style={{ fontSize: 12 }}>系统提示词</Typography.Text>\n          <div style={{ marginTop: 4 }}>\n            <Typography.Text style={{ fontSize: 12 }}>{systemPrompt}</Typography.Text>\n          </div>\n        </div>\n      </Space>\n    </Card>\n  );\n};\n\n// HTTP 请求节点\nconst HTTPNodeWidget = ({ data }) => {\n  const { method, url, headers, body } = data;\n  return (\n    <Card\n      size=\"small\"\n      bodyStyle={{ padding: '12px' }}\n    >\n      <Space direction=\"vertical\" size=\"small\" style={{ width: '100%' }}>\n        <div style={{ padding: '8px 12px', background: '#f9fafb', borderRadius: 6 }}>\n          <Typography.Text type=\"secondary\" style={{ fontSize: 12 }}>请求</Typography.Text>\n          <div style={{ marginTop: 4, display: 'flex', alignItems: 'center', gap: 8 }}>\n            <Tag color={method === 'GET' ? 'green' : 'blue'} style={{ margin: 0 }}>{method}</Tag>\n            <Typography.Text style={{ fontSize: 12 }} ellipsis={{tooltip:true}}>{url}</Typography.Text>\n          </div>\n        </div>\n        <div style={{ padding: '8px 12px', background: '#f9fafb', borderRadius: 6 }}>\n          <Typography.Text type=\"secondary\" style={{ fontSize: 12 }}>请求头</Typography.Text>\n          <div style={{ marginTop: 4 }}>\n            <Typography.Text style={{ fontSize: 12 }}>{JSON.stringify(headers)}</Typography.Text>\n          </div>\n        </div>\n        <div style={{ padding: '8px 12px', background: '#f9fafb', borderRadius: 6 }}>\n          <Typography.Text type=\"secondary\" style={{ fontSize: 12 }}>请求体</Typography.Text>\n          <div style={{ marginTop: 4 }}>\n            <Typography.Text style={{ fontSize: 12 }}>{JSON.stringify(body)}</Typography.Text>\n          </div>\n        </div>\n      </Space>\n    </Card>\n  );\n};\n\n// 问题分类器节点\nconst ClassifierNodeWidget = ({ data }) => {\n  const { categories = [], rules, defaultCategory } = data;\n  return (\n    <Card\n      size=\"small\"\n      bodyStyle={{ padding: '12px' }}\n    >\n      <Space direction=\"vertical\" size=\"small\" style={{ width: '100%' }}>\n        <div style={{ padding: '8px 12px', background: '#f9fafb', borderRadius: 6 }}>\n          <Typography.Text type=\"secondary\" style={{ fontSize: 12 }}>分类</Typography.Text>\n          <div style={{ marginTop: 4 }}>\n            <Space wrap>\n              {(categories || [])?.map(cat => (\n                <Tag\n                  key={cat}\n                  color={cat === defaultCategory ? 'purple' : 'default'}\n                  style={{ margin: 0 }}\n                >\n                  {cat}\n                </Tag>\n              ))}\n            </Space>\n          </div>\n        </div>\n        <div style={{ padding: '8px 12px', background: '#f9fafb', borderRadius: 6 }}>\n          <Typography.Text type=\"secondary\" style={{ fontSize: 12 }}>规则</Typography.Text>\n          <div style={{ marginTop: 4 }}>\n            <Typography.Text style={{ fontSize: 12 }}>{rules}</Typography.Text>\n          </div>\n        </div>\n      </Space>\n    </Card>\n  );\n};\n\nexport default () => {\n  const nodes = [\n    {\n      id: '1',\n      type: 'Start',\n      data: { input: '开始节点' },\n      position: { x: 10, y: 270 },\n      ports: [\n        { id: 'right', type: 'output', group: 'right' }\n      ]\n    },\n    {\n      id: '2',\n      type: 'LLM',\n      data: {\n        model: 'GPT-4',\n        temperature: 0.7,\n        maxTokens: 200,\n        systemPrompt: '你是一个专业的AI助手，请帮助用户解决问题。'\n      },\n      position: { x: 300, y: 140 },\n      ports: [\n        { id: 'left', type: 'input', group: 'left' },\n        { id: 'right', type: 'output', group: 'right' }\n      ]\n    },\n    {\n      id: '3',\n      type: 'HTTP',\n      data: {\n        method: 'POST',\n        url: 'https://api.example.com/process',\n        headers: \"{ 'Content-Type': 'application/json' }\",\n        body: \"{ 'key': 'value' }\"\n      },\n      position: { x: 600, y: 140 },\n      ports: [\n        { id: 'left', type: 'input', group: 'left' },\n        { id: 'right', type: 'output', group: 'right' }\n      ]\n    },\n    {\n      id: '4',\n      type: 'Classifier',\n      data: {\n        categories: ['技术问题', '业务问题', '其他'],\n        rules: '根据问题描述的关键词进行分类',\n        defaultCategory: '其他'\n      },\n      position: { x: 900, y: 140 },\n      ports: [\n        { id: 'left', type: 'input', group: 'left' },\n        { id: 'right', type: 'output', group: 'right' }\n      ]\n    },\n    {\n      id: '7',\n      type: 'End',\n      data: { input: '结束节点' },\n      position: { x: 1300, y: 270 },\n      ports: [\n        { id: 'left', type: 'input', group: 'left' }\n      ]\n    }\n  ];\n\n  const edges = [\n    { source: '1', target: '2', id: 'edge-1-2' },\n    { source: '2', target: '3', id: 'edge-2-3' },\n    { source: '3', target: '4', id: 'edge-3-4' },\n    { source: '4', target: '7', id: 'edge-4-5' },\n  ];\n\n  return (\n    <div style={{ height: '600px' }}>\n      <XFlow\n        initialValues={{ nodes, edges }}\n        settings={settings}\n        widgets={{\n          LLMNodeWidget,\n          HTTPNodeWidget,\n          ClassifierNodeWidget,\n        }}\n      />\n    </div>\n  );\n};\n"
  },
  {
    "path": "docs/xflow/demo/nodeWidget/setting.ts",
    "content": "const settings = [\n  {\n    type: 'Start',\n    title: '开始节点',\n    hidden: true,\n    targetHandleHidden: true,\n    icon: {\n      type: 'icon-start',\n      bgColor: '#17B26A',\n    },\n    settingSchema: {\n      type: 'object',\n      properties: {\n        input: {\n          type: 'string',\n          title: '输入',\n        },\n      },\n    },\n  },\n  {\n    type: 'LLM',\n    title: 'LLM 处理',\n    icon: {\n      type: 'icon-model',\n      bgColor: '#6172F3',\n    },\n    nodeWidget: 'LLMNodeWidget',\n    className: 'node-llm-style',\n    settingSchema: {\n      type: 'object',\n      properties: {\n        model: {\n          type: 'string',\n          title: '模型',\n          widget: 'select',\n          enum: ['GPT-4', 'GPT-3.5', 'Claude'],\n          default: 'GPT-4',\n        },\n        temperature: {\n          type: 'number',\n          title: '温度',\n          widget: 'slider',\n          minimum: 0,\n          maximum: 1,\n          default: 0.7,\n        },\n        maxTokens: {\n          type: 'number',\n          title: '最大Token',\n          default: 200,\n        },\n        systemPrompt: {\n          type: 'string',\n          title: '系统提示词',\n          default: '你是一个专业的AI助手，请帮助用户解决问题。',\n        },\n      },\n    },\n  },\n  {\n    type: 'HTTP',\n    title: 'HTTP请求',\n    icon: {\n      type: 'icon-http',\n      bgColor: '#875BF7',\n    },\n    nodeWidget: 'HTTPNodeWidget',\n    settingSchema: {\n      type: 'object',\n      properties: {\n        method: {\n          type: 'string',\n          title: '请求方法',\n          widget: 'select',\n          enum: ['GET', 'POST', 'PUT', 'DELETE'],\n          default: 'POST',\n        },\n        url: {\n          type: 'string',\n          title: '请求地址',\n          default: 'https://api.example.com/process',\n        },\n        headers: {\n          type: 'string',\n          title: '请求头',\n          default: JSON.stringify({ 'Content-Type': 'application/json' }),\n        },\n        body: {\n          type: 'string',\n          title: '请求体',\n          default: JSON.stringify({ key: 'value' }),\n        },\n      },\n    },\n  },\n  {\n    type: 'Classifier',\n    title: '问题分类',\n    icon: {\n      type: 'icon-gongju',\n      bgColor: '#2E90FA',\n    },\n    nodeWidget: 'ClassifierNodeWidget',\n    settingSchema: {\n      type: 'object',\n      properties: {\n        categories: {\n          type: 'array',\n          title: '分类',\n          widget: 'select',\n          enum: ['技术问题', '业务问题', '其他'],\n          default: ['技术问题', '业务问题', '其他'],\n        },\n        rules: {\n          type: 'string',\n          title: '规则',\n          default: '根据问题描述的关键词进行分类',\n        },\n        defaultCategory: {\n          type: 'string',\n          title: '默认分类',\n          widget: 'select',\n          enum: ['技术问题', '业务问题', '其他'],\n          default: '其他',\n        },\n      },\n    },\n  },\n  {\n    type: 'End',\n    title: '结束节点',\n    hidden: true,\n    sourceHandleHidden: true,\n    icon: {\n      type: 'icon-end',\n      bgColor: '#F79009',\n    },\n    settingSchema: {\n      type: 'object',\n      properties: {\n        input: {\n          type: 'string',\n          title: '输入',\n          widget: 'input',\n        },\n      },\n    },\n  },\n];\n\nexport default settings;\n"
  },
  {
    "path": "docs/xflow/demo/parallelNode/custome/index.tsx",
    "content": "import XFlow from '@xrenders/xflow';\nimport settings from './setting';\nimport React from 'react'\n\nconst CustomParallel = ({data,index}) => {\n  // data：为data.list循环数据中当前条件的item\n  // index：为data.list循环数据中当前条件的index\n  return <p style={{ wordWrap: 'break-word' }}>{data?.value}-{index}</p>;\n}\n\nexport default () => {\n  const nodes = [\n    {\n      id: 'mcelcsg6pinydoy7',\n      type: 'Parallel',\n      data: {\n        list: [\n          {\n            _id: 'id_30ds0x3evus7ogo2',\n            value: '事件1',\n            title: \"事件1\"\n          },\n          {\n            _id: 'id_m1l276eelcgn7s1p',\n            value: '事件2',\n            title: \"事件1\"\n          },\n        ],\n      },\n      position: {\n        x: -40,\n        y: 281.25,\n      },\n    },\n    {\n      id: '4m9tee00n819nyyy',\n      type: 'tool',\n      position: {\n        x: 400,\n        y: 227.5,\n      },\n    },\n    {\n      id: 'j0kufl0o4fca4ee9',\n      type: 'knowledge',\n\n      position: {\n        x: 312.5,\n        y: 438.75,\n      },\n\n    },\n    {\n      id: 'w4be9edh4bhdlokm',\n      type: 'Start',\n      position: {\n        x: -379.21875,\n        y: 348.75,\n      },\n    },\n    {\n      id: '3qloq2p1x3wcwbzg',\n      type: 'End',\n      position: {\n        x: 675,\n        y: 360,\n      },\n    },\n  ];\n\n  const edges = [\n    {\n      id: 'ky0eedrd6t2hqq81',\n      source: 'mcelcsg6pinydoy7',\n      target: '4m9tee00n819nyyy',\n      sourceHandle: 'id_30ds0x3evus7ogo2',\n    },\n    {\n      id: '7tm5a339lj94ugtn',\n      source: 'mcelcsg6pinydoy7',\n      target: 'j0kufl0o4fca4ee9',\n      sourceHandle: 'id_m1l276eelcgn7s1p',\n    },\n    {\n      type: 'buttonedge',\n      source: 'w4be9edh4bhdlokm',\n      target: 'mcelcsg6pinydoy7',\n      id: 'xy-edge__w4be9edh4bhdlokm-mcelcsg6pinydoy7',\n    },\n    {\n      type: 'buttonedge',\n      source: '4m9tee00n819nyyy',\n      target: '3qloq2p1x3wcwbzg',\n      id: 'xy-edge__4m9tee00n819nyyy-3qloq2p1x3wcwbzg',\n    },\n    {\n      type: 'buttonedge',\n      source: 'j0kufl0o4fca4ee9',\n      target: '3qloq2p1x3wcwbzg',\n      id: 'xy-edge__j0kufl0o4fca4ee9-3qloq2p1x3wcwbzg',\n    },\n  ];\n  return (\n    <div style={{ height: '600px' }}>\n      <XFlow\n        initialValues={{ nodes, edges }}\n        settings={settings}\n        nodeSelector={{\n          showSearch: true,\n        }}\n        widgets={{ CustomParallel }}\n      />\n    </div>\n  );\n};\n"
  },
  {
    "path": "docs/xflow/demo/parallelNode/custome/setting.tsx",
    "content": "export default [\n  {\n    title: '开始',\n    type: 'Start',\n    // hidden: true,\n    targetHandleHidden: true,\n    icon: {\n      type: 'icon-start',\n      bgColor: '#17B26A',\n    },\n    settingSchema: {\n      type: 'object',\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n        select: {\n          title: '变量二',\n          type: 'string',\n          widget: 'select',\n          props: {\n            options: [\n              { label: 'a', value: 'a' },\n              { label: 'b', value: 'b' },\n              { label: 'c', value: 'c' },\n            ],\n          },\n        },\n        radio1: {\n          title: '点击单选',\n          type: 'string',\n          widget: 'radio',\n          props: {\n            options: [\n              { label: '早', value: 'a' },\n              { label: '中', value: 'b' },\n              { label: '晚', value: 'c' }\n            ]\n          }\n        },\n        textarea1: {\n          title: '长文本',\n          type: 'string',\n          widget: 'textArea'\n        },\n        date1: {\n          title: '日期选择',\n          type: 'string',\n          widget: 'datePicker'\n        },\n        dateRange1: {\n          title: '日期范围',\n          type: 'range',\n          widget: 'dateRange'\n        },\n        time1: {\n          title: '时间选择',\n          type: 'string',\n          widget: 'timePicker'\n        },\n        timeRange1: {\n          title: '时间范围',\n          type: 'range',\n          widget: 'timeRange'\n        },\n      },\n    },\n  },\n  {\n    title: '结束',\n    type: 'End',\n    // hidden: true,\n    sourceHandleHidden: true,\n    icon: {\n      type: 'icon-end',\n      bgColor: '#F79009',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n        select: {\n          title: '变量二',\n          type: 'string',\n          widget: 'select',\n          props: {\n            options: [\n              { label: 'a', value: 'a' },\n              { label: 'b', value: 'b' },\n              { label: 'c', value: 'c' },\n            ],\n          },\n        },\n      }\n    }\n  },\n  {\n    title: 'Parallel',\n    type: 'Parallel',\n    description: '并行节点',\n    icon: {\n      type: 'icon-parallel',\n      bgColor: '#06AED4',\n    },\n    nodeWidget:\"CustomParallel\"\n  },\n  {\n    title: 'LLM',\n    type: 'LLM',\n    description: '调用大语言模型回答问题或者对自然语言进行处理',\n    icon: {\n      type: 'icon-model',\n      bgColor: '#6172F3',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n      }\n    }\n  },\n  {\n    title: 'Prompt',\n    type: 'Prompt',\n    description: '通过精心设计提示词，提升大语言模型回答效果',\n    icon: {\n      type: 'icon-prompt',\n      bgColor: '#17B26A',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n      }\n    }\n  },\n  {\n    title: '知识库',\n    type: 'knowledge',\n    description: '允许你从知识库中查询与用户问题相关的文本内容',\n    icon: {\n      type: 'icon-knowledge',\n      bgColor: '#6172F3',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n      }\n    }\n  },\n  {\n    title: 'Switch',\n    type: 'Switch',\n    description: '允许你根据 if/else 条件将 workflow 拆分成两个分支',\n    icon: {\n      type: 'icon-fenzhi',\n      bgColor: '#06AED4',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n      }\n    }\n  },\n  {\n    title: 'HSF',\n    type: 'hsf',\n    description: '允许通过 HSF 协议发送服务器请求',\n    icon: {\n      type: 'icon-hsf',\n      bgColor: '#875BF7',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n      }\n    }\n  },\n  {\n    title: 'Http',\n    type: 'http',\n    description: '允许通过 HTTP 协议发送服务器请求',\n    icon: {\n      type: 'icon-http',\n      bgColor: '#875BF7',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n      }\n    }\n  },\n  {\n    title: '代码执行',\n    type: 'Code',\n    description: '执行一段 Groovy 或 Python 或 NodeJS 代码实现自定义逻辑',\n    icon: {\n      type: 'icon-code',\n      bgColor: '#2E90FA',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n      }\n    }\n  },\n  {\n    title: '工具',\n    type: 'tool',\n    description: '允许使用工具能力',\n    icon: {\n      type: 'icon-gongju',\n      bgColor: '#2E90FA',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n      }\n    }\n  },\n  {\n    title: '工具',\n    type: '_group',\n    items: [\n      {\n        title: '代码执行',\n        type: 'Code',\n        description: '执行一段 Groovy 或 Python 或 NodeJS 代码实现自定义逻辑',\n        icon: {\n          type: 'icon-code',\n          bgColor: '#2E90FA',\n        },\n        settingSchema: {\n          type: \"object\",\n          properties: {\n            input: {\n              title: '变量一',\n              type: 'string',\n              widget: 'input',\n            },\n          }\n        }\n      },\n      {\n        title: '工具',\n        type: 'tool',\n        description: '允许使用工具能力',\n        icon: {\n          type: 'icon-gongju',\n          bgColor: '#2E90FA',\n        },\n        settingSchema: {\n          type: \"object\",\n          properties: {\n            input: {\n              title: '变量一',\n              type: 'string',\n              widget: 'input',\n            },\n          }\n        }\n      },\n    ],\n  },\n];\n"
  },
  {
    "path": "docs/xflow/demo/parallelNode/index.tsx",
    "content": "import XFlow from '@xrenders/xflow';\nimport settings from './setting';\nimport React from 'react'\n\nexport default () => {\n  const nodes = [\n    {\n      id: 'mcelcsg6pinydoy7',\n      type: 'Parallel',\n      data: {\n        list: [\n          {\n            _id: 'id_30ds0x3evus7ogo2',\n            value: '事件1',\n            title:\"标题1\"\n          },\n          {\n            _id: 'id_m1l276eelcgn7s1p',\n            value: '事件2',\n            title: \"标题2\"\n          },\n        ],\n      },\n      position: {\n        x: -40,\n        y: 281.25,\n      },\n    },\n    {\n      id: '4m9tee00n819nyyy',\n      type: 'tool',\n      position: {\n        x: 400,\n        y: 227.5,\n      },\n    },\n    {\n      id: 'j0kufl0o4fca4ee9',\n      type: 'knowledge',\n\n      position: {\n        x: 312.5,\n        y: 438.75,\n      },\n\n    },\n    {\n      id: 'w4be9edh4bhdlokm',\n      type: 'Start',\n      position: {\n        x: -379.21875,\n        y: 348.75,\n      },\n    },\n    {\n      id: '3qloq2p1x3wcwbzg',\n      type: 'End',\n      position: {\n        x: 675,\n        y: 360,\n      },\n    },\n  ];\n\n  const edges = [\n    {\n      id: 'ky0eedrd6t2hqq81',\n      source: 'mcelcsg6pinydoy7',\n      target: '4m9tee00n819nyyy',\n      sourceHandle: 'id_30ds0x3evus7ogo2',\n    },\n    {\n      id: '7tm5a339lj94ugtn',\n      source: 'mcelcsg6pinydoy7',\n      target: 'j0kufl0o4fca4ee9',\n      sourceHandle: 'id_m1l276eelcgn7s1p',\n    },\n    {\n      type: 'buttonedge',\n      source: 'w4be9edh4bhdlokm',\n      target: 'mcelcsg6pinydoy7',\n      id: 'xy-edge__w4be9edh4bhdlokm-mcelcsg6pinydoy7',\n    },\n    {\n      type: 'buttonedge',\n      source: '4m9tee00n819nyyy',\n      target: '3qloq2p1x3wcwbzg',\n      id: 'xy-edge__4m9tee00n819nyyy-3qloq2p1x3wcwbzg',\n    },\n    {\n      type: 'buttonedge',\n      source: 'j0kufl0o4fca4ee9',\n      target: '3qloq2p1x3wcwbzg',\n      id: 'xy-edge__j0kufl0o4fca4ee9-3qloq2p1x3wcwbzg',\n    },\n  ];\n  return (\n    <div style={{ height: '600px' }}>\n      <XFlow\n        initialValues={{ nodes, edges }}\n        settings={settings}\n        nodeSelector={{\n          showSearch: true,\n        }}\n      />\n    </div>\n  );\n};\n"
  },
  {
    "path": "docs/xflow/demo/parallelNode/setting.tsx",
    "content": "export default [\n  {\n    title: '开始',\n    type: 'Start',\n    // hidden: true,\n    targetHandleHidden: true,\n    icon: {\n      type: 'icon-start',\n      bgColor: '#17B26A',\n    },\n    settingSchema: {\n      type: 'object',\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n        select: {\n          title: '变量二',\n          type: 'string',\n          widget: 'select',\n          props: {\n            options: [\n              { label: 'a', value: 'a' },\n              { label: 'b', value: 'b' },\n              { label: 'c', value: 'c' },\n            ],\n          },\n        },\n        radio1: {\n          title: '点击单选',\n          type: 'string',\n          widget: 'radio',\n          props: {\n            options: [\n              { label: '早', value: 'a' },\n              { label: '中', value: 'b' },\n              { label: '晚', value: 'c' }\n            ]\n          }\n        },\n        textarea1: {\n          title: '长文本',\n          type: 'string',\n          widget: 'textArea'\n        },\n        date1: {\n          title: '日期选择',\n          type: 'string',\n          widget: 'datePicker'\n        },\n        dateRange1: {\n          title: '日期范围',\n          type: 'range',\n          widget: 'dateRange'\n        },\n        time1: {\n          title: '时间选择',\n          type: 'string',\n          widget: 'timePicker'\n        },\n        timeRange1: {\n          title: '时间范围',\n          type: 'range',\n          widget: 'timeRange'\n        },\n      },\n    },\n  },\n  {\n    title: '结束',\n    type: 'End',\n    // hidden: true,\n    sourceHandleHidden: true,\n    icon: {\n      type: 'icon-end',\n      bgColor: '#F79009',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n        select: {\n          title: '变量二',\n          type: 'string',\n          widget: 'select',\n          props: {\n            options: [\n              { label: 'a', value: 'a' },\n              { label: 'b', value: 'b' },\n              { label: 'c', value: 'c' },\n            ],\n          },\n        },\n      }\n    }\n  },\n  {\n    title: 'Parallel',\n    type: 'Parallel',\n    description: '并行节点',\n    icon: {\n      type: 'icon-parallel',\n      bgColor: '#06AED4',\n    },\n  },\n  {\n    title: 'LLM',\n    type: 'LLM',\n    description: '调用大语言模型回答问题或者对自然语言进行处理',\n    icon: {\n      type: 'icon-model',\n      bgColor: '#6172F3',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n      }\n    }\n  },\n  {\n    title: 'Prompt',\n    type: 'Prompt',\n    description: '通过精心设计提示词，提升大语言模型回答效果',\n    icon: {\n      type: 'icon-prompt',\n      bgColor: '#17B26A',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n      }\n    }\n  },\n  {\n    title: '知识库',\n    type: 'knowledge',\n    description: '允许你从知识库中查询与用户问题相关的文本内容',\n    icon: {\n      type: 'icon-knowledge',\n      bgColor: '#6172F3',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n      }\n    }\n  },\n  {\n    title: 'Switch',\n    type: 'Switch',\n    description: '允许你根据 if/else 条件将 workflow 拆分成两个分支',\n    icon: {\n      type: 'icon-fenzhi',\n      bgColor: '#06AED4',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n      }\n    }\n  },\n  {\n    title: 'HSF',\n    type: 'hsf',\n    description: '允许通过 HSF 协议发送服务器请求',\n    icon: {\n      type: 'icon-hsf',\n      bgColor: '#875BF7',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n      }\n    }\n  },\n  {\n    title: 'Http',\n    type: 'http',\n    description: '允许通过 HTTP 协议发送服务器请求',\n    icon: {\n      type: 'icon-http',\n      bgColor: '#875BF7',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n      }\n    }\n  },\n  {\n    title: '代码执行',\n    type: 'Code',\n    description: '执行一段 Groovy 或 Python 或 NodeJS 代码实现自定义逻辑',\n    icon: {\n      type: 'icon-code',\n      bgColor: '#2E90FA',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n      }\n    }\n  },\n  {\n    title: '工具',\n    type: 'tool',\n    description: '允许使用工具能力',\n    icon: {\n      type: 'icon-gongju',\n      bgColor: '#2E90FA',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n      }\n    }\n  },\n  {\n    title: '工具',\n    type: '_group',\n    items: [\n      {\n        title: '代码执行',\n        type: 'Code',\n        description: '执行一段 Groovy 或 Python 或 NodeJS 代码实现自定义逻辑',\n        icon: {\n          type: 'icon-code',\n          bgColor: '#2E90FA',\n        },\n        settingSchema: {\n          type: \"object\",\n          properties: {\n            input: {\n              title: '变量一',\n              type: 'string',\n              widget: 'input',\n            },\n          }\n        }\n      },\n      {\n        title: '工具',\n        type: 'tool',\n        description: '允许使用工具能力',\n        icon: {\n          type: 'icon-gongju',\n          bgColor: '#2E90FA',\n        },\n        settingSchema: {\n          type: \"object\",\n          properties: {\n            input: {\n              title: '变量一',\n              type: 'string',\n              widget: 'input',\n            },\n          }\n        }\n      },\n    ],\n  },\n];\n"
  },
  {
    "path": "docs/xflow/demo/quickStart/index.tsx",
    "content": "import React from 'react';\nimport XFlow from '@xrenders/xflow';\n\nexport default () => {\n  const nodeSettings = [\n    {\n      title: '开始',\n      type: 'Start',\n      hidden: true,\n      disabledShortcutDelete: true,\n      disabledShortcutCopy: true,\n      targetHandleHidden: true,\n      icon: {\n        type: 'icon-start',\n        bgColor: '#17B26A',\n      },\n      settingSchema: {\n        type: 'object',\n        properties: {\n          input: {\n            title: '变量一',\n            type: 'string',\n            widget: 'input',\n          },\n        },\n      },\n    },\n    {\n      title: '结束',\n      type: 'End',\n      hidden: true,\n      disabledShortcutDelete: true,\n      sourceHandleHidden: true,\n      icon: {\n        type: 'icon-end',\n        bgColor: '#F79009',\n      },\n      settingSchema: {\n        type: \"object\",\n        properties: {\n          input: {\n            title: '变量一',\n            type: 'string',\n            widget: 'input',\n          },\n        }\n      }\n    },\n    {\n      title: 'LLM',\n      type: 'LLM',\n      description: '调用大语言模型回答问题或者对自然语言进行处理',\n      icon: {\n        type: 'icon-model',\n        bgColor: '#6172F3',\n      },\n      settingSchema: {\n        type: 'object',\n        properties: {\n          model: {\n            title: '模型',\n            type: 'string',\n            enum: ['gpt-3.5', 'gpt-4'],\n            default: 'gpt-3.5'\n          },\n          temperature: {\n            title: '温度',\n            type: 'number',\n            default: 0.7,\n            minimum: 0,\n            maximum: 1\n          }\n        }\n      }\n    },\n    {\n      title: 'Prompt',\n      type: 'Prompt',\n      description: '通过精心设计提示词，提升大语言模型回答效果',\n      icon: {\n        type: 'icon-prompt',\n        bgColor: '#17B26A',\n      },\n    },\n    {\n      title: '知识库',\n      type: 'knowledge',\n      description: '允许你从知识库中查询与用户问题相关的文本内容',\n      icon: {\n        type: 'icon-knowledge',\n        bgColor: '#6172F3',\n      },\n    },\n  ];\n\n\n  const initialValues = {\n    nodes: [\n      {\n        id: 'start',\n        type: 'Start',\n        data: {\n          input: '开始节点'\n        },\n        position: {\n          x: 100,\n          y: 100\n        }\n      },\n      {\n        id: 'llm',\n        type: 'LLM',\n        data: {\n          model: 'gpt-3.5',\n          temperature: 0.7\n        },\n        position: {\n          x: 500,\n          y: 100\n        }\n      },\n      {\n        id: 'prompt',\n        type: 'Prompt',\n        data: {},\n        position: {\n          x: 900,\n          y: 100\n        }\n      },\n      {\n        id: 'end',\n        type: 'End',\n        data: {\n          input: '结束节点'\n        },\n        position: {\n          x: 1200,\n          y: 100\n        }\n      }\n    ],\n    edges: [\n      {\n        id: 'start-llm',\n        source: 'start',\n        target: 'llm'\n      },\n      {\n        id: 'llm-prompt',\n        source: 'llm',\n        target: 'prompt'\n      },\n      {\n        id: 'prompt-end',\n        source: 'prompt',\n        target: 'end'\n      }\n    ]\n  };\n\n\n  return (\n     <div style={{ height: '600px' }}>\n      <XFlow\n        settings={nodeSettings}\n        initialValues={initialValues}\n        iconFontUrl=\"//at.alicdn.com/t/a/font_4069358_caoh6qs1z9a.js\"\n      />\n     </div>\n  );\n}\n"
  },
  {
    "path": "docs/xflow/demo/switchNode/customSwitchNode/index.tsx",
    "content": "import XFlow from '@xrenders/xflow';\nimport settings from './setting';\nimport React from 'react';\n\nconst customWidget = ({ data, index }) => {\n  return <p style={{ wordWrap: 'break-word' }}>{data?.value}-{index}</p>;\n};\n\nexport default () => {\n  const nodes = [\n    {\n      type: 'Switch',\n      id: '2',\n      position: { x: 171.25, y: 218.75 },\n      data: { list:[{value:\"条件1\"}]}\n    },\n  ];\n\n  const edges = [];\n\n  return (\n    <div style={{ height: '600px' }}>\n      <XFlow\n        initialValues={{ nodes, edges }}\n        settings={settings}\n        nodeSelector={{\n          showSearch: true,\n        }}\n        widgets={{ customWidget }}\n      />\n    </div>\n  );\n};\n"
  },
  {
    "path": "docs/xflow/demo/switchNode/customSwitchNode/setting.tsx",
    "content": "export default [\n  {\n    title: 'Switch',\n    type: 'Switch',\n    description: '允许你根据 if/else 条件将 workflow 拆分成两个分支',\n    icon: {\n      type: 'icon-fenzhi',\n      bgColor: '#06AED4',\n    },\n   nodeWidget: \"customWidget\"   // 自定义节点面板渲染\n  },\n  {\n    title: '开始',\n    type: 'Start',\n    hidden: true,\n    targetHandleHidden: true,\n    icon: {\n      type: 'icon-start',\n      bgColor: '#17B26A',\n    },\n    settingSchema: {\n      type: 'object',\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n        select: {\n          title: '变量二',\n          type: 'string',\n          widget: 'select',\n          props: {\n            options: [\n              { label: 'a', value: 'a' },\n              { label: 'b', value: 'b' },\n              { label: 'c', value: 'c' },\n            ],\n          },\n        },\n        radio1: {\n          title: '点击单选',\n          type: 'string',\n          widget: 'radio',\n          props: {\n            options: [\n              { label: '早', value: 'a' },\n              { label: '中', value: 'b' },\n              { label: '晚', value: 'c' }\n            ]\n          }\n        },\n        textarea1: {\n          title: '长文本',\n          type: 'string',\n          widget: 'textArea'\n        },\n        date1: {\n          title: '日期选择',\n          type: 'string',\n          widget: 'datePicker'\n        },\n        dateRange1: {\n          title: '日期范围',\n          type: 'range',\n          widget: 'dateRange'\n        },\n        time1: {\n          title: '时间选择',\n          type: 'string',\n          widget: 'timePicker'\n        },\n        timeRange1: {\n          title: '时间范围',\n          type: 'range',\n          widget: 'timeRange'\n        },\n      },\n    },\n  },\n  {\n    title: '结束',\n    type: 'End',\n    hidden: true,\n    sourceHandleHidden: true,\n    icon: {\n      type: 'icon-end',\n      bgColor: '#F79009',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n        select: {\n          title: '变量二',\n          type: 'string',\n          widget: 'select',\n          props: {\n            options: [\n              { label: 'a', value: 'a' },\n              { label: 'b', value: 'b' },\n              { label: 'c', value: 'c' },\n            ],\n          },\n        },\n      }\n    }\n  },\n  {\n    title: 'Prompt',\n    type: 'Prompt',\n    description: '通过精心设计提示词，提升大语言模型回答效果',\n    icon: {\n      type: 'icon-prompt',\n      bgColor: '#17B26A',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '提示词',\n          type: 'string',\n          widget: 'textArea',\n        },\n      }\n    },\n  },\n  {\n    title: '知识库',\n    type: 'knowledge',\n    description: '允许你从知识库中查询与用户问题相关的文本内容',\n    icon: {\n      type: 'icon-knowledge',\n      bgColor: '#6172F3',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '知识库',\n          type: 'string',\n          widget: 'textArea',\n        },\n      }\n    },\n  },\n];\n"
  },
  {
    "path": "docs/xflow/demo/switchNode/index.tsx",
    "content": "import XFlow from '@xrenders/xflow';\nimport settings from './setting';\n\nexport default () => {\n  const nodes = [\n    {\n      id: 'hwqfyj734kgwljvt',\n      type: 'Start',\n      position: {\n        x: -159.0597703862493,\n        y: 323.4293111587479,\n      },\n    },\n    {\n      id: 'kshd2hp4vqm8ww19',\n      type: 'Switch',\n      position: {\n        x: 180.79626287562883,\n        y: 209.9170981759363,\n      },\n      data: {\n        list: [\n          {\n            _id: 'id_1nmzbhnbtv1se6sd', // 对应的 sourceHandle: 'id_1nmzbhnbtv1se6sd'的边\n            value: '条件1',\n            name: \"这里是条件一\",\n            title:\"默认title\"\n          },\n          {\n            value: '条件2',\n            _id: 'id_enxhwfca1ebn55td', // 对应sourceHandle: 'id_enxhwfca1ebn55td'的边\n            name:\"这里是条件一\"\n          },\n          {\n            value: '条件3',\n            _id: 'id_z1f7b93zwbx1xycu', // 对应sourceHandle: 'id_z1f7b93zwbx1xycu'的边\n            name:'这里是条件一'\n          },\n        ],\n        desc: '',\n      },\n    },\n    {\n      id: 'ql61j2tdli4xage0',\n      type: 'Prompt',\n      position: {\n        x: 578.75,\n        y: 176.25,\n      },\n    },\n    {\n      id: 'fab1j735q8iow6u3',\n      type: 'knowledge',\n      position: {\n        x: 577.5,\n        y: 318.75,\n      },\n    },\n    {\n      id: '9mc5i628mfrxdow0',\n      type: 'Prompt',\n      position: {\n        x: 603.75,\n        y: 422.5,\n      },\n    },\n    {\n      id: 'xgkajbbgs8cls8r1',\n      type: 'knowledge',\n      position: {\n        x: 932.5,\n        y: 556.25,\n      },\n    },\n    {\n      id: 'anmv2kcadqxj4k63',\n      type: 'Prompt',\n      position: {\n        x: 540,\n        y: 555,\n      },\n    },\n    {\n      id: '1obmsf5g1xfsypdj',\n      type: 'End',\n      position: {\n        x: 1359.5373712437117,\n        y: 359.3804592275014,\n      },\n    },\n  ];\n\n  const edges = [\n    {\n      id: '0lnn4hks9wnz0lnq',\n      source: 'hwqfyj734kgwljvt',\n      target: 'kshd2hp4vqm8ww19',\n    },\n    {\n      id: '5s6af77jbcqvzfod',\n      source: 'kshd2hp4vqm8ww19', // switch节点的边\n      target: 'ql61j2tdli4xage0',\n      sourceHandle: 'id_1nmzbhnbtv1se6sd', // 对应 _id为'id_1nmzbhnbtv1se6sd'的条件\n    },\n    {\n      id: '6tw709qgc3mtazrv',\n      source: 'kshd2hp4vqm8ww19', // switch节点的边\n      target: 'fab1j735q8iow6u3',\n      sourceHandle: 'id_enxhwfca1ebn55td', // 对应 _id为'id_enxhwfca1ebn55td'的条件\n    },\n    {\n      id: 'vzlvdo69ljcqegc8',\n      source: 'kshd2hp4vqm8ww19', // switch节点的边\n      target: '9mc5i628mfrxdow0',\n      sourceHandle: 'id_z1f7b93zwbx1xycu', // 对应 _id为'id_z1f7b93zwbx1xycu'的条件\n    },\n    {\n      id: 'j8p8fnr5au9k25pb',\n      source: 'kshd2hp4vqm8ww19', // switch节点的边\n      target: 'anmv2kcadqxj4k63',\n      sourceHandle: 'id_else', // id_else 对应默认路径ELSE\n    },\n    {\n      id: '0qfpkc9vcjdb31qp',\n      source: 'anmv2kcadqxj4k63',\n      target: 'xgkajbbgs8cls8r1',\n    },\n    {\n      id: '5h05o312rfbj4559',\n      source: 'ql61j2tdli4xage0',\n      target: '1obmsf5g1xfsypdj',\n    },\n    {\n      source: 'fab1j735q8iow6u3',\n      target: '1obmsf5g1xfsypdj',\n      id: 'xy-edge__fab1j735q8iow6u3-1obmsf5g1xfsypdj',\n    },\n    {\n      source: '9mc5i628mfrxdow0',\n      target: '1obmsf5g1xfsypdj',\n      id: 'xy-edge__9mc5i628mfrxdow0-1obmsf5g1xfsypdj',\n    },\n    {\n      source: 'xgkajbbgs8cls8r1',\n      target: '1obmsf5g1xfsypdj',\n      id: 'xy-edge__xgkajbbgs8cls8r1-1obmsf5g1xfsypdj',\n    },\n  ];\n  return (\n    <div style={{ height: '600px' }}>\n      <XFlow\n        initialValues={{ nodes, edges }}\n        settings={settings}\n        nodeSelector={{\n          showSearch: true,\n        }}\n        openColorfulMode={true}\n      />\n    </div>\n  );\n};\n"
  },
  {
    "path": "docs/xflow/demo/switchNode/setting.tsx",
    "content": "export default [\n  {\n    title: 'Switch',\n    type: 'Switch',\n    description: '允许你根据 if/else 条件将 workflow 拆分成两个分支',\n    icon: {\n      type: 'icon-fenzhi',\n      bgColor: '#06AED4',\n    },\n    switchExtra: {   // 条件节点额外属性配置\n      // hideElse: true,\n      valueKey: 'value',\n      titleKey: 'name'\n    }\n  },\n  {\n    title: '开始',\n    type: 'Start',\n    // hidden: true,\n    targetHandleHidden: true,\n    icon: {\n      type: 'icon-start',\n      bgColor: '#17B26A',\n    },\n    settingSchema: {\n      type: 'object',\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n        select: {\n          title: '变量二',\n          type: 'string',\n          widget: 'select',\n          props: {\n            options: [\n              { label: 'a', value: 'a' },\n              { label: 'b', value: 'b' },\n              { label: 'c', value: 'c' },\n            ],\n          },\n        },\n        radio1: {\n          title: '点击单选',\n          type: 'string',\n          widget: 'radio',\n          props: {\n            options: [\n              { label: '早', value: 'a' },\n              { label: '中', value: 'b' },\n              { label: '晚', value: 'c' }\n            ]\n          }\n        },\n        textarea1: {\n          title: '长文本',\n          type: 'string',\n          widget: 'textArea'\n        },\n        date1: {\n          title: '日期选择',\n          type: 'string',\n          widget: 'datePicker'\n        },\n        dateRange1: {\n          title: '日期范围',\n          type: 'range',\n          widget: 'dateRange'\n        },\n        time1: {\n          title: '时间选择',\n          type: 'string',\n          widget: 'timePicker'\n        },\n        timeRange1: {\n          title: '时间范围',\n          type: 'range',\n          widget: 'timeRange'\n        },\n      },\n    },\n  },\n  {\n    title: '结束',\n    type: 'End',\n    // hidden: true,\n    sourceHandleHidden: true,\n    icon: {\n      type: 'icon-end',\n      bgColor: '#F79009',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n        select: {\n          title: '变量二',\n          type: 'string',\n          widget: 'select',\n          props: {\n            options: [\n              { label: 'a', value: 'a' },\n              { label: 'b', value: 'b' },\n              { label: 'c', value: 'c' },\n            ],\n          },\n        },\n      }\n    }\n  },\n  {\n    title: 'Prompt',\n    type: 'Prompt',\n    description: '通过精心设计提示词，提升大语言模型回答效果',\n    icon: {\n      type: 'icon-prompt',\n      bgColor: '#17B26A',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '提示词',\n          type: 'string',\n          widget: 'textArea',\n        },\n      }\n    },\n  },\n  {\n    title: '知识库',\n    type: 'knowledge',\n    description: '允许你从知识库中查询与用户问题相关的文本内容',\n    icon: {\n      type: 'icon-knowledge',\n      bgColor: '#6172F3',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '知识库',\n          type: 'string',\n          widget: 'textArea',\n        },\n      }\n    },\n  },\n];\n"
  },
  {
    "path": "docs/xflow/index.md",
    "content": "---\norder: 0\ntitle: 介绍\nmobile: false\ngroup: \n  title: 使用教程\n  order: 0\n---\n\n<div style=\"display:flex;align-items:center;margin-bottom:24px\">\n  <img src=\"https://img.alicdn.com/tfs/TB17UtINiLaK1RjSZFxXXamPFXa-606-643.png\" alt=\"logo\" width=\"48px\"/>\n  <span style=\"font-size:30px;font-weight:600;display:inline-block;margin-left:12px\">XFlow</span>\n</div>\n<p style=\"display:flex;justify-content:space-between;width:440px\">\n  <a href=\"https://www.npmjs.com/package/@xrenders/xflow\" target=\"_blank\">\n    <img alt=\"npm\" src=\"https://img.shields.io/npm/v/@xrenders/xflow.svg?maxAge=3600&style=flat-square\">\n  </a>\n  <a href=\"https://npmjs.org/package/@xrenders/xflow\" target=\"_blank\">\n    <img alt=\"NPM downloads\" src=\"https://img.shields.io/npm/dm/@xrenders/xflow.svg?style=flat-square\">\n  </a>\n  <a href=\"https://npmjs.org/package/@xrenders/xflow\" target=\"_blank\">\n    <img alt=\"NPM all downloads\" src=\"https://img.shields.io/npm/dt/@xrenders/xflow.svg?style=flat-square\">\n  </a>\n  <a>\n    <img alt=\"PRs Welcome\" src=\"https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square\">\n  </a>\n</p>\n画布流程编排解决方案\n\n## 简介\n  XFlow 是一个基于 ReactFlow 底层能力和 Dify 交互体验实现的新一代开箱即用流程画布插件。它旨在为中后台项目提供统一、高效、易用的流程画布解决方案。\n\n## 特点\n\n* **性能卓越**：基于ReactFlow，提供高性能的图形渲染和交互能力。\n* **XRender 生态**：配置面板部分集成FormRender，以最小成本快速生成配置面板。\n* **快速上手**：提供丰富的预设配置和组件，大幅减少开发工作量。\n* **灵活定制**：支持自定义节点、配置面板和节点组件，满足各种复杂业务需求。\n* **操作便捷**：支持缩放、撤销/重做、自动布局、全屏展示等功能。\n\n## 效果预览\n\n<div class=\"preview-container\">\n  <div class=\"preview-card\">\n    <div class=\"preview-image\" >\n      <img src=\"https://img.alicdn.com/imgextra/i3/O1CN011ykg541qvcNWNDpni_!!6000000005558-0-tps-2094-1172.jpg\" alt=\"预览图1\" />\n    </div>\n    <div class=\"preview-title\">基础流程图</div>\n    <!-- <div class=\"preview-desc\">支持基础的节点和连线操作</div> -->\n  </div>\n  <div class=\"preview-card\">\n    <div class=\"preview-image\">\n      <img src=\"https://img.alicdn.com/imgextra/i1/O1CN015jBSE71Lbh3o3zk78_!!6000000001318-0-tps-2100-1170.jpg\" alt=\"预览图2\" />\n    </div>\n    <div class=\"preview-title\">自定义节点</div>\n    <!-- <div class=\"preview-desc\">支持自定义节点样式和交互</div> -->\n  </div>\n  <div class=\"preview-card\">\n    <div class=\"preview-image\" >\n      <img src=\"https://img.alicdn.com/imgextra/i2/O1CN01Y47YvN1s8GwCKZBtS_!!6000000005721-0-tps-2122-1198.jpg\" alt=\"预览图3\" />\n    </div>\n    <div class=\"preview-title\">最佳实践</div>\n    <!-- <div class=\"preview-desc\">支持节点配置面板和日志展示</div> -->\n  </div>\n</div>\n\n<style>\n.preview-container {\n  display: flex;\n  justify-content: space-between;\n  gap: 24px;\n  margin: 24px 0;\n}\n\n.preview-card {\n  flex: 1;\n  background: #fff;\n  border-radius: 8px;\n  overflow: hidden;\n  transition: all 0.3s ease;\n  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);\n}\n\n.preview-card:hover {\n  transform: translateY(-4px);\n  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);\n}\n\n.preview-image {\n  width: 100%;\n  height: 200px;\n  overflow: hidden;\n  cursor: pointer;\n}\n\n.preview-image img {\n  width: 100%;\n  height: 100%;\n  object-fit: cover;\n  transition: transform 0.3s ease;\n}\n\n.preview-image:hover img {\n  transform: scale(1.5);\n}\n\n.preview-title {\n  padding: 16px;\n  font-size: 16px;\n  font-weight: 500;\n  color: #1f1f1f;\n}\n\n.preview-desc {\n  padding: 0 16px 16px;\n  font-size: 14px;\n  color: #666;\n}\n\n.preview-modal {\n  position: fixed;\n  top: 0;\n  left: 0;\n  width: 100%;\n  height: 100%;\n  background: rgba(0, 0, 0, 0.85);\n  z-index: 9999;\n  display: none;\n  justify-content: center;\n  align-items: center;\n  opacity: 0;\n  transition: opacity 0.3s ease;\n}\n\n.preview-modal.active {\n  display: flex;\n  opacity: 1;\n}\n\n.preview-modal-content {\n  max-width: 90vw;\n  max-height: 90vh;\n}\n\n.preview-modal-content img {\n  max-width: 100%;\n  max-height: 90vh;\n  object-fit: contain;\n}\n</style>\n\n\n## 功能设计\n<div class=\"feature-table\">\n  <div class=\"feature-row\">\n    <div class=\"feature-name\">节点菜单</div>\n    <div class=\"feature-content\">\n      <div class=\"feature-desc\">\n        通过 JSON 配置的方式渲染画布节点菜单，支持节点组展示。每个节点包含标题、类型、描述和图标等信息，方便用户快速识别和使用不同类型的节点。\n      </div>\n      <div class=\"feature-image\">\n        <img src=\"https://img.alicdn.com/imgextra/i3/O1CN01rfAF9e28eMXX0fybx_!!6000000007957-0-tps-1896-1182.jpg\" alt=\"节点菜单\" >\n      </div>\n    </div>\n  </div>\n\n   <div class=\"feature-row\">\n    <div class=\"feature-name\">节点展示</div>\n    <div class=\"feature-content\">\n      <div class=\"feature-desc\">\n        <div class=\"desc-item\">默认情况下节点只会展示节点名称、描述信息。</div>\n        <div class=\"desc-item\">节点需要额外展示内容，可以通过配置 nodeWidget 组件来实现。</div>\n      </div>\n      <div class=\"feature-images\">\n       <div class=\"feature-image\" >\n          <img src=\"https://img.alicdn.com/imgextra/i3/O1CN01f6WFZg1D9HZ2ZjP1j_!!6000000000173-0-tps-1448-766.jpg\" alt=\"节点展示2\" />\n        </div>\n        <div class=\"feature-image\">\n          <img src=\"https://img.alicdn.com/imgextra/i2/O1CN01utg9VL1SBJYfXc6Na_!!6000000002208-0-tps-1892-1198.jpg\" alt=\"节点展示1\" />\n        </div>\n      </div>\n    </div>\n  </div>\n\n  <div class=\"feature-row\">\n    <div class=\"feature-name\">节点配置</div>\n    <div class=\"feature-content\">\n      <div class=\"feature-desc\">\n        提供 FormRender Schema 协议配置和自定义配置组件两种方式，支持节点数据的灵活配置。通过可视化界面，用户可以轻松设置节点参数和属性。\n      </div>\n      <div class=\"feature-images\">\n         <div class=\"feature-image node-show-img\">\n          <img src=\"https://img.alicdn.com/imgextra/i1/O1CN01ycuxbe1BsVROZAleA_!!6000000000001-0-tps-3102-1184.jpg\" alt=\"节点展示1\" />\n        </div>\n       <div class=\"feature-image \" >\n          <img src=\"https://img.alicdn.com/imgextra/i4/O1CN01dn5UGS1sXSikBsLnb_!!6000000005776-0-tps-606-672.jpg\" alt=\"节点展示2\" />\n        </div>\n        <div class=\"feature-image\">\n          <img src=\"https://img.alicdn.com/imgextra/i4/O1CN01Ia04jk1MznWGmzw4S_!!6000000001506-0-tps-604-340.jpg\" alt=\"节点展示1\" />\n        </div>\n      </div>\n    </div>\n  </div>\n\n  <div class=\"feature-row\">\n    <div class=\"feature-name\">内置节点</div>\n    <div class=\"feature-content\">\n      <div class=\"feature-desc\">\n        内置条件节点和并行节点，支持多个连接点。条件节点用于流程分支控制，并行节点用于并发任务处理，无需额外开发即可使用。\n        内置注释节点，方便添加节点注释。\n      </div>\n        <div class=\"feature-image node-show-img\" >\n        <img src=\"https://img.alicdn.com/imgextra/i3/O1CN01WKpbb01wbMYckbyOr_!!6000000006326-0-tps-3158-1234.jpg\" alt=\"内置节点\" />\n      </div>\n    </div>\n  </div>\n\n   <div class=\"feature-row\">\n    <div class=\"feature-name\">节点功能</div>\n    <div class=\"feature-content\">\n      <div class=\"feature-desc\">\n        支持节点的复制、删除、粘贴功能\n      </div>\n        <div class=\"feature-image\" >\n        <img src=\"https://img.alicdn.com/imgextra/i4/O1CN01UsgKOf1GoS0r7L6PA_!!6000000000669-0-tps-916-596.jpg\" alt=\"内置节点\" />\n      </div>\n    </div>\n  </div>\n\n   <div class=\"feature-row\">\n    <div class=\"feature-name\">节点状态和节点日志</div>\n    <div class=\"feature-content\">\n      <div class=\"feature-desc\">\n        支持展示节点的状态，自定义节点状态，以及显示节点的运行日志\n      </div>\n        <div class=\"feature-image\" >\n        <img src=\"https://img.alicdn.com/imgextra/i1/O1CN01m3yNGv1rpx5x3GKAQ_!!6000000005681-0-tps-2112-1192.jpg\" alt=\"内置节点\" />\n      </div>\n    </div>\n  </div>\n\n  <div class=\"feature-row\">\n    <div class=\"feature-name\">画布操作</div>\n    <div class=\"feature-content\">\n      <div class=\"feature-desc\">\n        支持画布缩放、撤销/重置、节点添加、鼠标模式切换、节点整理、全屏展示、画布快捷键等功能。提供丰富的画布操作工具，提升用户体验。\n      </div>\n       <div class=\"feature-image node-show-img\">\n        <img src=\"https://img.alicdn.com/imgextra/i3/O1CN01Pcb1wd1RKe0hVF7nG_!!6000000002093-0-tps-3172-1234.jpg\" alt=\"画布操作\" />\n      </div>\n    </div>\n  </div>\n</div>\n\n<style>\n.feature-table {\n  margin: 24px 0;\n  border: 1px solid #e8e8e8;\n  border-radius: 8px;\n  overflow: hidden;\n}\n\n.feature-row {\n  display: flex;\n  border-bottom: 1px solid #e8e8e8;\n}\n\n.feature-row:last-child {\n  border-bottom: none;\n}\n\n.feature-name {\n  width: 200px;\n  padding: 24px;\n  background: #fafafa;\n  font-weight: 500;\n  color: #1f1f1f;\n  display: flex;\n  align-items: center;\n}\n\n.feature-content {\n  flex: 1;\n  padding: 24px;\n  display: flex;\n  flex-direction: column;\n  gap: 24px;\n  align-items: flex-start;\n}\n\n.feature-images {\n  display: flex;\n  gap: 24px;\n  width: 100%;\n  justify-content: flex-start;\n  flex-wrap: wrap;\n}\n\n.feature-image {\n  border-radius: 4px;\n  overflow: hidden;\n  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);\n  width: 400px;\n  /* height: 300px; */\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  /* background: #fafafa; */\n}\n\n.node-show-img{\n   width: 800px;\n}\n\n.feature-image img {\n  max-width: 100%;\n  max-height: 100%;\n  width: auto;\n  height: auto;\n  object-fit: contain;\n  transition: transform 0.3s ease;\n}\n\n.feature-image:hover img {\n  transform: scale(1.05);\n}\n\n.feature-desc {\n  color: #666;\n  line-height: 1.6;\n  width: 100%;\n}\n\n.desc-item {\n  margin-bottom: 8px;\n}\n\n.desc-item:last-child {\n  margin-bottom: 0;\n}\n</style>\n\n\n"
  },
  {
    "path": "docs/xflow/layout.md",
    "content": "---\norder: 6\ntitle: '画布布局'\nmobile: false\ngroup: \n  title: 基础用法\n  order: 1\n---\n# 布局\n\nXFlow 提供了两种内置的布局方式：左右布局（LR）和上下布局（TB）。通过设置 `layout` 属性，可以自动调整节点的位置，使流程图更加美观。\n\n## 布局类型\n\n### 左右布局（LR）\n\n左右布局将节点按照从左到右的顺序排列，适合展示线性流程或分支较少的流程。\n\n```js\nimport React from 'react';\nimport XFlow from '@xrenders/xflow';\n\nconst Demo = () => {\n  return (\n    <div style={{ width: '100%', height: '600px' }}>\n      <XFlow\n        settings={settings}\n        initialValues={initialValues}\n        layout=\"LR\"  // 左右布局\n      />\n    </div>\n  );\n};\n```\n\n左右布局的特点:\n1. 节点按照从左到右的顺序排列\n2. 适合展示线性流程\n3. 分支节点会垂直排列\n4. 连线更加清晰直观\n\n### 上下布局（TB）\n\n上下布局将节点按照从上到下的顺序排列，适合展示层级关系或分支较多的流程。\n\n```js\nimport React from 'react';\nimport XFlow from '@xrenders/xflow';\n\nconst Demo = () => {\n  return (\n    <div style={{ width: '100%', height: '800px' }}>\n      <XFlow\n        settings={settings}\n        initialValues={initialValues}\n        layout=\"TB\"  // 上下布局\n      />\n    </div>\n  );\n};\n```\n\n上下布局的特点：\n1. 节点按照从上到下的顺序排列\n2. 适合展示层级关系\n3. 分支节点会水平排列\n4. 可以更好地利用垂直空间\n\n## 布局示例\n\n### LR（左右布局）\n\n<code src=\"./demo/layout/LR/index.tsx\"></code>\n\n这个示例展示了一个智能客服流程，包含以下特点：\n1. 使用左右布局，清晰展示流程走向\n2. 包含多个并行处理分支\n3. 展示了不同类型节点的组合使用\n4. 节点间距合理，连线清晰\n\n### TB（上下布局）\n\n<code src=\"./demo/layout/TB/index.tsx\"></code>\n\n这个示例展示了一个数据分析流程，包含以下特点：\n1. 使用上下布局，突出层级关系\n2. 包含数据转换和处理节点\n3. 展示了复杂的分支和合并\n4. 充分利用垂直空间\n"
  },
  {
    "path": "docs/xflow/log.md",
    "content": "---\norder: 7\ntitle: '节点日志和节点状态'\nmobile: false\ngroup: \n  title: 高级用法\n  order: 2\n---\n# 节点日志和节点状态\n\n\n## 节点状态\n 可配置不同状态下节点的边框颜色。通过`data._status`属性配置，比如：\n ```js\n   const nodes=[\n     {\n      id: 'w4be9edh4bhdlokm',\n      type: 'Start',\n      position: {\n        x: -379.21875,\n        y: 348.75,\n      },\n      data: {\n        _status: \"success\" // 状态配置，可自定义，可使用内置状态\n      },\n    },\n   ]\n ```\n - **内置状态**: 内置`success|error|warning`三种状态,对应颜色如下:\n  ```js\n  [\n    { value: 'success', color: '#52c41a',name: '成功'},\n    { value: 'error',color: '#ff4d4f',name: '失败'},\n    {value: 'warning',color: '#faad14',name: '告警'}\n  ]\n  ```\n - **自定义状态**:可通过`globalConfig.nodeView.status`自定义节点状态，同名可覆盖内置状态颜色。\n    ```js\n      globalConfig={{\n          nodeView: {\n            status: [\n              {\n                value: 'custom-error',  // 状态标识\n                color: 'red',   // 颜色值\n                name: '自定义失败状态',  // 状态名称\n              },\n              {\n                value: 'success',\n                color: 'green',\n                name: '自定义成功状态（覆盖内置成功状态）',\n              },\n            ],\n          }\n        }}\n   ```\n\n<code src=\"./demo/log/index.tsx\"></code>\n\n\n## 节点日志面板\n\n点击有状态的节点，会出现两个面板，左边为节点配置面板，右边为节点日志面板。如果不想使用日志面板，可以设置`logPanel.enable`属性为`false`关闭全局日志面板，默认会开启日志面板。\n\n日志面板全局配置参数如下：\n```js\n       <XFlow\n        widgets={{ CustomLogPanel }}\n        logPanel={{// 日志面板配置参数\n          logList, // 日志面板数据\n          loading, // 日志面板loading\n          logWidget:'CustomLogPanel'// 自定义日志面板\n          tabsProps,// 日志面板，详情和追踪选项卡切换组件,antd的tabs配置透传\n          enable: true, // 是否启用日志面板，默认为true，设置为false时全局禁用日志面板\n        }}\n```\n### 内置节点日志面板\n\n内置节点日志面板的数据来源于`logPanel.logList`,`logList`需要遵守以下数据格式,日志面板的追踪tab页默认只展示所有有状态的节点，即：`data._status`值存在的节点。\n```js\n Array< {\n  statusPanel?: {\n    status?: Array<{ label: string; value?: string; isBadge?: boolean }>; // isBadge是否为badge形式显示状态\n    extra?: string | ReactNode;\n  };\n  codePanel?: Array<{ title: string; code: string }>;\n  nodeId: string;// 节点ID\n  groupTitle:string;// 当有多个相同的nodeId，每个板块的分组名称\n  showDetailLogWidget:boolean;// 是否展示detailLogWidget组件\n  _status:string|number;  // 当前log的状态，如果没有，则以data._status为准\n}>; \n   \n```\n<code src=\"./demo/log/buildIn-log/index.tsx\"></code>\n\n\n### 自定义节点日志面板\n\n自定义组件`logPanel?.logList`作为`logList`透传。\n\n```js\nconst CustomLogPanel = ({ logList, node }) => {\n  console.log('自定义组件', logList, node);\n  return <p>自定义组件:{node?.id}</p>;\n};\n\n```\n\n<code src=\"./demo/log/custom-log/index.tsx\"></code>\n\n"
  },
  {
    "path": "docs/xflow/nodeBuildIn.md",
    "content": "---\norder: 5\ntitle: '内置节点'\nmobile: false\ngroup: \n  title: 高级用法\n  order: 2\n---\n# 内置节点\n\n## 条件内置节点\n内置条件节点，可以直接设置type为`Switch`使用。\n\n条件节点的数据格式为`data:{list:[{value:\"条件1\",_id:\"id_${随机数}\"}]}`，`_id`为边数据的`sourceHandle`，以便条件和边一一对应。\n有条件参数value(可以通过自定义条件节点配置面板更改配置参数)的放入list数组里面作为`IF或者ELIF`渲染，没有条件参数的，为默认执行路径`ELSE`,`ELSE`路径的固定`_id`为`id_else`,即边数据的`sourceHandle`必须为`id_else`才能链接`ELSE`链接头，比如：\n```js\n {\n      id: 'j8p8fnr5au9k25pb',\n      source: 'kshd2hp4vqm8ww19',\n      target: 'anmv2kcadqxj4k63',\n      sourceHandle: 'id_else', // else路径的边\n }\n```\n\n条件节点的每个连接头只能连接一个节点，不能连接多个节点，如果要更换节点，可以通过删除已连接节点或者在连接线上新增节点的方式更换目标节点。\n\n<code src=\"./demo/switchNode/index.tsx\"></code>\n\n\n## 自定义条件节点\n 可以通过`nodeWidget`自定义节点面板的渲染，也可以通过`settingSchema`和`settingWidget`自定义在弹窗中展示的业务配置组件。\n```js\n  const customWidget = ({ data, index }) => {\n  // data：为data.list循环数据中当前条件的item    \n  // index：为data.list循环数据中当前条件的index\n  return <p style={{ wordWrap: 'break-word' }}>{data?.value}-{index}</p>;\n};\n\n```\n<code src=\"./demo/switchNode/customSwitchNode/index.tsx\"></code>\n\n## 并行节点\n  内置并行节点，可以直接设置type为`Parallel`使用，并行节点的数据格式为`data:{list:[{title:\"事件一\",value:\"值1\",_id:\"id_${随机数}\"}]}`,`_id`为边数据的`sourceHandle`，以便条件和边一一对应。\n\n\n  并行节点的每个连接头只能连接一个节点，不能连接多个节点，如果要更换节点，可以通过删除已连接节点或者在连接线上新增节点的方式更换目标节点。\n\n<code src=\"./demo/parallelNode/index.tsx\"></code>\n\n## 自定义并行节点\n 可通过nodeWidget自定义并行节点面板的渲染，也可以通过settingSchema和settingWidget自定义在弹窗中展示的业务配置组件。\n ```js\n const CustomParallel = ({data,index}) => {\n  // data：为data.list循环数据中当前条件的item\n  // index：为data.list循环数据中当前条件的index\n  return <p style={{ wordWrap: 'break-word' }}>{data?.value}-{index}</p>;\n}\n```\n<code src=\"./demo/parallelNode/custome/index.tsx\"></code>\n\n\n\n"
  },
  {
    "path": "docs/xflow/nodeSetting.md",
    "content": "---\norder: 2\ntitle: 节点菜单配置\nmobile: false\ngroup: \n  title: 基础用法\n  order: 1\n---\n\n# 节点菜单配置\n\n节点菜单由右下角工具栏点出，展示画布面板的所有节点，可直接点击节点菜单面板中的节点，将节点添加进画布中。节点菜单支持分组展示，可以为不同类型的节点创建不同的分组，使界面更加清晰。\n\n## 配置说明\n\n### 1. 分组配置\n\n节点菜单支持分组展示，每个分组包含以下配置：\n\n```typescript\ninterface GroupConfig {\n  title: string;      // 分组名称\n  type: '_group';     // 分组类型，固定为 '_group'\n  items: NodeConfig[]; // 分组下的节点配置列表\n}\n```\n\n<div class=\"feature-image\">\n  <img src=\"https://img.alicdn.com/imgextra/i1/O1CN017qDeW21vUf9BGe0JI_!!6000000006176-2-tps-734-656.png\" alt=\"节点菜单分组展示\" />\n</div>\n\n### 2. 节点配置\n\n每个节点可以配置以下属性：\n\n```typescript\ninterface NodeConfig {\n  title: string;           // 节点名称\n  type: string;           // 节点类型\n  description?: string;   // 节点描述\n  icon?: {               // 节点图标配置\n    type: string;        // 图标类型\n    bgColor: string;     // 图标背景色\n  };\n  hideDesc?: boolean;    // 是否隐藏节点描述\n  nodePanel?: {         // 自定义节点的面板配置\n    width?: number;     // 面板宽度\n  };\n  settingSchema?: {     // 节点的业务配置信息\n    type: string;\n    className?: string;\n    properties: Record<string, any>;\n  };\n}\n```\n\n### 3. 节点图标配置\n\n节点的图标配置目前支持两种形式：\n\n1. [iconfont](https://www.iconfont.cn/) 的图标\n2. 自定义图标组件展示。通过这种方式可支持图标的 SVG 形式、图片形式、其他自定义样式。\n\n#### 3.1 iconfont 图标的用法\n\n1. 首先在 [iconfont](https://www.iconfont.cn/) 官网中选择图标后添加到项目中，在项目中找到图标，复制图标的代码，以及生成图标库的 Symbol 链接。\n\n <div class=\"feature-image\">\n  <img src=\"https://img.alicdn.com/imgextra/i3/O1CN013d3z1F1emfFXWFSiy_!!6000000003914-0-tps-2154-986.jpg\" alt=\"iconfont图标配置\" />\n</div>\n\n2. 在 iconFontUrl 属性中引入图标库的 Symbol 链接：\n\n```js\n<XFlow\n  settings={nodeSettings}\n  initialValues={initialValues}\n  iconFontUrl=\"//at.alicdn.com/t/a/font_4069358_caoh6qs1z9a.js\"\n/>\n```\n\n3. 然后在节点配置中配置 icon 属性，type 为 复制的图标的代码，bgColor 为图标的背景色：\n\n```js\n{\n  title: '结束',\n  type: 'End',\n  description: '流程结束节点，用于标记流程的终点',\n  icon: {\n    type: 'icon-xiaoxi',   // 图标类型\n    bgColor: '#F79009',    // 图标背景色\n  },\n  settingSchema: {\n    type: 'object',\n    properties: {\n      output: {\n        title: '输出结果',\n        type: 'string',\n        widget: 'textarea',\n      },\n    },\n  },\n}\n```\n\n#### 3.2 自定义图标组件\n\n   比如实现图标的 SVG 形式。首先创建SVG组件如下：\n\n  ```js\nconst CustomSvg = ({ setting }) => {\n  console.log('接收参数node setting', setting);\n  return (\n    <svg viewBox=\"0 0 1024 1024\" width=\"1em\" height=\"1em\" fill=\"currentColor\">\n      <title>Panda icon</title>\n      <path\n      d=\"M99.096 315.634s-82.58-64.032-82.58-132.13c0-66.064 33.032-165.162 148.646-148.646 83.37 11.91 99.096 165.162 99.096 165.162l-165.162 115.614zM924.906 315.634s82.58-64.032 82.58-132.13c0-66.064-33.032-165.162-148.646-148.646-83.37 11.91-99.096 165.162-99.096 165.162l165.162 115.614z\"\n      fill=\"#6B676E\"\n    />\n    <path\n      d=\"M1024 561.548c0 264.526-229.23 429.42-512.002 429.42S0 826.076 0 561.548 283.96 66.064 512.002 66.064 1024 297.022 1024 561.548z\"\n      fill=\"#FFEBD2\"\n    />\n    <path\n      d=\"M330.324 842.126c0 82.096 81.34 148.646 181.678 148.646s181.678-66.55 181.678-148.646H330.324z\"\n      fill=\"#E9D7C3\"\n    />\n    <path\n      d=\"M644.13 611.098C594.582 528.516 561.55 512 512.002 512c-49.548 0-82.58 16.516-132.13 99.096-42.488 70.814-78.73 211.264-49.548 247.742 66.064 82.58 165.162 33.032 181.678 33.032 16.516 0 115.614 49.548 181.678-33.032 29.18-36.476-7.064-176.93-49.55-247.74z\"\n      fill=\"#FFFFFF\"\n    />\n    <path\n      d=\"M611.098 495.484c0-45.608 36.974-82.58 82.58-82.58 49.548 0 198.194 99.098 198.194 165.162s-79.934 144.904-148.646 99.096c-49.548-33.032-132.128-148.646-132.128-181.678zM412.904 495.484c0-45.608-36.974-82.58-82.58-82.58-49.548 0-198.194 99.098-198.194 165.162s79.934 144.904 148.646 99.096c49.548-33.032 132.128-148.646 132.128-181.678z\"\n      fill=\"#6B676E\"\n    />\n    <path\n      d=\"M512.002 726.622c-30.06 0-115.614 5.668-115.614 33.032 0 49.638 105.484 85.24 115.614 82.58 10.128 2.66 115.614-32.944 115.614-82.58-0.002-27.366-85.556-33.032-115.614-33.032z\"\n      fill=\"#464655\"\n    />\n    <path\n      d=\"M330.324 495.484m-33.032 0a33.032 33.032 0 1 0 66.064 0 33.032 33.032 0 1 0-66.064 0Z\"\n      fill=\"#464655\"\n    />\n    <path\n      d=\"M693.678 495.484m-33.032 0a33.032 33.032 0 1 0 66.064 0 33.032 33.032 0 1 0-66.064 0Z\"\n      fill=\"#464655\"\n    />\n  </svg>    \n)};\n\nexport default CustomSvg;\n\n  ```\n\n  然后在全局widgets中引入SVG组件\n\n  ```js\n  <XFlow\n        settings={settings}\n        initialValues={initialValues}\n        widgets={{CustomSvg}} // 引入SVG组件\n   /> \n  ```\n\n  最后在节点配置`settings`中配置`iconSvg`属性\n\n  ```js\n   {\n    title: '流程控制',\n    type: '_group',\n    items: [\n      {\n        title: '开始',\n        type: 'Start',\n        description: '流程开始节点，用于标记流程的起点',\n        icon: {\n          bgColor: '#17B26A', // icon背景颜色\n        },\n        iconSvg: 'CustomSvg',  // 设置SVG组件\n      },\n    ],\n  },\n  ```\n\n## 完整示例\n\n <code src=\"./demo/nodeSetting/fullDemo/index.tsx\"></code>\n\n## 注意事项\n\n1. 节点类型（type）必须唯一，用于标识不同类型的节点\n2. 图标配置支持自定义图标类型和背景色，或者使用图标的SVG格式\n3. 节点配置面板（settingSchema）需要符合 FormRender 的协议规范\n4. 可以通过 hideDesc 属性控制节点描述的显示/隐藏\n\n<style>\n\n.feature-image img {\n  width: 800px;\n  height: auto;\n}\n</style>\n"
  },
  {
    "path": "docs/xflow/question.md",
    "content": "---\norder: 1\ntitle: '常见问题'\nmobile: false\ngroup: \n  title: '其他'\n  order: 5\n---\n# 常见问题\n\n### 1. XFlow 支持哪些功能？\n\nXFlow 提供了丰富的功能，包括节点和边的管理、自动布局、实时数据更新、可自定义的节点和边类型等。具体功能可以参考 [XFlow 文档](./index.md)。\n\n### 2. 如何自定义节点展示？\n\n当默认的节点内容展示不满足要求时，可以通过 `nodeWidget` 进行自定义渲染。具体步骤请参考 [自定义节点展示文档](./custom-node-view.md)。\n\n### 3. 如何处理节点的状态变化？\n\n使用 `useNodes` 钩子可以实时监听节点的状态变化，并在状态变化时执行相应的操作。具体用法请参考 [FlowProvider 文档](./FlowProvider.md)。\n\n### 4. 如何进行节点的自动布局？\n\nXFlow 提供了 `runAutoLayout` 方法来实现节点的自动布局。你可以在需要时调用该方法来整理画布上的节点。\n\n### 5. 如何报告 bug？\n\n如果你在使用 XFlow 时遇到问题，建议你在 GitHub 上提问。请遵循以下步骤以确保你的问题得到有效解答：\n\n1. **搜索已有问题**：在提问之前，先在 [GitHub Issues](https://github.com/alibaba/x-render/issues) 中搜索是否已有类似的问题被提出和解决。\n2. **提供详细信息**：\n   - **描述问题**：清晰地描述你遇到的问题，包括你期望的行为和实际的行为。\n   - **重现步骤**：提供重现问题的最小demo，尽量详细，以便其他人能够复现你的问题。\n   - **环境信息**：说明你的开发环境，包括操作系统、浏览器版本、XFlow 版本等。\n   - **代码示例**：如果可能，提供相关的代码片段或示例，以帮助他人理解问题。\n\n3. **使用标签**：在提问时，使用适当的标签（如 `bug`、`feature request` 等）来帮助维护者快速识别问题类型。\n\n## 其他资源\n\n- [XFlow GitHub 仓库](https://github.com/alibaba/x-render)\n- [XFlow 文档](./index.md)\n"
  },
  {
    "path": "docs/xflow/quickStart.md",
    "content": "---\norder: 1\ntitle: 快速开始\nmobile: false\ngroup: \n  title: 使用教程\n  order: 0\n---\n\n<div style=\"display:flex;align-items:center;margin-bottom:24px\">\n  <img src=\"https://img.alicdn.com/tfs/TB17UtINiLaK1RjSZFxXXamPFXa-606-643.png\" alt=\"logo\" width=\"48px\"/>\n  <span style=\"font-size:30px;font-weight:600;display:inline-block;margin-left:12px\">XFlow</span>\n</div>\n<p style=\"display:flex;justify-content:space-between;width:440px\">\n  <a href=\"https://www.npmjs.com/package/@xrenders/xflow\" target=\"_blank\">\n    <img alt=\"npm\" src=\"https://img.shields.io/npm/v/@xrenders/xflow.svg?maxAge=3600&style=flat-square\">\n  </a>\n  <a href=\"https://npmjs.org/package/@xrenders/xflow\" target=\"_blank\">\n    <img alt=\"NPM downloads\" src=\"https://img.shields.io/npm/dm/@xrenders/xflow.svg?style=flat-square\">\n  </a>\n  <a href=\"https://npmjs.org/package/@xrenders/xflow\" target=\"_blank\">\n    <img alt=\"NPM all downloads\" src=\"https://img.shields.io/npm/dt/@xrenders/xflow.svg?style=flat-square\">\n  </a>\n  <a>\n    <img alt=\"PRs Welcome\" src=\"https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square\">\n  </a>\n</p>\n\n画布流程编排解决方案\n\n## 安装\n\n```bash\nnpm install @xrenders/xflow\n```\n\n## 基础使用\n\n### 1. 引入组件\n\n```js\nimport XFlow from '@xrenders/xflow';\n```\n\n### 2. 配置节点菜单以及节点面板\n\n```js\n const nodeSettings = [\n    {\n      title: '开始',\n      type: 'Start',\n      hidden: true,\n      targetHandleHidden: true,\n      icon: {\n        type: 'icon-start',\n        bgColor: '#17B26A',\n      },\n      settingSchema: {\n        type: 'object',\n        properties: {\n          input: {\n            title: '变量一',\n            type: 'string',\n            widget: 'input',\n          },\n        },\n      },\n    },\n    {\n      title: '结束',\n      type: 'End',\n      hidden: true,\n      sourceHandleHidden: true,\n      icon: {\n        type: 'icon-end',\n        bgColor: '#F79009',\n      },\n      settingSchema: {\n        type: \"object\",\n        properties: {\n          input: {\n            title: '变量一',\n            type: 'string',\n            widget: 'input',\n          },\n        }\n      }\n    },\n    {\n      title: 'LLM',\n      type: 'LLM',\n      description: '调用大语言模型回答问题或者对自然语言进行处理',\n      icon: {\n        type: 'icon-model',\n        bgColor: '#6172F3',\n      },\n      settingSchema: {\n        type: 'object',\n        properties: {\n          model: {\n            title: '模型',\n            type: 'string',\n            enum: ['gpt-3.5', 'gpt-4'],\n            default: 'gpt-3.5'\n          },\n          temperature: {\n            title: '温度',\n            type: 'number',\n            default: 0.7,\n            minimum: 0,\n            maximum: 1\n          }\n        }\n      }\n    },\n    {\n      title: 'Prompt',\n      type: 'Prompt',\n      description: '通过精心设计提示词，提升大语言模型回答效果',\n      icon: {\n        type: 'icon-prompt',\n        bgColor: '#17B26A',\n      },\n    },\n    {\n      title: '知识库',\n      type: 'knowledge',\n      description: '允许你从知识库中查询与用户问题相关的文本内容',\n      icon: {\n        type: 'icon-knowledge',\n        bgColor: '#6172F3',\n      },\n    },\n  ];\n```\n\n\n### 3. 使用组件\n\n```js\nconst Demo = () => {\n  return (\n    <XFlow\n      settings={nodeSettings}\n      initialValues={{\n        nodes: [],\n        edges: []\n      }}\n    />\n  );\n};\n```\n\n## 完整示例\n\n<code src=\"./demo/quickStart/index.tsx\"></code>\n\n\n\n## 注意事项\n1. 确保项目中已安装必要的依赖：\n   - React\n   - XFlow\n2. 组件需要设置固定高度，建议使用百分比或视口单位\n3. 节点配置中的 `settingSchema` 需要符合 FormRender 的协议规范\n4. 初始值 `initialValues` 中的节点数据需要包含必要的字段：id、type、data、position\n"
  },
  {
    "path": "docs/xflow/schema/basic.ts",
    "content": "export default [\n  {\n    \"widget\": \"FPanel\",\n    \"style\": {\n      \"paddingTop\": \"20px\",\n      \"paddingLeft\": \"20px\",\n      \"paddingBottom\": \"20px\",\n      \"paddingRight\": \"20px\",\n      \"backgroundColor\": \"#ffffff\",\n      \"marginBottom\": \"12px\"\n    },\n    \"children\": [\n      {\n        \"widget\": \"FTitle\",\n        \"data\": \"基础信息\"\n      },\n      {\n        \"widget\": \"FDescriptions\",\n        \"column\": 3,\n        \"items\": [\n          {\n            \"label\": \"创建人\",\n            \"dataKey\": \"creator\"\n          },\n          {\n            \"label\": \"关联单据\",\n            \"dataKey\": \"relevanceCode\"\n          },\n          {\n            \"label\": \"单据备注\",\n            \"dataKey\": \"desc\"\n          },\n          {\n            \"label\": \"创建时间\",\n            \"dataKey\": \"create-time\"\n          },\n          {\n            \"label\": \"生效日期\",\n            \"dataKey\": \"effective-date\"\n          },\n          {\n            \"label\": \"描述项\",\n            \"showLevel\": 1\n          }\n        ],\n        \"style\": {\n          \"backgroundColor\": \"#ffffff\",\n          \"paddingTop\": \"0px\",\n          \"paddingLeft\": \"0px\",\n          \"paddingRight\": \"0px\",\n          \"paddingBottom\": \"0px\"\n        },\n        \"itemShowLevel\": 1,\n        \"getCompProps\": \"xxxx\"\n      }\n    ]\n  },\n  {\n    \"widget\": \"FTabs\",\n    \"items\": [\n      {\n        \"label\": \"负载均衡(SLB)\",\n        \"children\": [\n          {\n            \"widget\": \"FPanel\",\n            \"style\": {\n              \"paddingTop\": \"20px\",\n              \"paddingLeft\": \"20px\",\n              \"paddingBottom\": \"20px\",\n              \"paddingRight\": \"20px\",\n              \"backgroundColor\": \"#ffffff\",\n              \"marginBottom\": \"12px\"\n            },\n            \"children\": [\n              {\n                \"widget\": \"FTitle\",\n                \"data\": \"安全信息\"\n              },\n              {\n                \"widget\": \"FDescriptions\",\n                \"column\": 2,\n                \"items\": [\n                  {\n                    \"label\": \"安全构建名称\",\n                    \"dataKey\": \"name\"\n                  },\n                  {\n                    \"label\": \"所属应用\",\n                    \"dataKey\": \"app\"\n                  },\n                  {\n                    \"label\": \"构建模式\",\n                    \"dataKey\": \"mode\"\n                  },\n                  {\n                    \"label\": \"公网域名\",\n                    \"dataKey\": \"yum\"\n                  },\n                  {\n                    \"label\": \"保留计算实例\",\n                    \"dataKey\": \"fore\"\n                  }\n                ],\n                \"dataKey\": \"safety\"\n              }\n            ]\n          },\n          {\n            \"widget\": \"FPanel\",\n            \"style\": {\n              \"paddingTop\": \"20px\",\n              \"paddingLeft\": \"20px\",\n              \"paddingBottom\": \"20px\",\n              \"paddingRight\": \"20px\",\n              \"backgroundColor\": \"#ffffff\",\n              \"marginBottom\": \"12px\"\n            },\n            \"children\": [\n              {\n                \"widget\": \"FTitle\",\n                \"data\": \"操作日志\"\n              },\n              {\n                \"widget\": \"FTable\",\n                \"pagination\": {\n                  \"pageSize\": \"3\"\n                },\n                \"style\": {\n                  \"backgroundColor\": \"#ffffff\"\n                },\n                \"dataKey\": \"operLog\",\n                \"column\": {\n                  \"type\": {\n                    \"title\": \"操作类型\",\n                    \"dataKey\": \"type\"\n                  },\n                  \"creator\": {\n                    \"title\": \"操作人\",\n                    \"dataKey\": \"creator\"\n                  },\n                  \"time\": {\n                    \"title\": \"操作时间\",\n                    \"dataKey\": \"time\"\n                  },\n                  \"result\": {\n                    \"title\": \"执行结果\",\n                    \"dataKey\": \"result\"\n                  },\n                  \"desc\": {\n                    \"title\": \"备注\",\n                    \"dataKey\": \"desc\"\n                  }\n                }\n              }\n            ]\n          }\n        ]\n      },\n      {\n        \"label\": \"云服务器（ECS）\",\n        \"children\": []\n      }\n    ]\n  }\n]"
  },
  {
    "path": "docs/xflow/schema/custom-settings.ts",
    "content": "export default [\n  {\n    title: '开始',\n    type: 'Start',\n    hidden: true,\n    targetHandleHidden: true,\n    icon: {\n      type: 'icon-start',\n      bgColor: '#17B26A',\n    },\n    settingSchema: {\n      type: 'object',\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'customWidget',\n          required:true, // 必填效果，无法关闭和切换节点配置面板\n        },\n\n      }\n    },\n  },\n  {\n    title: '结束',\n    type: 'End',\n    hidden: true,\n    sourceHandleHidden: true,\n    icon: {\n      type: 'icon-end',\n      bgColor: '#F79009',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        switch1: {\n          title: '禁用输入框',\n          description:\"联动举例\",\n          type: 'boolean',\n          widget: 'switch'\n        },\n        input1: {\n          title: '输入框',\n          type: 'string',\n          disabled: '{{ formData.switch1 === true }}'\n        },\n        list: {\n          title: 'List 场景',\n          type: 'array',\n          widget: 'CardList',\n          defaultValue: [{}],\n          items: {\n            type: 'object',\n            widget: 'card',\n            title: 'List.Item',\n            properties: {\n              switch1: {\n                title: '隐藏输入框 2 ',\n                type: 'boolean',\n                widget: 'switch'\n              },\n              input1: {\n                title: '输入框 1',\n                type: 'string',\n                description: '给输入框 赋值'\n              },\n              input2: {\n                title: '输入框 2',\n                type: 'string',\n                defaultValue: '{{ rootValue.input1 }}',\n                hidden: '{{ rootValue.switch1 }}'\n              }\n            }\n          }\n        }\n      }\n    }\n  },\n  {\n    title: 'LLM',\n    type: 'LLM',\n    description: '调用大语言模型回答问题或者对自然语言进行处理',\n    icon: {\n      type: 'icon-model',\n      bgColor: '#6172F3',\n    },\n  },\n  {\n    title: 'Prompt',\n    type: 'Prompt',\n    description: '通过精心设计提示词，提升大语言模型回答效果',\n    icon: {\n      type: 'icon-prompt',\n      bgColor: '#17B26A',\n    },\n  },\n  {\n    title: '知识库',\n    type: 'knowledge',\n    description: '允许你从知识库中查询与用户问题相关的文本内容',\n    icon: {\n      type: 'icon-knowledge',\n      bgColor: '#6172F3',\n    },\n  },\n  {\n    title: 'Switch',\n    type: 'Switch',\n    description: '允许你根据 if/else 条件将 workflow 拆分成两个分支',\n    icon: {\n      type: 'icon-fenzhi',\n      bgColor: '#06AED4',\n    },\n  },\n  {\n    title: 'HSF',\n    type: 'hsf',\n    description: '允许通过 HSF 协议发送服务器请求',\n    icon: {\n      type: 'icon-hsf',\n      bgColor: '#875BF7',\n    },\n  },\n  {\n    title: 'Http',\n    type: 'http',\n    description: '允许通过 HTTP 协议发送服务器请求',\n    icon: {\n      type: 'icon-http',\n      bgColor: '#875BF7',\n    },\n  },\n  {\n    title: '代码执行',\n    type: 'Code',\n    description: '执行一段 Groovy 或 Python 或 NodeJS 代码实现自定义逻辑',\n    icon: {\n      type: 'icon-code',\n      bgColor: '#2E90FA',\n    },\n  },\n  {\n    title: '工具',\n    type: 'tool',\n    description: '允许使用工具能力',\n    icon: {\n      type: 'icon-gongju',\n      bgColor: '#2E90FA',\n    },\n  },\n  {\n    title: '工具',\n    type: '_group',\n    items: [\n      {\n        title: '代码执行',\n        type: 'Code',\n        description: '执行一段 Groovy 或 Python 或 NodeJS 代码实现自定义逻辑',\n        icon: {\n          type: 'icon-code',\n          bgColor: '#2E90FA',\n        },\n      },\n      {\n        title: '工具',\n        type: 'tool',\n        description: '允许使用工具能力',\n        icon: {\n          type: 'icon-gongju',\n          bgColor: '#2E90FA',\n        },\n      },\n    ],\n  },\n];\n"
  },
  {
    "path": "docs/xflow/schema/settings.ts",
    "content": "export default [\n  {\n    title: '开始',\n    type: 'Start',\n    hidden: true,\n    targetHandleHidden: true,\n    icon: {\n      type: 'icon-start',\n      bgColor: '#17B26A',\n    },\n    settingSchema: {\n      type: 'object',\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n        select: {\n          title: '变量二',\n          type: 'string',\n          widget: 'select',\n          props: {\n            options: [\n              { label: 'a', value: 'a' },\n              { label: 'b', value: 'b' },\n              { label: 'c', value: 'c' },\n            ],\n          },\n        },\n        radio1: {\n          title: '点击单选',\n          type: 'string',\n          widget: 'radio',\n          props: {\n            options: [\n              { label: '早', value: 'a' },\n              { label: '中', value: 'b' },\n              { label: '晚', value: 'c' }\n            ]\n          }\n        },\n        textarea1: {\n          title: '长文本',\n          type: 'string',\n          widget: 'textArea'\n        },\n        date1: {\n          title: '日期选择',\n          type: 'string',\n          widget: 'datePicker'\n        },\n        dateRange1: {\n          title: '日期范围',\n          type: 'range',\n          widget: 'dateRange'\n        },\n        time1: {\n          title: '时间选择',\n          type: 'string',\n          widget: 'timePicker'\n        },\n        timeRange1: {\n          title: '时间范围',\n          type: 'range',\n          widget: 'timeRange'\n        },\n      },\n    },\n  },\n  {\n    title: '结束',\n    type: 'End',\n    hidden: true,\n    sourceHandleHidden: true,\n    icon: {\n      type: 'icon-end',\n      bgColor: '#F79009',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n        select: {\n          title: '变量二',\n          type: 'string',\n          widget: 'select',\n          props: {\n            options: [\n              { label: 'a', value: 'a' },\n              { label: 'b', value: 'b' },\n              { label: 'c', value: 'c' },\n            ],\n          },\n        },\n      }\n    }\n  },\n  {\n    title: 'LLM',\n    type: 'LLM',\n    description: '调用大语言模型回答问题或者对自然语言进行处理',\n    icon: {\n      type: 'icon-model',\n      bgColor: '#6172F3',\n    },\n    settingSchema: {\n      type: \"object\",\n      properties: {\n        input: {\n          title: '变量一',\n          type: 'string',\n          widget: 'input',\n        },\n      }\n    }\n  },\n  {\n    title: 'Prompt',\n    type: 'Prompt',\n    description: '通过精心设计提示词，提升大语言模型回答效果',\n    icon: {\n      type: 'icon-prompt',\n      bgColor: '#17B26A',\n    },\n  },\n  {\n    title: '知识库',\n    type: 'knowledge',\n    description: '允许你从知识库中查询与用户问题相关的文本内容',\n    icon: {\n      type: 'icon-knowledge',\n      bgColor: '#6172F3',\n    },\n  },\n  {\n    title: 'Switch',\n    type: 'Switch',\n    description: '允许你根据 if/else 条件将 workflow 拆分成两个分支',\n    icon: {\n      type: 'icon-fenzhi',\n      bgColor: '#06AED4',\n    },\n  },\n  {\n    title: 'HSF',\n    type: 'hsf',\n    description: '允许通过 HSF 协议发送服务器请求',\n    icon: {\n      type: 'icon-hsf',\n      bgColor: '#875BF7',\n    },\n  },\n  {\n    title: 'Http',\n    type: 'http',\n    description: '允许通过 HTTP 协议发送服务器请求',\n    icon: {\n      type: 'icon-http',\n      bgColor: '#875BF7',\n    },\n  },\n  {\n    title: '代码执行',\n    type: 'Code',\n    description: '执行一段 Groovy 或 Python 或 NodeJS 代码实现自定义逻辑',\n    icon: {\n      type: 'icon-code',\n      bgColor: '#2E90FA',\n    },\n  },\n  {\n    title: '工具',\n    type: 'tool',\n    description: '允许使用工具能力',\n    icon: {\n      type: 'icon-gongju',\n      bgColor: '#2E90FA',\n    },\n  },\n  {\n    title: '工具',\n    type: '_group',\n    items: [\n      {\n        title: '代码执行',\n        type: 'Code',\n        description: '执行一段 Groovy 或 Python 或 NodeJS 代码实现自定义逻辑',\n        icon: {\n          type: 'icon-code',\n          bgColor: '#2E90FA',\n        },\n      },\n      {\n        title: '工具',\n        type: 'tool',\n        description: '允许使用工具能力',\n        icon: {\n          type: 'icon-gongju',\n          bgColor: '#2E90FA',\n        },\n      },\n    ],\n  },\n];\n"
  },
  {
    "path": "docs/xflow/singlePointDebug.md",
    "content": "---\norder: 8\ntitle: '单点调试'\nmobile: false\ngroup: \n  title: 高级用法\n  order: 2\n---\n\n# 单点调试\n\nXFlow 支持对单个节点进行调试。通过配置 `showTestingBtn` 展示单调调试按钮，调试按钮默认显示在节点的右上角，点击按钮可以实现节点的单点调试功能。\n\n## 基础用法\n\n### 1. 开启调试按钮\n\n在节点配置中设置 `showTestingBtn: true` 即可显示默认的调试按钮：\n\n```js\n const settings = [\n    {\n      title: 'LLM',\n      type: 'LLM',\n      description: '调用大语言模型回答问题或者对自然语言进行处理',\n      showTestingBtn: true,\n      icon: {\n        type: 'icon-model',\n        bgColor: '#6172F3',\n      },\n      settingSchema: {\n        type: 'object',\n        properties: {\n          input: {\n            title: '变量一',\n            type: 'string',\n            widget: 'input',\n          },\n        },\n      },\n    },\n  ];\n```\n\n### 2. 处理调试事件\n\n通过 `onTesting` 回调函数处理节点的调试逻辑：\n\n```js\n<XFlow\n  onTesting={(node, nodes) => {\n    // node: 当前调试的节点\n    // nodes: 所有节点数据\n    console.log(\"单点调试\", node, nodes);\n  }}\n/>\n```\n## 示例\n\n下面的示例展示了如何使用单点调试功能：\n\n<code src=\"./demo/log/runNode/index.tsx\"></code>"
  },
  {
    "path": "docs/xflow/updateLog.md",
    "content": "---\norder: 2\ntitle: '更新日志'\nmobile: false\ngroup: \n  title: '其他'\n  order: 5\n---\n\n# 更新日志\n\n### 1.0.3\n- [+] xflow 初始版本发布\n\n### 1.0.4\n- [+] 增加自定义组件完整案例，修复下拉框在全屏模式下无法显示的问题，增加撤销与重做按钮的隐藏配置\n\n### 1.0.5\n- [+] 补充节点图标配置文档，以及更新图标的SVG形式的用法\n"
  },
  {
    "path": "lerna.json",
    "content": "{\n  \"packages\": [\"packages/*\", \"widgets/*\", \"tools/schema-generator\"],\n  \"version\": \"independent\",\n  \"npmClient\": \"yarn\",\n  \"useWorkspaces\": true\n}\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"root\",\n  \"version\": \"0.0.1\",\n  \"private\": true,\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/alibaba/x-render.git\",\n    \"branch\": \"master\",\n    \"platform\": \"github\"\n  },\n  \"workspaces\": [\n    \"packages/*\",\n    \"tools/schema-generator\",\n    \"tools/form-render-excel\"\n  ],\n  \"scripts\": {\n    \"build\": \"turbo run build\",\n    \"changelog\": \"lerna-changelog\",\n    \"clean\": \"lerna clean -y\",\n    \"predeploy\": \"npm run build && npm run doc\",\n    \"deploy\": \"gh-pages -d docs-dist\",\n    \"dev\": \"set PORT=8080 && dumi dev\",\n    \"doc\": \"dumi build\",\n    \"format\": \"prettier -c --write \\\"docs/**/*\\\" \\\"packages/**/*\\\" \\\"tools/**/*\\\" \\\"widgets/**/*\\\"\",\n    \"prepare\": \"dumi setup\",\n    \"publish\": \"npm run build && lerna publish -m \\\"chore: publish\\\" --no-verify-access\",\n    \"size\": \"ANALYZE=1 dumi build\",\n    \"start\": \"npm run dev\",\n    \"test\": \"vitest run\",\n    \"test:clean\": \"vitest clean cache\",\n    \"test:coverage\": \"vitest --coverage\",\n    \"test:ui\": \"vitest --ui\"\n  },\n  \"lint-staged\": {\n    \"*.{js,jsx,less,md,json}\": [\n      \"prettier --write\"\n    ],\n    \"*.ts?(x)\": [\n      \"prettier --parser=typescript --write\"\n    ]\n  },\n  \"resolutions\": {\n    \"types-ramda\": \"0.29.4\"\n  },\n  \"devDependencies\": {\n    \"@ant-design/icons\": \"^4.0.2\",\n    \"@babel/core\": \"7.17.2\",\n    \"@babel/plugin-transform-react-jsx\": \"^7.19.0\",\n    \"@babel/preset-env\": \"7.16.11\",\n    \"@babel/preset-react\": \"7.16.7\",\n    \"@codemirror/lang-json\": \"^6.0.1\",\n    \"@jest/types\": \"^27.5.1\",\n    \"@monaco-editor/react\": \"^4.4.6\",\n    \"@testing-library/jest-dom\": \"^5.16.4\",\n    \"@testing-library/react\": \"^12.1.3\",\n    \"@testing-library/react-hooks\": \"^7.0.2\",\n    \"@types/enzyme\": \"^3.10.11\",\n    \"@types/enzyme-adapter-react-16\": \"^1.0.6\",\n    \"@types/jest\": \"^27.0.3\",\n    \"@types/lodash-es\": \"^4.17.6\",\n    \"@uiw/react-codemirror\": \"^4.23.7\",\n    \"@umijs/plugin-esbuild\": \"1.x\",\n    \"@umijs/preset-react\": \"1.x\",\n    \"@umijs/test\": \"^3.0.5\",\n    \"@vitejs/plugin-react\": \"^3.0.0\",\n    \"@vitest/coverage-c8\": \"^0.25.8\",\n    \"@vitest/ui\": \"^0.25.7\",\n    \"@wojtekmaj/enzyme-adapter-react-17\": \"^0.6.7\",\n    \"antd\": \"^5.x\",\n    \"antd-mobile\": \"5.x\",\n    \"babel-jest\": \"^27.4.4\",\n    \"babel-plugin-import\": \"^1.13.0\",\n    \"babel-plugin-no-debugger\": \"^0.0.1\",\n    \"babel-plugin-transform-remove-console\": \"^6.9.4\",\n    \"chart-render\": \"^0.1.9\",\n    \"cross-env\": \"^7.0.3\",\n    \"dumi\": \"2.4.17\",\n    \"dumi-theme-mobile\": \"^2.0.4\",\n    \"enzyme\": \"^3.11.0\",\n    \"enzyme-adapter-react-16\": \"^1.15.6\",\n    \"enzyme-to-json\": \"^3.6.2\",\n    \"father-build\": \"^1.17.2\",\n    \"fetch-jsonp\": \"^1.1.3\",\n    \"formRenderV1\": \"npm:form-render@^1.4.13\",\n    \"gh-pages\": \"^3.0.0\",\n    \"happy-dom\": \"^8.1.0\",\n    \"identity-obj-proxy\": \"^3.0.0\",\n    \"jest\": \"^27.4.4\",\n    \"jsdom\": \"^19.0.0\",\n    \"jsdom-global\": \"^3.0.2\",\n    \"lerna\": \"^3.22.1\",\n    \"lerna-changelog\": \"^1.0.1\",\n    \"lint-staged\": \"^10.0.7\",\n    \"lodash\": \"^4.17.21\",\n    \"monaco-editor-webpack-plugin\": \"^4.1.1\",\n    \"prettier\": \"^2.6.0\",\n    \"prettier-plugin-packagejson\": \"^2.2.16\",\n    \"prismjs\": \"^1.27.0\",\n    \"react\": \"^18.2.0\",\n    \"react-dom\": \"^18.2.0\",\n    \"react-monaco-editor\": \"^0.44.0\",\n    \"react-simple-code-editor\": \"^0.11.0\",\n    \"react-test-renderer\": \"^17.0.2\",\n    \"tinycolor2\": \"^1.6.0\",\n    \"translate-google\": \"^1.5.0\",\n    \"ts-jest\": \"^27.1.3\",\n    \"ts-node\": \"^10.5.0\",\n    \"turbo\": \"^1.3.1\",\n    \"typeit-react\": \"^2.6.4\",\n    \"typescript\": \"^5.2.2\",\n    \"vite\": \"^3.2.5\",\n    \"vitest\": \"^0.25.6\",\n    \"yorkie\": \"^2.0.0\",\n    \"braft-editor\": \"^2.3.9\"\n  }\n}\n"
  },
  {
    "path": "packages/chart-render/.fatherrc.ts",
    "content": "export default {\n  esm: 'rollup',\n  cjs: 'rollup',\n  lessInBabelMode: true,\n  lessInRollupMode: {},\n};\n"
  },
  {
    "path": "packages/chart-render/CHANGELOG.md",
    "content": "# Change Log\n\n### 1.0.0-alpha.3\n\n- [!] 修正依赖包\n\n### 1.0.0-alpha.2\n\n- [!] 修复 Search 样式\n\n### 1.0.0-alpha.1\n\n- [+] 完全的重构，使用上尽量与 `TableRender` 保持一致，通过写入 `api` 作为数据请求方法来达到简单出图表的效果\n- [+] 暂时支持柱状图、饼图展示\n\n### 0.1.9\n\n- [+] 更新 npm 包的文档信息以及补全对应的 package 信息\n\n### 0.1.7\n\n- [+] 折线图单指标双维度 增加百分数支持\n\n### 0.1.6\n\n- [+] 折线图单指标单维度 增加百分数支持\n\n### 0.1.5\n\n- [+] 折线图增加双指标 双维度 支持\n\n### 0.1.4\n\n- [-] 移除 CrossTreeTable 下钻树表，与 PivotTable 交叉表合并，通过 `leftExpandable`、`topExpandable` 参数控制是否可展开\n- [-] 移除 Area 面积图，与 Line 折线图合并，通过 `withArea` 参数控制是否以面积图展示\n- [+] 解决 交叉表的 `cellRender` 的入参问题\n\n### 0.1.3\n\n- [-] 重新发布\n\n### 0.1.2\n\n- [-] 移除 Bar 条形图，与 Column 柱状图合并，通过 `inverted` 参数区分\n- [+] 修正所有图标的图例提示展示\n\n### 0.1.1\n\n- [+] 迁移到大仓\n- [+] 正式发布\n"
  },
  {
    "path": "packages/chart-render/README.md",
    "content": "<div style=\"display:flex;align-items:center;margin-bottom:24px\">\n  <img src=\"https://img.alicdn.com/tfs/TB17UtINiLaK1RjSZFxXXamPFXa-606-643.png\" alt=\"logo\" width=\"48px\"/>\n  <h4 style=\"font-size:30px;font-weight:600;display:inline-block;margin-left:12px\">ChartRender</span>\n</div>\n<p style=\"display:flex;justify-content:space-between;width:440px\">\n  <a href=\"https://www.npmjs.com/package/chart-render?_blank\">\n    <img alt=\"npm\" src=\"https://img.shields.io/npm/v/chart-render.svg?maxAge=3600&style=flat-square\">\n  </a>\n  <a href=\"https://npmjs.org/package/chart-render\">\n    <img alt=\"NPM downloads\" src=\"https://img.shields.io/npm/dm/chart-render.svg?style=flat-square\">\n  </a>\n  <a href=\"https://npmjs.org/package/chart-render\">\n    <img alt=\"NPM all downloads\" src=\"https://img.shields.io/npm/dt/chart-render.svg?style=flat-square\">\n  </a>\n  <a>\n    <img alt=\"PRs Welcome\" src=\"https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square\">\n  </a>\n</p>\n\n> 易用的中后台**图表解决方案**，常用于**图表展示页**快速生成\n\n## 官网\n\n<https://xrender.fun/chart-render>\n\n## 优势\n\n1. **真正开箱即用**：只需要关心你的数据，传入 `meta`、`data` 即可出图。\n2. **开发体验舒适**：使用 TypeScript 开发，提供完整的类型定义文件。\n3. **无缝习惯使用**：图表用 Ant Design Charts 来提供，自定义的样式支持参数透传。\n\n## 何时使用\n\n1. 需要针对一堆数据快速建立可视化图表，并且需要 **折线图/柱状图/交叉表** 频繁切换查看。\n2. 前端小白，只关心手里的数据，不想看长篇大论了解那些图表库该怎么使用，只想搭个图表看。\n3. 提供折线图、柱状图、交叉表三类组件进行图表绘制：\n   - 折线图：常用来观察资料在一段维度之内的变化，如果 X 轴为时间，这种折线图又称为趋势图。\n   - 柱状图：描述的是分类数据，常用来回答的是每一个分类中「有多少？」这个问题。\n   - 交叉表：是一种矩阵形式的表格，拥有最强大的数据分析能力，可以展示无限指标和无限维度间的关系。\n\n## 如何使用\n\n### 安装\n\nchart-render 依赖 ant design，单独使用不要忘记安装～\n\n```bash\n$ npm install chart-render --save\n```\n\n### 代码演示\n\n```jsx\n/**\n * transform: true\n * defaultShowCode: true\n */\nimport React, { useState } from 'react';\nimport { Line, Column, PivotTable } from 'chart-render';\n\nexport default () => {\n  const [component, setComponent] = useState('Line');\n  const ChartRender = { Line, Column, PivotTable }[component];\n\n  return (\n    <div>\n      <div style={{ marginBottom: 10 }}>\n        <button\n          style={{ marginRight: 10 }}\n          className=\"ant-btn ant-btn-primary\"\n          onClick={() => setComponent('Line')}\n        >\n          折线图\n        </button>\n        <button\n          style={{ marginRight: 10 }}\n          className=\"ant-btn ant-btn-primary\"\n          onClick={() => setComponent('Column')}\n        >\n          柱状图\n        </button>\n        <button\n          style={{ marginRight: 10 }}\n          className=\"ant-btn ant-btn-primary\"\n          onClick={() => setComponent('PivotTable')}\n        >\n          交叉表\n        </button>\n      </div>\n\n      <ChartRender\n        meta={[\n          { id: 'date', name: '日期', isDim: true },\n          { id: 'pv', name: '访问量', isDim: false },\n          { id: 'uv', name: '访客数', isDim: false },\n        ]}\n        data={[\n          { date: '20200101', pv: 100, uv: 50 },\n          { date: '20200102', pv: 120, uv: 60 },\n          { date: '20200103', pv: 140, uv: 70 },\n          { date: '20200104', pv: 160, uv: 80 },\n        ]}\n      />\n    </div>\n  );\n};\n```\n\n## API\n\n### 通用参数\n\n所有的图表组件都有以下 4 个入参（**`data` 和 `meta` 是必传的参数**，请务必注意）：\n\n| 参数      | 说明               | 类型                  | 是否必填 |\n| --------- | ------------------ | --------------------- | -------- |\n| style     | 最外层容器的样式   | `React.CSSProperties` | 否       |\n| className | 最外层容器的类名   | `string`              | 否       |\n| data      | 数据配置项 `注1`   | `IDataItem[]`         | 是       |\n| meta      | 元数据配置项 `注2` | `IMetaItem[]`         | 是       |\n\n##### 注 1：通用参数 - data 数据配置项\n\n是普通的对象数组，形如：\n\n```js\n[\n  { date: '20200101', pv: 100, uv: 50 },\n  { date: '20200102', pv: 120, uv: 60 },\n  { date: '20200103', pv: 140, uv: 70 },\n  { date: '20200104', pv: 160, uv: 80 },\n];\n```\n\n##### 注 2：通用参数 - meta 元数据配置项\n\n用来描述 data 的各个字段的东西，形如：\n\n```js\n/**\n * id: 对应单条数据项的 key 名\n * name: 对应单条数据项的 key 的描述\n * isDim: 是否是维度，`true`-维度，`false`-指标\n * isRate: 是否是百分数，仅限指标使用，启用后，数值 `0.5` 会以 `50%` 来输出渲染\n */\n[\n  { id: 'date', name: '日期', isDim: true, isRate: false },\n  { id: 'pv', name: '访问量', isDim: false, isRate: false },\n  { id: 'uv', name: '访客数', isDim: false, isRate: false },\n];\n```\n\n### Line 折线图的额外参数\n\n| 参数     | 说明                                                                                                                                                         | 类型      | 默认值  | 是否必填 |\n| -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------- | ------- | -------- |\n| withArea | 是否以面积图展示<br> - 注意面积图默认堆叠展示，如不需要可以传入 `isStack={false}` 覆盖<br> - 开启面积图后方可使用 `areaStyle` `startOnZero` `isPercent` 属性 | `boolean` | `false` | 否       |\n\n如果你需要修改点、线等样式，可以参考参数表：[折线图参数表](https://charts.ant.design/zh-CN/demos/line?type=api) [面积图参数表](https://charts.ant.design/zh-CN/demos/area?type=api)，除了 `yField`、`xField`、`seriesField` 三个字段不做透传，其他字段均做透传处理。\n\n### Column 柱状图的额外参数\n\n| 参数     | 说明             | 类型      | 默认值  | 是否必填 |\n| -------- | ---------------- | --------- | ------- | -------- |\n| inverted | 是否以条形图展示 | `boolean` | `false` | 否       |\n\n如果你需要修改颜色、柱等样式，可以参考参数表：[柱状图参数表](https://charts.ant.design/zh-CN/demos/column?type=api)，除了 `yField`、`xField`、`seriesField` 三个字段不做透传，其他字段均做透传处理。\n\n### PivotTable 交叉表的额外参数\n\n| 参数                | 说明                                                                                                                                                                                             | 类型                                                                    | 默认值             | 是否必填 |\n| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------- | ------------------ | -------- |\n| showSubtotal        | 是否展示总计小计                                                                                                                                                                                 | `boolean`                                                               | `true`             | 否       |\n| subtotalText        | 总计小计的文案                                                                                                                                                                                   | `[string, string]`                                                      | `['总计', '小计']` | 否       |\n| indicatorSide       | 指标的展示位置                                                                                                                                                                                   | `'left' \\| 'top'`                                                       | `'top'`            | 否       |\n| size                | 表格尺寸                                                                                                                                                                                         | `'small' \\| 'middle' \\| 'large'`                                        | `'middle'`         | 否       |\n| leftDimensionLength | 左侧维度放多少个，超出的维度会放到表格顶部                                                                                                                                                       | `number`                                                                | -                  | 否       |\n| leftExpandable      | 左侧维度允许展开/收起                                                                                                                                                                            | `boolean`                                                               | `false`            | 否       |\n| topExpandable       | 顶部维度允许展开/收起                                                                                                                                                                            | `boolean`                                                               | `false`            | 否       |\n| cellRender          | 单元格自定义渲染函数，可见[交叉表案例 - 高级案例 - 自定义单元格渲染](https://xrender.fun/chart-render/demo/pivot-table#%E8%87%AA%E5%AE%9A%E4%B9%89%E5%8D%95%E5%85%83%E6%A0%BC%E6%B8%B2%E6%9F%93) | `(value: any, dimRecord: IDataItem, indId: string ) => React.ReactNode` | -                  | 否       |\n"
  },
  {
    "path": "packages/chart-render/package.json",
    "content": "{\n  \"name\": \"chart-render\",\n  \"version\": \"1.0.0-alpha.3\",\n  \"keywords\": [\n    \"Chart\",\n    \"ChartRender\",\n    \"Render\",\n    \"XRender\",\n    \"React\",\n    \"Json Schema\",\n    \"Ant Design\"\n  ],\n  \"homepage\": \"https://xrender.fun/chart-render\",\n  \"bugs\": {\n    \"url\": \"https://github.com/alibaba/x-render/issues\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git@github.com:alibaba/x-render.git\"\n  },\n  \"license\": \"MIT\",\n  \"main\": \"dist/index.js\",\n  \"module\": \"dist/index.esm.js\",\n  \"typings\": \"dist/index.d.ts\",\n  \"scripts\": {\n    \"beta\": \"npm publish --tag beta\",\n    \"build\": \"father-build\",\n    \"prepare\": \"npm run build\",\n    \"prettier\": \"prettier --write \\\"**/*.{js,jsx,tsx,ts,less,md,json}\\\"\",\n    \"postpublish\": \"git push --tags\",\n    \"test\": \"umi-test\",\n    \"test:coverage\": \"umi-test --coverage\"\n  },\n  \"lint-staged\": {\n    \"*.{js,jsx,less,md,json}\": [\n      \"prettier --write\"\n    ],\n    \"*.ts?(x)\": [\n      \"prettier --parser=typescript --write\"\n    ]\n  },\n  \"dependencies\": {\n    \"less\": \"^3.0.0\",\n    \"@ant-design/icons\": \"^4.2.2\",\n    \"@ant-design/plots\": \"^1.0.9\",\n    \"ahooks\": \"^3.4.1\",\n    \"classnames\": \"^2.3.1\",\n    \"formRenderV1\": \"npm:form-render@^1.4.13\",\n    \"zustand\": \"^4.0.0-rc.1\"\n  },\n  \"devDependencies\": {},\n  \"peerDependencies\": {\n    \"antd\": \"4.x\",\n    \"react\": \">=16.8.0\"\n  },\n  \"gitHooks\": {\n    \"pre-commit\": \"lint-staged\"\n  }\n}\n"
  },
  {
    "path": "packages/chart-render/src/components/ChartContainer/index.less",
    "content": ".cr-chart-container {\n  position: relative;\n}\n"
  },
  {
    "path": "packages/chart-render/src/components/ChartContainer/index.tsx",
    "content": "import classNames from 'classnames';\nimport React, { CSSProperties, FC, memo } from 'react';\nimport './index.less';\n\nconst ChartContainer: FC<{\n  children: React.ReactNode;\n  className?: string;\n  style?: CSSProperties;\n}> = ({ children, className, style }) => (\n  <div className={classNames('cr-chart-container', className)} style={style}>\n    {children}\n  </div>\n);\n\nexport default memo(ChartContainer);\n"
  },
  {
    "path": "packages/chart-render/src/components/ChartProvider/index.tsx",
    "content": "import { ConfigProvider } from 'antd';\nimport zh_CN from 'antd/lib/locale/zh_CN';\nimport React, { FC, ReactNode } from 'react';\nimport { createStore, Provider } from '../../utils/store';\n\nconst ChartProvider: FC<{ children?: ReactNode }> = ({ children }) => (\n  <ConfigProvider locale={zh_CN}>\n    {/* @ts-ignore */}\n    <Provider createStore={createStore}>{children}</Provider>\n  </ConfigProvider>\n);\n\n// @ts-ignore\nconst withChart = Component => props => {\n  return (\n    <ChartProvider>\n      <Component {...props} />\n    </ChartProvider>\n  );\n};\n\nexport { ChartProvider, withChart };\n"
  },
  {
    "path": "packages/chart-render/src/components/Search/index.less",
    "content": ".ant-card-extra > .cr-search {\n  display: flex;\n  align-items: center;\n  margin: -16px 0;\n\n  > .cr-search-button {\n    display: inline-block;\n    margin-left: 12px;\n  }\n\n  > .fr-container {\n    .fr-item-wrapper {\n      > div > div {\n        flex-direction: row;\n        flex-wrap: nowrap;\n        gap: 12px;\n        > .fr-field {\n          width: auto;\n        }\n      }\n    }\n    .fr-label {\n      width: auto !important;\n    }\n    .error-message,\n    .field-block {\n      position: absolute;\n      top: 100%;\n    }\n  }\n}\n\n.cr-search-hidden {\n  display: none;\n}\n\n.cr-search-button {\n  display: none;\n}\n"
  },
  {
    "path": "packages/chart-render/src/components/Search/index.tsx",
    "content": "import { SyncOutlined } from '@ant-design/icons';\nimport { createUpdateEffect, useDeepCompareEffect } from 'ahooks';\nimport { Button, ButtonProps } from 'antd';\nimport classNames from 'classnames';\nimport FormRender, { FRProps, useForm } from 'formRenderV1';\nimport React, {\n  CSSProperties,\n  FC,\n  memo,\n  useCallback,\n  useMemo,\n  useRef,\n} from 'react';\nimport { useChart } from '../../utils/store';\nimport { DataSource, ObjectFilters } from '../../utils/type';\nimport './index.less';\n\nconst useDeepCompareUpdateEffect = createUpdateEffect(useDeepCompareEffect);\n\nexport interface ISearchProps\n  extends Omit<\n    FRProps,\n    'form' | 'onFinish' | 'className' | 'style' | 'schema'\n  > {\n  /** 搜索头最外层容器的 className */\n  className: string;\n\n  /** 搜索头最外层容器的 style */\n  style: CSSProperties;\n\n  /** 是否隐藏 */\n  hidden: boolean;\n\n  /** 大小 */\n  size: 'small' | undefined;\n\n  /** 是否展示查询/刷新按钮，也可以透传 Button 参数 */\n  searchButton: boolean | ButtonProps;\n\n  /** 是否要挂载完毕后自动请求数据 */\n  searchOnMount: boolean;\n\n  /** 是否要在表单项变化后自动请求数据，也可以指定哪些字段变化时自动请求 */\n  searchOnChange: boolean | string[];\n\n  /** 请求方法，请尽量使用唯一通用的方法 */\n  api: (params: {\n    // schema: ISearchProps['schema'];\n    // dimensions: ISearchProps['dimensions'];\n    // indicators: ISearchProps['indicators'];\n    filters: ObjectFilters;\n    // orders: ObjectOrders;\n  }) => DataSource | Promise<DataSource>;\n\n  /** 表单筛选项 */\n  schema: FRProps['schema'];\n\n  /** 维度 */\n  // dimensions: string[];\n\n  /** 指标 */\n  // indicators: string[];\n\n  /** 固定筛选项 */\n  filters: ObjectFilters;\n\n  /** 固定排序项 */\n  // orders: ObjectOrders;\n}\n\nconst EMPTY_SCHEMA: ISearchProps['schema'] = {\n  type: 'object',\n  properties: {},\n};\n\nconst Search: FC<Partial<ISearchProps>> = props => {\n  const propsRef = useRef(props);\n  propsRef.current = props;\n  const {\n    className,\n    style,\n    api,\n    schema = EMPTY_SCHEMA,\n    filters = {},\n    searchButton = true,\n    searchOnMount = true,\n    searchOnChange = false,\n    size,\n    watch,\n    hidden = false,\n    ...restProps\n  } = props;\n\n  const loading = useChart(state => state.loading);\n  const setChart = useChart(state => state.setChart);\n\n  const form = useForm();\n\n  const refresh = useCallback(async () => {\n    setChart({ loading: true });\n    try {\n      const dataSource = await propsRef.current.api?.({\n        filters: { ...propsRef.current.filters, ...form.getValues() },\n      });\n      setChart({ dataSource });\n    } catch { }\n    setChart({ loading: false });\n  }, []);\n\n  useMemo(() => setChart({ form, refresh }), []);\n\n  useDeepCompareUpdateEffect(() => {\n    refresh();\n  }, [filters]);\n\n  return (\n    <div\n      style={{ display: hidden ? 'none' : undefined, ...style }}\n      className={classNames('cr-search', className, {\n        'cr-search-hidden': !Object.keys(schema.properties || {}).length,\n        'cr-search-small': size === 'small',\n      })}\n      onKeyDown={event => event.key === 'Enter' && form.submit()}\n    >\n      <FormRender\n        displayType=\"row\"\n        size={size}\n        form={form}\n        schema={schema || EMPTY_SCHEMA}\n        onFinish={(_: any, errors: any) => !errors.length && refresh()}\n        onMount={searchOnMount && !searchOnChange ? form.submit : undefined}\n        watch={{\n          ...watch,\n          ...Object.fromEntries(\n            (Array.isArray(searchOnChange)\n              ? searchOnChange\n              : searchOnChange\n                ? ['#']\n                : []\n            ).map(key => [key, refresh!])\n          ),\n        }}\n        {...restProps}\n      />\n\n      {searchButton && (\n        <Button\n          className=\"cr-search-button\"\n          icon={<SyncOutlined />}\n          onClick={form.submit}\n          loading={loading}\n          size={size}\n          {...(typeof searchButton === 'object' ? searchButton : {})}\n        />\n      )}\n    </div>\n  );\n};\n\nexport default memo(Search);\n"
  },
  {
    "path": "packages/chart-render/src/index.ts",
    "content": "export type { Schema } from 'formRenderV1';\nexport { ChartProvider, withChart } from './components/ChartProvider';\nexport { default as Search } from './components/Search';\nexport { useChart } from './utils/store';\n\nexport { default as Column } from './widgets/Column';\nexport { default as Pie } from './widgets/Pie';\n"
  },
  {
    "path": "packages/chart-render/src/utils/index.ts",
    "content": "import { MetaItem } from './type';\n\nexport function splitMeta(meta: MetaItem[] = []) {\n  const metaDim: MetaItem[] = [];\n  const metaInd: MetaItem[] = [];\n  meta.forEach(item => (item.isDim ? metaDim : metaInd).push(item));\n  return { metaDim, metaInd };\n}\n"
  },
  {
    "path": "packages/chart-render/src/utils/store.ts",
    "content": "import { FormInstance } from 'formRenderV1';\nimport create, { StoreApi } from 'zustand';\nimport createContext from 'zustand/context';\nimport { DataSource } from './type';\nexport interface IStore {\n  /** 修改全局状态的工具函数 */\n  readonly setChart: (store: Partial<IStore>) => void;\n\n  /** FormRender 实例 */\n  readonly form?: FormInstance;\n\n  /** 是否在加载中 */\n  loading: boolean;\n\n  /** 数据，提供给图表组件进行渲染的 */\n  dataSource: DataSource;\n\n  /** 重新请求数据的方法 */\n  refresh?: () => void;\n}\n\nexport const { Provider, useStore: useChart } =\n  createContext<StoreApi<IStore>>();\n\nexport const createStore = () =>\n  create<IStore>(setChart => ({\n    setChart,\n    loading: false,\n    dataSource: { meta: [], data: [] },\n  }));\n"
  },
  {
    "path": "packages/chart-render/src/utils/type.ts",
    "content": "export type ArrayFilters = Array<\n  | {\n      key: string;\n      operator: '=' | '!=' | (string & {});\n      value: string | number;\n      extra: any;\n    }\n  | {\n      key: string;\n      operator: 'in' | 'not in' | (string & {});\n      value: (string | number)[];\n      extra: any;\n    }\n  | {\n      key: string;\n      operator: 'between' | 'not between' | (string & {});\n      value: [string | number, string | number];\n      extra: any;\n    }\n>;\nexport type ObjectFilters = Record<string, ArrayFilters[0]['value']>;\n\n/**\n\nexport type ArrayOrders = Array<{\n  field: string;\n  order: 'asc' | 'desc' | (string & {});\n}>;\nexport type ObjectOrders = Record<string, ArrayOrders[0]['order']>;\n\n**/\n\nexport type MetaItem = {\n  /** 对应单条数据项的 key 名 */\n  id: string;\n\n  /** 对应单条数据项的 key 的描述 */\n  name?: string;\n\n  /** 是否是维度字段，`true`-维度，`false`-指标，默认按指标处理 */\n  isDim?: boolean;\n\n  /** 是否是百分数，仅限指标使用，启用后，数值 `0.5` 会以 `50%` 来输出渲染 */\n  isRate?: boolean;\n};\n\nexport type DataItem = Record<string, any>;\n\nexport type DataSource = { meta: MetaItem[]; data: DataItem[] };\n"
  },
  {
    "path": "packages/chart-render/src/widgets/Column/index.tsx",
    "content": "import {\n  Bar as AntBar,\n  Column as AntColumn,\n  ColumnConfig,\n} from '@ant-design/plots';\nimport React, { memo } from 'react';\nimport { DataSource } from '../../utils/type';\nimport { useChart } from '../../utils/store';\nimport { splitMeta } from '../../utils';\nimport ChartContainer from '../../components/ChartContainer';\n\nexport interface IColumnProps extends Omit<Partial<ColumnConfig>, 'data'> {\n  /** 是否倒置，倒置后柱形图会表现成条形图 */\n  inverted?: boolean;\n}\n\nexport function generateConfig(\n  meta: DataSource['meta'],\n  data: DataSource['data']\n): ColumnConfig {\n  const { metaDim, metaInd } = splitMeta(meta);\n\n  if (metaInd.length >= 1 && metaDim.length === 0) {\n    // case 1: N指标、0维度 => 指标名作为 x 轴，指标值作为 y 轴\n    const xField = 'type';\n    const yField = 'value';\n    return {\n      xField,\n      yField,\n      data: data\n        .map(item => {\n          return metaInd.map(({ id, name }) => {\n            return {\n              [xField]: id,\n              [yField]: item[id],\n            };\n          });\n        })\n        .flat(),\n      meta: {\n        [xField]: {\n          formatter: label =>\n            meta.find(({ id }) => label === id)?.name || label,\n        },\n      },\n      tooltip: {\n        // @ts-ignore\n        formatter: ({ [xField]: type, [yField]: value }) => ({\n          name: meta.find(({ id }) => type === id)?.name as string,\n          value,\n        }),\n      },\n    };\n  } else if (metaInd.length === 1 && metaDim.length === 1) {\n    // case 2: 单指标，单维度 => 维度作为 x 轴，指标作为 y 轴\n    const xField = metaDim.shift()?.id as string;\n    const yField = metaInd.shift()?.id as string;\n    return {\n      data,\n      xField,\n      yField,\n      meta: {\n        [yField]: { alias: meta.find(({ id }) => id === yField)?.name },\n      },\n    };\n  } else if (metaInd.length > 1 && metaDim.length === 1) {\n    // case 3: 多指标，单维度 => 维度作为 x 轴，指标名作为系列，指标值作为 y 轴\n    const xField = metaDim.shift()?.id as string;\n    const yField = 'value';\n    const seriesField = 'type';\n    return {\n      data: data\n        .map(item => {\n          return metaInd.map(({ id, name }) => {\n            return {\n              [xField]: item[xField],\n              [yField]: item[id],\n              [seriesField]: name,\n            };\n          });\n        })\n        .flat(),\n      xField,\n      yField,\n      seriesField,\n      isGroup: true,\n    };\n  } else if (metaInd.length === 1 && metaDim.length === 2) {\n    // case 3: 单指标，双维度\n    return {\n      data,\n      xField: metaDim.shift()?.id as string,\n      yField: metaInd.shift()?.id as string,\n      seriesField: metaDim.shift()?.id,\n      isGroup: true,\n    };\n  }\n  return { data, xField: '', yField: '' };\n}\n\nconst Column: React.FC<IColumnProps> = ({\n  className,\n  style,\n  inverted,\n  ...props\n}) => {\n  const loading = useChart(state => state.loading);\n  const { meta = [], data = [] } = useChart(state => state.dataSource) || {};\n  const { xField, yField, ...otherConfig } = generateConfig(meta, data);\n\n  return (\n    <ChartContainer className={className} style={style}>\n      {inverted ? (\n        <AntBar\n          loading={loading}\n          xField={yField || ''}\n          yField={xField || ''}\n          {...otherConfig}\n          {...props}\n        />\n      ) : (\n        <AntColumn\n          loading={loading}\n          xField={xField || ''}\n          yField={yField || ''}\n          {...otherConfig}\n          {...props}\n        />\n      )}\n    </ChartContainer>\n  );\n};\n\nexport default memo(Column);\n"
  },
  {
    "path": "packages/chart-render/src/widgets/Pie/index.tsx",
    "content": "import { Pie as AntPie } from '@ant-design/plots';\nimport { PieConfig } from '@ant-design/plots/es/components/pie';\nimport React, { FC, memo } from 'react';\nimport ChartContainer from '../../components/ChartContainer';\nimport { splitMeta } from '../../utils';\nimport { useChart } from '../../utils/store';\n\nexport interface IPieProps extends Omit<Partial<PieConfig>, 'data'> {}\n\nconst Pie: FC<IPieProps> = ({ className, style, ...props }) => {\n  const loading = useChart(state => state.loading);\n  const { meta, data } = useChart(state => state.dataSource) || {};\n\n  const { metaDim, metaInd } = splitMeta(meta);\n  const colorField = metaDim[0]?.id;\n  const angleField = metaInd[0]?.id;\n\n  return (\n    <ChartContainer className={className} style={style}>\n      <AntPie\n        loading={loading}\n        data={data || []}\n        colorField={colorField || ''}\n        angleField={angleField || ''}\n        {...props}\n      />\n    </ChartContainer>\n  );\n};\n\nexport default memo(Pie);\n"
  },
  {
    "path": "packages/chart-render/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"esnext\",\n    \"module\": \"esnext\",\n    \"moduleResolution\": \"node\",\n    \"importHelpers\": true,\n    \"jsx\": \"react\",\n    \"esModuleInterop\": true,\n    \"sourceMap\": true,\n    \"baseUrl\": \"./\",\n    \"strict\": true,\n    \"paths\": {\n      \"@/*\": [\"src/*\"],\n      \"@@/*\": [\"src/.umi/*\"]\n    },\n    \"allowSyntheticDefaultImports\": true\n  },\n  \"exclude\": [\n    \"node_modules\",\n    \"lib\",\n    \"es\",\n    \"dist\",\n    \"typings\",\n    \"**/__test__\",\n    \"test\",\n    \"docs\",\n    \"tests\"\n  ]\n}\n"
  },
  {
    "path": "packages/data-render/.fatherrc.js",
    "content": "import copy from 'rollup-plugin-copy';\n\nexport default {\n  cjs: 'babel',\n  esm: {\n    type: 'babel',\n    importLibToEs: true,\n  },\n  lessInBabelMode: true,\n  extraRollupPlugins: [\n    copy({\n      targets: [{ src: 'src/index.d.ts', dest: 'dist/' }],\n    }),\n  ],\n  extraBabelPlugins: [\n    [\n      'import',\n      {\n        libraryName: 'antd',\n        libraryDirectory: 'es',\n        style: true,\n      },\n      'antd',\n    ],\n    // [\n    //   'import',\n    //   {\n    //     libraryName: '@ant-design/icons',\n    //     libraryDirectory: 'lib/icons',\n    //     camel2DashComponentName: false,\n    //   },\n    //   '@ant-design/icons',\n    // ],\n  ],\n};\n"
  },
  {
    "path": "packages/data-render/CHANGELOG.md",
    "content": "# 更新日志\n\n\n"
  },
  {
    "path": "packages/data-render/CONTRIBUTING.md",
    "content": ""
  },
  {
    "path": "packages/data-render/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2019-present XRender Team\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "packages/data-render/README.md",
    "content": "<div style=\"display:flex;align-items:center;margin-bottom:24px\">\n  <img src=\"https://img.alicdn.com/tfs/TB17UtINiLaK1RjSZFxXXamPFXa-606-643.png\" alt=\"logo\" width=\"48px\"/>\n  <span style=\"font-size:30px;font-weight:600;display:inline-block;margin-left:12px\">DataView</span>\n</div>\n<p style=\"display:flex;justify-content:space-between;width:440px\">\n  <a href=\"https://www.npmjs.com/package/@xrenders/data-render\" target=\"_blank\">\n    <img alt=\"npm\" src=\"https://img.shields.io/npm/v/@xrenders/data-render.svg?maxAge=3600&style=flat-square\">\n  </a>\n  <a href=\"https://npmjs.org/package/@xrenders/data-render\" target=\"_blank\">\n    <img alt=\"NPM downloads\" src=\"https://img.shields.io/npm/dm/@xrenders/data-render.svg?style=flat-square\">\n  </a>\n  <a href=\"https://npmjs.org/package/@xrenders/data-render\" target=\"_blank\">\n    <img alt=\"NPM all downloads\" src=\"https://img.shields.io/npm/dt/@xrenders/data-render.svg?style=flat-square\">\n  </a>\n  <a>\n    <img alt=\"PRs Welcome\" src=\"https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square\">\n  </a>\n</p>\n\n中后台详情页解决方案，通过 schema 协议渲染页面\n\n\n## 安装\n```shell\nnpm i @xrenders/data-render --save\n```\n\n## 使用方式\n\n**函数组件**\n\n```jsx\n/**\n * transform: true\n * defaultShowCode: true\n */\nimport React from 'react';\nimport DataView from '@xrenders/data-render';\n\nconst data = {\n  \"creator\": \"清风徐来\",\n  \"relevanceCode\": \"421421\",\n  \"desc\": \"浙江省杭州市工专路\",\n  \"create-time\": \"2019-10-10\",\n  \"effective-date\": \"2019-10-10 ～ 2020-10-31\",\n  \"safety\": {\n    \"name\": \"Test demo 001\",\n    \"app\": \"中后台详情页\",\n    \"mode\": \"代码包\",\n    \"yum\": \"244444390482344744484889\",\n    \"fore\": \"23\"\n  },\n}\n\nconst schema = [\n  {\n    \"widget\": \"FDescriptions\",\n    \"column\": 3,\n    \"items\": [\n      {\n        \"label\": \"创建人\",\n        \"dataKey\": \"creator\"\n      },\n      {\n        \"label\": \"关联单据\",\n        \"dataKey\": \"relevanceCode\"\n      },\n      {\n        \"label\": \"单据备注\",\n        \"dataKey\": \"desc\"\n      },\n      {\n        \"label\": \"创建时间\",\n        \"dataKey\": \"create-time\"\n      },\n      {\n        \"label\": \"生效日期\",\n        \"dataKey\": \"effective-date\"\n      }\n    ]\n  }\n]\n\nexport default () => {\n  return (\n    <DataView schema={schema} data={data} />\n  );\n}\n```"
  },
  {
    "path": "packages/data-render/package.json",
    "content": "{\n  \"name\": \"@xrenders/data-render\",\n  \"version\": \"1.0.1-alpha.5\",\n  \"description\": \"\",\n  \"keywords\": [\n    \"DataView\",\n    \"Render\",\n    \"XRender\",\n    \"React\",\n    \"Json Schema\",\n    \"Ant Design\"\n  ],\n  \"homepage\": \"https://xrender.fun/form-render\",\n  \"bugs\": {\n    \"url\": \"https://github.com/alibaba/x-render/issues\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git@github.com:alibaba/form-render.git\"\n  },\n  \"license\": \"MIT\",\n  \"contributors\": [\n    {\n      \"name\": \"Tw93\",\n      \"email\": \"tw93@qq.com\"\n    },\n    {\n      \"name\": \"lhbxs\",\n      \"email\": \"596850703@qq.com\"\n    }\n  ],\n  \"main\": \"lib/index.js\",\n  \"module\": \"es/index.js\",\n  \"files\": [\n    \"es\",\n    \"lib\",\n    \"package.json\"\n  ],\n  \"scripts\": {\n    \"beta\": \"npm publish --tag beta\",\n    \"alpha\": \"npm publish --tag alpha --access public\",\n    \"build\": \"father-build\",\n    \"next\": \"npm publish --tag next\",\n    \"prepare\": \"npm run build\",\n    \"prettier\": \"prettier --write \\\"**/*.{js,jsx,tsx,ts,less,md,json}\\\"\",\n    \"postpublish\": \"git push --tags\",\n    \"test:ui\": \"vitest --ui\"\n  },\n  \"lint-staged\": {\n    \"*.{js,jsx,less,md,json}\": [\n      \"prettier --write\"\n    ],\n    \"*.ts?(x)\": [\n      \"prettier --parser=typescript --write\"\n    ]\n  },\n  \"dependencies\": {\n    \"@ant-design/icons\": \"^4.0.2\",\n    \"async-validator\": \"^3.5.1\",\n    \"classnames\": \"^2.3.1\",\n    \"color\": \"^3.1.2\",\n    \"lodash-es\": \"^4.17.21\",\n    \"dayjs\": \"^1.11.7\",\n    \"rc-color-picker\": \"^1.2.6\",\n    \"virtualizedtableforantd4\": \"^1.1.2\",\n    \"zustand\": \"^4.1.5\",\n    \"ahooks\": \"3.7.5\"\n  },\n  \"devDependencies\": {\n    \"deep-equal\": \"^2.0.3\",\n    \"rollup-plugin-copy\": \"^3.4.0\"\n  },\n  \"peerDependencies\": {\n    \"antd\": \"4.x || 5.x\",\n    \"react\": \">=16.8.0\",\n    \"react-dom\": \">=16.8.0\"\n  },\n  \"gitHooks\": {\n    \"pre-commit\": \"lint-staged\"\n  },\n  \"sideEffect\": false\n}"
  },
  {
    "path": "packages/data-render/src/core/index.less",
    "content": ""
  },
  {
    "path": "packages/data-render/src/core/index.tsx",
    "content": "import React from 'react';\nimport { isFunction } from 'lodash-es';\nimport RenderCore from './renderer';\nimport { getRequestParams } from '../utils/common';\nimport { parseExpression } from '../models/expression';\nimport { DataVProps } from '../type';\nimport './index.less';\n\nconst defaultConfig = {\n  showLevel: 0,\n};\n\nexport default (props: DataVProps) => {\n  const { schema, data, sourceData, widgets, methods = {}, config = {} } = props;\n\n  // 获取顶层数据\n  const getSourceData = () => {\n    return sourceData || data;\n  };\n\n  // 获取外部自定义方法\n  const getMethod = (_name: string) => {\n    let name = _name;\n    if (name && name.startsWith('method:')) {\n      const [_, funcName] = _name.replace(/\\s+/g, '').split('method:');\n      name = funcName;\n    }\n\n    const func = methods[name];\n    if (!isFunction(func)) {\n      console.warn(`${name}：自定义方法不存在或者 ${name}：并不是一个函数`);\n      return () => null;\n    }\n\n    return func;\n  };\n\n  // 获取外部自定义组件\n  const getWidget = (widgetName: string) => {\n    if (!widgets?.[widgetName]) {\n      console.warn(`${widgets} 未找到对应组件，请检查协议配置`);\n      return null;\n    }\n    return (widgets as any)[widgetName];\n  };\n\n  // 获取接口配置\n  const getRequestConfig = () => {\n    return {\n      dataKey: 'module',\n      ...config.request,\n    };\n  };\n\n  // 获取接口入参\n  const getRequestPrams = (params: any, { insideData }: any) => {\n    return getRequestParams(params, data, insideData);\n  };\n\n  const getConfig = () => ({\n    ...defaultConfig,\n    ...config,\n  });\n\n  const getDataFromKey = (_key: string, currentData: any, defaultValue: any) => {\n    if (!_key) {\n      return currentData;\n    }\n    const key = ['data:', 'source:', 'parent:', '{{'].some(item => _key.includes(item)) ? _key : `$d.${_key}`;\n    return parseExpression(key, { currentData, sourceData: data }) ?? defaultValue;\n  }\n\n  const renderer = ({ data, schema, addons }: any) => {\n    return <RenderCore schema={schema} data={data} addons={addons} />;\n  };\n\n  return (\n    <RenderCore\n      data={data || {}}\n      schema={schema}\n      addons={{\n        renderer,\n        getMethod,\n        getWidget,\n        getSourceData,\n        getRequestConfig,\n        getRequestPrams,\n        getConfig,\n        getDataFromKey\n      }}\n    />\n  );\n};\n"
  },
  {
    "path": "packages/data-render/src/core/renderer.tsx",
    "content": "import React from 'react';\nimport { Empty, Skeleton } from 'antd';\nimport { get } from 'lodash-es';\nimport decorator from '../models/resolver';\n\n// 对 schema 进行处理\nconst transformSchema = (schema: any, data: any) => {\n  if (!schema || typeof schema !== 'object') {\n    return [];\n  }\n\n  const arrayList = Array.isArray(schema) ? schema : [schema];\n\n  // 处理 repeat 组件按数据多次进行展示\n  const result: any[] = [];\n  arrayList.forEach((widget: any) => {\n    const { repeat, ...ohterProps } = widget;\n\n    // 不存在多次展示，直接返回\n    if (!repeat) {\n      result.push(widget);\n      return;\n    }\n\n    // 如果 repeat 是数组，直接根据 repeat 数据进行展示\n    if (Array.isArray(repeat)) {\n      repeat.forEach((item: any) => {\n        result.push({ ...ohterProps, ...item });\n      });\n      return;\n    }\n\n    if (repeat.dataKey) {\n      const array = get(data, repeat.dataKey, []);\n      array.forEach((item: any) => {\n        result.push({ ...ohterProps, data: item });\n      });\n    }\n\n    if (Array.isArray(data)) {\n      data.forEach((_, index: number) => {\n        result.push({ ...ohterProps, repeatIndex: index });\n      });\n    }\n  });\n\n  return result;\n};\n\n/**\n * 渲染器\n */\nexport default (props: any): any => {\n  const { schema, data, addons, showEmpty } = props;\n  const List = transformSchema(schema, data);\n\n  const componentList = List.map((item: any, index: number) => {\n    let currData = data;\n    if ((data && item.repeatIndex) || item.repeatIndex === 0) {\n      currData = data[item.repeatIndex];\n    }\n    const componentInfo = decorator(item, currData, addons);\n\n    if (!componentInfo) {\n      return;\n    }\n\n    const { component: Component, componentData, componentProps, asyncComptProps } = componentInfo;\n\n    return (\n      <Component\n        key={index}\n        {...componentProps}\n        data={componentData}\n        {...asyncComptProps}\n        addons={{\n          ...addons,\n          dataKey: item.dataKey,\n          getParentData: () => {\n            return currData;\n          }\n        }}\n      />\n    );\n  });\n\n  if (componentList.length === 0) {\n    if (showEmpty) {\n      return (\n        <Skeleton active loading={!addons.getSourceData()}>\n          <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />\n        </Skeleton>\n      );\n    }\n    return null;\n  }\n\n  return componentList;\n};\n"
  },
  {
    "path": "packages/data-render/src/index.ts",
    "content": "import DRender from './core';\nimport withProvider from './withProvider';\nimport * as defaultWidgets from './widgets';\n\nexport { default as FRender } from './core/renderer';\n\nexport const DataSlimRender = withProvider(DRender);\nexport default withProvider(DRender, defaultWidgets);\n"
  },
  {
    "path": "packages/data-render/src/models/context.ts",
    "content": "import { createContext } from 'react';\n\nexport const FRContext = createContext(null);\n\nexport const ConfigContext = createContext(null);\n"
  },
  {
    "path": "packages/data-render/src/models/expression.ts",
    "content": "import { cloneDeep, isObject } from 'lodash-es';\n\nconst recursionArray: any = (list: any[], dataMap: any) => {\n  const result = list.map((item) => {\n    if (Array.isArray(item)) {\n      return recursionArray(item);\n    }\n    if (isObject(item)) {\n      return parseAllExpression(item, dataMap);\n    }\n\n    if (isExpression(item)) {\n      return parseExpression(item, dataMap);\n    }\n    return item;\n  });\n\n  return result;\n};\n\nexport const isExpression = (str: string) => {\n  if (typeof str !== 'string') {\n    return false;\n  }\n\n  const pattern = /^{\\s*{(.+)}\\s*}$/;\n  const reg1 = /^{\\s*{function\\(.+}\\s*}$/;\n\n  return str.trim().match(pattern) && !str.trim().match(reg1);\n};\n\nexport const parseExpression = (\n  func: any,\n  { sourceData, parentData, currentData }: { sourceData?: any; parentData?: any; currentData?: any },\n) => {\n  if (typeof func === 'string') {\n    const funcBody = func.replace(/^{\\s*{/g, '').replace(/}\\s*}$/g, '');\n\n    const funcStr = `\n      return ${funcBody\n        .replace(/source:/g, JSON.stringify(sourceData) + '.')\n        .replace(/data:/g, JSON.stringify(currentData) + '.')\n        .replace(/parent:/g, JSON.stringify(parentData) + '.')\n        // 兼容两种写法\n        .replace(/\\$s/g, JSON.stringify(sourceData))\n        .replace(/\\$d/g, JSON.stringify(currentData))\n        .replace(/\\$p/g, JSON.stringify(parentData))}\n    `;\n\n    try {\n      const result = Function(funcStr)();\n      return result;\n    } catch (error) {\n      // console.log(error);\n      return null; // 如果计算有错误，return null 最合适\n    }\n  }\n\n  return func;\n};\n\nexport const parseAllExpression = (_schema: any, dataMap: any) => {\n  const schema: any = cloneDeep(_schema);\n\n  Object.keys(schema).forEach((key) => {\n    const item: any = schema[key];\n    const { mustacheParse } = item || {};\n\n    if (Array.isArray(item)) {\n      schema[key] = recursionArray(item, dataMap);\n    }\n    if (isObject(item) && (mustacheParse ?? true)) {\n      schema[key] = parseAllExpression(item, dataMap);\n    } else if (isExpression(item)) {\n      schema[key] = parseExpression(item, dataMap);\n    }\n  });\n\n  return schema;\n};\n"
  },
  {
    "path": "packages/data-render/src/models/isHidden.ts",
    "content": "import { getValueFromKey, parseString } from '../utils/common';\n\nconst hasAnd = (string: string) => string.includes('&&');\nconst hasOr = (string: string) => string.includes('||');\nconst getList = (string: string, operator: string) =>\n  string.split(operator).map((item: string) => item.trim());\n\nconst operators = ['===', '==', '!=', '>', '<', 'has', '!has']; // 支持四种基本运算符\n\n// 计算单个表达式的hidden值\nconst calculate = (str: string, data: any, addons: any) => {\n  if (!data || typeof data !== 'object' || Object.keys(data).length === 0) {\n    return false;\n  }\n\n  try {\n    const operator: any = operators.find((item) => {\n      let flag = str.indexOf(item) > -1;\n      if (item === 'has' && str.indexOf('!has') > -1) {\n        flag = false;\n      }\n      return flag;\n    });\n\n    let [left, right]: any = str\n      .replace(/\\s+/g, '')\n      .split(operator)\n      .map((item) => item.trim());\n\n    if (left.includes('data:')) {\n      left = getValueFromKey({\n        data,\n        path: left.replace(/data:/g, ''),\n        defaultValue: right === undefined ? '' : 'left-null',\n        addons,\n      });\n    } else if (left.includes('source:')) {\n      left = getValueFromKey({\n        data: addons.getSourceData(),\n        path: left.replace(/source:/g, ''),\n        defaultValue: right === undefined ? '' : 'left-null',\n        addons,\n      });\n    }\n\n    if (right === undefined) {\n      return left;\n    }\n\n    if (right?.includes('data:')) {\n      right = getValueFromKey({\n        data,\n        path: right.replace(/data:/g, ''),\n        defaultValue: left === undefined ? '' : 'right-null',\n        addons,\n      });\n    } else if (right?.includes('source:')) {\n      right = getValueFromKey({\n        data: addons.getSourceData(),\n        path: left.replace(/source:/g, ''),\n        defaultValue: left === undefined ? '' : 'right-null',\n        addons,\n      });\n    }\n\n    if (operator.includes('!has')) {\n      try {\n        if (!Array.isArray(left)) {\n          left = JSON.parse(left);\n        }\n\n        left = left.map((item: any) => item + '');\n        return !left.includes(`${right}`);\n      } catch (error) {\n        return false;\n      }\n    }\n\n    if (operator.includes('has')) {\n      try {\n        if (!Array.isArray(left)) {\n          left = JSON.parse(left);\n        }\n        left = left.map((item: any) => item + '');\n        return left.includes(`${right}`);\n      } catch (error) {\n        return false;\n      }\n    }\n\n    return parseString(`'${left}'${operator}'${right}'`);\n  } catch (e) {\n    console.error(e);\n  }\n  return false;\n};\n\n/**\n * 表达式判断\n */\nexport default (key: any, data: any, addons: any) => {\n  let newKey = key;\n\n  // 说明是函数表达式\n  if (\n    newKey.substring(0, 2) === '{{' &&\n    newKey.substring(newKey.length - 2, newKey.length) === '}}'\n  ) {\n    newKey = newKey.substring(2, newKey.length - 2);\n  }\n\n  // 相等，说明不是函数表达式\n  if (newKey === key || operators.every((item) => !newKey.includes(item))) {\n    return !getValueFromKey({ data, path: newKey, defaultValue: false, addons });\n  }\n\n  // 两种运算符都不存在\n  if (!hasOr(newKey) && !hasAnd(newKey)) {\n    return !calculate(newKey, data, addons);\n  }\n\n  // 只有 && 运算符\n  if (!hasOr(newKey) && hasAnd(newKey)) {\n    return !getList(newKey, '&&').every((item: string) => {\n      const res = calculate(item, data, addons);\n      return res;\n    });\n  }\n\n  // 只有 || 运算符\n  if (hasOr(newKey) && !hasAnd(newKey)) {\n    return !getList(newKey, '||').some((item: string) => calculate(item, data, addons));\n  }\n\n  // 两种运算符都存在\n  return !getList(newKey, '||').some((item: string) => {\n    // 存在 && 运算符\n    if (hasAnd(item)) {\n      return getList(item, '&&').every((item: string) => calculate(item, data, addons));\n    }\n    return calculate(item, data, addons);\n  });\n};\n"
  },
  {
    "path": "packages/data-render/src/models/resolver.tsx",
    "content": "import React from 'react';\nimport { transformData, isDataEmpty } from '../utils/common';\nimport classnames from 'classnames';\nimport { parseAllExpression } from './expression';\n\nconst InnerHtml = (props: any) => {\n  const { data, style, className } = props;\n\n  if (typeof data === 'object') {\n    return null;\n  }\n\n  return (\n    <span\n      className={classnames(null, { [className]: !!className })}\n      style={style}\n      dangerouslySetInnerHTML={{ __html: data }}\n    />\n  );\n};\n\nexport default (props: any, parentData: any, addons: any) => {\n  const { data, dataKey, defaultValue, children, ...rest } = props;\n  const { getDataFromKey, getSourceData, getMethod, getConfig, getWidget } = addons;\n  const sourceData = getSourceData();\n\n  // 当组件配置 dataKey，根据 dataKey 获取服务端对应数据，否则继承父级数据\n  let value = dataKey ? getDataFromKey(dataKey, parentData, defaultValue) : defaultValue ?? parentData;\n  \n  // 如果有传人的数据，直接使用\n  if (data !== undefined) {\n    value = data;\n  }\n\n  // 解析函数表达式，替换值\n  const restProps = parseAllExpression(rest, {\n    parentData,\n    sourceData,\n    currentData: value,\n  });\n  // console.log('before:', props, 'after:', restProps);\n\n  const { widget, showLevel: _showLevel, format, getCompProps, hidden, ...componentProps } = restProps;\n\n  if (hidden && typeof hidden === 'boolean') {\n    return;\n  }\n\n  if (hidden?.includes?.('method:')) {\n    // 如果是通过协议声明的函数，获取函数并执行\n    const func = getMethod(hidden);\n    if (func && func(props, { data: value, parentData, sourceData })) {\n      return null;\n    }\n  }\n\n  // 根据 widget 创建对应组件，先从外部获取，如果没有，再从内置组件中获取\n  const component = getWidget(widget);\n\n  if (!component) {\n    console.warn(widget, '未找到对应组件，请检查配置项 widget 是否配置正确');\n    return null;\n  }\n\n  // 数据进行格式化\n  if (['html'].includes(format?.type)) {\n    value = <InnerHtml data={value} />;\n  } else {\n    value = transformData(value, format, parentData, addons);\n  }\n\n  const showLevel = _showLevel ?? getConfig()?.showLevel;\n\n  // 当配置 showLevel 需要校验数据是否为空\n  if ([1, 2].includes(showLevel) && isDataEmpty(value, showLevel)) {\n    return null;\n  }\n\n  if (children) {\n    componentProps.childSchema = children;\n  }\n\n  // 通过外部方法，获取组件配置信息\n  let asyncComptProps = {};\n  const getPropsFunc = getCompProps && getMethod(getCompProps);\n  if (getPropsFunc) {\n    asyncComptProps = getPropsFunc(props, { data: value, parentData, sourceData }) || {};\n  }\n\n  return {\n    componentProps,\n    asyncComptProps,\n    componentData: value,\n    component\n  };\n};\n"
  },
  {
    "path": "packages/data-render/src/models/store.ts",
    "content": "import { createStore as createx } from 'zustand';\n\ntype FormStore = {\n  schema?: any;\n  flattenSchema: any;\n  context?: any;\n  initialized: boolean;\n  init?: (schema: FormStore['schema']) => any;\n  setContext: (context: any) => any;\n};\n\n// 将 useStore 改为 createStore， 并把它改为 create 方法\nexport const createStore = () =>\n  createx<FormStore>((setState: any, get: any) => ({\n    initialized: false,\n    schema: {},\n    flattenSchema: {},\n    context: {},\n    init: (data) => {\n      return setState({\n        initialized: true,\n        ...data,\n      });\n    },\n    setContext: (context) => {\n      return setState({ context });\n    },\n  }));\n"
  },
  {
    "path": "packages/data-render/src/type.ts",
    "content": "export interface DataVProps {\n  [key: string]: any;\n}\n\nexport default DataVProps;\n"
  },
  {
    "path": "packages/data-render/src/utils/common.ts",
    "content": "import { message } from 'antd';\nimport classnames from 'classnames';\nimport dayjs from 'dayjs';\n\nimport timezone from 'dayjs/plugin/timezone';\nimport utc from 'dayjs/plugin/utc';\nimport { isNumber, isObject, isString, get } from 'lodash-es';\ndayjs.extend(utc);\ndayjs.extend(timezone);\n\nexport const combineClass = (name: any, data: any, extra = {}) => {\n  let result: any = {};\n\n  if (typeof data === 'string') {\n    result = { [data]: true };\n  }\n  if (Array.isArray(data)) {\n    data.forEach((item) => {\n      result[item] = true;\n    });\n  }\n\n  return classnames(name, { ...result, ...extra });\n};\n\n/**\n *\n * @param string\n * @returns\n */\nexport const parseString = (string: string) => Function('\"use strict\";return (' + string + ')')();\n\n/**\n * @param path // 对应数据的路径，例如：'a.b.c'\n * @param defaultValue // 默认值\n * @param data // 数据\n * @param addons // 渲染器方法集，访问 getSourceData\n * @returns value\n */\nexport const getValueFromKey = (props: {\n  path: string;\n  data?: any;\n  defaultValue?: any;\n  addons: any;\n  valueType?: string;\n}): string | boolean => {\n  const { data, path = '', defaultValue = '', addons, valueType } = props;\n\n  let result = null;\n  let negation = null; // 否定标识 ！\n  let dataPath = path;\n\n  if (path.substring(0, 2) === '!!') {\n    negation = '!!';\n    dataPath = path.substring(2);\n  } else if (path.substring(0, 1) === '!') {\n    negation = '!';\n    dataPath = path.substring(1);\n  }\n\n  // 带 source 标识，表示从顶层获取数据\n  if (dataPath.includes('source:')) {\n    const [_, sourcePath]: any = dataPath.split('source:');\n    const sourceData = addons.getSourceData();\n    result = get(sourceData, sourcePath);\n  } else {\n    result = get(data, dataPath);\n  }\n\n  if (!result && result !== 0 && typeof result !== 'boolean') {\n    result = defaultValue;\n  }\n\n  // 根据 negation 取反, 返回结果\n  if (negation === '!') {\n    return !result;\n  }\n\n  if (negation === '!!') {\n    return !!result;\n  }\n\n  if (result && valueType === '!object' && typeof result === 'object') {\n    return '';\n  }\n\n  return result;\n};\n\n/**\n * 数据格式转换\n */\nexport const transformData = (value: any, format: any, parentData?: any, addons?: any) => {\n  if (!format || (!value && value !== 0 && format.type !== 'dateTime-range')) {\n    return value;\n  }\n\n  const getValue = (value: any, item: any) => {\n    const { type, config, conn = ' ~ ' }: any = item;\n    let result = value;\n\n    if (type === 'dateTime-range') {\n      const { startKey, endKey }: any = config;\n      let startTime: any = getValueFromKey({ data: parentData, path: startKey, addons });\n      if (startTime) {\n        startTime = dayjs.tz(startTime).format(config.format || 'YYYY-MM-DD HH:mm:ss');\n      }\n\n      let endTime: any = getValueFromKey({ data: parentData, path: endKey, addons });\n      if (endTime) {\n        endTime = dayjs.tz(endTime).format(config.format || 'YYYY-MM-DD HH:mm:ss');\n      }\n\n      if (startTime !== 'Invalid Date') {\n        result = startTime;\n      }\n\n      if (endTime !== 'Invalid Date') {\n        result += conn + endTime;\n      }\n    }\n    // 日期\n    if (type === 'date') {\n      result = dayjs.tz(value).format(config || 'YYYY-MM-DD');\n    }\n\n    // 时间\n    if (type === 'dateTime') {\n      result = dayjs.tz(value).format(config || 'YYYY-MM-DD HH:mm:ss');\n    }\n\n    // 枚举\n    if (type === 'enum' && typeof config === 'object') {\n      result = config[value] || config.default || value;\n    }\n\n    // 单位: 左边\n    if (type === 'leftUnit' && config) {\n      if (config.includes('dataKey:')) {\n        result = `${getValueFromKey({\n          data: parentData,\n          path: config.split('dataKey:')[1],\n          addons,\n          defaultValue: '',\n        })} ${value}`;\n      } else {\n        result = `${config} ${value}`;\n      }\n    }\n\n    // 单位: 右边\n    if (type === 'rightUnit' && config) {\n      if (config.includes('dataKey:')) {\n        result = `${value} ${getValueFromKey({\n          data: parentData,\n          path: config.split('dataKey:')[1],\n          addons,\n          defaultValue: '',\n        })}`;\n      } else {\n        result = `${value} ${config}`;\n      }\n    }\n\n    // 一分\n    if (type === 'penny') {\n      result = (value * 100).toFixed(config || 2);\n    }\n\n    // 分转元\n    if (type === 'spunYuan') {\n      result = value / 100;\n    }\n\n    // 百分比\n    if (type === 'percent') {\n      result = (value * 100).toFixed(config || 2) + '%';\n    }\n\n    // 浮点数转换\n    if (type === 'float') {\n      result = (value * 1).toFixed(config || 2);\n    }\n\n    // 函数\n    if (type === 'function') {\n      result = parseString(config)(value);\n    }\n\n    if (result === 'Invalid Date') {\n      result = '';\n      console.warn('日期转换错误');\n    }\n\n    if (result === 'NaN%') {\n      result = '';\n      console.warn('百分率转换错误');\n    }\n\n    return result;\n  };\n\n  return Array.isArray(format)\n    ? format.reduce((preValue, item) => getValue(preValue, item), value)\n    : getValue(value, format);\n};\n\n// 是否是数组\nexport function isArray(input: any): input is any[] {\n  return Array.isArray(input);\n}\n\n// 获取请求参数\nexport const getRequestParams = (cond: any, data: any, insideData?: any) => {\n  if (!cond) {\n    return {};\n  }\n\n  let params: any = {};\n  for (let key in cond) {\n    let value = get(data, cond[key], null);\n    if (cond[key].includes('dataKey:')) {\n      value = get(insideData, cond[key].split(':')[1], null);\n    }\n    params[key] = value;\n  }\n\n  return params;\n};\n\n// 复制\nexport const clipboardCopy = (url: string) => {\n  navigator.clipboard.writeText(url).then(\n    function () {\n      /* clipboard successfully set */\n      message.success('复制成功');\n    },\n    function () {\n      /* clipboard write failed */\n      message.error('复制失败');\n    },\n  );\n};\n\n// 判断数据为空\nexport const isDataEmpty = (data: any, level: 1 | 2) => {\n  if (\n    Object.prototype.toString.call(data) === '[object Object]' &&\n    Object.keys(data).length === 0\n  ) {\n    return true;\n  }\n\n  if (isArray(data) && data.length === 0) {\n    return true;\n  }\n\n  if (level === 1 && (!data || data === '0')) {\n    return true;\n  }\n\n  if (level === 2 && !data && data !== 0) {\n    return true;\n  }\n};\n\n// 判断是否是子协议\nexport const isReactNodeSchema = (schema: any = {}) => {\n  return JSON.stringify(schema).includes('widget');\n};\n\nexport const startsWith = (str: string, startStr: string) => {\n  const reg = new RegExp('^' + startStr);\n  return reg.test(str);\n};\n\nexport const isThenable = (func: any) => {\n  return Boolean(func && typeof func.then === 'function');\n};\n\nconst validatorMap = {\n  string: isString,\n  number: isNumber,\n  object: isObject,\n  array: isArray,\n};\n\nexport const isType = (val: any, types: Array<keyof typeof validatorMap>) => {\n  return types.some((t) => {\n    const func = validatorMap[t];\n    return func(val);\n  });\n};\n\nexport const transDataKeyToData = (object: any, { data, addons }: any) => {\n  // 进行动态数据绑定\n  Object.keys(object).forEach((key) => {\n    const item = object[key];\n    if (typeof item !== 'string') {\n      return;\n    }\n\n    if (startsWith(item, 'source:')) {\n      object[key] = getValueFromKey({ path: item, data, addons });\n      return;\n    }\n\n    if (startsWith(item, 'data:')) {\n      const path = item.split('data:')[1]?.trim();\n      object[key] = getValueFromKey({ path, data, addons });\n    }\n  });\n};\n"
  },
  {
    "path": "packages/data-render/src/widgets/FButton/index.less",
    "content": ".dr-button {\n  height: 22px;\n  padding: 0;\n  line-height: 22px;\n}\n\n.dr-button-modal {\n  .ant-modal-confirm-content {\n    margin-left: 0 !important;\n    overflow: auto;\n  }\n}\n"
  },
  {
    "path": "packages/data-render/src/widgets/FButton/index.tsx",
    "content": "import React from 'react';\nimport { Button, Modal, message, Popconfirm } from 'antd';\nimport type { ButtonProps, ModalProps, PopconfirmProps } from 'antd';\nimport createIconFont from '../utils/createIconFont';\nimport { combineClass, isThenable } from '../utils/common';\nimport { debounce as debounceFunc } from 'lodash-es';\nimport './index.less';\nconst OriginModal: any = Modal;\n\ninterface FButtonProps extends ButtonProps {\n  data: any;\n  content: any;\n  addons: Record<string, (...params: any) => any>;\n  /** 事件类型 */\n  eventType: string;\n  /** 传入的方法配置 */\n  method?:\n    | string\n    | {\n        [key: string]: any;\n        name: string;\n      };\n  /** 弹窗配置 */\n  modal?: ModalProps & { type?: string; request?: any };\n  /** 请求配置 */\n  request?: any;\n  /** 图标配置 */\n  iconSetting?: ButtonProps['icon'] & {\n    type?: any;\n    style?: React.CSSProperties;\n  };\n  /** 是否开启异步方法自动 loading */\n  autoLoading?: boolean;\n  /** loading 时的文案 */\n  loadingText?: string;\n  /** 气泡确认框 */\n  popConfirm?: Omit<PopconfirmProps, 'onConfirm'>;\n  /** 防抖配置 */\n  debounce?: {\n    /** 需要延迟的毫秒数 */\n    wait?: number;\n    /** 指定在延迟开始前调用 */\n    leading?: boolean;\n    /** 设置函数允许被延迟的最大值 */\n    maxWait?: number;\n    /** 指定在延迟结束后调用 */\n    trailing?: boolean;\n  };\n  autoLoding: boolean;\n}\n\nconst FButton: React.FC<FButtonProps> = (props) => {\n  const {\n    data,\n    addons,\n    className,\n    eventType,\n    content,\n    method,\n    modal,\n    href,\n    request,\n    type = 'link',\n    target = '_blank',\n    iconSetting,\n    debounce,\n    popConfirm,\n    autoLoading,\n    loadingText,\n    ...otherProps\n  } = props;\n\n  const [loading, setLoading] = React.useState(false);\n\n  // 发送请求\n  const sendRequest = async (req: any) => {\n    const { api, name, params: condition, config } = req;\n    const params = {\n      ...addons.getRequestParams(condition, { insideData: data }),\n    };\n    const requestFunc = addons.getMethod(name || 'request');\n    const requestConfig = addons.getRequestConfig();\n\n    const res = (await requestFunc(api, params, config, request)) || {};\n    return res[requestConfig.dataKey];\n  };\n\n  // 打开弹窗\n  const openModal = (modalData?: any) => {\n    const {\n      type = 'info',\n      children,\n      width = '640px',\n      centered = true,\n      okText = '关闭',\n      ...modalProps\n    } = modal || {};\n    let contentWidth = width;\n    if (width === '100%') {\n      contentWidth =\n        (document.documentElement.clientWidth || document.body.clientWidth || 675) - 80;\n    }\n\n    OriginModal[type]({\n      icon: null,\n      width: contentWidth,\n      centered,\n      okText,\n      className: 'dr-button-modal',\n      ...modalProps,\n      content: addons.renderer({ schema: children, data: modalData, addons }),\n    });\n  };\n\n  // 打开 iframe\n  const openIframe = () => {\n    const windowW = (document.documentElement.clientWidth || document.body.clientWidth || 675) - 80;\n    const windowH =\n      (document.documentElement.clientHeight || document.body.clientHeight || 715) * 0.8;\n    Modal.info({\n      icon: null,\n      width: windowW,\n      centered: true,\n      okText: '关闭',\n      content: <iframe width=\"100%\" height={windowH} src={props.href} frameBorder=\"0\" />,\n    });\n  };\n\n  const apply = async (\n    func: (...params: any) => any | ((...params: any) => Promise<any>),\n    ...arg: any\n  ) => {\n    // popConfirm 自动处理了 Promise 方法，就不用自己处理了\n    if (popConfirm) {\n      return await func(...arg);\n    }\n\n    // Promise 方法自动 loading\n    const returnValue = func(...arg);\n    if (isThenable(returnValue) && autoLoading) {\n      setLoading(true);\n      return returnValue.then((res: any) => {\n        setLoading(false);\n        return res;\n      });\n    } else {\n      return returnValue;\n    }\n  };\n\n  const handleClick = async (ev: any) => {\n    // 传人外置方法，实现按钮点击事件\n    if (eventType === 'method') {\n      const funcName = typeof method === 'string' ? method : method?.name;\n      const func = addons.getMethod(funcName);\n      await apply(func, data, method);\n      return;\n    }\n\n    // 通过配置 API 请求协议，实现按钮点击事件\n    if (eventType === 'request') {\n      await apply(sendRequest, request);\n      return;\n    }\n\n    // 打开弹窗\n    if (eventType === 'modal') {\n      const { request } = modal || {};\n      if (request) {\n        const result = await apply(sendRequest, request);\n\n        if (!result) {\n          return message.error('数据异常！');\n        }\n\n        openModal(result);\n      } else {\n        openModal(addons.getParentData());\n      }\n      return;\n    }\n\n    // 打开 Iframe\n    if (eventType === 'iframe') {\n      openIframe();\n      return;\n    }\n  };\n\n  const Icon = createIconFont(addons.getConfig().iconFontUrl);\n  const iconContent = <Icon type={iconSetting?.type} style={iconSetting?.style} />;\n  const debounceClick = React.useCallback(debounceFunc(handleClick, debounce?.wait, debounce), [\n    method,\n    addons,\n    request,\n    eventType,\n    modal,\n    href,\n    debounce,\n  ]);\n\n  const onClick = debounce ? debounceClick : handleClick;\n  const text = content || data;\n\n  const buttonProps: ButtonProps = {\n    className: combineClass('dr-button', className),\n    type,\n    target,\n    href: eventType === 'iframe' ? undefined : href,\n    ...otherProps,\n    icon: iconSetting && iconContent,\n    loading,\n    onClick,\n  };\n\n  if (popConfirm) {\n    delete buttonProps.onClick;\n  }\n\n  const button = <Button {...buttonProps}>{loading && !!loadingText ? loadingText : text}</Button>;\n\n  if (popConfirm) {\n    return (\n      <Popconfirm {...popConfirm} onConfirm={onClick}>\n        {button}\n      </Popconfirm>\n    );\n  }\n\n  return button;\n};\n\nexport default FButton;\n"
  },
  {
    "path": "packages/data-render/src/widgets/FButtonFold/index.less",
    "content": ".dr-button {\n  height: 22px;\n  padding: 0;\n  line-height: 22px;\n}\n\n.dr-button-modal {\n  .ant-modal-confirm-content {\n    margin-left: 0 !important;\n    overflow: auto;\n  }\n}\n"
  },
  {
    "path": "packages/data-render/src/widgets/FButtonFold/index.tsx",
    "content": "import React, { useState } from 'react';\nimport { Button } from 'antd';\nimport { DownOutlined } from '@ant-design/icons';\nimport { combineClass } from '../utils/common';\nimport './index.less';\n\nconst FButtonFold = (props: any) => {\n  const {\n    data,\n    content,\n    addons,\n    className,\n    method,\n    childSchema,\n    expandRender,\n    ...otherProps\n  } = props;\n\n  const [isExpand, setExpand] = useState(false);\n\n  const handleClick = async (ev: any) => {\n    setExpand(!isExpand);\n    // 传人外置方法，实现按钮点击事件\n    if (method) {\n      const func = addons.getMethod(method?.name || method);\n      func(data, method);\n      return;\n    }\n  };\n\n  let expandContent;\n  if (expandRender) {\n    expandContent = addons.getMethod(expandRender)(data);\n  }\n\n  return (\n    <>\n      <Button\n        className={combineClass('dr-button-fold', className)}\n        type=\"link\"\n        {...otherProps}\n        onClick={handleClick}\n      >\n        {content}\n        <DownOutlined rotate={isExpand ? 180 : 0} />\n      </Button>\n      {isExpand && (\n        <div style={{ width: '100%' }}>\n          {expandRender\n            ? expandContent\n            : addons.renderer({ schema: childSchema, data, addons })}\n        </div>\n      )}\n    </>\n  );\n};\n\nexport default FButtonFold;\n"
  },
  {
    "path": "packages/data-render/src/widgets/FCard/index.less",
    "content": ".dr-card {\n  margin-top: 16px;\n  border-color: #f4f4f4;\n}\n"
  },
  {
    "path": "packages/data-render/src/widgets/FCard/index.tsx",
    "content": "import React from 'react';\nimport { Card } from 'antd';\nimport { combineClass, isReactNodeSchema } from '../utils/common';\nimport './index.less';\n\nexport default (props: any) => {\n  const { data, childSchema, className, style, title, extra, addons, ...otherProps } = props;\n\n  let cardTitle = <div dangerouslySetInnerHTML={{ __html: title }} />;\n  let cardExtra = extra;\n\n  if (isReactNodeSchema(title)) {\n    cardTitle = addons.renderer({ schema: title, data, addons });\n  }\n\n  if (isReactNodeSchema(extra)) {\n    cardExtra = addons.renderer({ schema: extra, data, addons });\n  }\n  \n  return (\n    <Card\n      className={combineClass('dr-card', className)}\n      style={style}\n      {...otherProps}\n      title={cardTitle}\n      extra={cardExtra}\n    >\n      {addons.renderer({ schema: childSchema, data, addons })}\n    </Card>\n  );\n}\n"
  },
  {
    "path": "packages/data-render/src/widgets/FCollapse/index.tsx",
    "content": "import React from 'react';\nimport { Skeleton, Empty } from 'antd';\nimport BaseCollapse from '../components/BaseCollapse';\n\ninterface IProps {\n  data: any;\n  childSchema: any[];\n  title: string;\n  header?: any[];\n  className?: any;\n  style?: object;\n  addons?: any;\n}\n\n/**\n * 折叠面板\n */\nconst FCollapse = (props: IProps) => {\n  const { data, header, childSchema, addons, ...collapseProps } = props;\n  const sourceData = addons.getSourceData();\n\n  let loading = false;\n  if (!sourceData || !Object.keys(sourceData).length) {\n    loading = true;\n  }\n\n  const content = addons.renderer({ schema: childSchema, data, addons, showEmpty: true });\n\n  return (\n    <BaseCollapse\n      header={addons.renderer({ schema: header, data, addons })}\n      {...collapseProps}\n    >\n      <Skeleton active loading={loading}>\n        {content || (\n          <Empty image=\"https://img.alicdn.com/imgextra/i2/O1CN01zkSBPx1w25lRyCZz0_!!6000000006249-55-tps-94-61.svg\" />\n        )}\n      </Skeleton>\n    </BaseCollapse>\n  );\n};\n\nexport default FCollapse;\n"
  },
  {
    "path": "packages/data-render/src/widgets/FDescriptions/index.less",
    "content": ".dv-descriptions {\n  background-color: #fff;\n  .item-label-box {\n    display: inline-flex;\n    align-items: center;\n  }\n\n  .item-label-icon {\n    margin-left: 2px;\n    color: #ccc;\n    font-size: 18px;\n    cursor: pointer;\n  }\n\n  .ant-descriptions-item-label {\n    // text-align: right;\n    display: block;\n    justify-content: right;\n    color: #8a8a8a;\n  }\n\n  .ant-descriptions-item-no-colon:after {\n    display: none;\n  }\n\n  .item-sub-label {\n    font-size: 12px;\n    margin-left: 5px;\n  }\n\n  .ant-descriptions-item-content {\n    display: flex;\n    flex-wrap: wrap;\n    align-items: center;\n    text-align: left;\n  }\n\n  .ant-descriptions-item-container {\n    align-items: flex-start;\n    .ant-descriptions-item-content {\n      width: 0;\n    }\n  }\n\n  .content-left-text {\n    margin-right: 10px;\n  }\n\n  .content-right-text {\n    margin-left: 10px;\n  }\n}\n\n.dv-descriptions-hasbackgournd {\n  padding: 16px 16px 0 16px;\n}\n\n.dv-descriptions-hasborder {\n  padding-bottom: 16px;\n}\n\n.dv-descriptions-no-header {\n  .ant-descriptions-header {\n    display: none;\n  }\n}\n"
  },
  {
    "path": "packages/data-render/src/widgets/FDescriptions/index.tsx",
    "content": "import React from 'react';\nimport { Descriptions, Tooltip } from 'antd';\nimport { isFunction } from 'lodash-es';\n\nimport createIconFont from '../utils/createIconFont';\nimport { combineClass } from '../utils/common';\nimport InnerHtml from '../components/InnerHtml';\nimport ReactNode from '../components/ReactNode';\nimport './index.less';\n\nconst { Item: DescriptionItem } = Descriptions;\nconst WIDGETNAME = 'dv-descriptions';\n\nconst renderItemLabel = (item: any, data: any, addons: any) => {\n  const { label, subLabel, labelToolTip } = item;\n\n  // 如果 label 是函数\n  if (isFunction(label)) {\n    return label(item, data);\n  }\n\n  // 如果 label 是字符串函数名\n  if (typeof label === 'string' && label.includes('method:')) {\n    const [_, funcName] = label.split('method:');\n    const func = addons.getMethod(funcName);\n    return func(item, data);\n  }\n\n  // 不存在 lable 提示，直接返回 label\n  if (!labelToolTip?.title) {\n    return (\n      <>\n        {label}\n        {subLabel && <span className='item-sub-label'>({subLabel})</span>}\n      </>\n    );\n  }\n\n  // 存在 lable 提示，进行聚合\n  let { icon, title, overlayInnerStyle, ...tooltipProps } = labelToolTip || {};\n  const Icon = createIconFont(addons.getConfig().iconFontUrl);\n\n  return (\n    <>\n      <span className='item-label-box'>\n        {label}\n        {labelToolTip && (\n          <Tooltip\n            color='#fff'\n            overlayInnerStyle={{ color: 'black', ...overlayInnerStyle }}\n            title={<InnerHtml data={title} />}\n            {...tooltipProps}\n          >\n            <Icon\n              type={icon?.type || 'icon-wenhao'}\n              className='item-label-icon'\n              style={{ ...icon?.style }}\n            />\n          </Tooltip>\n        )}\n      </span>\n      {subLabel && <div className='item-sub-label'>{subLabel}</div>}\n    </>\n  );\n};\n\nconst getDescriptionItems = (items = [], { addons, data, itemShowLevel }) => {\n  return items.map((_item: any) => {\n    const { defaultValue = '', dataKey, metaData, showLevel, hidden } = _item || {};\n    const { getDataFromKey, getSourceData, getMethod, getConfig } = addons;\n    const config = getConfig();\n\n    let item = _item;\n    if (metaData && config?.descriptions?.asyncItemProps) {\n      const asyncProps = config.descriptions.asyncItemProps(_item, { parentData: data, metaData: metaData })\n      item = {\n        ..._item,\n        ...asyncProps\n      };\n    }\n    \n    let _itemData = getDataFromKey(dataKey, data, defaultValue);\n    if (metaData && !dataKey) {\n      _itemData = item.data;\n    }\n\n    const level = showLevel ?? itemShowLevel ?? config?.showLevel;\n    // 如果描述项没有数据（包含 0），则描述项不显示\n    if (level === 1 && (!_itemData || _itemData === '0')) {\n      return null;\n    }\n\n    // 如果描述项没有数据（不包含 0），则示描述项不显\n    if (level === 2 && !_itemData && _itemData !== 0) {\n      return null;\n    }\n\n    if (hidden && typeof hidden === 'boolean') {\n      return null;\n    }\n\n    const sourceData = getSourceData();\n    // 如果是函数\n    if (typeof hidden === 'function' && hidden(data, sourceData)) {\n      return null;\n    }\n    \n    if (hidden?.includes?.('method:')) {\n      const func = getMethod(hidden);\n      if (func && func(data, sourceData)) {\n        return null;\n      }\n    }\n\n    return { ...item, _itemData };\n  }).filter(item => item);\n};\n\n/**\n * 描述列表\n */\nconst FDescriptions = (props: any) => {\n  const {\n    data,\n    className,\n    itemStyle,\n    labelStyle,\n    contentStyle,\n    itemShowLevel,\n    title,\n    extra,\n    addons,\n    items: _items,\n    ...restProps\n  } = props;\n\n  const items = getDescriptionItems(_items, { data, addons, itemShowLevel });\n  let _column = props.column || 3;\n  \n  return (\n    <Descriptions\n      className={combineClass(WIDGETNAME, className, {\n        'dv-descriptions-hasbackgournd': restProps?.style?.hasOwnProperty('backgroundColor'),\n        'dv-descriptions-hasborder': !!restProps?.bordered,\n        'dv-descriptions-no-header': !(title || extra),\n      })}\n      size='small'\n      {...restProps}\n      extra={<ReactNode schema={extra} data={data} addons={addons} />}\n      title={<ReactNode schema={title} data={data} addons={addons} />}\n    >\n      {items.map((item, index) => {\n        const {\n          showLevel,\n          _itemData,\n          span: _span,\n          style: _itemStyle,\n          labelStyle: _labelStyle,\n          contentHidden,\n          label,\n          dataKey,\n          ...itemProps\n        } = item || {};\n\n        const level = showLevel ?? itemShowLevel;\n        const leveMap: any = { 3: 1, 4: 2 };\n\n        // 动态计算 span\n        let span = _span || 1;\n        if (items[index+1]) {\n          const nextSpan = items[index+1].span ?? 1;\n\n          if ((_span || 1) + nextSpan > _column) {\n            span = _column;\n            _column = props.column;\n          } else {\n            _column = _column -1;\n          }\n        }\n\n        return (\n          <DescriptionItem\n            key={index}\n            label={renderItemLabel(item, data, addons)}\n            span={span}\n            style={{ ...itemStyle, ..._itemStyle }}\n            labelStyle={{ ...labelStyle, ..._labelStyle }}\n            contentStyle={{ ...contentStyle, ...item.contentStyle }}\n          >\n            {addons.renderer({ \n              data,\n              addons,\n              schema: {\n                widget: 'FText',\n                data: _itemData,\n                ...itemProps,\n                hidden: contentHidden,\n                useType: 'internal',\n                showLevel: leveMap[level] || null,\n              }\n            })}\n          </DescriptionItem>\n        );\n      })}\n    </Descriptions>\n  );\n};\n\nexport default FDescriptions;\n"
  },
  {
    "path": "packages/data-render/src/widgets/FEncryption/index.tsx",
    "content": "import React from 'react';\nimport Encryption from '../components/Encryption';\n\n/**\n *\n * 加密组件\n */\nconst FEncryption = (props: any) => {\n  const { data, method, addons, ...otherProps } = props;\n\n  const sourceData = addons.getSourceData();\n  const parentData = addons.getParentData();\n  const dataKey = addons.dataKey;\n  const encryInfo: any = sourceData?.encryInfo || {};\n  \n  let showKey = (method?.showKey ?? '') + dataKey;\n  if (method?.extraShowKey) {\n    showKey = parentData[dataKey + method.extraShowKey];\n  }\n\n  const conent = encryInfo[showKey] || '';\n\n  const handleClick = async (ev: any) => {\n    // 传人外置方法，实现按钮点击事件\n    let funcName = 'getEncryInfo'; // 默认使用这个方法\n    if (typeof method === 'string') {\n      funcName = method;\n    }\n    if (method?.name) {\n      funcName = method.name;\n    }\n\n    const func = addons.getMethod(funcName);\n    func({ dataKey, method, data: parentData }, ev);\n  };\n\n  return (\n    <Encryption\n      label={conent ? null : data}\n      data={conent}\n      onClick={handleClick}\n      {...otherProps}\n      iconFontUrl={addons.getConfig().iconFontUrl}\n    />\n  );\n};\n\nexport default FEncryption;\n"
  },
  {
    "path": "packages/data-render/src/widgets/FIconLabel/index.tsx",
    "content": "import React from 'react';\nimport IconLabel from '../components/IconLabel';\n\nconst FIconLabel = (props: any) => {\n  const {\n    data,\n    leftText,\n    rightText,\n    icon,\n    method,\n    href,\n    target = '_blank',\n    addons,\n    ...otherProps\n  } = props;\n\n  const parentData = addons.getParentData();\n  const dataKey = addons.dataKey;\n\n  const handleClick = async (ev: any) => {\n    if (href) {\n      if (target === '_self') {\n        window.location.href = href;\n      } else {\n        window.open(href);\n      }\n      return;\n    }\n    // 传人外置方法，实现按钮点击事件\n    const func = addons.getMethod(method?.name || method);\n    func({ dataKey, method, data: parentData }, ev);\n  };\n\n  return (\n    <IconLabel\n      onClick={handleClick}\n      data={`${leftText || ''} ${data} ${rightText || ''}`}\n      {...otherProps}\n      {...icon}\n      iconFontUrl={addons.getConfig().iconFontUrl}\n    />\n  );\n};\n\nexport default FIconLabel;\n"
  },
  {
    "path": "packages/data-render/src/widgets/FImage/index.less",
    "content": ".custom-image-widget {\n  width: 170px;\n  height: 100px;\n  margin: 8px 0;\n  background-image: linear-gradient(180deg, rgba(0, 0, 0, 0) 50%, rgba(0, 0, 0, 0.25) 100%);\n  border-radius: 2px;\n\n  img {\n    width: 100%;\n    height: 100%;\n    border-radius: 2px;\n  }\n}\n"
  },
  {
    "path": "packages/data-render/src/widgets/FImage/index.tsx",
    "content": "import React from 'react';\nimport { Image } from 'antd';\nimport { combineClass } from '../utils/common';\nimport './index.less';\n\n/**\n *\n * 图片组件\n */\nexport default (props: any) => {\n  const { data, className, style, images, preview = false, addons, ...imageProps } = props;\n\n  let src = data;\n  if (images) {\n    src = images[data] || images.default;\n  }\n\n  return (\n    <Image\n      className={combineClass('dv-image', className)}\n      style={style}\n      src={src}\n      preview={preview}\n      {...imageProps}\n    />\n  );\n}\n"
  },
  {
    "path": "packages/data-render/src/widgets/FLabel/index.less",
    "content": ".dr-label {\n  font-size: 14px;\n  line-height: 24px;\n\n  .label {\n    display: inline-block;\n    font-weight: bold;\n    text-align: right;\n  }\n\n  .content {\n    color: #141414;\n  }\n}\n"
  },
  {
    "path": "packages/data-render/src/widgets/FLabel/index.tsx",
    "content": "import React from 'react';\nimport { combineClass } from '../utils/common';\nimport FText from '../FText';\n\nimport './index.less';\n\nconst FLabel = (props: any) => {\n  const { colon = true, label, className, style, labelStyle, contentStyle, ...otherProps } = props;\n\n  return (\n    <div className={combineClass('dr-label', className)} style={style}>\n      <span className=\"label\" style={labelStyle}>\n        {label}\n        {colon && '：'}\n      </span>\n      <span className=\"content\" style={contentStyle}>\n        <FText {...otherProps} useType=\"internal\" />\n      </span>\n    </div>\n  );\n};\n\nexport default FLabel;\n"
  },
  {
    "path": "packages/data-render/src/widgets/FPanel/index.less",
    "content": ""
  },
  {
    "path": "packages/data-render/src/widgets/FPanel/index.tsx",
    "content": "import React from 'react';\nimport { combineClass } from '../utils/common';\nimport FTitle from '../FTitle';\n\nimport './index.less';\n\nconst FPanel = (props: any) => {\n  const {\n    className,\n    style,\n    data,\n    addons,\n\n    title,\n    titleStyle,\n    titleShowIcon,\n    childSchema,\n    render,\n  } = props;\n  \n  return (\n    <div className={combineClass('dr-panel', className)} style={style}>\n      {title && <FTitle data={title} showIcon={titleShowIcon} style={titleStyle} />}\n      {render\n        ? addons.getMethod(render)(data, props)\n        : addons.renderer({ schema: childSchema, data, addons })}\n    </div>\n  );\n};\n\nexport default FPanel;\n"
  },
  {
    "path": "packages/data-render/src/widgets/FProgress/index.less",
    "content": ".dr-progress {\n  width: 100%;\n  padding: 10px 0 0 5px;\n  overflow-y: auto;\n  white-space: nowrap;\n\n  .step-item {\n    position: relative;\n    display: inline-block;\n    width: 260px;\n    margin: 0 14px;\n    padding-top: 11px;\n    padding-right: 20px;\n    white-space: normal;\n    vertical-align: top;\n  }\n\n  .step-item-active {\n    border-top: 1px solid rgba(0, 0, 0, 0.15);\n  }\n\n  .step-title {\n    color: rgba(20, 20, 20, 0.85);\n    font-size: 14px;\n    line-height: 22px;\n  }\n\n  .step-time {\n    color: #8a8a8a;\n    font-size: 12px;\n    line-height: 22px;\n  }\n\n  .step-dot {\n    position: absolute;\n    top: -5px;\n    left: -18px;\n    width: 10px;\n    height: 10px;\n    background-color: #fff;\n    border: 2px solid #ccc;\n    border-radius: 10px;\n  }\n\n  .step-item-active .step-dot {\n    border: 2px solid #3f7ffb;\n  }\n}\n"
  },
  {
    "path": "packages/data-render/src/widgets/FProgress/index.tsx",
    "content": "import React from 'react';\nimport { combineClass } from '../utils/common';\nimport FTitle from '../FTitle';\nimport './index.less';\n\nconst FTimeline = (props: any) => {\n  const { data, title, className } = props;\n\n  return (\n    <>\n      <FTitle data={title} />\n      <div className={combineClass('dr-progress', className)}>\n        {data.map((item: any, index: number) => (\n          <div\n            key={item.content}\n            className={combineClass('step-item', null, { 'step-item-active': true })}\n            style={data.length - 1 === index ? { border: 'none' } : {}}\n          >\n            <div className=\"step-time\">{item.time}</div>\n            <div className=\"step-title\">{item.content}</div>\n            <div className=\"step-dot\"></div>\n          </div>\n        ))}\n      </div>\n    </>\n  );\n};\n\nexport default FTimeline;\n"
  },
  {
    "path": "packages/data-render/src/widgets/FProgressBar/index.tsx",
    "content": "import React from 'react';\nimport { Progress } from 'antd';\nimport type { ProgressProps } from 'antd';\nimport { combineClass, isType } from '../utils/common';\n\ninterface FProgressBarProps extends Omit<ProgressProps, 'formatFunc' | 'percent'> {\n  /** 内容的模版函数的 Key */\n  formatFunc: string;\n  data: any;\n  addons: Record<string, (...params: any) => any>;\n}\n\nconst FProgressBar: React.FC<FProgressBarProps> = (props) => {\n  const { className, style, formatFunc, addons, data, ...restProps } = props;\n\n  const handleformatFunc = (percent?: number, successPrecent?: number) => {\n    if (formatFunc) {\n      const func = addons.getMethod(formatFunc);\n      if (typeof func === 'function') {\n        return func(percent, successPrecent);\n      }\n    }\n\n    return percent + '%';\n  };\n\n  return (\n    <Progress\n      className={combineClass('dr-progess', className)}\n      style={style}\n      format={formatFunc ? handleformatFunc : undefined}\n      percent={isType(data, ['number', 'string']) ? data : 0}\n      {...restProps}\n    />\n  );\n};\n\nexport default FProgressBar;\n"
  },
  {
    "path": "packages/data-render/src/widgets/FRadioGroup/index.tsx",
    "content": "import React, { useEffect, useState } from 'react';\nimport { Radio, RadioChangeEvent } from 'antd';\nimport type { RadioGroupProps } from 'antd';\nimport { combineClass, isArray } from '../utils/common';\nimport { get } from 'lodash-es';\n\ninterface FRadioGroupProps extends Omit<RadioGroupProps, 'onChange'> {\n  data: any;\n  addons: Record<string, (...params: any) => any>;\n  /** 自定义 onChange 方法，参数是 event，需要返回最终的 value，支持异步方法 */\n  onChange: string;\n  /** 自定义 label, value, disable 的字段 */\n  optionKeys: {\n    label: string;\n    value: string;\n    disable: string;\n  };\n  [key: string]: any;\n}\n\nconst defaultOptionKeys = {\n  label: 'label',\n  value: 'value',\n  disable: 'disable',\n};\n\nconst FRadioGroup: React.FC<FRadioGroupProps> = (props) => {\n  const {\n    options,\n    data,\n    className,\n    onChange,\n    style,\n    addons,\n    dataKey,\n    optionKeys = defaultOptionKeys,\n    ...restProps\n  } = props;\n\n  useEffect(() => {\n    setValue(data);\n  }, [data]);\n\n  const [value, setValue] = useState();\n\n  let _options: any = isArray(options) ? options : [];\n  _options = _options?.map((item: any, index: number) => ({\n    label: get(item, optionKeys.label, ''),\n    value: get(item, optionKeys.value, index),\n    disable: get(item, optionKeys.disable, false),\n  }));\n\n  const handleChange = (e: RadioChangeEvent) => {\n    const value = e.target.value;\n    const func = addons.getMethod(onChange);\n    const result = func(value, { options });\n\n    if (result?.then) {\n      result.then((res: any) => {\n        if (res === 'stop') {\n          return;\n        }\n        setValue(res || value);\n      });\n      return;\n    } else if (result === 'stop') {\n      return;\n    }\n\n    setValue(value);\n  };\n\n  return (\n    <Radio.Group\n      className={combineClass('dr-radio-group', className)}\n      style={style}\n      onChange={handleChange}\n      value={value}\n      options={_options}\n      buttonStyle=\"solid\"\n      optionType=\"button\"\n      {...restProps}\n    />\n  );\n};\n\nexport default FRadioGroup;\n"
  },
  {
    "path": "packages/data-render/src/widgets/FRow/index.less",
    "content": ".dr-row {\n  height: 100%;\n\n  .col-item {\n    width: 0;\n  }\n\n  .col-item-background {\n    position: relative;\n    padding: 10px 15px;\n    background: #fff;\n    border-radius: 3px;\n\n    box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.06);\n  }\n}\n"
  },
  {
    "path": "packages/data-render/src/widgets/FRow/index.tsx",
    "content": "import React from 'react';\nimport { Row, Col } from 'antd';\n\nimport { combineClass } from '../utils/common';\n\nimport './index.less';\n\nconst FRow = (props: any) => {\n  const { items, data, hasBackground, className, addons, ...options } = props;\n\n  return (\n    <Row className={combineClass('dr-row', className)} wrap={false} {...options}>\n      {(items || []).map((item: any, index: number) => {\n        const { children, className: itemClassName, ...itemOptions } = item;\n        return (\n          <Col\n            key={index}\n            className={combineClass('col-item', {\n              'col-item-background': hasBackground,\n              [className]: itemClassName,\n            })}\n            {...itemOptions}\n          >\n            {addons.renderer({ key: index, schema: children, data, addons })}\n          </Col>\n        );\n      })}\n    </Row>\n  );\n};\n\nexport default FRow;\n"
  },
  {
    "path": "packages/data-render/src/widgets/FSpace/index.tsx",
    "content": "import React from 'react';\nimport { Space, Divider } from 'antd';\nimport { combineClass } from '../utils/common';\n\nconst FSpace = (props: any) => {\n  const {\n    data,\n    label,\n    split = 'divider',\n    className,\n    style,\n    labelStyle,\n    spaceStyle,\n    childSchema,\n    addons,\n    ...spaceProps\n  } = props;\n\n  return (\n    <div className={combineClass('dr-space', className)} style={style}>\n      {label && <span style={labelStyle}>{label}：</span>}\n      <Space style={spaceStyle} split={split ? <Divider type=\"vertical\" /> : null} {...spaceProps}>\n        {childSchema.map((schema: any, index: number) =>\n          addons.renderer({ key: index, schema, data, addons }),\n        )}\n      </Space>\n    </div>\n  );\n};\n\nexport default FSpace;\n"
  },
  {
    "path": "packages/data-render/src/widgets/FStatistic/index.tsx",
    "content": "import React from 'react';\nimport { Statistic } from 'antd';\nimport type { StatisticProps } from 'antd';\nimport { combineClass, isReactNodeSchema, isType } from '../utils/common';\n\ninterface FStatisticProps extends Omit<StatisticProps, 'value'> {\n  data: any;\n  addons: Record<string, (...params: any) => any>;\n}\n\nconst FStatistic: React.FC<FStatisticProps> = (props) => {\n  const { className, data, addons, style, formatter, title, suffix, prefix, ...restProps } =\n    props;\n\n  let titleNode = title;\n  let suffixNode = suffix;\n  let prefixNode = prefix;\n\n  if (isReactNodeSchema(title)) {\n    titleNode = addons.renderer({ schema: title, data, addons });\n  }\n\n  if (isReactNodeSchema(suffix)) {\n    suffixNode = addons.renderer({ schema: suffix, data, addons });\n  }\n\n  if (isReactNodeSchema(prefix)) {\n    prefixNode = addons.renderer({ schema: prefix, data, addons });\n  }\n\n  const handleFormatter = (value: any) => {\n    let result = value;\n    if (formatter) {\n      const func = addons.getMethod(formatter);\n      result = func(value);\n    }\n\n    return result;\n  };\n\n  return (\n    <Statistic\n      className={combineClass('dr-statistic', className)}\n      style={style}\n      value={isType(data, ['number', 'string']) ? data : 0}\n      title={titleNode}\n      suffix={suffixNode}\n      prefix={prefixNode}\n      formatter={handleFormatter}\n      {...restProps}\n    />\n  );\n};\n\nexport default FStatistic;\n"
  },
  {
    "path": "packages/data-render/src/widgets/FSteps/index.less",
    "content": ".dr-steps {\n  width: 100%;\n  padding: 10px 15px;\n}\n"
  },
  {
    "path": "packages/data-render/src/widgets/FSteps/index.tsx",
    "content": "import React from 'react';\nimport { Steps } from 'antd';\nimport { get } from 'lodash-es';\nimport { combineClass } from '../utils/common';\n\nimport './index.less';\n\nconst { Step } = Steps;\n\nconst FSteps = (props: any) => {\n  const {\n    data = [],\n    stautsKey = 'status',\n    titleKey = 'title',\n    descriptionKey = 'description',\n    subTitleKey = 'subTitle',\n    size = 'small',\n    className,\n    style,\n    addons,\n    ...otherProps\n  } = props;\n\n  return (\n    <Steps\n      {...otherProps}\n      size={size}\n      className={combineClass('dr-steps', className)}\n      style={style}\n    >\n      {data.map((item: any, index: number) => (\n        <Step\n          key={index}\n          status={get(item, stautsKey, '')}\n          title={get(item, titleKey, '')}\n          description={get(item, descriptionKey, '')}\n          subTitle={get(item, subTitleKey, '')}\n        />\n      ))}\n    </Steps>\n  );\n};\n\nexport default FSteps;\n"
  },
  {
    "path": "packages/data-render/src/widgets/FSuckNav/index.tsx",
    "content": "import React from 'react';\nimport { combineClass } from '../utils/common';\nimport Tabs from '../components/SuckTabs';\n\n/**\n *\n * 自定义 Tabs组件\n */\nconst FSuckNav = (props: any) => {\n  const {\n    scrollContainer,\n    items = [],\n    tabsId = 'tab',\n    startY = 68,\n    className,\n    style,\n    fixed = true,\n    addons,\n    ...otherProps\n  } = props;\n\n  const childSchema: any[] = [];\n  const tabs = items.map((item: any) => {\n    const { children, ...otherItem } = item;\n    childSchema.push(children);\n    return otherItem;\n  });\n\n  return (\n    <Tabs\n      className={className}\n      tabsId={tabsId}\n      tabs={tabs}\n      startY={startY}\n      fixed={fixed}\n      style={style}\n      scrollContainer={scrollContainer}\n    >\n      {childSchema.map((item: any, index: number) =>\n        addons.renderer({ key: index, schema: item, addons, ...otherProps }),\n      )}\n    </Tabs>\n  );\n};\n\nexport default FSuckNav;\n"
  },
  {
    "path": "packages/data-render/src/widgets/FTable/detalColumn.tsx",
    "content": "import React from 'react';\n\n// 处理表格 column\nconst detalColumn = (colmnMap: any, { addons, template, renderFRender, repeatIndex }: any) => {\n  const column: any = {};\n\n  for (const key of Object.keys(colmnMap || {})) {\n    const value = colmnMap[key];\n    const item = typeof value === 'string' ? { title: value } : { ...value };\n    const {\n      column: childColumn,\n      children,\n      dataEnum,\n      leftUnit,\n      rightUnit,\n      style,\n      ...otherItem\n    } = item;\n    const { render, notRender } = otherItem;\n\n    if (typeof otherItem?.title === 'string' && otherItem?.title?.includes('{index}')) {\n      otherItem.title = otherItem.title.replace('{index}', repeatIndex + 1);\n    }\n\n    // 对合并数据进行递归处理\n    if (childColumn) {\n      otherItem.column = detalColumn(childColumn, {\n        addons,\n        template,\n        renderFRender,\n        repeatIndex,\n      });\n    }\n\n    if (dataEnum || style || leftUnit || rightUnit) {\n      otherItem.render = (data: any) => {\n        let result = data;\n\n        // 当配置 dataEnum，根据 dataEnum 进行数据转换\n        if (dataEnum) {\n          result = dataEnum[result];\n        }\n\n        if (!result && result !== 0) {\n          return '';\n        }\n\n        // 当配置 leftUnit | rightUnit，需要进行数据转换\n        if (leftUnit) {\n          result = `${leftUnit} ${result}`;\n        }\n\n        if (rightUnit) {\n          result = `${result} ${rightUnit}`;\n        }\n\n        return <div style={style}>{result}</div>;\n      };\n    }\n\n    // 直接字符串声明的需要转译一下\n    if (typeof children === 'string') {\n      otherItem.render = (data: any) => renderFRender(data, [{ widget: children }]);\n    } else if (children) {\n      otherItem.render = (data: any) => renderFRender(data, children);\n    }\n\n    // 表格 td 走 通用模版\n    if (template && !render && !notRender) {\n      otherItem.render = (data: any) => renderFRender(data, template);\n    }\n\n    // 配置外置 render 方法进行渲染\n    if (render && typeof render === 'string') {\n      const renderFunc = addons.getMethod(render);\n      otherItem.render = (data: any, record: any, index: any) =>\n        renderFunc(data, item, record, index);\n    }\n\n    column[key] = { ...otherItem };\n  }\n\n  return column;\n};\n\nexport default detalColumn;\n"
  },
  {
    "path": "packages/data-render/src/widgets/FTable/index.tsx",
    "content": "import React from 'react';\nimport SimpleTable from '../components/BaseTable';\nimport RequestTable from '../components/RequestTable';\nimport detalColumn from './detalColumn';\nimport FTitle from '../FTitle';\n\n/**\n *\n * 自定义-Table\n */\nconst FTable = (props: any) => {\n  const {\n    title,\n    data,\n    column,\n    template,\n    request,\n    addons,\n    titleStyle,\n    repeatIndex,\n    rowSelection,\n    style,\n    ...otherProps\n  } = props;\n\n  const renderFRender = (value: any, schema: any) => {\n    return addons.renderer({ schema, data: value, addons });\n  };\n\n  const columnMap = detalColumn(column, { addons, template, renderFRender, repeatIndex });\n\n  // if (rowSelection) {\n  //   otherProps.rowSelection = addons('getAppHelperParams', rowSelection);\n  // }\n\n  return (\n    <>\n      {title && <FTitle data={title} style={titleStyle} />}\n      {!request && (\n        <SimpleTable\n          style={{\n            marginBottom: title ? '16px' : 0,\n            ...style,\n          }}\n          {...otherProps}\n          data={data}\n          column={columnMap}\n          fRender={renderFRender}\n        />\n      )}\n      {request && (\n        <RequestTable\n          {...otherProps}\n          style={{\n            marginBottom: title ? '16px' : 0,\n            ...style,\n          }}\n          request={request}\n          addons={addons}\n          column={columnMap}\n          fRender={renderFRender}\n        />\n      )}\n    </>\n  );\n};\n\nexport default FTable;\n"
  },
  {
    "path": "packages/data-render/src/widgets/FTabs/index.tsx",
    "content": "import React from 'react';\nimport { Tabs } from 'antd';\nimport { combineClass } from '../utils/common';\n\nconst { TabPane } = Tabs;\n\nconst FTabList: React.FC = (props: any) => {\n  const { className, style, items = [], data, addons } = props;\n\n  return (\n    <Tabs className={combineClass('dv-tabs', className)} defaultActiveKey=\"0\" style={style}>\n      {items.map((item: any, index: number) => (\n        <TabPane tab={item.label} key={index} style={{ paddingTop: '16px' }}>\n          {addons.renderer({ schema: item.children, data, addons })}\n        </TabPane>\n      ))}\n    </Tabs>\n  );\n};\n\nexport default FTabList;\n"
  },
  {
    "path": "packages/data-render/src/widgets/FTags/index.tsx",
    "content": "import React, { FC, isValidElement } from 'react';\nimport { Tag } from 'antd';\nimport { get } from 'lodash-es';\nimport { combineClass, transformData } from '../utils/common';\nimport InnerHtml from '../components/InnerHtml';\n\nconst renderTag = (tagProps: any, addons: any) => (data: any, index: number) => {\n  const { dataKey, format, ...otherProps } = tagProps || {};\n  let value = data;\n  if (dataKey) {\n    value = get(data, dataKey, null);\n  }\n\n  // 数据进行格式化\n  if (['html'].includes(format?.type)) {\n    value = <InnerHtml data={value} />;\n  } else {\n    value = transformData(value, format, addons);\n  }\n\n  if (!value || (typeof value === 'object' && !isValidElement(value))) {\n    return null;\n  }\n\n  return (\n    <Tag key={index} {...otherProps}>\n      {value}\n    </Tag>\n  );\n};\n\nconst FTags: FC = (props: any) => {\n  const { className, style, data, tagProps, addons } = props;\n\n  let list = data || [];\n\n  if (!Array.isArray(list)) {\n    list = [data];\n  }\n\n  return (\n    <div className={combineClass('dr-tags', className)} style={style}>\n      {list.map(renderTag(tagProps, addons))}\n    </div>\n  );\n};\n\nexport default FTags;\n"
  },
  {
    "path": "packages/data-render/src/widgets/FText/index.less",
    "content": ".dv-text {\n  display: inline-flex;\n  align-items: center;\n  min-width: 20px;\n  min-height: 18px;\n  margin-right: 10px;\n  font-size: 14px;\n\n  .left-icon {\n    margin-right: 4px;\n  }\n\n  .right-icon {\n    margin-left: 4px;\n  }\n}\n"
  },
  {
    "path": "packages/data-render/src/widgets/FText/index.tsx",
    "content": "import React, { isValidElement, CSSProperties } from 'react';\n\nimport { transformData, combineClass, transDataKeyToData } from '../utils/common';\nimport createIconFont from '../utils/createIconFont';\nimport { renderText } from '../components/TextView';\nimport InnerHtml from '../components/InnerHtml';\nimport './index.less';\n\ninterface IProps {\n  className: string;\n  style: CSSProperties;\n  dataKey: string; // 数据 key\n  data: any; // 数据\n  defaultValue: any; // 默认值\n  leftText: string; // 左文案\n  rightText: string; // 右文案\n  leftTextStyle: CSSProperties; // 左文案样式\n  rightTextStyle: CSSProperties; // 右文案样式\n  childSchema: any; // 子协议\n  render: string; // 自定义渲染函数名\n  showKey: string | ((a: any, b: any) => boolean); // 根据 showKey 获取显示结果，ture 显示、false 不显示\n  showLevel: number; // 控制显示等级\n  format: any; // 数据格式化配置\n  iconSetting: any; // 图标配置\n  useType: string; // 使用方式，区别是否被其他组件调用，如果是，需要自行格式化数据\n  addons: any; // data-render 插件总控\n  [key: string]: any;\n}\n\n/**\n *\n * 文本组件\n */\nconst FText = (props: IProps) => {\n  const {\n    className,\n    style,\n    data,\n    leftText,\n    rightText,\n    leftTextStyle,\n    rightTextStyle,\n    useType,\n    childSchema,\n    render,\n    format,\n    iconSetting,\n    addons,\n  } = props;\n\n  const parentData = addons.getParentData();\n\n  let value = data;\n\n  // 布尔值特殊处理一下\n  if (typeof value === 'boolean') {\n    value = value + '';\n  }\n\n  // 被其他组件调用\n  if (useType === 'internal') {\n    // 数据进行格式化\n    if (['html'].includes(format?.type)) {\n      value = <InnerHtml data={value} />;\n    } else {\n      value = transformData(value, format, parentData, addons);\n    }\n  }\n\n  // 自定义渲染：配置外置 render 方法\n  if (render && typeof render === 'string') {\n    const renderFunc = addons.getMethod(render);\n    return renderFunc(value, props, parentData) || null;\n  }\n\n  // 协议渲染：支持协议嵌套渲染子内容\n  if (childSchema) {\n    let schema = childSchema;\n    // 协议结构化：在协议简写时需要转换\n    if (typeof childSchema === 'string') {\n      schema = { widget: childSchema };\n    }\n    return addons.renderer({ schema, data: value, addons });\n  }\n\n  // 异常处理，这样的数据不符合渲染规则，不渲染\n  if (typeof value === 'object' && !isValidElement(value)) {\n    return null;\n  }\n\n  let ellipsisStyle: any = {};\n  if (props.ellipsis && style?.width) {\n    ellipsisStyle = { width: 0, flex: 1 };\n  }\n  if (style?.color || style?.fontSize) {\n    ellipsisStyle.color = style.color;\n    ellipsisStyle.fontSize = style.fontSize;\n  }\n\n  // 支持文本复制、省略功能\n  value = renderText(value, props, ellipsisStyle);\n\n  const content = (\n    <>\n      {leftText && (\n        <span className=\"content-left-text\" style={leftTextStyle}>\n          {leftText}\n        </span>\n      )}\n      {value}\n      {rightText && (\n        <span className=\"content-right-text\" style={rightTextStyle}>\n          {rightText}\n        </span>\n      )}\n    </>\n  );\n\n  // 组件被其他组件调用，直接返回\n  if (useType === 'internal') {\n    return content;\n  }\n\n  const handleIconClick = (ev: any) => {\n    transDataKeyToData(iconSetting, { data: parentData, addons });\n    if (iconSetting.href) {\n      if (iconSetting.target === '_self') {\n        window.location.href = iconSetting.href;\n      } else {\n        window.open(iconSetting.href);\n      }\n      return;\n    }\n    // 传人外置方法，实现按钮点击事件\n    const func = addons.getMethod(iconSetting?.method?.name || iconSetting?.method);\n    func({ dataKey: props.dataKey, method: iconSetting.method, data: parentData }, ev);\n  };\n\n  const Icon = createIconFont(addons.getConfig().iconFontUrl);\n  const isClick = !!iconSetting?.href || !!iconSetting?.method;\n  const iconContent = (\n    <Icon\n      type={iconSetting?.type}\n      style={{ cursor: isClick ? 'pointer' : 'auto', ...iconSetting?.style }}\n      onClick={handleIconClick}\n    />\n  );\n\n  return (\n    <div className={combineClass('dv-text', className)} style={style}>\n      {iconSetting?.direct === 'left' && <span className=\"left-icon\">{iconContent}</span>}\n      {content}\n      {iconSetting?.direct !== 'left' && <span className=\"right-icon\">{iconContent}</span>}\n    </div>\n  );\n};\n\nexport default FText;\n"
  },
  {
    "path": "packages/data-render/src/widgets/FTextEllipsis/index.tsx",
    "content": "import React from 'react';\nimport { combineClass } from '../utils/common';\nimport TextEllipsis from '../components/TextEllipsis';\n\n/**\n *\n * 文本组件\n */\nconst FTextEllipsis = (props: any) => {\n  const {\n    className,\n    style,\n    data,\n    addons,\n\n    contentStyle,\n    height = 24,\n    leftSlot = [],\n    rightSlot = [],\n\n  } = props;\n\n  const parentData = addons.getParentData();\n\n  return (\n    <div className={combineClass('dr-textellipsis', className)} style={style}>\n      <TextEllipsis\n        height={height}\n        data={data}\n        contentStyle={contentStyle}\n        leftSlot={\n          leftSlot && addons.renderer({ schema: leftSlot, data: parentData, addons })\n        }\n        rightSlot={\n          rightSlot && addons.renderer({ schema: rightSlot, data: parentData, addons })\n        }\n      />\n    </div>\n  );\n};\n\nexport default FTextEllipsis;\n"
  },
  {
    "path": "packages/data-render/src/widgets/FTimeline/index.tsx",
    "content": "import React from 'react';\nimport { Timeline } from 'antd';\nimport { combineClass } from '../utils/common';\nimport FTitle from '../FTitle';\n\nconst FTimeline = (props: any) => {\n  const { data = [], title, className, style, lineItem = {}, addons, ...otherProps } = props;\n  const {\n    timeKey = 'time',\n    contenKey = 'content',\n    colorKey = 'color',\n    ...otherLineItem\n  } = lineItem;\n\n  return (\n    <>\n      <FTitle data={title} />\n      <Timeline className={combineClass('dr-timeline', className)} style={style} {...otherProps}>\n        {(data || []).map((item: any = {}, index: number) => (\n          <Timeline.Item key={index} {...otherLineItem} color={item[colorKey] || 'gray'}>\n            <span className=\"line-item-time\">{item[timeKey]}</span>\n            <span className=\"line-item-content\">{item[contenKey]}</span>\n          </Timeline.Item>\n        ))}\n      </Timeline>\n    </>\n  );\n};\n\nexport default FTimeline;\n"
  },
  {
    "path": "packages/data-render/src/widgets/FTitle/index.less",
    "content": ".dr-title {\n  display: flex;\n  align-items: center;\n  margin-bottom: 10px;\n\n  .view-title {\n    margin: 0;\n    color: #141414;\n  }\n\n  .view-title-icon {\n    width: 6px;\n    height: 6px;\n    margin-right: 10px;\n    background-color: #3f7ffb;\n    border-radius: 6px;\n  }\n}\n"
  },
  {
    "path": "packages/data-render/src/widgets/FTitle/index.tsx",
    "content": "import React from 'react';\nimport { Typography } from 'antd';\nimport { combineClass } from '../utils/common';\nimport './index.less';\n\nconst { Title } = Typography;\n\nconst FTitle = (props: any) => {\n  const { className, style, data, level = 6, showType, addons, ...otherProps } = props;\n\n  let { color, fontSize, ...otherStyle } = style || {};\n  if (!fontSize && level === 6) {\n    fontSize = '14px';\n  }\n\n  return (\n    <div className={combineClass('dr-title', className)} style={otherStyle}>\n      {showType === 1 && <span className=\"view-title-icon\" />}\n      <Title className=\"view-title\" level={level} style={{ color, fontSize }} {...otherProps}>\n        {data}\n      </Title>\n    </div>\n  );\n};\n\nexport default FTitle;\n"
  },
  {
    "path": "packages/data-render/src/widgets/FTooltip/index.less",
    "content": ".dr-tooltip {\n  margin-top: 8px;\n}\n"
  },
  {
    "path": "packages/data-render/src/widgets/FTooltip/index.tsx",
    "content": "import React from 'react';\nimport { Tooltip } from 'antd';\nimport { combineClass } from '../utils/common';\nimport './index.less';\n\nconst FTooltip = (props: any) => {\n  const { data, childSchema, className, style, title, tooltip, addons, ...otherProps } = props;\n  const tooltipTitle = <div dangerouslySetInnerHTML={{ __html: title }} />;\n\n  return (\n    <Tooltip\n      className={combineClass('dr-tooltip', className)}\n      color=\"#fff\"\n      overlayInnerStyle={{\n        color: '#141414',\n      }}\n      {...tooltip}\n      title={tooltipTitle}\n    >\n      <div className=\"content\" style={{ display: 'inline-block', ...style }}>\n        {addons.renderer({ schema: childSchema, data, addons, ...otherProps })}\n      </div>\n    </Tooltip>\n  );\n};\n\nexport default FTooltip;\n"
  },
  {
    "path": "packages/data-render/src/widgets/components/BaseCollapse/index.less",
    "content": ".dr-collapse {\n  margin-top: 16px;\n  background-color: #fff;\n  border: 1px solid #f0f0f0;\n\n  .collapse-title {\n    height: 24px;\n    margin-right: 16px;\n    font-weight: bold;\n    font-size: 16px;\n    line-height: 24px;\n  }\n\n  .header-item-label {\n    height: 14px;\n    margin-left: 30px;\n    color: black;\n    font-weight: bold;\n    font-size: 14px;\n    line-height: 14px;\n    span {\n      color: #141414;\n    }\n  }\n\n  .expand-icon-box {\n    position: absolute;\n    top: 20px;\n    right: 12px;\n    display: flex;\n    align-items: center;\n    color: #999;\n    margin-right: 12px;\n  }\n\n  .expand-icon-desc {\n    margin-left: 4px;\n  }\n\n  > .ant-collapse-item .ant-collapse-header {\n    display: flex;\n    align-items: center !important;\n    padding: 16px 24px !important;\n    background-color: #fff;\n    border-radius: 8px !important;\n\n    > .ant-collapse-expand-icon {\n      padding-inline-end: 0 !important;\n    }\n  }\n\n  .ant-collapse-header-text {\n    display: flex;\n    align-items: center;\n  }\n\n  .tag-no-data {\n    color: rgba(0, 0, 0, 0.65);\n  }\n\n  .ant-collapse-content-box {\n    padding: 0 16px 16px 16px !important;\n\n    .ant-skeleton-title,\n    .ant-skeleton-paragraph > li {\n      animation: none !important;\n    }\n  }\n}\n"
  },
  {
    "path": "packages/data-render/src/widgets/components/BaseCollapse/index.tsx",
    "content": "import React, { useState, FC } from 'react';\nimport { Collapse } from 'antd';\nimport { combineClass } from '../../utils/common';\nimport { DownOutlined } from '@ant-design/icons';\nimport './index.less';\n\nconst { Panel } = Collapse;\ninterface IProps {\n  className?: any;\n  style?: object;\n  children: any;\n  title?: string;\n  header?: any;\n  defaultExpand?: boolean;\n}\n\n/**\n * 折叠面板\n */\nconst BaseCollapse: FC<IProps> = (props) => {\n  const { className, style, children, title, header, defaultExpand = true } = props;\n\n  const [activeKey, setActiveKey] = useState<string>(defaultExpand ? 'single' : '');\n\n  const collapseHeader = (\n    <>\n      {title && <div className='collapse-title'>{title}</div>}\n      {header && header}\n    </>\n  );\n\n  const renderExpandIcon = ({ isActive }: any): JSX.Element => {\n    return (\n      <div className='expand-icon-box'>\n        <DownOutlined rotate={isActive ? 180 : 0} />\n        <span className='expand-icon-desc'>{isActive ? '收起' : '展开'}</span>\n      </div>\n    );\n  };\n\n  return (\n    <Collapse\n      className={combineClass('dr-collapse', className)}\n      style={style}\n      ghost={true}\n      activeKey={[activeKey]}\n      expandIcon={renderExpandIcon}\n      onChange={() => setActiveKey(activeKey ? '' : 'single')}\n    >\n      <Panel key='single' header={collapseHeader}>\n        {children}\n      </Panel>\n    </Collapse>\n  );\n};\n\nexport default BaseCollapse;\n"
  },
  {
    "path": "packages/data-render/src/widgets/components/BaseTable/basic.ts",
    "content": "import { get } from 'lodash-es';\n\n// columnObj 递归便利\nconst recursionColumn = (result: any, columnObj: any, columnConfig?: any, extraParams?: any) => {\n  for (const key of Object.keys(columnObj)) {\n    const item = columnObj[key];\n    let column: any = {\n      key,\n      align: 'center',\n      dataIndex: key,\n      ...columnConfig,\n    };\n\n    // 配置 item 字符串类型\n    if (typeof item === 'string') {\n      column['title'] = item;\n      result.push(column);\n      continue;\n    }\n\n    // 存在表头合并\n    if (item.column) {\n      column = {\n        title: item.title,\n        children: [],\n      };\n      recursionColumn(column.children, item.column, columnConfig, extraParams);\n    } else {\n      // 正常情况\n      column = {\n        ...column,\n        ...item,\n      };\n    }\n\n    if (column.title.children) {\n      const { fRender } = extraParams || {};\n      column.title = fRender({ name: item.title.name }, column.title.children);\n    }\n\n    result.push(column);\n  }\n};\n\n//  生成 columns 数据\nexport const getColumns = (columnObj: any, columnConfig?: any, extraParams?: any) => {\n  const result: any = [];\n  // 递归，进行格式转换\n  recursionColumn(result, columnObj, columnConfig, extraParams);\n  return result;\n};\n\n// 生成 dataSource 数据\nexport const getDataSource = (\n  columnObj: object,\n  dataList: any[] = [],\n  extra: { key?: string; that?: any } = {},\n) => {\n  const dataSource: any[] = [];\n  const { key, that } = extra;\n\n  const dealData = (column: any) => {\n    for (const code in column) {\n      const item = column[code];\n\n      // 存在合并列头\n      if (item.column) {\n        dealData(item.column);\n        continue;\n      }\n\n      (dataList || []).forEach((data, index) => {\n        let dataItem = dataSource[index];\n\n        if (!dataItem) {\n          dataItem = { ...data, key: `${key ? data[key] : index}` };\n          dataSource.push(dataItem);\n        }\n\n        if (code.includes('x-operate')) {\n          dataItem[code] = { ...data, that, dataIndex: index };\n        } else {\n          dataItem[code] = get(data, code, '');\n        }\n      });\n    }\n  };\n\n  // 递归遍历，映射数据\n  dealData(columnObj);\n  return dataSource;\n};\n\n// 表格数据合并\nexport const combineDataSource = (data: any[], field: any) => {\n  let count = 0; // 重复项的第一项\n  let index = 1; // 下一项\n  while (index < data.length) {\n    const item = data.slice(count, count + 1)[0]; // 获取没有比较的第一个对象\n    if (!item.rowSpan) {\n      item.rowSpan = 1; // 初始化为 1\n    }\n    if (item[field] === data[index][field]) {\n      // 第一个对象与后面的对象相比，有相同项就累加，并且后面相同项设置为 0\n      item.rowSpan++;\n      data[index].rowSpan = 0;\n    } else {\n      count = index;\n    }\n    index++;\n  }\n};\n"
  },
  {
    "path": "packages/data-render/src/widgets/components/BaseTable/index.less",
    "content": ".dv-base-table {\n  width: 100%;\n\n  .ant-table-cell {\n    color: #141414;\n    font-weight: normal;\n  }\n}\n"
  },
  {
    "path": "packages/data-render/src/widgets/components/BaseTable/index.tsx",
    "content": "import React from 'react';\nimport { Table } from 'antd';\nimport { isArray, combineClass } from '../../utils/common';\nimport { getColumns, getDataSource, combineDataSource } from './basic';\n\nimport './index.less';\n\n/**\n *\n * 基础表格\n */\nconst BaseTable = (props: any) => {\n  const {\n    column,\n    data,\n    style,\n    className,\n    onChange,\n    combineData,\n    expandable,\n    pagination: pageConfig,\n    columnConfig,\n    tableIndexText = '',\n    fRender,\n    ...tableProps\n  } = props;\n\n  let dataList = data || [];\n  if (!isArray(dataList)) {\n    dataList = [data];\n  }\n\n  let pagination = false;\n  if (pageConfig) {\n    pagination = {\n      total: dataList.length,\n      showSizeChanger: false,\n      showQuickJumper: true,\n      pageSize: 10,\n      ...pageConfig,\n    };\n  }\n\n  const columns = getColumns(column, columnConfig, { fRender });\n  let dataSource = getDataSource(column, dataList);\n\n  if (column.tableIndex) {\n    dataSource = dataSource.map((item: any, index: number) => ({\n      ...item,\n      tableIndex: tableIndexText + (index + 1),\n    }));\n  }\n\n  // 表格数据合并\n  if (combineData) {\n    const { key: combineKey, columns: combineColumns }: any = combineData;\n    combineDataSource(dataSource, combineKey);\n    columns.forEach((colum: any) => {\n      if (combineColumns.includes(colum.key)) {\n        colum.onCell = (data: any) => {\n          const { rowSpan, colSpan } = data;\n          let result: any = {};\n          if (typeof rowSpan !== 'undefined') {\n            result.rowSpan = rowSpan;\n          }\n          if (typeof colSpan !== 'undefined') {\n            result.colSpan = colSpan;\n          }\n          return result;\n        };\n      }\n    });\n  }\n\n  let customExpandable = null;\n  if (expandable) {\n    const { children, ...otherExpandable } = expandable;\n    customExpandable = {\n      ...otherExpandable,\n      expandedRowRender: (record: any, index: number, indent: any, expanded: any) => {\n        return fRender({ ...record, expandedParams: { index, indent, expanded } }, children);\n      },\n    };\n  }\n\n  return (\n    <Table\n      className={combineClass('dv-base-table', className)}\n      style={style}\n      bordered\n      size=\"small\"\n      pagination={pagination}\n      onChange={onChange}\n      {...tableProps}\n      columns={columns}\n      dataSource={dataSource}\n      expandable={customExpandable}\n    />\n  );\n};\n\nexport default BaseTable;\n"
  },
  {
    "path": "packages/data-render/src/widgets/components/CopyLabel/index.less",
    "content": ".icon-label-view {\n  display: inline-flex;\n  align-items: center;\n  margin-right: 5px;\n  .anticon {\n    cursor: pointer;\n  }\n}\n"
  },
  {
    "path": "packages/data-render/src/widgets/components/CopyLabel/index.tsx",
    "content": "import React from 'react';\n\nimport IconLabel from '../IconLabel';\nimport { clipboardCopy } from '../../utils/common';\nimport './index.less';\n\nconst CopyLabel = (props: any) => {\n  const { onClick, style, data, fontSize = 22, color = '#1677FF' } = props;\n\n  const handleClick = (ev: any) => {\n    if (onClick) {\n      onClick(ev);\n      return;\n    }\n    clipboardCopy(data);\n    ev.stopPropagation();\n  };\n\n  if (!data) {\n    return null;\n  }\n\n  return (\n    <IconLabel\n      type=\"icon-copy\"\n      fontSize={fontSize}\n      color={color}\n      style={style}\n      data={data}\n      onClick={handleClick}\n    />\n  );\n};\n\nexport default CopyLabel;\n"
  },
  {
    "path": "packages/data-render/src/widgets/components/Encryption/index.less",
    "content": ".encryption-view {\n  display: inline-flex;\n  align-items: center;\n  .line-label {\n    margin-right: 5px;\n  }\n\n  .icon-text {\n    height: 22px;\n    margin: 0 3px;\n    color: #3f7ffb;\n    font-size: 14px;\n    line-height: 22px;\n    cursor: pointer;\n  }\n\n  .line-span {\n    display: flex;\n    align-items: center;\n    margin-right: 5px;\n    cursor: pointer;\n  }\n\n  .encry-content {\n    margin-right: 5px;\n  }\n}\n"
  },
  {
    "path": "packages/data-render/src/widgets/components/Encryption/index.tsx",
    "content": "import React from 'react';\nimport classnames from 'classnames';\nimport createIconFont from '../../utils/createIconFont';\nimport { renderString } from '../../utils/common';\nimport CopyLabel from '../CopyLabel';\nimport './index.less';\n\ninterface IIcon {\n  text: string;\n  type: string;\n  style?: any;\n  showText?: boolean;\n  className?: string | any;\n}\ninterface IProps {\n  className?: string | any;\n  label?: string;\n  icon?: IIcon;\n  data?: string;\n  copy?: boolean;\n  onClick?: () => void;\n  iconFontUrl: string;\n}\n\nconst Encryption = (props: IProps) => {\n  const { className, label, icon, data, copy = true, onClick, iconFontUrl } = props;\n  const {\n    text = '查看电话',\n    showText = true,\n    type = 'icon-phone',\n    style: iconStyle,\n    className: iconClass,\n  } = icon || {};\n\n  const Icon = createIconFont(iconFontUrl);\n\n  return (\n    <div className={classnames('encryption-view', { [className]: className })}>\n      {label && <span className='line-label'>{renderString(label)}</span>}\n      {data &&\n        (copy ? (\n          <CopyLabel data={data} iconFontUrl={iconFontUrl} />\n        ) : (\n          <span className='encry-content'>{data}</span>\n        ))}\n      {!data && (\n        <span onClick={onClick} className={classnames('line-span', { [iconClass]: iconClass })}>\n          {type && <Icon type={type} style={{ color: '#1677FF', fontSize: 18, ...iconStyle }} />}\n          {showText && <span className='icon-text'>{text}</span>}\n        </span>\n      )}\n    </div>\n  );\n};\n\nexport default Encryption;\n"
  },
  {
    "path": "packages/data-render/src/widgets/components/IconLabel/index.less",
    "content": ".custom-icon-label-view {\n  display: inline-flex;\n  align-items: center;\n  margin-right: 5px;\n\n  .content {\n    margin: 0 2px;\n  }\n}\n"
  },
  {
    "path": "packages/data-render/src/widgets/components/IconLabel/index.tsx",
    "content": "import React from 'react';\nimport classnames from 'classnames';\nimport createIconFont from '../../utils/createIconFont';\n\nimport './index.less';\n\nconst IconLabel = (props: any) => {\n  const {\n    data,\n    onClick,\n    type,\n    className,\n    style,\n    contentStyle,\n    iconStyle,\n    direct = 'right',\n    fontSize,\n    color,\n    iconFontUrl,\n  } = props;\n\n  const Icon = createIconFont(iconFontUrl);\n\n  const IconView = <Icon type={type} style={{ fontSize, color, ...iconStyle }} onClick={onClick} />;\n\n  return (\n    <span\n      className={classnames('custom-icon-label-view', { [className]: className })}\n      style={style}\n    >\n      {direct === 'left' && IconView}\n      <span className=\"content\" style={contentStyle}>\n        {data}\n      </span>\n      {direct === 'right' && IconView}\n    </span>\n  );\n};\n\nexport default IconLabel;\n"
  },
  {
    "path": "packages/data-render/src/widgets/components/IconView/index.tsx",
    "content": "\nimport { createFromIconfontCN } from '@ant-design/icons';\n\n/**\n * icon 图标库\n *\n * 图标库资源变动需要更新 scriptUrl 资源路径\n */\n\nconst IconView = createFromIconfontCN({\n  scriptUrl: '//at.alicdn.com/t/font_2750617_db4bg3gyled.js',\n});\n\nexport default IconView;\n"
  },
  {
    "path": "packages/data-render/src/widgets/components/InnerHtml/index.tsx",
    "content": "import React from 'react';\nimport classnames from 'classnames';\n\nconst InnerHtml = (props: any) => {\n  const { data, style, className } = props;\n\n  if (typeof data === 'object') {\n    return null;\n  }\n\n  return (\n    <span\n      className={classnames(null, { [className]: className })}\n      style={style}\n      dangerouslySetInnerHTML={{ __html: data }}\n    />\n  );\n};\n\nexport default InnerHtml;\n"
  },
  {
    "path": "packages/data-render/src/widgets/components/ReactNode/index.tsx",
    "content": "import React from 'react';\nimport InnerHtml from '../InnerHtml';\n\nconst ReactNode = (props: any) => {\n  const { schema, data, addons } = props;\n\n  if (!schema) {\n    return null;\n  }\n\n  // 当 data 为字符串时，直接返回\n  if (typeof schema === 'string') {\n    //  直接返回\n    if (!schema.includes('method:')) {\n      return <InnerHtml data={schema} />;\n    }\n\n    // 如果字符串中包含 render: 则调用 render 方法进行渲染\n    const [_, funcName] = schema.split('method:');\n    const renderFunc = addons.getMethod(funcName);\n    return renderFunc(data);\n  }\n\n  // 当 data 为对象时，则调用 FRender 组件进行渲染\n  return addons.renderer({ schema, data, addons });\n};\n\nexport default ReactNode;\n"
  },
  {
    "path": "packages/data-render/src/widgets/components/RequestTable/index.less",
    "content": ".custom-table {\n  width: 100%;\n  margin: 10px 0;\n}\n"
  },
  {
    "path": "packages/data-render/src/widgets/components/RequestTable/index.tsx",
    "content": "import React, { useEffect } from 'react';\nimport { Empty, Button } from 'antd';\nimport { get } from 'lodash-es';\n\nimport { useSet } from '../../utils/hooks';\nimport { getRequestParams } from '../../utils/common';\nimport BaseTable from '../BaseTable';\n\nimport './index.less';\n\n/**\n *\n * 自动请求数据-Table\n */\nconst RequestTable = (props: any) => {\n  const { request, pagination: pageConfig, addons, ...tableProps } = props;\n\n  const [state, setState] = useSet({\n    total: 0,\n    pageSize: pageConfig?.pageSize || 10,\n    dataSource: [],\n    error: false,\n    loading: false,\n  });\n  const { pageSize, total, dataSource, error, loading } = state;\n\n  let pagination: any = false;\n  if (pageConfig) {\n    pagination = {\n      ...pageConfig,\n      total,\n      pageSize,\n    };\n  }\n\n  useEffect(() => {\n    getTableList({ pageSize, current: 1 });\n  }, []);\n\n  // 获取表格数据\n  const getTableList = async (pagination: any) => {\n    const { pageSize, current: pageNo } = pagination;\n    const { name, api, params: condition, dataKey, listKey = 'list', totalKey = 'count' } = request;\n\n    const params = {\n      pageNo,\n      pageSize,\n      ...getRequestParams(condition, addons.getSourceData()),\n    };\n\n    const requestFunc = addons.getMethod(name || 'request');\n    const requestConfig = addons.getRequestConfig();\n\n    setState({ loading: true });\n    const res = (await requestFunc(api, params, request)) || {};\n    const data = res[dataKey || requestConfig.dataKey];\n\n    if (!data) {\n      if (pageNo === 1) {\n        setState({ error: true, loading: false });\n      }\n      return;\n    }\n    setState({\n      dataSource: get(data, listKey, []),\n      total: get(data, totalKey, 0),\n      error: false,\n      loading: false,\n    });\n  };\n\n  if (error) {\n    return (\n      <Empty\n        image=\"https://img.alicdn.com/imgextra/i2/O1CN01zkSBPx1w25lRyCZz0_!!6000000006249-55-tps-94-61.svg\"\n        imageStyle={{\n          height: 60,\n        }}\n        description=\"数据异常！\"\n      >\n        <Button type=\"primary\" onClick={() => getTableList({ pageSize, current: 1 })}>\n          点击查询\n        </Button>\n      </Empty>\n    );\n  }\n\n  return (\n    <BaseTable\n      {...tableProps}\n      data={dataSource}\n      pagination={pagination}\n      onChange={getTableList}\n      loading={loading}\n    />\n  );\n};\n\nexport default RequestTable;\n"
  },
  {
    "path": "packages/data-render/src/widgets/components/SuckTabs/index.less",
    "content": ".dv-suck-nav {\n  .seat-view {\n    display: none;\n    height: 40px;\n    border-radius: 4px;\n  }\n\n  .tabs-view {\n    position: relative;\n    display: flex;\n    align-items: center;\n    justify-content: space-evenly;\n    height: 40px;\n    background-color: #fff;\n    border-radius: 4px;\n    box-shadow: 0 2px 4px 2px rgba(221, 225, 232, 1);\n  }\n\n  .tabs-view-padding-top {\n    position: absolute;\n    top: 0;\n    left: 0;\n    width: 100%;\n    height: 10px;\n    background: #f0f2f5;\n  }\n\n  .tab-suction {\n    position: fixed;\n    top: 0;\n    right: 0;\n    left: 0;\n    z-index: 1000;\n  }\n\n  .tab-item {\n    min-width: 84px;\n    height: 40px;\n    color: #141414;\n    font-size: 14px;\n    line-height: 40px;\n    text-align: center;\n    cursor: pointer;\n  }\n\n  .tab-active {\n    position: relative;\n    color: #3f7ffb;\n  }\n\n  .tab-active::after {\n    position: absolute;\n    right: 20px;\n    bottom: -1px;\n    left: 20px;\n    height: 1px;\n    background: #3f7ffb;\n    content: '';\n  }\n}\n"
  },
  {
    "path": "packages/data-render/src/widgets/components/SuckTabs/index.tsx",
    "content": "import React, { FC, useState, useEffect, useRef } from 'react';\nimport classnames from 'classnames';\nimport { throttle } from 'lodash-es';\n\nimport './index.less';\n\nlet activeIndx = 0; // 当前tab下标\nlet lockScroll = false; // 不做滚动判断\nlet isSubTop = false; // 是否吸顶\n\ninterface ITabItem {\n  code: string;\n  title: string;\n  action?: string;\n}\n\nconst SuckTabs: FC<any> = (props) => {\n  const { tabs, tabsId = 'tab', startY = -10, children, className, style, fixed } = props;\n\n  const scrollContainer = props.scrollContainer || window;\n\n  const [tabIndex, setIndex] = useState<number>(0);\n  const containerRef = useRef(null);\n  const tabRef = useRef(null);\n  const seatRef = useRef(null);\n\n  useEffect(() => {\n    scrollContainer.addEventListener('scroll', onScroll);\n    return () => {\n      scrollContainer.removeEventListener('scroll', onScroll);\n    };\n  }, []);\n\n  useEffect(() => {\n    fixedChange(fixed);\n  }, []);\n\n  const getScrollParams = () => {\n    let scrollContainerHeight =\n      document.documentElement?.clientHeight || document.body?.clientHeight;\n    let scrollTop =\n      document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop;\n\n    if (props.scrollContainer) {\n      scrollContainerHeight = scrollContainer?.clientHeight;\n      scrollTop = scrollContainer.scrollTop;\n    }\n    return { scrollContainerHeight, scrollTop };\n  };\n\n  const onScroll = throttle(() => {\n    const { scrollContainerHeight, scrollTop } = getScrollParams();\n\n    const contaCurrent: any = containerRef.current;\n    const offsetTop = contaCurrent.offsetTop;\n\n    /** 还未滚到此块区域，不做处理 */\n    if (scrollContainerHeight + scrollTop < offsetTop) {\n      return;\n    }\n    isSubTop = offsetTop <= scrollTop - 1; // 是否吸顶\n\n    // 处理吸顶\n    fixedChange(isSubTop || fixed);\n\n    // 手动点击，不处理滑动逻辑\n    if (lockScroll) {\n      return;\n    }\n\n    // 距离底部的长度\n    const distance =\n      Math.ceil((scrollContainer.scrollHeight - scrollContainerHeight - scrollTop) / 10) || 1;\n\n    // 进行位置 tab 选中标记\n    for (let i = tabs.length - 1; i > -1; i--) {\n      const { code } = tabs[i];\n      let scrollH = document.getElementById(`${tabsId}-${code}`)?.offsetTop ?? 0;\n      if (isSubTop) {\n        scrollH -= startY;\n      }\n\n      if (scrollH <= scrollTop) {\n        // 滚动距离不够的时候，进行移位处理\n        if (tabs.length - i > distance) {\n          const idx = tabs.length - distance;\n          if (activeIndx !== idx) {\n            activeIndx = idx;\n            setIndex(idx);\n          }\n        } else if (activeIndx !== i) {\n          activeIndx = i;\n          setIndex(i);\n        }\n        break;\n      }\n    }\n  }, 100);\n\n  /** tab 吸顶处理 */\n  const fixedChange = (flag: boolean) => {\n    const tabElement: any = tabRef.current;\n    const seatElement: any = seatRef.current;\n\n    if (flag) {\n      seatElement.style.display = 'block';\n      tabElement.classList.add('tab-suction');\n    } else {\n      seatElement.style.display = 'none';\n      tabElement.classList.remove('tab-suction');\n    }\n  };\n\n  // tab点击，滚动定位相应模块位置\n  const handleTabClick = (index: number) => () => {\n    // 标记当前锚点模块序号\n    activeIndx = index;\n\n    // 滚动锁死，防止触发滚动计算逻辑\n    lockScroll = true;\n\n    // 获取目标容器距离顶部高度， 高度 - 10 优化定位间隙\n    const { code } = tabs[index];\n    let scrollH = (document.getElementById(`${tabsId}-${code}`)?.offsetTop ?? 0) - startY;\n\n    // 进行定位\n    scrollContainer.scrollTo(0, scrollH);\n    setIndex(index);\n\n    // 释放死锁，后续触发滚动能够正常计算\n    setTimeout(() => {\n      lockScroll = false;\n    }, 500);\n  };\n\n  return (\n    <div\n      className={classnames('dv-suck-nav', { [className]: className })}\n      ref={containerRef}\n      style={style}\n    >\n      <div className=\"seat-view\" ref={seatRef} />\n      <div className=\"tabs-view\" ref={tabRef}>\n        {tabs.map((tab: ITabItem, index: number) => (\n          <div\n            key={tab.code}\n            className={classnames(tab.code, {\n              'tab-item': true,\n              'tab-active': index === tabIndex,\n            })}\n            onClick={handleTabClick(index)}\n          >\n            {tab.title}\n          </div>\n        ))}\n      </div>\n      {tabs.map((tab: ITabItem, index: number) => (\n        <div key={`${tabsId}-${tab.code}`} id={`${tabsId}-${tab.code}`} className=\"tab-row\">\n          {children[index]}\n        </div>\n      ))}\n    </div>\n  );\n};\n\nexport default SuckTabs;\n"
  },
  {
    "path": "packages/data-render/src/widgets/components/TextEllipsis/index.less",
    "content": ".text-ellipsis-hidden {\n  visibility: hidden;\n}\n.text-ellipsis-box {\n  overflow: hidden;\n\n  .text-ellipsis-all {\n    padding: 0 16px;\n    color: rgba(63, 127, 251, 1);\n    font-size: 14px;\n    cursor: pointer;\n  }\n}\n"
  },
  {
    "path": "packages/data-render/src/widgets/components/TextEllipsis/index.tsx",
    "content": "import React, { FC, useMemo, useRef, useEffect } from 'react';\nimport classnames from 'classnames';\nimport { useSet } from '../../utils/hooks';\n\nimport './index.less';\ninterface IProps {\n  data: string;\n  height: number;\n  leftSlot?: JSX.Element | string;\n  rightSlot?: JSX.Element | string;\n  contentStyle?: any;\n}\n\nconst initState = {\n  hidden: true, // 先隐藏截取执行完成之后展示出来\n  diff: 0,\n  isEllipsis: true,\n  isFirst: true, // 第一次\n};\n\nconst TextEllipsis: FC<IProps> = (props) => {\n  const { height, leftSlot, rightSlot, data, contentStyle } = props;\n\n  const [state, setState] = useSet({\n    cutText: data, // 截取后剩下的文字\n    lastLength: data.length, // 上一次的长度\n    ...initState,\n  });\n\n  const conRef = useRef(null);\n  const { cutText, hidden, isEllipsis, lastLength, diff, isFirst } = state;\n\n  useEffect(() => {\n    const onsize = function () {\n      setState({\n        cutText: props.data,\n        lastLength: props.data.length,\n        ...initState,\n      });\n    };\n\n    window.addEventListener('resize', onsize);\n    return () => window.removeEventListener('resize', onsize);\n  }, []);\n\n  useEffect(() => {\n    if (!data) {\n      return;\n    }\n    setState({\n      cutText: props.data,\n      lastLength: props.data.length,\n      ...initState,\n    });\n  }, [data]);\n\n  useEffect(() => {\n    // 无显示内容不处理\n    if (!data) {\n      return;\n    }\n    // 第一次已经处理过直接返回\n    if (isFirst) {\n      firstTimeRender();\n      return;\n    }\n    // 进行截取处理\n    const { clientHeight }: any = conRef.current || {};\n    const cutTextLength = cutText.length;\n    const length = Math.floor((cutTextLength - diff) / 2 + diff);\n\n    // 真实高度超出固定高度,需要继续截取字符串\n    if (clientHeight - height > 5) {\n      setState({\n        cutText: cutText.slice(0, length),\n        lastLength: cutTextLength,\n      });\n      return;\n    }\n    // 找到合适值，终止遍历\n    if (cutTextLength === diff) {\n      setState({ hidden: false });\n      return;\n    }\n    // 回退截取，找到一个合适值\n    setState({\n      diff: cutTextLength,\n      cutText: data.slice(0, lastLength),\n    });\n  }, [cutText]);\n\n  const firstTimeRender = () => {\n    const { clientHeight }: any = conRef.current || {};\n    const cutTextLength = cutText.length;\n    // 真实高度超出固定高度,需要截取字符串\n    if (clientHeight - height > 5) {\n      setState({\n        cutText: cutText.slice(0, cutTextLength / 2),\n        lastLength: cutTextLength,\n        isFirst: false,\n      });\n      return;\n    }\n    // 真实高度没有超出固定高度, 直接显示，不需要...\n    setState({ isEllipsis: false, hidden: false });\n  };\n\n  return useMemo(() => {\n    if (!data) {\n      return null;\n    }\n    return (\n      <div className=\"text-ellipsis-box\" style={{ height: isEllipsis ? height : 'auto' }}>\n        <div\n          ref={conRef}\n          className={classnames('text-ellipsis-view', { 'text-ellipsis-hidden': hidden })}\n        >\n          {isEllipsis ? (\n            <>\n              {leftSlot && <span>{leftSlot}</span>}\n              <span style={contentStyle}>{`${cutText}...`}</span>\n              <span className=\"text-ellipsis-all\" onClick={() => setState({ isEllipsis: false })}>\n                展开\n              </span>\n              {rightSlot && <span>{rightSlot}</span>}\n            </>\n          ) : (\n            <>\n              {leftSlot && <span>{leftSlot}</span>}\n              <span style={contentStyle}>{data}</span>\n              {data !== cutText && (\n                <span className=\"text-ellipsis-all\" onClick={() => setState({ isEllipsis: true })}>\n                  收起\n                </span>\n              )}\n              {rightSlot && <span>{rightSlot}</span>}\n            </>\n          )}\n        </div>\n      </div>\n    );\n  }, [cutText, hidden, isEllipsis]);\n};\n\nexport default TextEllipsis;\n"
  },
  {
    "path": "packages/data-render/src/widgets/components/TextView/index.tsx",
    "content": "import React from 'react';\nimport { message, Tooltip, Typography } from 'antd';\n\nexport const renderEllipsis = (dom: any, text: any, item: { ellipsis: any }) => {\n  if (!item.ellipsis) {\n    return dom;\n  }\n  return <Tooltip title={text}>{dom}</Tooltip>;\n};\n\nexport const renderCopyable = (\n  text: any,\n  item: { copyable: boolean; ellipsis: any; delete: boolean },\n  style: any,\n) => {\n  if (!item.copyable && !item.ellipsis && !item.delete) {\n    return text;\n  }\n\n  let copyable: boolean | object = false;\n  if (item.copyable && text) {\n    copyable = { tooltips: false, text, onCopy: () => message.success('复制成功') };\n  }\n\n  return (\n    <Typography.Text\n      style={{\n        maxWidth: '100%',\n        margin: 0,\n        padding: 0,\n        ...style,\n      }}\n      delete={item.delete}\n      copyable={copyable}\n      ellipsis={item.ellipsis || false}\n    >\n      {text}\n    </Typography.Text>\n  );\n};\n\n// 渲染单元格\nexport const renderText = (val: string, item: any, style: any = {}) => {\n  const textHoc = renderCopyable(val, item, style);\n  return renderEllipsis(textHoc, val, item);\n};\n"
  },
  {
    "path": "packages/data-render/src/widgets/index.ts",
    "content": "export { default as FDescriptions } from './FDescriptions';\nexport { default as FSuckNav } from './FSuckNav';\nexport { default as FCollapse } from './FCollapse';\nexport { default as FText } from './FText';\nexport { default as FTable } from './FTable';\nexport { default as FTabs } from './FTabs';\nexport { default as FButton } from './FButton';\nexport { default as FButtonFold } from './FButtonFold';\nexport { default as FProgress } from './FProgress';\nexport { default as FPanel } from './FPanel';\nexport { default as FIconLabel } from './FIconLabel';\nexport { default as FTimeline } from './FTimeline';\nexport { default as FLabel } from './FLabel';\nexport { default as FTags } from './FTags';\nexport { default as FSteps } from './FSteps';\nexport { default as FTitle } from './FTitle';\nexport { default as FEncryption } from './FEncryption';\nexport { default as FTooltip } from './FTooltip';\nexport { default as FRow } from './FRow';\nexport { default as FSpace } from './FSpace';\nexport { default as FImage } from './FImage';\nexport { default as FCard } from './FCard';\nexport { default as FProgressBar } from './FProgressBar';\nexport { default as FStatistic } from './FStatistic';\nexport { default as FRadioGroup } from './FRadioGroup';"
  },
  {
    "path": "packages/data-render/src/widgets/utils/common.ts",
    "content": "import { message } from 'antd';\nimport classnames from 'classnames';\nimport dayjs from 'dayjs';\n\nimport timezone from 'dayjs/plugin/timezone';\nimport utc from 'dayjs/plugin/utc';\nimport { isNumber, isObject, isString, get } from 'lodash-es';\ndayjs.extend(utc);\ndayjs.extend(timezone);\n\nexport const combineClass = (name: any, data: any, extra = {}) => {\n  let result: any = {};\n\n  if (typeof data === 'string') {\n    result = { [data]: true };\n  }\n  if (Array.isArray(data)) {\n    data.forEach((item) => {\n      result[item] = true;\n    });\n  }\n\n  return classnames(name, { ...result, ...extra });\n};\n\n/**\n *\n * @param string\n * @returns\n */\nexport const parseString = (string: string) => Function('\"use strict\";return (' + string + ')')();\n\n/**\n * @param path // 对应数据的路径，例如：'a.b.c'\n * @param defaultValue // 默认值\n * @param data // 数据\n * @param addons // 渲染器方法集，访问 getSourceData\n * @returns value\n */\nexport const getValueFromKey = (props: {\n  path: string;\n  data?: any;\n  defaultValue?: any;\n  addons: any;\n  valueType?: string;\n}): string | boolean => {\n  const { data, path = '', defaultValue = '', addons, valueType } = props;\n\n  let result: any = null;\n  let negation: any = null; // 否定标识 ！\n  let dataPath = path;\n\n  if (path.substring(0, 2) === '!!') {\n    negation = '!!';\n    dataPath = path.substring(2);\n  } else if (path.substring(0, 1) === '!') {\n    negation = '!';\n    dataPath = path.substring(1);\n  }\n\n  // 带 source 标识，表示从顶层获取数据\n  if (dataPath.includes('source:')) {\n    const [_, sourcePath]: any = dataPath.split('source:');\n    const sourceData = addons.getSourceData();\n    result = get(sourceData, sourcePath);\n  } else {\n    result = get(data, dataPath);\n  }\n\n  if (!result && result !== 0 && typeof result !== 'boolean') {\n    result = defaultValue;\n  }\n\n  // 根据 negation 取反, 返回结果\n  if (negation === '!') {\n    return !result;\n  }\n\n  if (negation === '!!') {\n    return !!result;\n  }\n\n  if (result && valueType === '!object' && typeof result === 'object') {\n    return '';\n  }\n\n  return result;\n};\n\n/**\n * 数据格式转换\n */\nexport const transformData = (value: any, format: any, parentData?: any, addons?: any) => {\n  if (!format || (!value && value !== 0 && format.type !== 'dateTime-range')) {\n    return value;\n  }\n\n  const getValue = (value: any, item: any) => {\n    const { type, config }: any = item;\n    let result = value;\n\n    if (type === 'dateTime-range') {\n      const { startKey, endKey }: any = config;\n      let startTime: any = getValueFromKey({ data: parentData, path: startKey, addons });\n      if (startTime) {\n        startTime = dayjs.tz(startTime).format(config.format || 'YYYY-MM-DD HH:mm:ss');\n      }\n\n      let endTime: any = getValueFromKey({ data: parentData, path: endKey, addons });\n      if (endTime) {\n        endTime = dayjs.tz(endTime).format(config.format || 'YYYY-MM-DD HH:mm:ss');\n      }\n\n      if (startTime !== 'Invalid Date') {\n        result = startTime;\n      }\n\n      if (endTime !== 'Invalid Date') {\n        result += ' ~ ' + endTime;\n      }\n    }\n    // 日期\n    if (type === 'date') {\n      result = dayjs.tz(value).format(config || 'YYYY-MM-DD');\n    }\n\n    // 时间\n    if (type === 'dateTime') {\n      result = dayjs.tz(value).format(config || 'YYYY-MM-DD HH:mm:ss');\n    }\n\n    // 枚举\n    if (type === 'enum' && typeof config === 'object') {\n      result = config[value] || config.default || value;\n    }\n\n    // 单位: 左边\n    if (type === 'leftUnit' && config) {\n      if (config.includes('dataKey:')) {\n        result = `${getValueFromKey({\n          data: parentData,\n          path: config.split('dataKey:')[1],\n          addons,\n          defaultValue: '',\n        })} ${value}`;\n      } else {\n        result = `${config} ${value}`;\n      }\n    }\n\n    // 单位: 右边\n    if (type === 'rightUnit' && config) {\n      if (config.includes('dataKey:')) {\n        result = `${value} ${getValueFromKey({\n          data: parentData,\n          path: config.split('dataKey:')[1],\n          addons,\n          defaultValue: '',\n        })}`;\n      } else {\n        result = `${value} ${config}`;\n      }\n    }\n\n    // 一分\n    if (type === 'penny') {\n      result = (value * 100).toFixed(config || 2);\n    }\n\n    // 分转元\n    if (type === 'spunYuan') {\n      result = value / 100;\n    }\n\n    // 百分比\n    if (type === 'percent') {\n      result = (value * 100).toFixed(config || 2) + '%';\n    }\n\n    // 浮点数转换\n    if (type === 'float') {\n      result = (value * 1).toFixed(config || 2);\n    }\n\n    // 函数\n    if (type === 'function') {\n      result = parseString(config)(value);\n    }\n\n    if (result === 'Invalid Date') {\n      result = '';\n      console.warn('日期转换错误');\n    }\n\n    if (result === 'NaN%') {\n      result = '';\n      console.warn('百分率转换错误');\n    }\n\n    return result;\n  };\n\n  return Array.isArray(format)\n    ? format.reduce((preValue, item) => getValue(preValue, item), value)\n    : getValue(value, format);\n};\n\n// 是否是数组\nexport function isArray(input: any): input is any[] {\n  return Array.isArray(input);\n}\n\n// 获取请求参数\nexport const getRequestParams = (cond: any, data: any, insideData?: any) => {\n  if (!cond) {\n    return {};\n  }\n\n  let params: any = {};\n  for (let key in cond) {\n    let value = get(data, cond[key], null);\n    if (cond[key].includes('dataKey:')) {\n      value = get(insideData, cond[key].split(':')[1], null);\n    }\n    params[key] = value;\n  }\n\n  return params;\n};\n\n// 复制\nexport const clipboardCopy = (url: string) => {\n  navigator.clipboard.writeText(url).then(\n    function () {\n      /* clipboard successfully set */\n      message.success('复制成功');\n    },\n    function () {\n      /* clipboard write failed */\n      message.error('复制失败');\n    },\n  );\n};\n\n// 判断数据为空\nexport const isDataEmpty = (data: any, level: 1 | 2) => {\n  if (\n    Object.prototype.toString.call(data) === '[object Object]' &&\n    Object.keys(data).length === 0\n  ) {\n    return true;\n  }\n\n  if (isArray(data) && data.length === 0) {\n    return true;\n  }\n\n  if (level === 1 && (!data || data === '0')) {\n    return true;\n  }\n\n  if (level === 2 && !data && data !== 0) {\n    return true;\n  }\n};\n\n// 判断是否是子协议\nexport const isReactNodeSchema = (schema: any = {}) => {\n  return JSON.stringify(schema).includes('widget');\n};\n\nexport const startsWith = (str: string, startStr: string) => {\n  const reg = new RegExp('^' + startStr);\n  return reg.test(str);\n};\n\nexport const isThenable = (func: any) => {\n  return Boolean(func && typeof func.then === 'function');\n};\n\nconst validatorMap = {\n  string: isString,\n  number: isNumber,\n  object: isObject,\n  array: isArray,\n};\n\nexport const isType = (val: any, types: Array<keyof typeof validatorMap>) => {\n  return types.some((t) => {\n    const func = validatorMap[t];\n    return func(val);\n  });\n};\n\nexport const transDataKeyToData = (object: any, { data, addons }: any) => {\n  // 进行动态数据绑定\n  Object.keys(object).forEach((key) => {\n    const item = object[key];\n    if (typeof item !== 'string') {\n      return;\n    }\n\n    if (startsWith(item, 'source:')) {\n      object[key] = getValueFromKey({ path: item, data, addons });\n      return;\n    }\n\n    if (startsWith(item, 'data:')) {\n      const path = item.split('data:')[1]?.trim();\n      object[key] = getValueFromKey({ path, data, addons });\n    }\n  });\n};\n\n\nexport const renderString = (str: any) => {\n  if (isObject(str)) {\n    return '';\n  }\n  return str;\n}"
  },
  {
    "path": "packages/data-render/src/widgets/utils/createIconFont.ts",
    "content": "import { createFromIconfontCN } from '@ant-design/icons';\n\nexport default (url?: string) => {\n  return createFromIconfontCN({\n    scriptUrl: url || '//at.alicdn.com/t/a/font_2750617_sax751jyfjl.js',\n  });\n};\n"
  },
  {
    "path": "packages/data-render/src/widgets/utils/hooks.ts",
    "content": "import { useReducer, useRef, useEffect } from 'react';\n\nexport function usePrevious(value: any) {\n  const ref = useRef(null);\n  useEffect(() => {\n    ref.current = value;\n  }, [value]);\n  return ref.current;\n}\n\n// 类似于class component的setState\nexport const useSet = (initState: any) => {\n  return useReducer((state: any, newState: any) => {\n    return { ...state, ...newState };\n  }, initState);\n};\n"
  },
  {
    "path": "packages/data-render/src/withProvider.tsx",
    "content": "import React, { useEffect, useRef } from 'react';\nimport { ConfigProvider } from 'antd';\nimport dayjs from 'dayjs';\n\nimport zhCN from 'antd/lib/locale/zh_CN';\nimport enUS from 'antd/lib/locale/en_US';\nimport 'dayjs/locale/zh-cn';\n\nimport { createStore } from './models/store';\nimport { FRContext } from './models/context';\n\nexport default function withProvider<T>(\n  Element: React.ComponentType<T>,\n  defaultWidgets?: any,\n): React.ComponentType<T> {\n  return (props: any) => {\n    const {\n      configProvider,\n      locale = 'zh-CN',\n      widgets,\n      ...rest\n    } = props;\n\n    const storeRef = useRef(createStore());\n    const store: any = storeRef.current;\n\n    useEffect(() => {\n      if (locale === 'en-US') {\n        dayjs.locale('en');\n        return;\n      }\n      dayjs.locale('zh-cn');\n    }, [locale]);\n\n    const antdLocale = locale === 'zh-CN' ? zhCN : enUS;\n\n    const langPack: any = {\n      ...antdLocale,\n      ...configProvider?.locale,\n    };\n\n    return (\n      <ConfigProvider {...configProvider} locale={langPack}>\n        <FRContext.Provider value={store}>\n          <Element {...rest} widgets={{ ...defaultWidgets, ...widgets }} />\n        </FRContext.Provider>\n      </ConfigProvider>\n    );\n  };\n}\n"
  },
  {
    "path": "packages/data-render/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"ES2015\",\n    \"module\": \"ES2015\",\n    \"moduleResolution\": \"node\",\n    \"importHelpers\": true,\n    \"jsx\": \"react\",\n    \"esModuleInterop\": true,\n    \"sourceMap\": true,\n    \"baseUrl\": \"./\",\n    \"skipLibCheck\": true,\n    // \"strict\": true,\n    \"declaration\": true,\n    \"paths\": {\n      \"@/*\": [\n        \"src/*\"\n      ],\n      \"@@/*\": [\n        \"src/.umi/*\"\n      ],\n      \"form-render\": [\n        \"packages/form-render/src/*\"\n      ],\n    },\n    \"allowJs\": true,\n    \"allowSyntheticDefaultImports\": true,\n    \"noImplicitAny\": false,\n    \"resolveJsonModule\": true\n  },\n  \"exclude\": [\n    \"node_modules\",\n    \"lib\",\n    \"es\",\n    \"dist\",\n    \"typings\",\n    \"**/__test__\",\n    \"test\",\n    \"tests\",\n    \"docs\",\n    \"**/*.js\"\n  ]\n}\n"
  },
  {
    "path": "packages/form-render/.fatherrc.js",
    "content": "import copy from 'rollup-plugin-copy';\n\nexport default {\n  cjs: 'babel',\n  esm: {\n    type: 'babel',\n    importLibToEs: true,\n  },\n  lessInBabelMode: true,\n  extraRollupPlugins: [\n    copy({\n      targets: [{ src: 'src/index.d.ts', dest: 'dist/' }],\n    }),\n  ],\n  extraBabelPlugins: [\n    [\n      'import',\n      {\n        libraryName: 'antd',\n        libraryDirectory: 'es',\n        style: true,\n      },\n      'antd',\n    ],\n    [\n      'import',\n      {\n        libraryName: '@ant-design/icons',\n        libraryDirectory: 'lib/icons',\n        camel2DashComponentName: false,\n      },\n      '@ant-design/icons',\n    ],\n  ],\n};\n"
  },
  {
    "path": "packages/form-render/CHANGELOG.md",
    "content": "# 更新日志\n### 2.4.8\n- [!] 修复 drawerList 组件，编辑时打开弹窗，表单数据一直是最后一条\n\n### 2.4.7 \n- [+] 兼容 safari 浏览器 ?. 正则表达式不全逻辑\n\n### 2.4.6\n- [1!] 函数表达式为了兼容性考虑会替换成 ?. 但是需要剔除数字场景\n\n### 2.4.5\n- [+] 函数表达式兼容，formData.x['y'] 语法\n- [+] 适配，formData.x.y， formData.x undefined 时，表达式解析异常\n\n### 2.4.3 \n- [!] 优化 drawerList 组件 watch 联动事件不生效，修复 validateTrigger 属性配置不生效。\n\n### 2.4.0 \n- [+] FR 增加 footer 透传 actionBtns \n\n### 2.3.9\n- [+] TableList 增加 hideEmptyTable 属性\n\n### 2.3.8\n- [!] 修复 schema 多层嵌套下，getSchemaByPath 获取异常\n\n### 2.3.7\n- [+] 增加 antdVersion 配置字段，对应值 'v4' | 'v5'，用于处理 ant v4 与 v5 API 不兼容性警告提示，例如 visible & open，默认是 v5 版本。\n\n### 2.3.6\n- [!] 修复 List 组件初始化数据，删除按钮未显示\n- [!] 修复因配置 validator 导致通过 watch 触发 setSchemaBypath 初次不生效\n\n### 2.3.5 \n- [+] labelWidget、descWidget 增加 addons 访问属性\n- [!] 兼容 widget 大小写配置\n\n### 2.3.0\n- [+] 优化 search-form 折叠收起逻辑，不再判断 dom 真实渲染高度，提升渲染性能\n- [!] form.getValues 判断 removeHiddenData = true 时，才去除隐藏控件数据\n\n### 2.2.18\n- [+] 优化 ListTab 性能优化\n### 2.2.17 \n- [!] List 嵌套下去默认去除隐藏值\n### 2.2.16\n- [!] 修复 SearchForm schema undefined 异常处理\n### 2.2.14\n- [!] 修复 List 嵌套下 输入控件被隐藏，提交数据值还能被获取\n### 2.2.13\n- [!] 修复 List 嵌套 list 组件自身函数表达式不生效\n### 2.2.9\n- [!] 修复 schema 依赖更新异常\n### 2.2.8\n- [+] SearchForm 增加 onReset 自定义重置方法\n- [-] 修复 setSchemaByPath 判断，在某些情况不生效\n- [-] 修复 SearchForm layoutAuto 自适应布局异常\n### 2.2.6\n- [+] SearchForm 增加 closeReturnSearch 关闭回车查询属性\n\n### 2.2.5\n- [!] 修复 react hook 提示错误\n### 2.2.4\n- [!] 修复 SearchForm 开启layoutAuto，重新激活页面，collapsed会失效\n### 2.2.3\n- [!] 修复 List 组件使用函数表达式，依赖值变化组件不更新\n- [+] collapse 折叠组件，支持配置 bordered、ghost 属性\n- [+] 进一步优化组件更新逻辑，精确更新，自动匹配关联表单项值是否变化\n- [+] 增加 widgetType 字段，当 widgetType = 'field'时，强制走自定义组件逻辑，用于区分容器组件逻辑一般无需配置\n### 2.1.29\n- [!] 修复子表单场景，默认只在表单提交时触发校验\n- [+] 增加 form.getFieldRef API，自定义组件可以暴露实例，供 getFieldRef 使用\n\n### 2.1.28\n- [+] 增加 ListTable、ListVirtual 校验增加非气泡模式配置\n globalConfig = { listValidatePopover: false }\n- [+] 增加子表单校验配置，详细内容查看表单校验文档\n\n### 2.1.25\n- [+] 增加 form.getFlattenSchema API\n- [+] List 场景支持 rules: []，整体校验配置\n- [!] 修复 TableList 场景，输入框控件自定义校验提示异常\n\n### 2.1.21\n- [!] 修复 DrawerList 编辑时，报错\n### 2.1.20\n- [!] 修复 validateFields 调用无效\n### 2.1.17\n- [!] 修复 labelWidth 设置 0，无效\n### 2.1.16\n- [+] 增强 dependencies 字段能力，支持 List 嵌套 dependencies 配置\n### 2.1.15\n- [+] 增强 bind 字段能力，支持 List 嵌套 bind 配置\n- [+] 增加 Field className 配置\n### 2.1.14\n- [!] 修复 单个控件校验提示出现多个，例如 required 和 max 不满足时会同时出现提示\n\n### 2.1.13\n- [!] 修复 校验 min = 0 时，不生效\n- [+] cardList 增加 useMemo 提升表单输入性能，避免输入卡顿\n\n### 2.1.12\n\n### 2.1.11\n- [!] 修复 form-render schema.enum.indexof 错误\n### 2.1.10\n- [+] 扩展 list 组件 actionColumnProps 配置属性\n### 2.1.9\n- [!] 修复 form-render ts 错误\n### 2.1.8\n- [+] 新增 footer 按钮配置栏\n\n### 2.1.7\n- [+] 新增 组件按需功能，导出 FormSlimRender 实例\n### 2.1.6\n- [+] 新增 order 排序\n- [!] 去除 setSchemaByPath 属性合并，只对 schema.props 进行简单合并\n\n### 2.1.5\n- [!] 修复 select 清除异常报错\n### 2.1.1-2.1.4\n- [!] 修复 searchForm 列布局，查询按钮高度异常\n### 2.1.0\n- [+] 增加 span 属性支持表单项宽度自定义\n### 2.0.19\n- [!] 修复 html 组件 select 回显问题\n\n### 2.0.17\n- [+] 增加 labelWidget，配置用于 form.item lable 的自定义\n### 2.0.15\n- [!] 修复查询表单, displayType='column' 布局异常\n- [!] 修复 simpleList, 新增报错\n\n### 2.0.14\n- [+] CardList 增加计数提示\n### 2.0.13\n- [+] 增加输入控件，extra、help 属性配置\n- [+] 自定义校验 validator 支持返回一个 { status: true/ false , message: '错误信息' }，用于动态设置 message\n### 2.0.11\n- [+] SearchForm 增加 layoutAuto 是否自适应布局\n### 2.0.10\n- [!] 修复 Modal 模式下，弹窗关闭，表单值无法清除\n\n### 2.0.9\n- [+] 优化 displayType inline 模式下表单项布局\n\n### 2.0.6\n- [!] 修复 DatePicker 组件显示默认是英文提示\n### 2.0.3\n- [+] 增加查询表单 SearchForm\n\n### 2.0.1\n- [+] 添加全局 props `globalProps`, 解决与 form-render 无关的全局数据的注入问题。自定义组件中可以获取到 globalProps。这也解决了自定义组件在不同的页面也许需要接受不同的 props 的问题\n### 2.0.0\n\n- [+] form-render 2.0 正式发版\n"
  },
  {
    "path": "packages/form-render/CONTRIBUTING.md",
    "content": "# 如何贡献代码\n\n欢迎给 FormRender 提优化建议，或者修复已有 Bug，共促其发展\n\n## Branch 管理\n\n```\nmaster\n ↑\ndev         <--- Develop/PR\n```\n\n- `dev` 分支\n  - 所有的开发均在 dev 分支进行\n  - 提 PR 时候请提交到 dev 分支\n- `master` 分支\n  - `master` 是稳定不改的分支，不会在上面进行代码开发\n  - 在 dev 分支 publish 后会 merge 到 master，同时打对应 tag\n\n## Commit 格式\n\n```\n[{action}] {description}\n```\n\n- `{action}`\n  - `+` 新增功能\n  - `!` 更新或者修复 bug\n  - `-` 移除功能\n- `{description}`\n  - 尽可能详细的描述就好\n\nfor example:\n\n- [+] 列表选项新增拖拽功能\n- [!] 修复输入框长按闪烁的问题\n\n## 更多\n\n- 很推荐在提交 PR 前，先在钉钉群里进行讨论，已防止此功能已经有同学在开发了\n- 但是如果是想修复文档和明显代码错误，直接提交 PR 就好\n\n<img src=https://img.alicdn.com/imgextra/i2/O1CN01RoUHQF1EoKLWyotFn_!!6000000000398-0-tps-750-990.jpg width=250/>\n"
  },
  {
    "path": "packages/form-render/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2019-present XRender Team\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "packages/form-render/README.md",
    "content": "<div style=\"display:flex;align-items:center;margin-bottom:24px\">\n  <img src=\"https://img.alicdn.com/tfs/TB17UtINiLaK1RjSZFxXXamPFXa-606-643.png\" alt=\"logo\" width=\"48px\"/>\n  <h4 style=\"font-size:30px;font-weight:600;display:inline-block;margin-left:12px\">FormRender</h4>\n</div>\n<p style=\"display:flex;justify-content:space-between;width:440px\">\n  <a href=\"https://www.npmjs.com/package/form-render?_blank\">\n    <img alt=\"npm\" src=\"https://img.shields.io/npm/v/form-render.svg?maxAge=3600&style=flat-square\">\n  </a>\n  <a href=\"https://npmjs.org/package/form-render\">\n    <img alt=\"NPM downloads\" src=\"https://img.shields.io/npm/dm/form-render.svg?style=flat-square\">\n  </a>\n  <a href=\"https://npmjs.org/package/form-render\">\n    <img alt=\"NPM all downloads\" src=\"https://img.shields.io/npm/dt/form-render.svg?style=flat-square\">\n  </a>\n  <a>\n    <img alt=\"PRs Welcome\" src=\"https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square\">\n  </a>\n</p>\n\n> 一站式中后台**表单解决方案**\n\n## 官网\n\n<https://xrender.fun/form-render>\n\nFormRender 是中后台开箱即用的表单解决方案，通过 JsonSchema 协议动态渲染表单。为了能切实承接日益复杂的表单场景需求，2.0 我们进行了底层重构。我们的目标是以强大的扩展能力对表单场景 100% 的覆盖支持，同时保持开发者能快速上手，并以表单编辑器、插件、自定义组件等一系列周边产品带来极致的开发体验。在开发 1.0 的道路上，我们做了一系列的取舍，详见[v2 升级方案](https://xrender.fun/form-render/migrate)\n\n## 安装\n\nFormRender 依赖 ant design，单独使用不要忘记同时安装 `antd`\n\n```shell\nnpm i form-render --save\n```\n\n## 使用\n\n**最简使用 demo：**\n\n```jsx\nimport React from 'react';\nimport { Button } from 'antd';\nimport FormRender, { connectForm } from 'form-render';\n\nconst schema = {\n  type: 'object',\n  properties: {\n    input1: {\n      title: '简单输入框',\n      type: 'string',\n      required: true,\n    },\n    select1: {\n      title: '单选',\n      type: 'string',\n      props: {\n        options: [\n          { label: '早', value: 'a' },\n          { label: '中', value: 'b' },\n          { label: '晚', value: 'c' }\n        ]\n      }\n    },\n  },\n};\n\nclass Demo extends React.Component {\n  render() {\n    const { form } = this.props;\n    return (\n      <div>\n        <FormRender form={form} schema={schema} />\n        <Button type=\"primary\" onClick={form.submit}>\n          提交\n        </Button>\n      </div>\n    );\n  }\n}\n\nexport default connectForm(Demo);\n```\n\n**对于函数组件，FormRender 提供了 `useForm` hooks, 书写更为灵活**\n\n```jsx\nimport React from 'react';\nimport { Button } from 'antd';\nimport FormRender, { useForm } from 'form-render';\n\nconst schema = {\n  type: 'object',\n  properties: {\n    input1: {\n      title: '简单输入框',\n      type: 'string',\n      required: true,\n    },\n    select1: {\n      title: '单选',\n      type: 'string',\n      props: {\n        options: [\n          { label: '早', value: 'a' },\n          { label: '中', value: 'b' },\n          { label: '晚', value: 'c' }\n        ]\n      }\n    }\n  }\n};\n\nconst Demo = () => {\n  const form = useForm();\n  return (\n    <div>\n      <FormRender form={form} schema={schema} />\n      <Button type=\"primary\" onClick={form.submit}>\n        提交\n      </Button>\n    </div>\n  );\n};\n\nexport default Demo;\n```"
  },
  {
    "path": "packages/form-render/__tests__/core-utils.spec.ts",
    "content": "import { describe, it, expect } from 'vitest';\nimport { flattenSchema } from '../src/form-render-core/src/utils';\n\ndescribe('Test FormRender Utils', () => {\n  it('Test flattenSchema', () => {\n    const schema = {\n      type: 'object',\n      properties: {\n        input1: {\n          title: '简单输入框',\n          type: 'string',\n          order: 2,\n          required: true,\n        },\n        select1: {\n          title: '单选',\n          type: 'string',\n          order: 1,\n          enum: ['a', 'b', 'c'],\n          enumNames: ['早', '中', '晚'],\n        },\n      },\n    };\n\n    const _schema = flattenSchema(schema);\n    expect(Object.keys(_schema)).toEqual(['select1', 'input1', '#']);\n  });\n});\n"
  },
  {
    "path": "packages/form-render/__tests__/demo.tsx",
    "content": "import * as React from 'react';\nimport { useState } from 'react';\nimport FormRender, { useForm } from '../src/index';\nimport { normalSchema } from './schema';\n\nconst SimpleForm = () => {\n  const form = useForm();\n  const [state, setState] = useState({\n    input1: 'fr',\n    select1: 'd',\n  });\n  const onFinish = (formData, errors) => {\n    setState(formData);\n  };\n\n  const watch = {\n    // # 为全局\n    '#': val => {\n      console.log('表单的实时数据为：', val);\n    },\n    input1: {\n      handler: val => {\n        console.log(val);\n      },\n      immediate: true,\n    },\n    onSearch: val => {},\n  };\n\n  const onMount = () => {\n    form.setValueByPath('link', 'www.baidu.com');\n  };\n\n  const onClick = () => {\n    form.setValueByPath('link', 'www.baidu.com');\n  };\n\n  return (\n    <div>\n      <FormRender\n        form={form}\n        schema={normalSchema as any}\n        onFinish={onFinish}\n        watch={watch}\n        onMount={onMount}\n      />\n      <div data-testid=\"fr-value\">\n        <div data-testid=\"input\">{state?.input1}</div>\n        <div data-testid=\"select\">{state?.select1}</div>\n      </div>\n      <button data-testid=\"submit\" onClick={form.submit}>\n        提交\n      </button>\n      <button data-testid=\"test\" onClick={onClick}>\n        提交\n      </button>\n    </div>\n  );\n};\n\nexport default SimpleForm;\n"
  },
  {
    "path": "packages/form-render/__tests__/form-demo.tsx",
    "content": "import * as React from 'react';\nimport { useState } from 'react';\nimport FormRender, { useForm } from '../src/index';\n\nconst schema = {\n  type: 'object',\n  properties: {\n    input1: {\n      type: 'object',\n      properties: {\n        test: {\n          title: '简单输入框',\n          type: 'string',\n        },\n      },\n    },\n    select1: {\n      title: '单选',\n      type: 'string',\n      enum: ['a', 'b', 'c'],\n      enumNames: ['早', '中', '晚'],\n    },\n  },\n};\n\nconst SimpleForm = () => {\n  const form = useForm();\n  const [state, setState] = useState();\n  const setRes = data => {\n    let res;\n    if (typeof data === 'object') {\n      res = JSON.stringify(data);\n    }\n    res = (res || data) + '';\n    setState(res);\n  };\n\n  const handleIsFieldTouched = () => {\n    const res = form.isFieldTouched('input1.test');\n    setRes(res);\n  };\n\n  const handleIsFieldsTouched = () => {\n    const res = form.isFieldsTouched(['input1.test', 'select1'], true);\n    setRes(res);\n  };\n\n  const handleIsFieldValidating = () => {\n    const res = form.isFieldValidating('select1');\n    setRes(res);\n  };\n\n  const handleSetFields = () => {\n    form.setFields([\n      {\n        name: 'input1.test',\n        touched: true,\n        error: ['set input1.test error'],\n        value: 'input1.test value',\n      },\n      {\n        name: 'select1',\n        validating: true,\n        value: 'select1 value',\n      },\n    ]);\n  };\n\n  const handleGetFieldError = () => {\n    const res = form.getFieldError('input1.test');\n    setRes(res);\n  };\n\n  const handleValidateFields = () => {\n    form.validateFields().then(data => {\n      // data:\n      // {\n      //     input1: {\n      //     test: 'input1.test value',\n      //     },\n      //     select1: 'select1 value,\n      // }\n      setRes(data);\n    });\n  };\n\n  const handleGetValues = () => {\n    const res = form.getValues(['input1.test', 'select1'], ({ touched }) => {\n      return touched;\n    });\n    setRes(res);\n  };\n\n  return (\n    <div>\n      <FormRender form={form} schema={schema} />\n      <div data-testid=\"fieldTouched\" onClick={handleIsFieldTouched}></div>\n      <div data-testid=\"fieldsTouched\" onClick={handleIsFieldsTouched}></div>\n      <div\n        data-testid=\"fieldValidating\"\n        onClick={handleIsFieldValidating}\n      ></div>\n      <div data-testid=\"setFields\" onClick={handleSetFields}></div>\n      <div data-testid=\"getFieldError\" onClick={handleGetFieldError}></div>\n      <div data-testid=\"getValues\" onClick={handleGetValues}></div>\n      <div data-testid=\"validateFields\" onClick={handleValidateFields}></div>\n      <div data-testid=\"result\">{state}</div>\n    </div>\n  );\n};\n\nexport default SimpleForm;\n"
  },
  {
    "path": "packages/form-render/__tests__/form-fields.spec.tsx",
    "content": "import { describe, test, afterAll, expect } from 'vitest';\nimport * as React from 'react';\nimport '@testing-library/jest-dom';\nimport { render, act, cleanup } from '@testing-library/react';\nimport Demo from './form-demo';\n\nfunction sleep(ms): Promise<never> {\n  return new Promise(resolve => setTimeout(resolve, ms));\n}\n\nafterAll(cleanup);\n\ndescribe('FormRender API', () => {\n  test('📦  api test setFields and getFieldError success', async () => {\n    const { getByTestId, unmount } = render(<Demo />);\n    // 测试 setFields + getFieldError\n    act(() => {\n      getByTestId('setFields').click();\n    });\n    await act(() => sleep(500));\n    act(() => {\n      getByTestId('getFieldError').click();\n    });\n    await act(() => sleep(500));\n    expect(getByTestId('result')).toHaveTextContent(\n      JSON.stringify(['set input1.test error'])\n    );\n    act(() => {\n      unmount();\n    });\n  });\n  test('📦  api test validateFields success', async () => {\n    const { getByTestId, unmount } = render(<Demo />);\n    act(() => {\n      getByTestId('setFields').click();\n    });\n    await act(() => sleep(500));\n    act(() => {\n      getByTestId('validateFields').click();\n    });\n    await act(() => sleep(500));\n    expect(getByTestId('result')).toHaveTextContent(\n      JSON.stringify({\n        input1: {\n          test: 'input1.test value',\n        },\n        select1: 'select1 value',\n      })\n    );\n    act(() => {\n      unmount();\n    });\n  });\n  test('📦  api test isFieldValidating success', async () => {\n    const { getByTestId, unmount } = render(<Demo />);\n    // 测试 isFieldValidating\n    act(() => {\n      getByTestId('setFields').click();\n    });\n    await act(() => sleep(500));\n    act(() => {\n      getByTestId('fieldValidating').click();\n    });\n    await act(() => sleep(500));\n    expect(getByTestId('result')).toHaveTextContent('true');\n    act(() => {\n      unmount();\n    });\n  });\n\n  test('📦  api test isFieldTouched success', async () => {\n    const { getByTestId, unmount } = render(<Demo />);\n    // 测试 isFieldTouched\n    act(() => {\n      getByTestId('setFields').click();\n    });\n    await act(() => sleep(500));\n    act(() => {\n      getByTestId('fieldTouched').click();\n    });\n    await act(() => sleep(500));\n    expect(getByTestId('result')).toHaveTextContent('true');\n    act(() => {\n      unmount();\n    });\n  });\n\n  test('📦  api test isFieldsTouched success', async () => {\n    const { getByTestId, unmount } = render(<Demo />);\n    act(() => {\n      getByTestId('setFields').click();\n    });\n    await act(() => sleep(500));\n    // 测试 isFieldsTouched\n    act(() => {\n      getByTestId('fieldsTouched').click();\n    });\n    await act(() => sleep(500));\n    expect(getByTestId('result')).toHaveTextContent('false');\n    act(() => {\n      unmount();\n    });\n  });\n\n  test('📦  api test getValues success', async () => {\n    const { getByTestId, unmount } = render(<Demo />);\n\n    // 测试 getValues\n    act(() => {\n      getByTestId('setFields').click();\n    });\n    await act(() => sleep(500));\n    // 测试 getValues\n    act(() => {\n      getByTestId('getValues').click();\n    });\n    await act(() => sleep(500));\n    expect(getByTestId('result')).toHaveTextContent(\n      JSON.stringify({\n        input1: {\n          test: 'input1.test value',\n        },\n      })\n    );\n    act(() => {\n      unmount();\n    });\n  });\n});\n"
  },
  {
    "path": "packages/form-render/__tests__/form.spec.tsx",
    "content": "import { describe, it, afterAll, expect } from 'vitest';\nimport * as React from 'react';\nimport { render, act, cleanup } from '@testing-library/react';\nimport '@testing-library/jest-dom';\nimport Demo from './demo';\n\nfunction sleep(ms): Promise<never> {\n  return new Promise(resolve => setTimeout(resolve, ms));\n}\n\nafterAll(cleanup);\n\ndescribe('FormRender', () => {\n  it('📦  Render FR Success', async () => {\n    const { getByTestId, unmount } = render(<Demo />);\n    act(() => {\n      getByTestId('submit').click();\n      getByTestId('test').click();\n    });\n    await act(() => sleep(500));\n    expect(getByTestId('input')).toHaveTextContent('简单输入框');\n    expect(getByTestId('select')).toHaveTextContent('a');\n\n    act(() => {\n      unmount();\n    });\n  });\n});\n"
  },
  {
    "path": "packages/form-render/__tests__/get-descriptor.spec.ts",
    "content": "import { describe, expect, it } from 'vitest';\nimport { getDescriptorSimple } from '../src/form-render-core/src/getDescriptorSimple';\n\n// 主要对转换格式进行单元测试\ndescribe('get-descriptor utils', () => {\n  it('transform test 1', () => {\n    const schema = {\n      title: '代号',\n      type: 'string',\n      required: true,\n      rules: [{ pattern: '^[a-z]+$', message: 'incorrect province' }],\n    };\n\n    const res = getDescriptorSimple(schema, 'count');\n    const expectData = {\n      count: [\n        { required: true, type: 'string' },\n        { pattern: /^[a-z]+$/, message: 'incorrect province' },\n      ],\n    };\n\n    expect(res).toEqual(expectData);\n  });\n\n  it('transform test 2', () => {\n    const schema = {\n      title: '代号',\n      type: 'string',\n      rules: [\n        { required: true, message: '必填' },\n        { pattern: '^[a-z]+$', message: 'incorrect province' },\n      ],\n    };\n\n    const res = getDescriptorSimple(schema, 'count');\n    const expectData = {\n      count: [\n        { required: true, message: '必填' },\n        { pattern: /^[a-z]+$/, message: 'incorrect province' },\n        { type: 'string' },\n      ],\n    };\n\n    expect(res).toEqual(expectData);\n  });\n\n  it('transform test 3', () => {\n    const schema = {\n      title: '代号',\n      type: 'string',\n      rules: [{ pattern: '^[a-z]+$', message: 'incorrect province' }],\n    };\n\n    const res = getDescriptorSimple(schema, 'count');\n\n    const expectData = {\n      count: [\n        { type: 'string' },\n        { pattern: /^[a-z]+$/, message: 'incorrect province' },\n      ],\n    };\n\n    expect(res).toEqual(expectData);\n  });\n\n  it('transform test 4', () => {\n    const schema = {\n      title: '代号',\n      type: 'string',\n      required: true,\n    };\n\n    const res = getDescriptorSimple(schema, 'count');\n\n    const expectData = {\n      count: [{ type: 'string', required: true }],\n    };\n\n    expect(res).toEqual(expectData);\n  });\n\n  it('transform test 5', () => {\n    const schema = {\n      title: '代号',\n      type: 'string',\n    };\n\n    const res = getDescriptorSimple(schema, 'count');\n\n    const expectData = {\n      count: [{ type: 'string' }],\n    };\n\n    expect(res).toEqual(expectData);\n  });\n\n  it('transform test 6', () => {\n    const schema = {\n      title: '时间选择',\n      type: 'string',\n      widget: 'site',\n      format: 'time',\n      required: true,\n    };\n\n    const res = getDescriptorSimple(schema, 'time');\n\n    const expectData =\n      '{\"time\":[{\"required\":true},{\"type\":\"string\",\"message\":\"${title}的格式错误\"}]}';\n\n    expect(JSON.stringify(res)).toEqual(expectData);\n  });\n\n  it('transform test 7', () => {\n    const schema = {\n      title: '时间选择',\n      type: 'string',\n      widget: 'site',\n      format: 'time',\n    };\n\n    const res = getDescriptorSimple(schema, 'time');\n    const expectData =\n      '{\"time\":[{\"type\":\"string\",\"message\":\"${title}的格式错误\"}]}';\n\n    expect(JSON.stringify(res)).toEqual(expectData);\n  });\n\n  it('transform test 9', () => {\n    const schema = {\n      title: '时间选择',\n      type: 'string',\n      widget: 'site',\n      format: 'time',\n      required: true,\n      rules: [{ pattern: '^[a-z]+$', message: 'incorrect province' }],\n    };\n\n    const res = getDescriptorSimple(schema, 'count');\n    const expectData =\n      '{\"count\":[{\"required\":true},{\"type\":\"string\",\"message\":\"${title}的格式错误\"},{\"pattern\":{},\"message\":\"incorrect province\"}]}';\n    expect(JSON.stringify(res)).toEqual(expectData);\n  });\n});\n"
  },
  {
    "path": "packages/form-render/__tests__/schema.ts",
    "content": "export const normalSchema = {\n  type: 'object',\n  properties: {\n    input1: {\n      title: '简单输入框',\n      type: 'string',\n      required: true,\n      default: '简单输入框',\n      placeholder: '尝试在此输入',\n      className: 'input-with-px',\n      props: {\n        addonAfter: 'px',\n      },\n    },\n    numberDemo: {\n      title: '数字',\n      description: '数字输入框',\n      type: 'number',\n      min: 10,\n      max: 100,\n      step: 10,\n    },\n    textareaDemo: {\n      title: '输入框',\n      type: 'string',\n      widget: 'textarea',\n      default: 'FormRender\\nHello World!',\n      required: true,\n    },\n    imgDemo: {\n      title: '图片',\n      type: 'string',\n      format: 'image',\n      default:\n        'https://img.alicdn.com/tfs/TB1P8p2uQyWBuNjy0FpXXassXXa-750-1334.png',\n    },\n    uploadDemo: {\n      title: '文件上传',\n      type: 'string',\n      default:\n        'https://img.alicdn.com/tfs/TB1P8p2uQyWBuNjy0FpXXassXXa-750-1334.png',\n      widget: 'upload',\n      props: {\n        action: 'https://www.mocky.io/v2/5cc8019d300000980a055e76',\n      },\n    },\n    disabledDemo: {\n      title: '不可用',\n      type: 'string',\n      default: '我是一个被 disabled 的值',\n      disabled: true,\n    },\n    select: {\n      title: '单选',\n      type: 'string',\n      enum: ['a', 'b', 'c'],\n      enumNames: ['<span>早</span>', '中', '晚'],\n      default: 'a',\n      props: {\n        showSearch: true,\n        onSearch: 'onSearch',\n      },\n    },\n    select1: {\n      title: '单选',\n      type: 'string',\n      default: 'a',\n      props: {\n        options: [\n          { label: '早', value: 'a' },\n          { label: '晚', value: 'b' },\n        ],\n      },\n    },\n    select2: {\n      title: '复选',\n      type: 'array',\n      enum: ['a', 'b', 'c'],\n      enumNames: ['早', '中', '晚'],\n      widgets: 'checkboxes',\n      default: 'a',\n    },\n    select3: {\n      title: '多选',\n      type: 'array',\n      enum: ['a', 'b', 'c', 'd', 'e', 'f', 'g'],\n      enumNames: ['早', '中', '晚', 'd', 'e', 'f', 'g'],\n      default: 'a',\n    },\n    radio: {\n      title: '单选radio',\n      type: 'string',\n      enum: ['a', 'b'],\n      enumNames: ['早', '中'],\n      default: 'a',\n    },\n    fontSize: {\n      title: '字体大小',\n      readOnly: false,\n      required: false,\n      type: 'number',\n      widget: 'slider',\n      default: 24,\n      min: 12,\n      max: 64,\n    },\n    width: {\n      title: '宽度',\n      readOnly: false,\n      required: false,\n      type: 'number',\n      widget: 'slider',\n      default: 280,\n      min: 100,\n      max: 560,\n      props: {\n        hideInput: true,\n      },\n    },\n    time: {\n      title: '时间',\n      type: 'string',\n      format: 'time',\n    },\n    time2: {\n      title: '时间范围',\n      type: 'range',\n      format: 'time',\n    },\n    link: {\n      title: '链接',\n      type: 'string',\n      format: 'url',\n      props: {\n        prefix: 'https://',\n        suffix: '.com',\n      },\n    },\n    dateDemo: {\n      title: '时间',\n      format: 'dateTime',\n      type: 'string',\n      widget: 'date',\n      width: '50%',\n      default: '2018-11-22',\n      required: true,\n    },\n    dateRange: {\n      title: '时间范围',\n      format: 'dateTime',\n      type: 'range',\n      width: '50%',\n    },\n    objDemo: {\n      title: '单个对象',\n      description: '这是一个对象类型',\n      type: 'object',\n      properties: {\n        isLike: {\n          title: '是否显示颜色选择',\n          type: 'boolean',\n          default: true,\n        },\n        background: {\n          title: '颜色选择',\n          description: '特殊面板',\n          format: 'color',\n          type: 'string',\n          hidden: '{{rootValue.isLike === false}}',\n          default: '#ffff00',\n        },\n        wayToTravel: {\n          title: '旅行方式',\n          type: 'string',\n          enum: ['self', 'group'],\n          enumNames: ['自驾', '跟团'],\n          widget: 'radio',\n        },\n        canDrive: {\n          title: '是否拥有驾照',\n          type: 'boolean',\n          default: false,\n          hidden: \"{{rootValue.wayToTravel !== 'self'}}\",\n        },\n      },\n      required: ['background'],\n    },\n    html1: {\n      title: '纯字符串',\n      type: 'html',\n      default: 'hello world',\n    },\n    list: {\n      title: 'list',\n      type: 'array',\n    },\n    objectName: {\n      type: 'object',\n      description: '这是一个对象类型',\n      collapsed: false,\n      properties: {\n        input1: {\n          title: '简单输入框',\n          type: 'string',\n          required: true,\n        },\n      },\n    },\n  },\n};\n\nexport const listSchema = {\n  type: 'object',\n  properties: {\n    listName2: {\n      title: '对象数组',\n      description: '对象数组嵌套功能',\n      type: 'array',\n      // widget: 'cardList',\n      items: {\n        type: 'object',\n        properties: {\n          input1: {\n            title: '简单输入框',\n            type: 'string',\n            required: true,\n          },\n          select1: {\n            title: '单选',\n            type: 'string',\n            enum: ['a', 'b', 'c'],\n            enumNames: ['早', '中', '晚'],\n            default: 'a',\n          },\n          obj: {\n            title: '对象',\n            type: 'object',\n            properties: {\n              input1: {\n                title: '简单输入框',\n                type: 'string',\n                required: true,\n                default: '卡片列表',\n              },\n              select1: {\n                title: '单选',\n                type: 'string',\n                enum: ['a', 'b', 'c'],\n                enumNames: ['早', '中', '晚'],\n              },\n            },\n          },\n        },\n      },\n    },\n    listName3: {\n      title: '对象数组',\n      description: '对象数组嵌套功能',\n      type: 'array',\n      widget: 'simpleList',\n      items: {\n        type: 'object',\n        properties: {\n          input1: {\n            title: '简单输入框',\n            type: 'string',\n            required: true,\n          },\n          select1: {\n            title: '单选',\n            type: 'string',\n            enum: ['a', 'b', 'c'],\n            enumNames: ['早', '中', '晚'],\n          },\n        },\n      },\n    },\n    listName4: {\n      title: '对象数组',\n      description: '对象数组嵌套功能',\n      type: 'array',\n      widget: 'tableList',\n      items: {\n        type: 'object',\n        properties: {\n          input1: {\n            title: '简单输入框',\n            type: 'string',\n            required: true,\n          },\n          input2: {\n            title: '简单输入框2',\n            type: 'string',\n          },\n          input3: {\n            title: '简单输入框3',\n            type: 'string',\n          },\n          select1: {\n            title: '单选',\n            type: 'string',\n            enum: ['a', 'b', 'c'],\n            enumNames: ['早', '中', '晚'],\n            widget: 'select',\n          },\n        },\n      },\n    },\n  },\n};\n"
  },
  {
    "path": "packages/form-render/__tests__/utils.spec.ts",
    "content": "import { describe, test, expect } from 'vitest';\nimport { getWidgetName } from '../src/form-render-core/src/mapping';\nimport { getArray, getFormat, isUrl } from '../src/utils';\n\ndescribe('Test Utils', () => {\n  test('Test getFormat', () => {\n    expect(getFormat('date')).toBe('YYYY-MM-DD');\n    expect(getFormat('time')).toBe('HH:mm:ss');\n    expect(getFormat('dateTime')).toBe('YYYY-MM-DD HH:mm:ss');\n    expect(getFormat('week')).toBe('YYYY-w');\n    expect(getFormat('year')).toBe('YYYY');\n    expect(getFormat('quarter')).toBe('YYYY-Q');\n    expect(getFormat('month')).toBe('YYYY-MM');\n    expect(getFormat('YYYY-MM-DD')).toBe('YYYY-MM-DD');\n    expect(getFormat(123)).toBe('YYYY-MM-DD');\n  });\n\n  test('Test isUrl', () => {\n    expect(isUrl('https://github.com/alibaba/x-render')).toBe(true);\n    expect(isUrl('http://github.com/alibaba/x-render')).toBe(true);\n    expect(isUrl('github.com/alibaba/x-render')).toBe(false);\n    expect(isUrl(123)).toBe(false);\n  });\n\n  test('Test getArray', () => {\n    expect(getArray(['hangzhou', 'nanjing'])).toEqual(['hangzhou', 'nanjing']);\n    expect(getArray('test')).toEqual([]);\n  });\n\n  test('Test getWidgetName', () => {\n    expect(\n      getWidgetName({\n        type: 'string',\n        format: 'date',\n      })\n    ).toEqual('date');\n  });\n});\n"
  },
  {
    "path": "packages/form-render/package.json",
    "content": "{\n  \"name\": \"form-render\",\n  \"version\": \"2.5.7-beta.1\",\n  \"description\": \"通过 JSON Schema 生成标准 Form，常用于自定义搭建配置界面生成\",\n  \"keywords\": [\n    \"Form\",\n    \"FormRender\",\n    \"Render\",\n    \"XRender\",\n    \"React\",\n    \"Json Schema\",\n    \"Ant Design\"\n  ],\n  \"homepage\": \"https://xrender.fun/form-render\",\n  \"bugs\": {\n    \"url\": \"https://github.com/alibaba/x-render/issues\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git@github.com:alibaba/form-render.git\"\n  },\n  \"license\": \"MIT\",\n  \"contributors\": [\n    {\n      \"name\": \"Tw93\",\n      \"email\": \"tw93@qq.com\"\n    },\n    {\n      \"name\": \"lhbxs\",\n      \"email\": \"596850703@qq.com\"\n    }\n  ],\n  \"main\": \"lib/index.js\",\n  \"module\": \"es/index.js\",\n  \"files\": [\n    \"es\",\n    \"lib\",\n    \"package.json\"\n  ],\n  \"scripts\": {\n    \"beta\": \"npm publish --tag beta\",\n    \"build\": \"father-build\",\n    \"next\": \"npm publish --tag next\",\n    \"prepare\": \"npm run build\",\n    \"prettier\": \"prettier --write \\\"**/*.{js,jsx,tsx,ts,less,md,json}\\\"\",\n    \"postpublish\": \"git push --tags\",\n    \"test:ui\": \"vitest --ui\"\n  },\n  \"lint-staged\": {\n    \"*.{js,jsx,less,md,json}\": [\n      \"prettier --write\"\n    ],\n    \"*.ts?(x)\": [\n      \"prettier --parser=typescript --write\"\n    ]\n  },\n  \"dependencies\": {\n    \"@ant-design/icons\": \"^4.0.2\",\n    \"async-validator\": \"^3.5.1\",\n    \"classnames\": \"^2.3.1\",\n    \"color\": \"^3.1.2\",\n    \"lodash-es\": \"^4.17.21\",\n    \"dayjs\": \"^1.11.7\",\n    \"rc-color-picker\": \"^1.2.6\",\n    \"virtualizedtableforantd4\": \"^1.1.2\",\n    \"zustand\": \"^4.1.5\",\n    \"ahooks\": \"^3.7.5\"\n  },\n  \"devDependencies\": {\n    \"deep-equal\": \"^2.0.3\",\n    \"rollup-plugin-copy\": \"^3.4.0\"\n  },\n  \"peerDependencies\": {\n    \"antd\": \"4.x || 5.x\",\n    \"react\": \">=16.9.0\",\n    \"react-dom\": \">=16.9.0\"\n  },\n  \"gitHooks\": {\n    \"pre-commit\": \"lint-staged\"\n  },\n  \"sideEffect\": false\n}"
  },
  {
    "path": "packages/form-render/src/derivative/SearchForm/ActionView.tsx",
    "content": "import React, { useContext } from 'react';\nimport { Button, Space, ConfigProvider } from 'antd';\nimport { DownOutlined, UpOutlined } from '@ant-design/icons';\nimport { translation } from '../../utils'\n\nconst ActionView = (props: any) => {\n  const {\n    onReset,\n    searchBtnRender,\n    style,\n    className,\n    form,\n    searchText,\n    resetText,\n    hasCollapse,\n    setLimitHeight,\n    setExpand,\n    isExpand,\n    loading,\n    retainBtn,\n    mode,\n  } = props;\n\n  const configCtx = useContext(ConfigProvider.ConfigContext);\n  const t = translation(configCtx);\n\n  const handleReset = () => {\n    if (onReset) {\n      onReset(form);\n      return;\n    }\n    form.resetFields();\n    form.submit();\n  };\n\n  const handleCollapse = () => {\n    setExpand(!isExpand);\n  };\n\n  const searchBtnArr = typeof searchBtnRender === 'function' ? searchBtnRender(form.submit, handleReset, { loading }) : [];\n\n  if (searchBtnRender) {\n    return (\n      <div className='flex justify-end w-100'>\n        {Array.isArray(searchBtnArr) &&\n          searchBtnArr.map((ui, idx) => {\n            return (\n              <div key={idx.toString()} style={{ marginLeft: 8 }}>\n                {ui}\n              </div>\n            );\n          })}\n      </div>\n    );\n  }\n\n  const submitShow = (mode === 'simple' && (typeof retainBtn === 'boolean' || retainBtn?.includes('submit')) || mode !== 'simple');\n  const resetShow = (mode === 'simple' && (typeof retainBtn === 'boolean' || retainBtn?.includes('reset')) || mode !== 'simple');\n\n  return (\n    <div\n      className={`flex justify-end w-100 ${className || ''}`}\n      style={style}\n    >\n      <Space>\n        {submitShow && <Button loading={loading} type='primary' onClick={form.submit}>{searchText}</Button>}\n        {resetShow && <Button onClick={handleReset}>{resetText}</Button>}\n        {hasCollapse && (\n          <a onClick={handleCollapse} style={{ cursor: 'pointer' }}>\n            {isExpand ? (\n              <>\n                {t('fold')}\n                <UpOutlined />\n              </>\n            ) : (\n              <>\n                {t('expand')}\n                <DownOutlined />\n              </>\n            )}\n          </a>\n        )}\n      </Space>\n    </div>\n  );\n}\n\nexport default ActionView;\n"
  },
  {
    "path": "packages/form-render/src/derivative/SearchForm/index.less",
    "content": "@ant-prefix: ant;\n\n.fr-search {\n  width: 100%;\n  background: #fff;\n  padding: 24px 24px 0 16px;\n  margin-bottom: 16px;\n  box-sizing: border-box;\n  position: relative;\n\n  .search-action-col {\n    flex: 1;\n    display: flex;\n    justify-content: flex-end;\n    align-items: baseline;\n    height: 56px;\n  }\n\n  .search-action-fixed {\n    position: absolute;\n    right: 0;\n    bottom: 0;\n    background-color: #fff;\n    padding-right: 24px !important;\n  }\n\n  .search-action-column {\n    height: auto;\n  }\n\n  .fr-form > .@{ant-prefix}-row {\n    align-items: center;\n  }\n}\n\n.fr-column-search {\n  padding-left: 24px;\n}\n"
  },
  {
    "path": "packages/form-render/src/derivative/SearchForm/index.tsx",
    "content": "import React, { useContext, useMemo, useRef } from 'react';\nimport { Col, ConfigProvider } from 'antd';\nimport { useUpdateEffect, useMount, useSetState } from 'ahooks';\nimport classnames from 'classnames';\nimport { debounce } from 'lodash-es';\n\nimport FormRender from '../../form-core';\nimport { translation } from '../../utils';\nimport withProvider from '../../withProvider';\nimport ActionView from './ActionView';\nimport {\n  Input,\n  InputNumber,\n  TextArea,\n  Select,\n  MultiSelect,\n  Checkbox,\n  Checkboxes,\n  Radio,\n  DatePicker,\n  DateRange,\n  TimePicker,\n  TimeRange,\n  Slider,\n  Switch,\n  TreeSelect,\n  UrlInput,\n  ImageInput,\n  Html,\n  PercentSlider,\n} from '../../widgets';\nimport './index.less';\nimport { SearchProps } from '../../type';\n\nconst getIsColumn = (isColumn: boolean, obj: object, column: number) => {\n  let count = 0;\n  Object.keys(obj || {}).forEach(key => {\n    const item = obj[key];\n    if (item.visible === undefined) {\n      count += 1;\n    }\n  });\n  return isColumn && (count % column !== 0);\n};\n\nconst SearchForm: <RecordType extends object = any>(props: SearchProps<RecordType>) => React.ReactElement = props => {\n  if (props.hidden) {\n    return null;\n  }\n\n  const configCtx = useContext(ConfigProvider.ConfigContext);\n  const t = translation(configCtx);\n  const {\n    className,\n    style,\n    mode,\n    layoutAuto = false,\n    form,\n    hidden,\n    loading,\n    column: _column = 4,\n    collapsed,\n    defaultCollapsed = true,\n    schema,\n    retainBtn,\n    closeReturnSearch,\n    resetAfter,\n    searchBtnStyle,\n    searchBtnClassName,\n    searchText = t('search'),\n    resetText = t('reset'),\n    searchWithError = true,\n    searchOnMount = true,\n    onMount,\n    onSearch,\n    onReset,\n    searchBtnRender,\n    ...restProps\n  } = props;\n\n  const fieldNum = Object.keys(schema?.properties || {}).length;\n  const isColumn = (restProps.displayType || schema.displayType) === 'column';\n  const operateShow = mode !== 'simple' || (mode === 'simple' && retainBtn);\n  const containerRef = useRef<any>(null);\n\n  const [state, setState] = useSetState({\n    hasCollapse: false, // 是否有折叠\n    isExpand: !defaultCollapsed, // 折叠展开状态\n    column: schema.column || _column // 一行几列\n  });\n  const { hasCollapse, isExpand, column } = state;\n\n  const actionProps = {\n    style: searchBtnStyle,\n    className: searchBtnClassName,\n    searchText,\n    resetText,\n    loading,\n    form,\n    hasCollapse,\n    isExpand,\n    onReset,\n    searchBtnRender,\n  };\n\n  useMount(() => {\n    initMount();\n  });\n\n  useUpdateEffect(() => {\n    if (isExpand) {\n      initMount();\n    }\n  }, [isExpand]);\n\n  useMount(() => {\n    if (!collapsed) {\n      return;\n    }\n    if ((!isColumn && fieldNum > (column * 2 - 1)) || (isColumn && fieldNum > (column -1))) {\n      setState({ hasCollapse: true });\n    }\n    handleContainerResize();\n  });\n\n  useUpdateEffect(() => {\n    if (!collapsed) {\n      return;\n    }\n\n    if ((!isColumn && fieldNum > (column * 2 - 1)) || (isColumn && fieldNum > (column -1))) {\n      setState({ hasCollapse: true });\n    } else {\n      setState({ hasCollapse: true });\n    }\n  }, [column]);\n\n  useUpdateEffect(() => {\n    if (collapsed) {\n      if ((!isColumn && fieldNum > (column * 2 - 1)) || (isColumn && fieldNum > (column -1))) {\n        setState({\n          hasCollapse: true,\n          isExpand: !defaultCollapsed\n        });\n      } else {\n        setState({\n          hasCollapse: false,\n          isExpand: true\n        });\n      }\n    } else {\n      setState({\n        hasCollapse: false,\n        isExpand: true,\n      });\n    }\n  }, [collapsed]);\n\n  const initMount = async () => {\n    if (!searchOnMount) {\n      return;\n    }\n    if (typeof onMount === 'function') {\n      await onMount();\n    }\n    form.submit();\n  };\n\n  const properties = useMemo(() => {\n    if (!collapsed) {\n      return schema?.properties;\n    }\n    const result = {};\n    Object.keys(schema?.properties || {}).forEach((key, index) => {\n      const item = { ...(schema.properties[key] || {}) };\n      if (\n        (!isExpand && isColumn && index >= column - 1) ||\n        (!isExpand && !isColumn && index >= column * 2 - 1) // 只显示两行\n      ) {\n        item.visible = false;\n      }\n      result[key] = item;\n    });\n    return result;\n  }, [JSON.stringify(schema), column, isColumn, isExpand, collapsed]);\n\n  const handleContainerResize = () => {\n    if (!layoutAuto) {\n      return;\n    }\n    const resizeObserver = new ResizeObserver(debounce(() => {\n      const { clientWidth } = containerRef?.current || {};\n      for (let i = _column; i > 0; i--) {\n        const item = clientWidth / i;\n        if (item >= (layoutAuto?.fieldMinWidth || 340)) {\n          setState({ column: i });\n          break;\n        }\n        if (i === 1) {\n          setState({ column: 1 });\n        }\n      }\n    }, 300, { leading: true }));\n\n    resizeObserver.observe(containerRef.current);\n    () => {\n      resizeObserver.disconnect();\n    };\n  };\n\n  const handleFinish = (values: any) => {\n    onSearch?.(values);\n  };\n\n  const handleFinishFailed = ({ values }) => {\n    if (!searchWithError) {\n      return;\n    }\n    onSearch?.(values);\n  };\n\n  const handleKeyDown = (ev: any) => {\n    if (ev.keyCode !== 13) {\n      return;\n    }\n    form.submit();\n  };\n\n  return (\n    <div\n      className={classnames('fr-search', {[className]: !!className, 'fr-column-search': isColumn })}\n      style={style}\n      ref={containerRef}\n      onKeyDown={!closeReturnSearch ? handleKeyDown : undefined}\n    >\n      <FormRender\n        displayType='row'\n        {...restProps}\n        schema={{\n          ...schema,\n          properties,\n          column\n        }}\n        onFinish={handleFinish}\n        onFinishFailed={handleFinishFailed}\n        form={form}\n        operateExtra={operateShow && (\n          <Col\n            className={classnames('search-action-col', {\n              'search-action-column': getIsColumn(isColumn, properties, column)\n            })}\n            style={{ minWidth: (1 / column) * 100 + '%' }}\n          >\n            <ActionView\n              {...actionProps}\n              retainBtn={retainBtn}\n              mode={mode}\n              setExpand={(value: boolean) => setState({ isExpand: value })}\n            />\n          </Col>\n        )}\n      />\n    </div>\n  );\n};\n\nexport default withProvider(SearchForm, {\n  Input,\n  InputNumber,\n  TextArea,\n  Select,\n  MultiSelect,\n  Switch,\n  Radio,\n  Checkbox,\n  Checkboxes,\n  DatePicker,\n  DateRange,\n  TimePicker,\n  TimeRange,\n  TreeSelect,\n  ImageInput,\n  UrlInput,\n  Slider,\n  Html,\n  PercentSlider,\n});\n"
  },
  {
    "path": "packages/form-render/src/derivative/SlimRender/index.tsx",
    "content": "import FormCore from '../../form-core';\nimport withProvider from '../../withProvider';\nimport Html from '../../widgets/fields/html';\n\nexport default withProvider(FormCore, { Html });\n"
  },
  {
    "path": "packages/form-render/src/form-core/connectForm.tsx",
    "content": "import React, { forwardRef } from 'react';\nimport useForm from '../models/useForm';\n\nexport default (Component: React.FC<any>) => {\n  return forwardRef((props, ref) => {\n    const form = useForm();\n\n    return <Component ref={ref} {...props} form={form} />;\n  });\n}"
  },
  {
    "path": "packages/form-render/src/form-core/index.less",
    "content": "@ant-prefix: ant;\n\n.fr-hide-label > .@{ant-prefix}-form-item-row > .@{ant-prefix}-form-item-label {\n  visibility: hidden;\n}\n\n.fr-field {\n  .@{ant-prefix}-form-item-row {\n    flex-wrap: nowrap;\n  }\n  .@{ant-prefix}-form-item-control-input-content {\n    display: flex;\n  }\n}\n\n.fr-field-visibility {\n  width: 0;\n  height: 0;\n  visibility: hidden;\n  position: absolute;\n  opacity: 0;\n}\n\n.fr-form {\n  // 影响宽度样式，暂时注释\n  // .@{ant-prefix}-form-item-control {\n  //   width: 0;\n  // }\n\n  .fr-inline-field {\n    .@{ant-prefix}-form-item-control {\n      width: auto;\n    }\n  }\n}"
  },
  {
    "path": "packages/form-render/src/form-core/index.tsx",
    "content": "import React, { useEffect, useContext, FC, useMemo } from 'react';\nimport { Form, Row, Col, Button, Space, ConfigProvider } from 'antd';\nimport classNames from 'classnames';\nimport { cloneDeep } from 'lodash-es';\nimport { useStore } from 'zustand';\n\nimport { FRContext } from '../models/context';\nimport transformProps from '../models/transformProps';\nimport { parseValuesToBind } from '../models/bindValues';\nimport filterValuesHidden from '../models/filterValuesHidden';\nimport filterValuesUndefined from '../models/filterValuesUndefined';\nimport { getFormItemLayout } from '../models/layout';\nimport { translation, isFunction } from '../utils';\n\nimport {\n  valuesWatch,\n  immediateWatch,\n  yymmdd,\n  msToTime,\n  getSessionItem,\n  setSessionItem\n} from '../models/formCoreUtils';\n\nimport { FRProps } from '../type';\nimport RenderCore from '../render-core';\nimport './index.less';\n\nconst FormCore:FC<FRProps> = (props) => {\n  const store: any = useContext(FRContext);\n  const schema = useStore(store, (state: any) => state.schema);\n  const flattenSchema = useStore(store, (state: any) => state.flattenSchema);\n  const setContext = useStore(store, (state: any) => state.setContext);\n\n  const configCtx = useContext(ConfigProvider.ConfigContext);\n  const t = translation(configCtx);\n\n  const { type, properties, ...schemProps } = schema || {};\n  const {\n    formProps,\n    displayType,\n    beforeFinish,\n    watch,\n    onMount,\n    column,\n    labelWidth,\n    labelCol,\n    fieldCol,\n    maxWidth,\n    form,\n    onFinish,\n    onFinishFailed,\n    readOnly,\n    disabled,\n    footer,\n    removeHiddenData,\n    operateExtra,\n    logOnMount,\n    logOnSubmit,\n    id,\n    className,\n    validateTrigger,\n    antdVersion,\n  } = transformProps({ ...props, ...schemProps });\n\n  useEffect(() => {\n    form.__initStore(store);\n    setTimeout(initial, 0);\n    (window as any).antdVersion = antdVersion;\n  }, []);\n\n  useEffect(() => {\n    form.setSchema(props.schema, true);\n  }, [JSON.stringify(props.schema || {})]);\n\n  useEffect(() => {\n    store.setState({ removeHiddenData });\n  }, [removeHiddenData]);\n\n  useEffect(() => {\n    const context = {\n      column,\n      readOnly,\n      disabled,\n      labelWidth,\n      displayType,\n      labelCol,\n      fieldCol,\n      maxWidth,\n      validateTrigger\n    };\n    setContext(context);\n  }, [column, labelCol, fieldCol, displayType, labelWidth, maxWidth, readOnly, disabled, validateTrigger]);\n\n  const initial = async () => {\n    onMount && await onMount();\n    onMountLogger();\n    setTimeout(() => {\n      const values = form.getValues();\n      immediateWatch(watch, values);\n    }, 0);\n  };\n\n  const onMountLogger = () => {\n    const start = new Date().getTime();\n    if (isFunction(logOnMount)|| isFunction(logOnSubmit)) {\n      setSessionItem('FORM_MOUNT_TIME', start);\n      setSessionItem('FORM_START', start);\n    }\n    if (isFunction(logOnMount)) {\n      const logParams: any = {\n        schema: props.schema,\n        url: location.href,\n        formData: JSON.stringify(form.getValues()),\n        formMount: yymmdd(start),\n      };\n      if (id) {\n        logParams.id = id;\n      }\n      logOnMount(logParams);\n    }\n    // 如果是要计算时间，在 onMount 时存一个时间戳\n    if (isFunction(logOnSubmit)) {\n      setSessionItem('NUMBER_OF_SUBMITS', 0);\n      setSessionItem('FAILED_ATTEMPTS', 0);\n    }\n  };\n\n  const onSubmitLogger = (params: any) => {\n    if (!isFunction(logOnSubmit)) {\n      return;\n    }\n   \n    const start = getSessionItem('FORM_START');\n    const mount = getSessionItem('FORM_MOUNT_TIME');\n\n    const numberOfSubmits = getSessionItem('NUMBER_OF_SUBMITS') + 1;\n    const end = new Date().getTime();\n\n    let failedAttempts = getSessionItem('FAILED_ATTEMPTS');\n    if (params.errorFields.length > 0) {\n      failedAttempts = failedAttempts + 1;\n    }\n    const logParams: any = {\n      formMount: yymmdd(mount),\n      ms: end - start,\n      duration: msToTime(end - start),\n      numberOfSubmits: numberOfSubmits,\n      failedAttempts: failedAttempts,\n      url: location.href,\n      formData: JSON.stringify(params.values),\n      errors: JSON.stringify(params.errorFields),\n      schema: JSON.stringify(schema),\n    };\n    if (id) {\n      logParams.id = id;\n    }\n    logOnSubmit(logParams);\n    setSessionItem('FORM_START', end);\n    setSessionItem('NUMBER_OF_SUBMITS', numberOfSubmits);\n    setSessionItem('FAILED_ATTEMPTS', failedAttempts);\n  };\n\n  const handleValuesChange = (changedValues: any, _allValues: any) => {\n    const allValues = filterValuesUndefined(_allValues, true);\n    valuesWatch(changedValues, allValues, watch);\n  };\n\n  const transFormValues = (_values: any) => {\n    let values = cloneDeep(_values);\n    values = removeHiddenData ? filterValuesHidden(values, flattenSchema) : cloneDeep(form.getFieldsValue(true));\n    values = parseValuesToBind(values, flattenSchema);\n    values = filterValuesUndefined(values);\n    return values;\n  };\n\n  const handleFinish = async (_values: any) => {\n    const values = transFormValues(_values);\n    const fieldsError = beforeFinish ? await beforeFinish({ data: values, schema, errors: [] }) : null;\n    // console.log(values, form.getValues(true), _values);\n    if (fieldsError?.length > 0) {\n      form.setFields(fieldsError);\n      return;\n    }\n    onSubmitLogger({ values });\n    onFinish && onFinish(values, []);\n  };\n\n  const handleFinishFailed = async (params: any) => {\n    const values = transFormValues(params.values);\n    onSubmitLogger({ ...params, values });\n    if (!onFinishFailed) {\n      return;\n    }\n    onFinishFailed({ ...params, values });\n  };\n\n  const operlabelCol = getFormItemLayout(column, {}, { labelWidth })?.labelCol;\n\n  const actionBtns = [];\n  if (!footer?.reset?.hide) {\n    actionBtns.push(\n      <Button key='reset' {...footer?.reset} onClick={() => form.resetFields()}>\n        {footer?.reset?.text || t('reset')}\n      </Button>\n    );\n  }\n  if (!footer?.submit?.hide) {\n    actionBtns.push(\n      <Button key='submit' type='primary' onClick={form.submit} {...footer?.submit}>\n        {footer?.submit?.text || t('submit')}\n      </Button>\n    );\n  }\n  \n  return (\n    <Form\n      className={classNames('fr-form', { [className]: !!className } )}\n      labelWrap={true}\n      {...formProps}\n      disabled={disabled}\n      form={form}\n      onFinish={handleFinish}\n      onFinishFailed={handleFinishFailed}\n      onValuesChange={handleValuesChange}\n    >\n      <Row gutter={displayType === 'row' ? 16 : 24}>\n        <RenderCore schema={schema} />\n        {operateExtra}\n      </Row>\n      {schema && !!footer && (\n        <Row gutter={displayType === 'row' ? 16 : 24}>\n          <Col span={24 / column}>\n            <Form.Item\n              label={ displayType !== 'column' ?  'hideLabel' : null}\n              labelCol={operlabelCol}\n              className='fr-hide-label'\n            >\n               {isFunction(footer) ? (\n                <Space>{footer(actionBtns)}</Space>\n              ) : (\n                <Space>{actionBtns}</Space>\n              )}\n            </Form.Item>\n          </Col>\n        </Row>\n      )}\n    </Form>\n  );\n}\n\nexport default FormCore;\n"
  },
  {
    "path": "packages/form-render/src/index.ts",
    "content": "import FormCore from './form-core';\nimport withProvider from './withProvider';\nimport * as defaultWidgets from './widgets';\n\nexport * from './widgets';\nexport { mapping } from './models/mapping';\n\nexport { default as useForm } from './models/useForm';\nexport { default as connectForm } from './form-core/connectForm';\nexport { default as SearchForm } from './derivative/SearchForm';\nexport { default as FormSlimRender } from './derivative/SlimRender';\n\nexport type {\n  default as FR,\n  Schema,\n  FRProps,\n  FormInstance,\n  FormParams,\n  FieldParams,\n  WatchProperties,\n  SchemaType,\n  SchemaBase,\n  ValidateParams,\n  ResetParams,\n  RuleItem,\n  WidgetProps,\n} from './type';\n\nexport default withProvider(FormCore, defaultWidgets);\n"
  },
  {
    "path": "packages/form-render/src/locales/en_US.ts",
    "content": "export default {\n  \"copy_max_tip\": \"The maximum number of table items has been reached and cannot be copied\",\n  \"copy\": \"Copy\",\n  \"add_item\": \"Add a new line\",\n  \"confirm_delete\": \"Are you sure to delete?\",\n  \"confirm\": \"Yes\",\n  \"cancel\": \"No\",\n  \"operate\": \"Operate\",\n  \"delete\": \"Delete\",\n  \"edit\": \"Edit\",\n  \"img_src_error\": \"Image address error\",\n  \"upload\": \"Upload\",\n  \"upload_success\": \"upload success\",\n  \"upload_fail\": \"upload failed\",\n  \"uploaded_address\": \"Uploaded address\",\n  \"test_src\": \"Test address\",\n  \"schema_not_match\": \"Schema does not match the display component：\",\n  \"item\": \"Item\",\n  \"search\": \"Search\",\n  \"reset\": \"Reset\",\n  \"expand\": \"Expand\",\n  \"fold\": \"Fold\",\n  \"submit\": \"Submit\",\n  \"save\": \"Save\",\n  \"moveDown\": \"Move Down\",\n  \"moveUp\": \"Move Up\"\n}"
  },
  {
    "path": "packages/form-render/src/locales/index.ts",
    "content": "import enUS from './en_US';\nimport zhCN from './zh_CN';\n\nexport default {\n  'en-US':  enUS,\n  'zh-CN': zhCN,\n}"
  },
  {
    "path": "packages/form-render/src/locales/zh_CN.ts",
    "content": "export default {\n  \"copy_max_tip\": \"已达表单项数量上限，无法复制！\",\n  \"copy\": \"复制\",\n  \"add_item\": \"新增一条\",\n  \"confirm_delete\": \"确定删除?\",\n  \"confirm\": \"确定\",\n  \"cancel\": \"取消\",\n  \"operate\": \"操作\",\n  \"delete\": \"删除\",\n  \"edit\": \"编辑\",\n  \"img_src_error\": \"图片地址错误\",\n  \"upload\": \"上传\",\n  \"upload_success\": \"上传成功\",\n  \"upload_fail\": \"上传失败\",\n  \"uploaded_address\": \"已上传地址\",\n  \"test_src\": \"测试链接\",\n  \"schema_not_match\": \"schema未匹配到展示组件：\",\n  \"item\": \"项目\",\n  \"search\": \"查询\",\n  \"reset\": \"重置\",\n  \"expand\": \"展开\",\n  \"fold\": \"收起\",\n  \"submit\": \"提交\",\n  \"save\": \"保存\",\n  \"moveDown\": \"下移\",\n  \"moveUp\": \"上移\"\n};"
  },
  {
    "path": "packages/form-render/src/models/bindValues.ts",
    "content": "import { get, set, unset } from 'lodash-es';\nimport {\n  _cloneDeep,\n  isArray,\n  isObject,\n  safeGet\n} from '../utils/index';\n\nconst isMultiBind = (array: string[]) => isArray(array) && array.every(item => typeof item === 'string');\n\n// Need to consider list nested controls\nconst transformPath = (path: string) => {\n  const result: string[] = [];\n\n  const recursion = (str: string) => {\n    const index = str.indexOf('[]');\n    if (index === -1) {\n      result.push(str);\n      return;\n    }\n    result.push(str.substring(0, index));\n    recursion(str.substring(index+3))\n  };\n\n  recursion(path);\n\n  if (result.length === 1) {\n    return result[0];\n  }\n  return result;\n};\n\nconst transformValueToBind = (data: any, path: any, bind: false | string | string[]) => {\n  if (bind === false) {\n    unset(data, path);\n    return;\n  }\n\n  if (typeof bind === 'string') {\n    let value = get(data, path);\n    const preValue = get(data, bind);\n    if (isObject(preValue)) {\n      value = { ...preValue, ...value };\n    }\n    set(data, bind, value);\n    unset(data, path);\n    return;\n  }\n\n  // The array is converted to multiple fields.\n  if (isMultiBind(bind)) {\n    const value = get(data, path);\n    unset(data, path);\n\n    if (Array.isArray(value)) {\n      value.forEach((item, index) => {\n        const bindPath = bind[index];\n        bindPath && set(data, bindPath, item);\n      });\n    }\n  }\n}\n\nconst transformBindToValue = (data: any, path: any, bind: any) => {\n  if (typeof bind === 'string') {\n    let value = get(data, bind);\n    const preValue = get(data, path);\n    if (isObject(preValue)) {\n      value = { ...preValue, ...value };\n    }\n    set(data, path, value);\n    unset(data, bind);\n    return;\n  }\n\n  // The array is converted to multiple fields.\n  if (isMultiBind(bind)) {\n    const value = [];\n    bind.forEach(key => {\n      const bindValue = get(data, key);\n      // if (bindValue != undefined) {\n      //   value.push(bindValue);\n      // }\n      value.push(bindValue);\n      unset(data, key);\n    });\n\n    if (value.length > 0) {\n      set(data, path, value);\n    }\n  }\n}\n\n\nexport const parseValuesToBind = (values: any, flatten: any) => {\n  // No bind field exists, no processing\n  if (!JSON.stringify(flatten).includes('bind')) {\n    return values;\n  }\n\n  const data = _cloneDeep(values);\n\n  const dealFieldList = (obj: any, [path, ...rest]: any, bind: any) => {\n    if (rest.length === 1) {\n      const list = get(obj, path, [])||[];\n      list.forEach((item: any, index: number) => {\n        const value = get(item, rest[0]);\n        if (bind === 'root') {\n          list[index] = value;\n          return;\n        }\n        transformValueToBind(item, rest[0], bind);\n      });\n    }\n\n    if (isArray(obj)) {\n      obj.forEach((item: any) => dealFieldList(item, [path, ...rest], bind));\n    } else if (isObject(obj)) {\n      const value = get(obj, path);\n      dealFieldList(value, rest, bind);\n    }\n  };\n\n  Object.keys(flatten).forEach(key => {\n    const bind = flatten[key]?.schema?.bind;\n    if (bind === undefined) {\n      return;\n    }\n    const path = transformPath(key);\n    isArray(path) ? dealFieldList(data, path, bind) : transformValueToBind(data, path, bind);\n  });\n\n  return data;\n};\n\nexport const parseBindToValues = (values: any, flatten: any) => {\n  if (!JSON.stringify(flatten).includes('bind')) {\n    return values;\n  }\n\n  const data = _cloneDeep(values);\n  const dealFieldList = (obj: any, [path, ...rest]: any, bind: any) => {\n    if (rest.length === 1) {\n      const list = safeGet(obj, path, []);\n      list.forEach((item: any, index: number) => {\n        if (bind === 'root') {\n          list[index] = { [rest[0]] : item };\n          return;\n        }\n        transformBindToValue(item, rest[0], bind);\n      });\n    }\n\n    if (isArray(obj)) {\n      obj.forEach((item: any) => dealFieldList(item, [path, ...rest], bind));\n    } else if (isObject(obj)) {\n      const value = get(obj, path);\n      dealFieldList(value, rest, bind);\n    }\n  };\n\n  Object.keys(flatten).forEach(key => {\n    const bind = flatten[key]?.schema?.bind;\n    if (bind === undefined) {\n      return;\n    }\n    const path = transformPath(key);\n\n    isArray(path) ? dealFieldList(data, path, bind) : transformBindToValue(data, path, bind);\n  });\n\n  return data;\n};\n\n\n"
  },
  {
    "path": "packages/form-render/src/models/context.ts",
    "content": "import { createContext } from 'react';\n\nexport const FRContext = createContext(null);\n\nexport const ConfigContext = createContext(null);"
  },
  {
    "path": "packages/form-render/src/models/expression.ts",
    "content": "import { get } from 'lodash-es';\nimport { isObject, _cloneDeep, isArray } from '../utils/index';\nimport { createDataSkeleton } from './formDataSkeleton';\n\nexport const isExpression = (str: string) => {\n  if (typeof str !== 'string') {\n    return false;\n  }\n\n  const pattern = /^{\\s*{(.+)}\\s*}$/s;\n  const reg1 = /^{\\s*{function\\(.+}\\s*}$/;\n  return str.match(pattern) && !str.match(reg1);\n}\n\nexport const isHasExpression = (schema: any) => {\n  const result = Object.keys(schema).some((key: string) => {\n    const item = schema[key];\n\n    // 子协议不做递归确认\n    if (key === 'properties') {\n      return false;\n    }\n\n    const recursionArray = (list: any[]) => {\n      const result = list.some(ite => {\n        if (isArray(ite)) {\n          return recursionArray(ite);\n        }\n\n        if (isObject(ite)) {\n          return isHasExpression(ite);\n        }\n        return isExpression(ite);\n      });\n      return result;\n    };\n\n    if (isArray(item)) {\n      return recursionArray(item);\n    }\n\n    if (isObject(item)) {\n      return isHasExpression(item);\n    }\n\n    return isExpression(item);\n  });\n\n  return result;\n};\n\nconst parseFunc = (funcBody: string) => {\n  const funcBodyTemp = funcBody.replace(/(\\.|\\?\\.)/g, '?.'); // 将. 和 ?. 统一替换为?.\n  const funcBodyStr = funcBodyTemp.replace(/(\\d+)\\?\\.(\\d+)/g, '$1.$2'); //  排除数字中的?.\n  const result = [...funcBodyStr].reduce((acc, char, index) => {\n    if (char === '[') {\n      if (index > 0 && funcBodyStr[index - 1] !== '\\n') {\n        // 排除开头[]\n        return `${acc}?.${char}`;\n      }\n    }\n    return `${acc}${char}`;\n  }, '');\n  return result;\n};\n\nexport const parseExpression = (\n  func: any,\n  formData = {},\n  parentPath: string | []\n) => {\n  const parentData = get(formData, parentPath) || {};\n\n  if (typeof func === 'string') {\n    const funcBody = func\n      .replace(/^{\\s*{/g, '')\n      .replace(/}\\s*}$/g, '')\n      .trim();\n    let isHandleData =\n      funcBody?.startsWith('formData') || funcBody?.startsWith('rootValue');\n\n    let funcBodyStr = isHandleData ? parseFunc(funcBody) : funcBody;\n\n    const funcStr = `\n      return ${funcBodyStr\n        .replace(/formData/g, JSON.stringify(formData))\n        .replace(/rootValue/g, JSON.stringify(parentData))}\n    `;\n    try {\n      const result = Function(funcStr)();\n      return result;\n    } catch (error) {\n      console.log(error, funcStr, parentPath);\n      return null; // 如果计算有错误，return null 最合适\n    }\n  }\n\n  return func;\n}\n\nexport function getRealDataPath(path) {\n  if (typeof path !== 'string') {\n    throw Error(`id ${path} is not a string!!! Something wrong here`);\n  }\n\n  if (path.match(/[$]void_[^.]+$/)) {\n    return undefined;\n  }\n\n  return path.replace(/[$]void_[^.]+./g, '');\n}\n\nexport function getValueByPath(formData, path) {\n  if (path === '#' || !path) {\n    return formData || {};\n  } else if (typeof path === 'string') {\n    const realPath = getRealDataPath(path);\n    return realPath && get(formData, realPath);\n  } else {\n    console.error('path has to be a string');\n  }\n}\n\nexport const parseAllExpression = (_schema: any, _formData: any, dataPath: string, formSchema?: any) => {\n  const schema = _cloneDeep(_schema);\n  let formData = _formData;\n  if (formSchema) {\n    formData = createDataSkeleton(formSchema, formData);\n  }\n\n  const recursionArray = (list: any[]) => {\n    const result = list.map(item => {\n      if (isArray(item)) {\n        return recursionArray(item);\n      }\n      if (isObject(item)) {\n        return parseAllExpression(item, formData, dataPath);\n      }\n\n      if (isExpression(item)) {\n        return parseExpression(item, formData, dataPath);\n      }\n      return item;\n    });\n\n    return result;\n  }\n\n  Object.keys(schema).forEach(key => {\n    const value = schema[key];\n\n    if (isArray(value)) {\n      schema[key] = recursionArray(value);\n    } if (isObject(value) && (value.mustacheParse ?? true)) {\n      schema[key] = parseAllExpression(value, formData, dataPath);\n    } else if (isExpression(value)) {\n      schema[key] = parseExpression(value, formData, dataPath);\n    }\n  });\n\n  return schema;\n};\n\n"
  },
  {
    "path": "packages/form-render/src/models/fieldShouldUpdate.ts",
    "content": "import { parseExpression } from './expression';\n\n// 提取 formData. 开头的字符串\nconst extractFormDataStrings = (list: string[]) => {\n  let result = [];\n  list.forEach(str => {\n    // TODO: 为啥要拆开来获取？\n    // const regex = /formData.\\w+(.\\w+)*(\\(.*\\))?/g; // 匹配formData.后面跟着字母、数字、下划线间隔的组合\n    // const regex = /formData(\\.\\w+|\\[\\w+\\])(\\.\\w+|\\[\\w+\\])*/g; // 1.同时匹配两种格式\n    const regex = /formData(\\.\\w+|\\[(['\"])?\\w+\\2?\\])*(\\.\\w+)?/g;// 1.支持formData.xx 格式 以及formData[]格式 []中变量名只能包含字母、数字、下划线(_)\n    const matches = str.match(regex);\n    if (matches) {\n      result = result.concat(\n        matches\n      );\n    }\n  });\n\n  return result;\n};\n\n// 提取 rootValue. 开头的字符串\nconst extractRootValueStrings = (list: string[]) => {\n  let result = [];\n  list.forEach(str => {\n    // const regex = /rootValue.\\w+(.\\w+)*(\\(.*\\))?/g; // 匹配formData.后面跟着字母、数字、下划线间隔的组合\n    // const regex = /rootValue(\\.\\w+|\\[\\w+\\])(\\.\\w+|\\[\\w+\\])*/g; // 1.同时匹配两种格式\n    const regex = /rootValue(\\.\\w+|\\[(['\"])?\\w+\\2?\\])*(\\.\\w+)?/g;// 1.支持rootValue.xx 格式 以及rootValue[]格式 []中变量名只能包含字母、数字、下划线(_)\n    const matches = str.match(regex);\n    if (matches) {\n      result = result.concat(\n        matches\n      );\n    }\n  });\n  return result;\n};\n\n// 提取 {{ }} 里面的内容\nconst findStrList = (str: any, type: string) => {\n  const regex = /{{(.*?)}}/g;\n  const matches = [];\n  let match;\n  while ((match = regex.exec(str)) !== null) {\n    matches.push(match[1]);\n  };\n\n  if (type === 'formData') {\n    return extractFormDataStrings(matches);\n  }\n\n  if (type === 'rootValue') {\n    return extractRootValueStrings(matches);\n  }\n  return [];\n};\n\nconst getListEveryResult = (list: string[], preValue: any, nextValue: any, dataPath: string) => {\n  return list.every(item => {\n    const pre = parseExpression(item, preValue, dataPath);\n    const curr = parseExpression(item, nextValue, dataPath);\n    return pre === curr;\n  });\n};\n\nexport default (str: string, dataPath: string, dependencies: any[], shouldUpdateOpen: boolean) => (preValue: any, nextValue: any) => {\n    // dependencies 先不处理\n    if (dependencies) {\n      return true;\n    }\n\n    const formDataList = findStrList(str, 'formData');\n    const rootValueList = findStrList(str, 'rootValue');\n    const formDataRes = getListEveryResult(formDataList, preValue, nextValue, dataPath);\n    const rootValueRes = getListEveryResult(rootValueList, preValue, nextValue, dataPath);\n\n    if (formDataRes && rootValueRes) {\n      return false;\n    }\n\n    return true;\n  };\n"
  },
  {
    "path": "packages/form-render/src/models/filterValuesHidden.ts",
    "content": "import { isObject, isArray } from '../utils';\n\nconst transformHidden = (str: any, formData = {}, parentData = {}) => {\n  if (typeof str !== 'string') {\n    return !!str;\n  }\n\n  const funcBody = str.replace(/^{\\s*{/g, '').replace(/}\\s*}$/g, '').trim();\n  const funcStr = `\n    return ${funcBody\n      .replace(/formData/g, JSON.stringify(formData))\n      .replace(/rootValue/g, JSON.stringify(parentData))}\n  `;\n  try {\n    const result = Function(funcStr)();\n    return result;\n  } catch (error) {\n    return false;\n  }\n};\n\n/**\n * 过滤 field.schema.hidden = true，的值\n */\nexport default (_values: any, flattenSchema: object) => {\n\n  const recursiveArray = (list: any[], _path: string) => {\n    return list.map(item => {\n      if (isObject(item)) {\n        return recursiveObj(item, _path, item);\n      }\n      return item;\n    });\n  };\n  \n  const recursiveObj = (obj: any, prePath?: string, parentData?: any) => {\n\n    for (let key of Object.keys(obj)) {\n      const item = obj[key];\n      let path = prePath ? `${prePath}.${key}` : key;\n      let schema = flattenSchema[path]?.schema;\n\n      if (isArray(item) && !schema) {\n        path = prePath ? `${prePath}.${key}[]` : `${key}[]`;\n        schema = flattenSchema[path]?.schema;\n      }\n\n      // 剔除隐藏数据\n      if (schema?.hidden) {\n        const hidden = transformHidden(schema.hidden, _values, parentData);\n        if (hidden) {\n          obj[key] = undefined;\n          continue;\n        }\n      }\n      \n      if (isObject(item)) {\n        obj[key] = recursiveObj(item, path, parentData);\n        continue;\n      }\n  \n      if (isArray(item) && schema?.items) {\n        obj[key] = recursiveArray(item, path) || [];\n        continue;\n      }\n\n      obj[key] = item;\n    }\n\n    return obj;\n  };\n  \n  return recursiveObj(_values) || {};\n}"
  },
  {
    "path": "packages/form-render/src/models/filterValuesUndefined.ts",
    "content": "import { isUndefined, omitBy } from 'lodash-es';\nimport { isObject, isArray } from '../utils';\n\nexport default (values: any, notFilter?: boolean) => {\n  const recursiveArray = (list: any[]) => {\n    let result = list.map(item => {\n      if (isObject(item)) {\n        return recursiveObj(item, false);\n      }\n      if (isArray(item)) {\n        return recursiveArray(item);\n      }\n      return item;\n    });\n    if (Object.keys(result).length === 0) {\n      return undefined;\n    }\n    return result;\n  };\n\n  const recursiveObj = (_obj: any, filter = true) => {\n    if (_obj._isAMomentObject) {\n      return _obj;\n    }\n\n    let obj =  omitBy(_obj, isUndefined);\n    Object.keys(obj).forEach(key => {\n      const item = obj[key];\n\n      if (isObject(item)) {\n        obj[key] = recursiveObj(item);\n      }\n\n      if (isArray(item)) {\n        const data = recursiveArray(item);\n        obj[key] = data;\n        if (!notFilter && data) {\n          obj[key] = data.filter((item: any) => item !== undefined);\n        }\n      }\n    });\n\n    obj = omitBy(obj, isUndefined);\n    if (Object.keys(obj).length === 0 && filter) {\n      return undefined;\n    }\n    return obj;\n  };\n \n  return recursiveObj(values) || {};\n};"
  },
  {
    "path": "packages/form-render/src/models/flattenSchema.ts",
    "content": "import { _cloneDeep, isObjType, isListType } from '../utils/index';\nimport sortProperties from './sortProperties';\n\nexport const getKeyFromPath = (path = '#') => {\n  try {\n    const arr = path.split('.');\n    const last = arr.slice(-1)[0];\n    const result = last.replace('[]', '');\n    return result;\n  } catch (error) {\n    console.error(error, 'getKeyFromPath');\n    return '';\n  }\n};\n\nexport function getSchemaFromFlatten(flatten: any, path = '#') {\n  let schema: any = {};\n  const item = _cloneDeep(flatten[path]);\n\n  if (!item) {\n    return schema;\n  }\n  \n  schema = item.schema;\n  // schema.$id && delete schema.$id;\n  if (item.children.length > 0) {\n    item.children.forEach((child: any) => {\n      if (!flatten[child]) return;\n      const key = getKeyFromPath(child);\n      if (isObjType(schema)) {\n        schema.properties[key] = getSchemaFromFlatten(flatten, child);\n      }\n      if (isListType(schema)) {\n        schema.items.properties[key] = getSchemaFromFlatten(flatten, child);\n      }\n    });\n  }\n\n  return schema;\n}\n\n// TODO: more tests to make sure weird & wrong schema won't crush\nexport function flattenSchema(_schema = {}, name?: any, parent?: any, _result?: any) {\n  // 排序\n  // _schema = orderBy(_schema, item => item.order, ['asc']);\n\n  const result = _result || {};\n\n  const schema: any = _cloneDeep(_schema) || {};\n  let _name = name || '#';\n  if (!schema.$id) {\n    schema.$id = _name; // path as $id, for easy access to path in schema\n  }\n  const children: any[] = [];\n  if (isObjType(schema)) {\n    sortProperties(Object.entries(schema.properties)).forEach(\n      ([key, value]) => {\n        const _key = isListType(value) ? key + '[]' : key;\n        const uniqueName = _name === '#' ? _key : _name + '.' + _key;\n        children.push(uniqueName);\n\n        flattenSchema(value, uniqueName, _name, result);\n      }\n    );\n\n    schema.properties = {};\n  }\n  if (isListType(schema)) {\n    sortProperties(Object.entries(schema.items.properties)).forEach(\n      ([key, value]) => {\n        const _key = isListType(value) ? key + '[]' : key;\n        const uniqueName = _name === '#' ? _key : _name + '.' + _key;\n        children.push(uniqueName);\n        flattenSchema(value, uniqueName, _name, result);\n      }\n    );\n\n    schema.items.properties = {};\n  }\n\n  if (schema.type) {\n    result[_name] = { parent, schema, children };\n  }\n \n  return result;\n}\n\n"
  },
  {
    "path": "packages/form-render/src/models/formCoreUtils.ts",
    "content": "import { isObject, isArray, _get, _has, isFunction, isObjType } from '../utils';\n\nconst executeCallBack = (watchItem: any, value: any, path: string, index?: any) => {\n  if (isFunction(watchItem)) {\n    try {\n      watchItem(value, index);\n    } catch (error) {\n      console.log(`${path}对应的watch函数执行报错：`, error);\n    }\n  }\n\n  if (isFunction(watchItem?.handler)) {\n    try {\n      watchItem.handler(value, index);\n    } catch (error) {\n      console.log(`${path}对应的watch函数执行报错：`, error);\n    }\n  }\n};\n\nconst traverseValues = ({ changedValues, allValues, flatValues }) => {\n\n  const traverseArray = (list: any[], fullList: any, path: string, index: number[]) => {\n    if (!list.length) {\n      return\n    }\n\n    const _path = path += '[]';\n    const filterLength = list.filter(item => (item || item === undefined)).length;\n\n    let flag = filterLength !== fullList.length || list.length === 1;\n    let isRemove = false;\n    if (filterLength > 1 && filterLength < fullList.length) {\n      flag = false;\n      isRemove = true;\n    }\n\n    list.forEach((item: any, idx: number) => {\n      if (!isRemove) {\n        flatValues[_path] =  { value: fullList[idx], index };\n      }\n      if (isObject(item)) {\n        traverseObj(item, fullList[idx], _path, [...index, idx], !flag);\n      }\n      if (isArray(item)) {\n        traverseArray(item, fullList[idx], _path, [...index, idx]);\n      }\n    });\n  };\n\n  const traverseObj = (obj: any, fullObj: any, path: string, index: number[], flag?: boolean) => {\n    Object.keys(obj).forEach((key: string) => {\n      const item = obj[key];\n      const fullItem = fullObj?.[key];\n      let value = item;\n\n      const _path = path ? (path + '.' + key) : key;\n\n      let last = true;\n\n      if (isArray(item)) {\n        value = fullItem ? [...fullItem] : fullItem;\n        last = false;\n        traverseArray(item, fullItem, _path, index);\n      }\n\n      if (isObject(item)) {\n        last = false;\n        traverseObj(item, fullItem, _path, index, flag);\n      }\n\n      if (!last || !flag) {\n        flatValues[_path] =  { value, index };\n      }\n    });\n  };\n\n  traverseObj(changedValues, allValues, null, []);\n};\n\nexport const valuesWatch = (changedValues: any, allValues: any, watch: any) => {\n  if (Object.keys(watch || {})?.length === 0) {\n    return;\n  }\n\n  const flatValues = {\n    '#': { value: allValues, index: changedValues }\n  };\n\n  traverseValues({ changedValues, allValues, flatValues });\n\n  Object.keys(watch).forEach(path => {\n    if (!_has(flatValues, path)) {\n      return;\n    }\n    const { value, index } = _get(flatValues, path) as { value: any; index: any; };\n    const item = watch[path];\n    executeCallBack(item, value, path, index)\n  });\n};\n\nexport const transformFieldsData = (_fieldsError: any, getFieldName: any) => {\n  let fieldsError = _fieldsError;\n  if (isObject(fieldsError)) {\n    fieldsError = [fieldsError];\n  }\n\n  if (!(isArray(fieldsError) && fieldsError.length > 0)) {\n    return;\n  }\n\n  return fieldsError.map((field: any) => ({ errors: field.error, ...field, name: getFieldName(field.name) }));\n};\n\nexport const immediateWatch = (watch: any, values: any) => {\n  if (Object.keys(watch || {})?.length === 0) {\n    return;\n  }\n\n  const watchObj = {};\n  Object.keys(watch).forEach(key => {\n    const watchItem = watch[key];\n    if (watchItem?.immediate && isFunction(watchItem?.handler)) {\n      watchObj[key] = watchItem;\n    }\n  });\n\n  valuesWatch(values, values, watchObj);\n};\n\nexport const getSchemaFullPath = (path: string, schema: any) => {\n  if (!path || !path.includes('.')) {\n    return 'properties.' + path;\n  }\n\n  // 补全 list 类型 path 路径\n  while(path.includes('[]')) {\n    const index = path.indexOf('[]');\n    path = path.substring(0, index) + '.items' + path.substring(index + 2);\n  }\n\n  // 补全 object 类型 path 路径\n  let result = 'properties';\n  const pathList = path.split('.');\n  pathList.forEach((item, index) => {\n    const key = result + '.' + item;\n    const itemSchema = _get(schema, key, {});\n    if (isObjType(itemSchema) && index !== pathList.length-1) {\n      result = key + '.properties';\n      return ;\n    }\n    result = key;\n  });\n\n  return result;\n};\n\nexport function yymmdd(timeStamp) {\n  const date_ob = new Date(Number(timeStamp));\n  const adjustZero = num => ('0' + num).slice(-2);\n  let day = adjustZero(date_ob.getDate());\n  let month = adjustZero(date_ob.getMonth());\n  let year = date_ob.getFullYear();\n  let hours = adjustZero(date_ob.getHours());\n  let minutes = adjustZero(date_ob.getMinutes());\n  let seconds = adjustZero(date_ob.getSeconds());\n  return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;\n}\n\nexport function msToTime(duration) {\n  let seconds: any = Math.floor((duration / 1000) % 60);\n  let minutes: any = Math.floor((duration / (1000 * 60)) % 60);\n  let hours: any = Math.floor((duration / (1000 * 60 * 60)) % 24);\n\n  hours = hours < 10 ? '0' + hours : hours;\n  minutes = minutes < 10 ? '0' + minutes : minutes;\n  seconds = seconds < 10 ? '0' + seconds : seconds;\n  return hours + ':' + minutes + ':' + seconds;\n}\n\nexport const getSessionItem = (key: string) => {\n  return Number(sessionStorage.getItem(key) || 0);\n}\n\nexport const setSessionItem = (key: string, data: any) => {\n  sessionStorage.setItem(key, data +'');\n}\n\n"
  },
  {
    "path": "packages/form-render/src/models/formDataSkeleton.ts",
    "content": "import { _cloneDeep, isObjType, isListType } from '../utils/index';\n\nexport const createDataSkeleton = (schema: any, formData?: any) => {\n  let _formData = _cloneDeep(formData);\n  let result = _formData;\n  \n  if (isObjType(schema)) {\n    if (_formData === undefined || typeof _formData !== 'object') {\n      _formData = {};\n      result = {};\n    }\n    Object.keys(schema.properties).forEach(key => {\n      const childSchema = schema.properties[key];\n      const childData = _formData[key];\n      const childResult = createDataSkeleton(childSchema, childData);\n      result[key] = childResult;\n    });\n  } else if (_formData !== undefined) {\n    // result = _formData;\n  } else if (schema.default !== undefined) {\n    result = _cloneDeep(schema.default);\n  } else if (isListType(schema)) {\n    result = [createDataSkeleton(schema.items)];\n  } else if (schema.type === 'boolean' && !schema.widget) {\n    // result = false;\n    result = undefined;\n  } else {\n    result = undefined;\n  }\n  return result;\n};"
  },
  {
    "path": "packages/form-render/src/models/layout.ts",
    "content": "export const getFormItemLayout = (column: number, schema: any, { labelWidth, displayType, _labelCol, _fieldCol }: any) => {\n  let labelCol: any = { span: 5 };\n  let fieldCol: any = { span: 9 };\n\n  if (column === 2) {\n    labelCol = { span: 6 };\n    fieldCol = { span: 14 }\n  }\n\n  if (column > 2) {\n    labelCol = { span: 7 };\n    fieldCol = { span: 16 }\n  }\n\n  if (displayType === 'column') {\n    // labelCol = { xl: 9, xxl: 6 };\n    // if (column > 1) {\n    //   labelCol = {};\n    //   fieldCol = {};\n    // }\n    labelCol = {};\n    fieldCol = {};\n  }\n\n  if (_labelCol) {\n    labelCol = _labelCol;\n    if (displayType === 'column') {\n      labelCol = {};\n    }\n  }\n\n  if (_fieldCol) {\n    fieldCol = _fieldCol;\n    if (typeof _fieldCol === 'number') {\n      fieldCol = { span: _fieldCol }\n    }\n  }\n\n  if (displayType === 'inline') {\n    labelCol = {};\n    fieldCol = {};\n  }\n\n  // 兼容一下 1.0 版本\n  if ((labelWidth || labelWidth === 0) && displayType !== 'column') {\n    labelCol = { flex : labelWidth + 'px' };\n    fieldCol = { flex: 'auto' };\n  }\n\n  // 自定义进行覆盖\n  if (schema.cellSpan) {\n    fieldCol = {};\n  }\n\n\n  if (schema.labelCol || schema.labelCol === 0) {\n    labelCol = schema.labelCol;\n  }\n\n  if (schema.fieldCol || schema.fieldCol === 0) {\n    fieldCol = schema.fieldCol;\n  }\n\n  if (typeof labelCol === 'number') {\n    labelCol = { span: labelCol }\n  }\n\n  if (typeof fieldCol === 'number') {\n    fieldCol = { span: fieldCol }\n  }\n  \n  return { labelCol, fieldCol }\n}"
  },
  {
    "path": "packages/form-render/src/models/mapping.tsx",
    "content": "export const mapping = {\n  default: 'input',\n  string: 'input',\n  array: 'list',\n  boolean: 'checkbox',\n  integer: 'number',\n  number: 'inputNumber',\n  object: 'map',\n  html: 'html',\n  card: 'card',\n  collapse: 'collapse',\n  lineTitle: 'lineTitle',\n  line: 'line',\n  subItem: 'subItem',\n  panel: 'panel',\n  'string:upload': 'upload',\n  'string:url': 'urlInput',\n  'string:dateTime': 'datePicker',\n  'string:date': 'datePicker',\n  'string:year': 'datePicker',\n  'string:month': 'datePicker',\n  'string:week': 'datePicker',\n  'string:quarter': 'datePicker',\n  'string:time': 'timePicker',\n  'string:textarea': 'textArea',\n  'string:color': 'color',\n  'string:image': 'imageInput',\n  'range:time': 'timeRange',\n  'range:dateTime': 'dateRange',\n  'range:date': 'dateRange',\n  'range:year': 'dateRange',\n  'range:month': 'dateRange',\n  'range:week': 'dateRange',\n  'range:quarter': 'dateRange',\n  '*?enum': 'radio',\n  '*?enum_long': 'select',\n  'array?enum': 'checkboxes',\n  'array?enum_long': 'multiSelect',\n  '*?readOnly': 'html', // TODO: html widgets for list / object\n};\n\nexport function getWidgetName(schema, _mapping = mapping) {\n  const { type, format, enum: enums, readOnly, widget, props } = schema;\n\n  //如果已经注明了渲染widget，那最好\n  if (schema['ui:widget'] || schema.widget) {\n    return schema['ui:widget'] || schema.widget;\n  }\n\n  const list: string[] = [];\n  if (readOnly) {\n    list.push(`${type}?readOnly`);\n    list.push('*?readOnly');\n  }\n\n  if (enums) {\n    // 根据 enum 长度来智能选择控件\n    if (\n      Array.isArray(enums) &&\n      ((type === 'array' && enums.length > 6) ||\n        (type !== 'array' && enums.length > 2))\n    ) {\n      list.push(`${type}?enum_long`);\n      list.push('*?enum_long');\n    } else {\n      list.push(`${type}?enum`);\n      // array 默认使用 list，array?enum 默认使用 checkboxes，*?enum 默认使用select\n      list.push('*?enum');\n    }\n  }\n\n  if (props?.options) {\n    if ((type === 'array' && props.options.length > 6) || (type !== 'array' && props.options.length > 2)) {\n\n      list.push(`${type}?enum_long`);\n      list.push('*?enum_long');\n    } else {\n      list.push(`${type}?enum`);\n      // array 默认使用 list，array?enum 默认使用 checkboxes，*?enum 默认使用select\n      list.push('*?enum');\n    }\n  }\n  \n  const _widget = format;\n  if (_widget) {\n    list.push(`${type}:${_widget}`);\n  }\n  \n  if (type === 'object') {\n    list.push((schema.theme === 'tile' ? 'lineTitle' : schema.theme) || 'collapse');\n  } else {\n    list.push(type); // 放在最后兜底，其他都不match时使用type默认的组件\n  }\n\n  let widgetName = '';\n  list.some(item => {\n    widgetName = _mapping[item];\n    return !!widgetName;\n  });\n\n  return widgetName;\n}\n\n\nfunction capitalizeFirstLetter(str: any) {\n  if (!str) {\n    return str;\n  }\n  return str.charAt(0).toUpperCase() + str.slice(1);\n}\n\nexport const getWidget = (name: string, widgets: any) => {\n  let widget = widgets[name];\n\n  // name 转成首字母大写\n  if (!widget) {\n    widget = widgets[capitalizeFirstLetter(name)];\n  }\n\n  if (!widget) {\n    widget = widgets['Html'] || null;\n  }\n\n  return widget;\n}\n\nexport const extraSchemaList = {\n  checkbox: {\n    valuePropName: 'checked',\n  },\n  switch: {\n    valuePropName: 'checked',\n  },\n};\n"
  },
  {
    "path": "packages/form-render/src/models/sortProperties.ts",
    "content": "export default (properties, orderKey = 'order') => {\n  const orderHash = new Map();\n  // order不为数字的数据\n  const unsortedList: any[] = [];\n  const insert = (item: any) => {\n    const [, value] = item;\n    if (typeof value[orderKey] !== 'number') {\n      unsortedList.push(item);\n      return;\n    }\n    if (orderHash.has(value[orderKey])) {\n      orderHash.get(value[orderKey]).push(item);\n    } else {\n      orderHash.set(value[orderKey], [item]);\n    }\n  };\n\n  properties.forEach(item => insert(item));\n  const sortedList = Array.from(orderHash.entries())\n    .sort(([order1], [order2]) => order1 - order2) // order值越小越靠前\n    .flatMap(([, items]) => items);\n  return sortedList.concat(unsortedList);\n}"
  },
  {
    "path": "packages/form-render/src/models/store.ts",
    "content": "import { createStore as createx } from 'zustand';\n\ntype FormStore = {\n  schema?: any;\n  flattenSchema: any;\n  context?: any;\n  initialized: boolean,\n  init?: (schema: FormStore['schema']) => any;\n  setContext: (context: any) => any;\n};\n\n// 将 useStore 改为 createStore， 并把它改为 create 方法\nexport const createStore = () => createx<FormStore>((setState: any, get: any) => ({\n  initialized: false,\n  schema: {},\n  flattenSchema: {},\n  context: {},\n  init: data => {\n    return setState({ \n      initialized: true, \n      ...data\n    });\n  },\n  setContext: context => {\n    return setState({ context });\n  }\n}));"
  },
  {
    "path": "packages/form-render/src/models/transformProps.ts",
    "content": "const displayTypeEnum = {\n  column: 'vertical',\n  row: 'horizontal',\n  inline: 'inline',\n};\n\nconst transformProps =  (props: any) => {\n  const {\n    schema,\n    beforeFinish,\n    onMount,\n    displayType = 'column',\n    watch,\n    removeHiddenData = true,\n    readOnly,\n    column = 1,\n    mapping,\n    debugCss,\n    locale,\n    configProvider,\n    validateMessages,\n    debug,\n    id,\n    labelWidth,\n    maxWidth,\n    form,\n    onFinish,\n    onFinishFailed,\n    footer,\n    operateExtra,\n    logOnMount,\n    logOnSubmit,\n    labelCol,\n    fieldCol,\n    disabled,\n    className,\n    validateTrigger,\n    antdVersion,\n    ...otherProps\n  } = props;\n\n  const formProps = {\n    ...otherProps,\n  };\n\n  if (displayType) {\n    formProps.layout = displayTypeEnum[displayType] || 'horizontal';\n  }\n\n  return {\n    formProps,\n    schema,\n    displayType,\n    onFinish,\n    beforeFinish, // form 没有这个 api, 感觉找不到时机\n    onMount,\n    watch,\n    readOnly,\n    disabled,\n    column,\n    mapping,\n    debugCss, // 好像没用了\n    locale,\n    configProvider,\n    footer,\n    form,\n    labelWidth,\n    validateMessages,\n    debug, // 换成 form 还有用吗？\n    id,\n    onFinishFailed,\n    removeHiddenData,\n    operateExtra,\n    logOnMount,\n    logOnSubmit,\n    labelCol,\n    fieldCol,\n    maxWidth,\n    className,\n    validateTrigger,\n    antdVersion\n  };\n};\n\nexport default transformProps;"
  },
  {
    "path": "packages/form-render/src/models/useForm.ts",
    "content": "import { useRef } from 'react';\nimport { Form } from 'antd';\nimport { cloneDeep } from 'lodash-es';\n\nimport { transformFieldsData, getSchemaFullPath } from './formCoreUtils';\nimport { parseBindToValues, parseValuesToBind } from './bindValues';\nimport { _isMatch, _set, _get, _has, _merge, _mergeWith, isFunction, isObject, isArray, _isUndefined, hasFuncProperty } from '../utils';\nimport filterValuesUndefined from './filterValuesUndefined';\nimport filterValuesHidden from './filterValuesHidden';\nimport { flattenSchema as flatten } from './flattenSchema';\nimport type { FormInstance } from '../type';\n\nconst updateSchemaByPath = (_path: string, _newSchema: any, formSchema: any) => {\n  const path = getSchemaFullPath(_path, formSchema);\n  const currSchema = _get(formSchema, path, {});\n  const newSchema = isFunction(_newSchema) ? _newSchema(currSchema) : _newSchema;\n\n  const result = {\n    ...currSchema,\n    ...newSchema,\n  }\n\n  if (newSchema.props) {\n    result.props = {\n      ...currSchema?.props,\n      ...newSchema.props\n    }\n  }\n\n  _set(formSchema, path, result);\n};\n\nconst getFieldName = (_path: any): any => {\n  if (!_path) {\n    return undefined;\n  }\n\n  if (typeof _path === 'boolean') {\n    return _path;\n  }\n\n  let result: any[] = [];\n\n  if (isArray(_path)) {\n    result = _path.map((item: any) => {\n      return item.split('.').map((ite: any) => {\n        if (!isNaN(Number(ite))) {\n          return ite * 1;\n        }\n        return ite;\n      });\n    });\n  }\n\n  result = _path.split('.').map((item: any) => {\n    if (!isNaN(Number(item))) {\n      return item * 1;\n    }\n    return item;\n  });\n\n  result = result.map(item => {\n    if (typeof item === 'string' && item?.indexOf('[') === 0  && item?.indexOf(']') === item?.length -1) {\n      return Number(item.substring(1, item.length-1));\n    }\n    return item;\n  });\n\n  return result;\n};\n\nconst useForm = () => {\n  const [form] = Form.useForm();\n\n  const flattenSchemaRef = useRef({});\n  const storeRef: any = useRef(null);\n  const schemaRef = useRef({});\n  const fieldRefs = useRef({});\n\n  const {\n    getFieldError,\n    getFieldsError,\n    getFieldInstance,\n    setFieldsValue,\n    setFields,\n    scrollToField,\n    isFieldsTouched,\n    isFieldTouched,\n    isFieldValidating,\n    resetFields,\n    validateFields,\n    ...otherForm\n  } = form;\n\n  const xform: any = otherForm;\n\n  const setStoreData = (data: any) => {\n    const { setState } = storeRef.current;\n\n    if (!setState) {\n      setTimeout(() => {\n        setState({ schema: schemaRef.current, flattenSchema: flattenSchemaRef.current });\n      }, 0);\n    }\n    setState(data);\n  };\n\n  // 更新协议\n  const handleSchemaUpdate = (newSchema: any) => {\n    // form.__schema = Object.freeze(newSchema);\n    flattenSchemaRef.current = flatten(newSchema) || {};\n    schemaRef.current = newSchema;\n    setStoreData({ schema: newSchema, flattenSchema: flattenSchemaRef.current });\n  };\n\n  // 设置协议\n  xform.setSchema = (obj: any, cover = false) => {\n    if (!isObject(obj)) {\n      return;\n    }\n\n    if (cover) {\n      handleSchemaUpdate(obj);\n      return;\n    }\n\n    const schema = cloneDeep(schemaRef.current);\n    Object.keys(obj || {}).forEach(path => {\n      updateSchemaByPath(path, obj[path], schema);\n    });\n\n    handleSchemaUpdate(schema);\n  }\n\n  // 设置某个字段的协议\n  xform.setSchemaByPath = (_path: string, _newSchema: any) => {\n    // diff 判断是否需要更新，存在函数跳过\n    if (!hasFuncProperty(_newSchema) && _isMatch(_newSchema, xform.getSchemaByPath(_path))) {\n      return;\n    }\n\n    const schema = cloneDeep(schemaRef.current);\n    updateSchemaByPath(_path, _newSchema, schema);\n    handleSchemaUpdate(schema);\n  }\n\n  // form.setSchemaByFullPath = (path: string, newSchema: any) => {\n  //   const schema = _cloneDeep(schemaRef.current);\n  //   const currSchema = _get(schema, path, {});\n\n  //   const result = _mergeWith(currSchema, newSchema, (objValue, srcValue, key) => {\n  //     return srcValue;\n  //   });\n\n  //   _set(schema, path, result);\n  //   handleSchemaUpdate(schema);\n  // }\n\n  // 设置表单数据\n  xform.setValues = (_values: any) => {\n    const values = parseBindToValues(_values, flattenSchemaRef.current);\n    setFieldsValue(values);\n  }\n\n  // 获取表单数据\n  xform.getValues = (nameList?: any, filterFunc?: any, notFilterUndefined?:boolean,notFilterHideData:boolean=true) => {\n    let values = cloneDeep(form.getFieldsValue(getFieldName(nameList), filterFunc));\n    const { removeHiddenData } = storeRef.current?.getState() || {};\n    if (notFilterHideData && removeHiddenData) {\n      values = filterValuesHidden(values, flattenSchemaRef.current);\n    }\n    if (!notFilterUndefined) {\n      values = filterValuesUndefined(values);\n    }\n    return parseValuesToBind(values, flattenSchemaRef.current);\n  }\n\n  xform.getValueByPath = (path: string) => {\n    const name = getFieldName(path);\n    return form.getFieldValue(name);\n  }\n\n  // 设置某个字段的值\n  xform.setValueByPath = (path: string, value: any) => {\n    if (!form.setFieldValue) {\n      const values = form.getFieldsValue();\n      _set(values, path, value);\n      xform.setValues(values);\n      return;\n    }\n\n    const name = getFieldName(path);\n    form.setFieldValue(name, value);\n\n    try {\n      if (JSON.stringify(form.getFieldValue(name)) !== JSON.stringify(value)) {\n        form.setFieldValue(name, value);\n      }\n    } catch (error) {\n\n    }\n  }\n\n  // 通过某个字段的 schema\n  xform.getSchemaByPath = (_path: string) => {\n    if (typeof _path !== 'string') {\n      console.warn('请输入正确的路径');\n    }\n    const path = getSchemaFullPath(_path, schemaRef.current);\n    return _get(schemaRef.current, path);\n  };\n\n  // 获取协议\n  xform.getSchema = () => {\n    return schemaRef.current;\n  };\n\n  // 设置一组字段错误\n  xform.setErrorFields = (fieldsError: any[]) => {\n    const fieldsData = transformFieldsData(fieldsError, getFieldName);\n    if (!fieldsData) {\n      return;\n    }\n\n    setFields(fieldsData);\n  };\n\n  // 清空某个字段的错误\n  xform.removeErrorField = (path: any) => {\n    setFields([{ name: getFieldName(path), errors: [] }]);\n  };\n\n  // 获取对应字段名的错误信息\n  xform.getFieldError = (path: string) => {\n    const name = getFieldName(path);\n    return form.getFieldError(name);\n  }\n\n  // 获取一组字段名对应的错误信息，返回为数组形式\n  xform.getFieldsError = (path: string[]) => {\n    const name = getFieldName(path);\n    return getFieldsError(name);\n  }\n\n  // 获取对应字段实例\n  xform.getFieldInstance = (path: string) => {\n    const name = getFieldName(path);\n    return getFieldInstance(name);\n  }\n\n  // 获取隐藏字段数据\n  xform.getHiddenValues = () => {\n    const values = xform.getValues();\n    const allValues = xform.getValues(true);\n    const hiddenValues = {};\n\n    const recursion = (obj1: any, obj2: any, path: any) => {\n      Object.keys(obj1).forEach((key: string) => {\n        const value = obj1[key];\n        const _path = path ? `${path}.${key}` : key;\n        if (!obj2.hasOwnProperty(key)) {\n          _set(hiddenValues, _path, value);\n          return;\n        }\n\n        if (isObject(value)) {\n          recursion(value, obj2[key], _path);\n        }\n\n        if (isArray(value)) {\n          value.map((item: any, index: number) => {\n            recursion(item, _get(obj2, `${key}[${index}]`, []), `${_path}[${index}]`)\n          });\n        }\n      });\n    };\n\n    recursion(allValues, values, null);\n    return hiddenValues;\n  }\n\n  // 设置一组字段状态\n  xform.setFields = (nameList: any[]) => {\n    const fieldsData = transformFieldsData(nameList, getFieldName);\n    if (!fieldsData) {\n      return;\n    }\n    setFields(fieldsData);\n  }\n\n  xform.__initStore = (store: any) => {\n    storeRef.current = store;\n  }\n\n  // 滚动到对应字段位置\n  xform.scrollToPath = (path: string, ...rest: any[]) => {\n    const name = getFieldName(path);\n    scrollToField(name, ...rest);\n  }\n\n  // 检查一组字段是否被用户操作过，allTouched 为 true 时检查是否所有字段都被操作过\n  xform.isFieldsTouched = (pathList?: string[], allTouched?: boolean) => {\n    const nameList = (pathList || []).map(path => getFieldName(path));\n    return isFieldsTouched(nameList, allTouched);\n  }\n\n  // 检查对应字段是否被用户操作过\n  xform.isFieldTouched = (path: string) => {\n    const name = getFieldName(path);\n    return isFieldTouched(name);\n  }\n\n  // 检查对应字段是否被用户操作过\n  xform.isFieldValidating = (path: string) => {\n    const name = getFieldName(path);\n    return isFieldValidating(name);\n  }\n\n  xform.resetFields = (pathList?: string[]) => {\n    const nameList = (pathList || []).map(path => getFieldName(path));\n    if (nameList.length > 0) {\n      resetFields(nameList);\n    } else {\n      resetFields();\n    }\n  }\n\n  // 触发表单验证\n  xform.validateFields = (pathList?: string[], config?: object) => {\n    const nameList = (pathList || []).map(path => getFieldName(path));\n    if (nameList.length > 0) {\n      return validateFields(nameList, config);\n    }\n    return validateFields();\n  };\n\n\n  xform.getFlattenSchema = (path?: string) => {\n    if (!path) {\n      return flattenSchemaRef.current;\n    }\n    return flattenSchemaRef.current?.[path];\n  }\n\n  // 老 API 兼容\n  xform.onItemChange = xform.setValueByPath;\n\n  xform.setFieldRef = (path: string, ref: any) => {\n    if (!path) {\n      return;\n    }\n    fieldRefs.current[path] = ref;\n  }\n\n  xform.getFieldRef = (path: string) => {\n    return fieldRefs.current[path];\n  }\n\n  return xform as FormInstance;\n};\n\nexport default useForm;\n"
  },
  {
    "path": "packages/form-render/src/models/validateMessage.ts",
    "content": "const typeTemplate = \"'${label}' is not a valid ${type}\";\nconst typeTemplateCN = \"数据类型必须是 ${type}\";\n\nexport const validateMessagesEN = {\n  default: \"Validation error on field '${label}'\",\n  required: \"'${label}' is required\",\n  enum: \"'${label}' must be one of [${enum}]\",\n  whitespace: \"'${label}' cannot be empty\",\n  date: {\n    format: \"'${label}' is invalid for format date\",\n    parse: \"'${label}' could not be parsed as date\",\n    invalid: \"'${label}' is invalid date\",\n  },\n  types: {\n    string: typeTemplate,\n    method: typeTemplate,\n    array: typeTemplate,\n    object: typeTemplate,\n    number: typeTemplate,\n    date: typeTemplate,\n    boolean: typeTemplate,\n    integer: typeTemplate,\n    float: typeTemplate,\n    regexp: typeTemplate,\n    email: typeTemplate,\n    url: typeTemplate,\n    hex: typeTemplate,\n  },\n  string: {\n    len: \"'${label}' must be exactly ${len} characters\",\n    min: \"'${label}' must be at least ${min} characters\",\n    max: \"'${label}' cannot be longer than ${max} characters\",\n    range: \"'${label}' must be between ${min} and ${max} characters\",\n  },\n  number: {\n    len: \"'${label}' must equal ${len}\",\n    min: \"'${label}' cannot be less than ${min}\",\n    max: \"'${label}' cannot be greater than ${max}\",\n    range: \"'${label}' must be between ${min} and ${max}\",\n  },\n  array: {\n    len: \"'${label}' must be exactly ${len} in length\",\n    min: \"'${label}' cannot be less than ${min} in length\",\n    max: \"'${label}' cannot be greater than ${max} in length\",\n    range: \"'${label}' must be between ${min} and ${max} in length\",\n  },\n  pattern: {\n    mismatch: \"'${label}' does not match pattern ${pattern}\",\n  },\n};\n\nexport const validateMessagesCN = {\n  default: '${label}未通过校验',\n  required: '${label}必填',\n  whitespace: '${label}不能为空',\n  date: {\n    format: '${label}的格式错误',\n    parse: '${label}无法被解析',\n    invalid: '${label}数据不合法',\n  },\n  types: {\n    string: typeTemplateCN,\n    method: typeTemplateCN,\n    array: typeTemplateCN,\n    object: typeTemplateCN,\n    number: typeTemplateCN,\n    date: typeTemplateCN,\n    boolean: typeTemplateCN,\n    integer: typeTemplateCN,\n    float: typeTemplateCN,\n    regexp: typeTemplateCN,\n    email: typeTemplateCN,\n    url: typeTemplateCN,\n    hex: typeTemplateCN,\n  },\n  string: {\n    len: '${label}长度不是${len}',\n    min: '${label}长度不能小于${min}',\n    max: '${label}长度不能大于${max}',\n    range: '${label}长度需在${min}与${max}之间',\n  },\n  number: {\n    len: '${label}不等于${len}',\n    min: '${label}不能小于${min}',\n    max: '${label}不能大于${max}',\n    range: '${label}需在${min}与${max}之间',\n  },\n  array: {\n    len: '${label}长度不是${len}',\n    min: '${label}长度不能小于${min}',\n    max: '${label}长度不能大于${max}',\n    range: '${label}长度需在${min}与${max}之间',\n  },\n  pattern: {\n    mismatch: '${label}未通过正则判断${pattern}',\n  },\n};\n"
  },
  {
    "path": "packages/form-render/src/models/validates.ts",
    "content": "import Color from 'color';\nimport { isUrl, isObject, isFunction } from '../utils';\nimport { cloneDeep } from 'lodash-es';\n\nconst insertLengthRule = (schema: any, rules: any[]) => {\n  const { type, max, min, message } = schema;\n\n  if (max || max === 0) {\n    rules.push({ type, max, message: message?.max });\n  }\n\n  if (min || min === 0) {\n    rules.push({ type, min, message: message?.min });\n  }\n};\n\nconst insertRequiredRule = (schema: any, rules: any[]) => {\n  let { \n    type,\n    format,\n    required,\n    message,\n    widget,\n    title\n  } = schema;\n\n  const requiredAlready = schema?.rules?.some((item: any) => item?.required);\n \n  // 未声明 required，或已经存在 required 校验\n  if (!required || requiredAlready) {\n    return;\n  }\n\n  let rule: any = { required: true, message: message?.required };\n\n  if (['year','quarter', 'month', 'week', 'date', 'dateTime', 'time'].includes(format) && type === 'range') {\n    rule = {\n      type: 'array',\n      required: true,\n      len: 2,\n      fields: {\n        0: { type: 'string', required: true },\n        1: { type: 'string', required: true },\n      }\n    };\n  } else if (widget === 'checkbox') {\n    rule = { type, required: true,  whitespace: true, message: title + '必填' };\n  } else if (type === 'string') {\n    rule = { type: 'string', required: true,  whitespace: true, message: message?.required || (!title ? '内容必填' : undefined) };\n  }\n\n  rules.push(rule);\n};\n\nexport const transformRules = (rules = [], methods: any, form: any) => {\n  return rules.map(((item: any) => {\n    if (item.validator && !item.transformed) {\n      const validator = isFunction(item.validator) ? item.validator : methods[item.validator];\n      item.validator = async (_: any, value: any) => {\n        const result = await validator(_, value, { form });\n        if (isObject(result)) {\n          return result?.status ? Promise.resolve() : Promise.reject(new Error(result.message || item.message));\n        }\n        return result ? Promise.resolve() : Promise.reject(new Error(item.message));\n      };;\n      item.transformed = true;\n    }\n    return item;\n  }));\n};\n\nexport default (_schema: any, form: any, methods: any, fieldRef: any) => {\n  const schema = cloneDeep(_schema);\n  let { \n    format,\n    rules: ruleList = [],\n    pattern,\n    message,\n  } = schema;\n\n  const rules: any = [...ruleList];\n \n  insertRequiredRule(schema, rules);\n  insertLengthRule(schema, rules);\n\n  rules.push({\n    validator: async (_: any) => {\n      if (!isFunction(fieldRef?.current?.validator)) {\n        return true;\n      }\n      const res = await fieldRef.current?.validator();\n      return res;\n    }\n  });\n\n  if (pattern) {\n    rules.push({ pattern, message: message?.pattern });\n  }\n\n  if (format === 'url') {\n    rules.push({ type: 'url', message: message?.url });\n  }\n\n  if (format === 'email') {\n    rules.push({ type: 'email', message: message?.email });\n  }\n\n  if (format === 'image') {\n    rules.push({\n      validator: (_: any, value: any) => {\n        if (!value) {\n          return true;\n        }\n        const imagePattern = '([/|.|w|s|-])*.(?:jpg|gif|png|bmp|apng|webp|jpeg|json)';\n        const _isUrl = isUrl(value);\n        const _isImg = new RegExp(imagePattern).test(value);\n        return _isUrl || _isImg;\n      }, \n      message: message?.email ?? '请输入正确的图片格式'\n    });\n  }\n\n  if (format === 'color') {\n    rules.push({\n      validator: (_: any, value: any) => {\n        try {\n          Color(value || null); // 空字符串无法解析会报错，出现空的情况传 null\n          return true;\n        } catch (e) {\n          return false;\n        }\n      }, \n      message: message?.color ?? '请填写正确的颜色格式'\n    });\n  }\n\n  return transformRules(rules, methods, form);\n}"
  },
  {
    "path": "packages/form-render/src/render-core/FieldItem/field.tsx",
    "content": "import React, { useContext, useEffect } from 'react';\nimport { Button, ConfigProvider, Form } from 'antd';\nimport { useUpdateEffect } from 'ahooks';\nimport { _get, translation, isFunction } from '../../utils';\n\nexport const FieldWrapperStatus = (props: any) => {\n  const { Field, fieldProps, maxWidth, initialValue, acitonRender, ...otherProps } = props;\n  const { onStatusChange, addons, ...otherFieldProps } = fieldProps;\n  const style = maxWidth ? { maxWidth, ...fieldProps?.style } : { ...fieldProps?.style } ;\n\n  const { status } = Form.Item.useStatus();\n  const errors = addons.getFieldError(addons.dataPath);\n\n  useEffect(() => {\n    onStatusChange && onStatusChange(status, errors);\n  }, [JSON.stringify(errors)]);\n\n  useUpdateEffect(() => {\n    otherProps.onChange(initialValue);\n  }, [JSON.stringify(initialValue)]);\n\n  return (\n    <>\n      <Field \n        {...otherProps}\n        {...otherFieldProps}\n        style={style}\n        addons={addons}\n      />\n      {acitonRender && (\n        <span className='ant-form-item-actions'>\n          {acitonRender()}\n        </span>\n      )}\n    </>\n  );\n};\n\nexport const FieldWrapper = (props: any) => {\n  const { Field, fieldProps, maxWidth, initialValue, acitonRender, ...otherProps } = props;\n  const { addons, schema } = fieldProps;\n\n  const _style = maxWidth ? { maxWidth, ...fieldProps?.style }: { ...fieldProps?.style }\n  const { removeBtn } = schema;\n \n  const configCtx = useContext(ConfigProvider.ConfigContext);\n  const t = translation(configCtx);\n\n  useUpdateEffect(() => {\n    otherProps.onChange(initialValue);\n  }, [JSON.stringify(initialValue)]);\n\n  const handleRemove = () => {\n    if (isFunction(removeBtn?.onClick)) {\n      removeBtn.onClick(() => {\n        addons.setSchemaByPath(addons.schemaPath, { hidden: true });\n      });\n      return;\n    }\n    addons.setSchemaByPath(addons.schemaPath, { hidden: true });\n  };\n\n  return (\n    <>\n      <Field\n        {...otherProps} \n        {...fieldProps}\n        style={_style}\n      />\n      {removeBtn && (\n        <Button\n          type='link'\n          danger\n          {...removeBtn}\n          onClick={handleRemove}\n        >\n          {removeBtn?.text || t('delete')}\n        </Button>\n      )}\n      {acitonRender && (\n        <span className='fr-item-actions'>\n          {acitonRender()}\n        </span>\n      )}\n    </>\n  );\n}"
  },
  {
    "path": "packages/form-render/src/render-core/FieldItem/index.tsx",
    "content": "import React, { useContext } from 'react';\nimport { Form } from 'antd';\n\nimport { _get } from '../../utils';\nimport { getDependValues } from './module';\nimport { FRContext, ConfigContext } from '../../models/context';\nimport { isHasExpression, parseAllExpression } from '../../models/expression';\nimport fieldShouldUpdate from '../../models/fieldShouldUpdate';\nimport Main from './main';\n\nexport default (props: any) => {\n  const { schema, rootPath, ...restProps } = props;\n\n  const store: any = useContext(FRContext);\n  const { schema: formSchema } = store.getState();\n\n  const configCtx: any = useContext(ConfigContext);\n  const mustacheDisabled = configCtx?.globalConfig?.mustacheDisabled;\n  const shouldUpdateOpen = configCtx?.globalConfig?.shouldUpdateOpen;\n\n  const dependencies = schema?.dependencies;\n\n  // No function expressions exist\n  if ((!isHasExpression(schema) && !mustacheDisabled) && (!dependencies || !dependencies?.length)) {\n    return <Main {...props} store={store} configCtx={configCtx} />;\n  }\n\n  const schemaStr = JSON.stringify(schema);\n  // Need to listen to form values for dynamic rendering\n  return (\n    <Form.Item\n      noStyle\n      shouldUpdate={fieldShouldUpdate(schemaStr, rootPath, dependencies, shouldUpdateOpen)}\n    >\n      {(form: any) => {\n        const formData = form.getFieldsValue(true);\n        const formDependencies: any[] = [];\n        const dependValues = (dependencies || []).map((depPath: string) => {\n          const item:any[] = [];\n          formDependencies.push(item);\n          return getDependValues(formData, depPath, props, item);\n        });\n        const newSchema = mustacheDisabled ? schema : parseAllExpression(schema, formData, rootPath, formSchema);\n\n        return (\n          <Main \n            schema={{\n              ...newSchema,\n              dependencies: formDependencies\n            }} \n            rootPath={rootPath} \n            {...restProps}\n            dependValues={dependValues} \n            store={store} \n            configCtx={configCtx} \n          />\n        );\n      }}\n    </Form.Item>\n  );\n}\n"
  },
  {
    "path": "packages/form-render/src/render-core/FieldItem/main.tsx",
    "content": "import React, { createContext, useContext, useRef, useEffect } from 'react';\nimport { Form, Col, Row } from 'antd';\nimport { useStore } from 'zustand';\nimport classnames from 'classnames';\n\nimport { isCheckBoxType, _get } from '../../utils';\nimport { getWidgetName, getWidget } from '../../models/mapping';\nimport { getFormItemLayout } from '../../models/layout';\nimport getRuleList from '../../models/validates';\n\nconst UpperContext: any = createContext(() => {});\nconst valuePropNameMap = {\n  checkbox: 'checked',\n  switch: 'checked',\n  Checkbox: 'checked',\n  Switch: 'checked'\n};\n\nimport {\n  FieldWrapper,\n  FieldWrapperStatus\n} from './field';\n\nimport {\n  getParamValue,\n  getFieldProps,\n  getPath,\n  getLabel,\n  getColSpan,\n  getExtraView,\n  getTooltip\n} from './module';\n\nexport default (props: any) => {\n  const { configCtx, store, schema, path, children, dependValues, rootPath } = props;\n\n  const fieldRef: any = useRef(null);\n  const formCtx: any = useStore(store, (state: any) => state.context);\n  const upperCtx: any = useContext(UpperContext);\n\n  const { form, widgets, methods, globalProps } = configCtx;\n  const {\n    reserveLabel,\n    hidden,\n    properties,\n    dependencies,\n    inlineMode: _inlineMode,\n    remove,\n    removeText,\n    visible = true,\n    ...otherSchema\n  } = schema;\n\n  const getValueFromKey = getParamValue(formCtx, upperCtx, schema);\n\n  const widgetName = getWidgetName(schema);\n  let Widget = getWidget(widgetName, widgets);\n\n  const fieldProps = getFieldProps(widgetName, schema, {\n    widgets,\n    methods,\n    form,\n    dependValues,\n    globalProps,\n    path: getPath(path),\n    rootPath,\n    fieldRef\n  });\n\n  useEffect(() => {\n    form.setFieldRef(fieldProps.addons.dataPath, fieldRef);\n  }, []);\n\n  if (schema?.hidden) {\n    return null;\n  }\n\n  // Component not found\n  if (!widgetName) {\n    const ErrorSchema = widgets['errorSchema'] || widgets['ErrorSchema'];\n    return <ErrorSchema schema={schema} />;\n  }\n\n  if (schema.type === 'void') {\n    return (\n      <Col span={24}>\n        <Widget {...fieldProps } />\n      </Col>\n    );\n  }\n\n  const displayType = getValueFromKey('displayType');\n\n  let inlineSelf = _inlineMode || upperCtx?.displayType === 'inline';\n  // inexistence containers\n  if (!upperCtx.exist) {\n    inlineSelf = _inlineMode || formCtx?.displayType === 'inline';\n  }\n  const inlineChild = displayType === 'inline';\n  const labelWidth = getValueFromKey('labelWidth');\n\n  // Render Container Components\n  if (children) {\n    let childElement = (\n      <div className='fr-inline-container'>\n        {children}\n      </div>\n    );\n\n    if (!inlineChild) {\n      const gutter = { row: 16, column: 24 }[displayType];\n      childElement = (\n        <Row gutter={gutter}>\n          {children}\n        </Row>\n      );\n    }\n\n    fieldProps.children = childElement;\n    const content = <Widget labelWidth={labelWidth} displayType={schema.displayType} {...fieldProps} {...otherSchema} />;\n\n    return (\n      <UpperContext.Provider\n        value={{\n          column: schema.column,\n          labelCol: schema.labelCol,\n          fieldCol: schema.fieldCol,\n          displayType: schema.displayType,\n          labelWidth: schema.labelWidth,\n          noStyle: schema.noStyle,\n          exist: true,\n        }}\n      >\n        {inlineSelf ? content : <Col span={24} className={classnames('fr-obj-col', { [schema.className] : !!schema.className })}>{content}</Col>}\n      </UpperContext.Provider>\n    );\n  }\n\n  // Render field components\n  let label = getLabel(schema, displayType, widgets, fieldProps.addons);\n  let noStyle = getValueFromKey('noStyle');\n\n  const span = getColSpan(formCtx, upperCtx, schema);\n  const extra = getExtraView('extra', schema, widgets, fieldProps.addons);\n  const help = getExtraView('help', schema, widgets, fieldProps.addons);\n  const action = getExtraView('action', schema, widgets, fieldProps.addons);\n\n  const tooltip = getTooltip(schema, displayType);\n  const ruleList = getRuleList(schema, form, methods, fieldRef);\n  const readOnly = getValueFromKey('readOnly');\n  const disabled = getValueFromKey('disabled');\n  const validateTrigger = getValueFromKey('validateTrigger');\n\n  const _labelCol = getValueFromKey('labelCol');\n  const _fieldCol = getValueFromKey('fieldCol');\n  const maxWidth = getValueFromKey('maxWidth');\n  const { labelCol, fieldCol } = getFormItemLayout(Math.floor(24 / span * 1), schema, { displayType, labelWidth, _labelCol, _fieldCol });\n  const valuePropName = schema.valuePropName || valuePropNameMap[widgetName] || undefined;\n\n  if (readOnly) {\n    fieldProps.readOnly = readOnly;\n  }\n\n  if (disabled) {\n    fieldProps.disabled = disabled;\n  }\n\n  if (reserveLabel && !label && displayType !== 'column') {\n    label = 'fr-hide-label';\n  }\n\n  if (readOnly) {\n    Widget = widgets[schema.readOnlyWidget] || widgets['Html'];\n  }\n\n  // checkbox 布局有点特殊\n  if (isCheckBoxType(schema, readOnly)) {\n    fieldProps.title = label;\n\n    label = null;\n    if (displayType === 'row') {\n      label = 'fr-hide-label';\n    }\n  }\n\n  const initialValue = schema.default ?? schema.defaultValue;\n  const classRest = { 'fr-hide-label': label === 'fr-hide-label', 'fr-inline-field': inlineSelf, 'fr-field-visibility': !visible, [schema.className] : !! schema.className };\n\n  const formItem = (\n    <Form.Item\n      className={classnames('fr-field', classRest)}\n      label={label}\n      name={path}\n      valuePropName={valuePropName}\n      rules={readOnly ? [] : ruleList}\n      hidden={hidden}\n      tooltip={tooltip}\n      extra={extra}\n      help={help}\n      initialValue={initialValue}\n      labelCol={labelCol}\n      wrapperCol={fieldCol}\n      noStyle={noStyle}\n      dependencies={dependencies}\n      validateTrigger={ validateTrigger ?? (fieldRef?.current?.validator ? 'onSubmit' : 'onChange') }\n    >\n      {fieldProps.onStatusChange ? (\n        <FieldWrapperStatus\n          Field={Widget}\n          fieldProps={fieldProps}\n          maxWidth={maxWidth}\n          initialValue={initialValue}\n          acitonRender={action ? () => action : undefined}\n        />\n      ) : (\n        <FieldWrapper\n          Field={Widget}\n          fieldProps={fieldProps}\n          maxWidth={maxWidth}\n          initialValue={initialValue}\n          acitonRender={action ? () => action : undefined}\n        />\n      )}\n    </Form.Item>\n  );\n\n  if (inlineSelf) {\n    if (noStyle) {\n      return (\n        <div\n          className={classnames('fr-inline-field', { 'fr-field-visibility': !visible, [schema.className] : !! schema.className })}\n        >\n          {formItem}\n        </div>\n      );\n    }\n    return formItem;\n  }\n\n  return (\n    <Col\n      span={span}\n      className={classnames(null, { 'fr-field-visibility': !visible })}\n    >\n      {formItem}\n    </Col>\n  );\n}\n"
  },
  {
    "path": "packages/form-render/src/render-core/FieldItem/module.tsx",
    "content": "import React from 'react';\nimport { _get, isObject, getArray, isArray, isNumber } from '../../utils';\n\nconst filterHiddenData = (list: any[]) => {\n  if (!isArray(list)) {\n    return list;\n  }\n\n  let result = [];\n  for (let i = 0; i < list.length; i++) {\n    const item = list[i];\n    if (!item.hidden) {\n      let node = {\n        ...item,\n      };\n\n      if (item.children) {\n        let children = filterHiddenData(item.children);\n        if (children.length > 0) {\n          node.children = children;\n        }\n      }\n\n      if (item.items) {\n        let items = filterHiddenData(item.items);\n        if (items.length > 0) {\n          node.items = items;\n        }\n      }\n\n      result.push(node);\n    }\n  }\n  return result;\n}\n\n// return dataIndex、dataPath、schemaPath\nconst getPathObj = ({ rootPath = [], path }) => {\n  const pathList = (path || '').split('.');\n  const dataIndex: any[] = [];\n  const schemaIndex: any[] = [];\n  const dataPathList: any[] = [];\n\n  // dataIndex\n  rootPath.forEach((item: any, index: number) => {\n    if (isNumber(item)) {\n      dataIndex.push(item);\n      return;\n    }\n\n    if (isNumber(rootPath[index+1])) {\n      schemaIndex.push(`${item}[]`);\n    } else {\n      schemaIndex.push(item);\n    }\n  });\n\n  // dataPath\n  let list: any[] = [...rootPath];\n  list.pop();\n  list = [...list, ...pathList];\n\n  list.forEach((item: any, index: number) => {\n    if (isNumber(item)) {\n      dataPathList.push(`[${item}]`)\n    } else {\n      dataPathList.push(item)\n    }\n  });\n\n  const dataPath = dataPathList.join('.');\n\n  // schemaPath\n  const _path = pathList;\n  if (_path[0] && isNumber(_path[0])) {\n    _path.splice(0, 1);\n  }\n  const schemaPath = [...schemaIndex, _path].join('.');\n\n  // console.log(path, rootPath, '-------', dataIndex, dataPath, schemaPath);\n\n  return {\n    dataIndex,\n    dataPath,\n    schemaPath\n  };\n};\n\nexport const getPath = (path: any) => {\n  if (!path) {\n    return null;\n  }\n  if (isArray(path)) {\n    return path.join('.');\n  }\n\n  return path;\n};\n\nexport const getLabel = (schema: any, displayType: string, widgets: any, addons: any) => {\n  const { title, description, descWidget, labelWidget } = schema;\n\n  const LabelNode = widgets[labelWidget];\n\n  if (LabelNode) {\n    return <LabelNode schema={schema} addons={addons} />\n  }\n\n  if ((!description && !descWidget)) {\n    return title;\n  }\n\n  const RenderDesc = () => {\n    const Widget = widgets[descWidget];\n    if (Widget) {\n      return <Widget schema={schema} addons={addons} />;\n    }\n\n    if (description) {\n      return (\n        <span className='fr-desc'>\n          ({description})\n        </span>\n      )\n    }\n    return null;\n  };\n\n  // if (displayType === 'inline') {\n  //   return title;\n  // }\n\n  return (\n    <>\n      {title}\n      <RenderDesc />\n    </>\n  )\n};\n\nexport const getTooltip = (schema: any, displayType: string) => {\n  const { descType, description, tooltip } = schema;\n\n  if (tooltip) {\n    if (typeof tooltip === 'string') {\n      return { title: <span dangerouslySetInnerHTML={{ __html: tooltip }} /> };\n    }\n\n    return {\n      ...tooltip,\n      title: <span dangerouslySetInnerHTML={{ __html: tooltip.title }} />,\n    };\n  }\n\n  if (descType === 'widget' || !description) {\n    return null;\n  }\n\n  if (displayType === 'column' && descType === 'icon') {\n    return {\n      title: description\n    }\n  }\n\n  return null;\n};\n\nexport const getExtraView = (extraKey: string, schema: any, widgets: any, addons: any) => {\n  const extra = schema[extraKey];\n  if (!extra) {\n    return;\n  }\n  \n  // extra 自定义\n  const widgetName = extra?.widget || extra;\n  const Widget = widgets[widgetName];\n  if (!!Widget) {\n    return <Widget schema={schema} addons={addons} />;\n  } if (!Widget && extra?.widget) {\n    return;\n  }\n\n  let __html = '';\n  if (typeof extra === 'string') {\n    __html = extra;\n  }\n\n  // 内部BU使用的口子，这个api不对外，也没有必要\n  if (extra?.text) {\n    __html = extra.text;\n  }\n\n  if (!__html) {\n    return;\n  }\n\n  return (\n    <div\n      className='fr-form-item-extra'\n      dangerouslySetInnerHTML={{ __html }}\n    />\n  )\n}\n\nexport const getColSpan = (formCtx: any, parentCtx: any, schema: any) => {\n  let span = 24;\n\n  const column = getParamValue(formCtx, parentCtx, schema)('column');\n\n  if (column) {\n    span = 24 / column;\n  }\n\n  // 兼容 1.0 逻辑\n  if (schema.width) {\n    if (schema.width === '100%') {\n      span = 24;\n    } else if (schema.width === '50%') {\n      span = 12;\n    } else if (schema.width === '20%') {\n      span = 5;\n    } else if (schema.width < '50%') {\n      span = 8;\n    }\n  }\n\n  if (schema.cellSpan) {\n    span = schema.cellSpan * span;\n  }\n\n  if (schema.span) {\n    span = schema.span;\n  }\n  return span > 24 ? 24 : span;\n};\n\nexport const getParamValue = (formCtx: any, upperCtx: any, schema: any) => (valueKey: string, isTop = true) => {\n  if (isTop) {\n    return schema[valueKey] ?? upperCtx[valueKey] ?? formCtx[valueKey];\n  }\n  return schema[valueKey] ?? upperCtx[valueKey];\n};\n\nexport const getFieldProps = (widgetName: string, schema: any, { widgets, methods, form, dependValues, globalProps, path, rootPath, fieldRef }) => {\n  const pathObj = getPathObj({ path, rootPath });\n \n  let fieldProps = {\n    ...schema.props,\n    addons: {\n      ...form,\n      globalProps,\n      dependValues,\n      fieldRef,\n      ...pathObj\n    }\n  };\n\n  if (dependValues?.length > 0) {\n    fieldProps.dependValues = dependValues;\n  }\n\n  ['placeholder', 'disabled', 'format', 'onStatusChange'].forEach(key => {\n    if (schema[key]) {\n      fieldProps[key] = schema[key];\n    }\n  });\n\n  // 兼容 1.0 版本逻辑 enum => options\n  if (schema.enum && !schema.props?.options) {\n    const { enum: enums, enumNames } = schema;\n    fieldProps.options = getArray(enums).map((item: any, index: number) => {\n      let label = enumNames && Array.isArray(enumNames) ? enumNames[index] : item;\n      const isHtml = typeof label === 'string' && label[0] === '<';\n      if (isHtml) {\n        label = <span dangerouslySetInnerHTML={{ __html: label }} />;\n      }\n      return { label, value: item };\n    });\n  }\n\n  if (isArray(fieldProps.options)) {\n    fieldProps = {\n      ...fieldProps,\n      options: filterHiddenData(fieldProps.options)\n    }\n  }\n\n  // 以 props 结尾的属性，直接透传\n  Object.keys(schema).forEach(key => {\n    if (\n      typeof key === 'string' &&\n      key.toLowerCase().indexOf('props') > -1 &&\n      key.length > 5\n    ) {\n      fieldProps[key] = schema[key];\n    }\n  });\n\n  // 支持 addonAfter 为自定义组件的情况\n  if (isObject(fieldProps.addonAfter) && fieldProps.addonAfter.widget) {\n    const AddonAfterWidget = widgets[fieldProps.addonAfter.widget];\n    fieldProps.addonAfter = <AddonAfterWidget {...schema} />;\n  }\n\n  if (['treeSelect', 'inputNumber', 'multiSelect', 'select'].includes(widgetName)) {\n    fieldProps.style = {\n      width: '100%',\n      ...fieldProps.style\n    }\n  }\n\n  if (widgetName === 'multiSelect') {\n    fieldProps.mode = 'multiple';\n  }\n\n  // Dynamic Mapping of Methods\n  if (isObject(schema.methods)) {\n    Object.keys(schema.methods).forEach(key => {\n      const name = schema.methods[key];\n      fieldProps[key] = methods[name];\n    });\n  }\n\n  fieldProps.schema = schema;\n  return fieldProps;\n};\n\n\n/*\n   * Get depend values\n   *\n   * 1. normal path\n   * Just get value of path in formData\n   *\n   * 2. list path\n   * Like `list[].foo`.`[]` means the same index as the current item.\n   * You can pass `[index]` to get specific item at the index of list, such as `list[1].foo`.\n   * Support more complex path like `list[].foo[].bar`\n   */\nexport const getDependValues = (formData: any, dependPath: string, props: any, dependencieItem: any[]) => {\n  const indexReg =/\\[[0-9]*\\]/;\n\n  if (indexReg.test(dependPath)) {\n    const currentIndex = _get(props, 'path.0')\n    const dependIndex = dependPath\n      .match(indexReg)[0]\n      .replace('[', '')\n      .replace(']', '')\n\n    const listPath = dependPath.split(indexReg)[0];\n    const itemIndex = dependIndex || currentIndex;\n    const itemPath = dependPath.replace(`${listPath}[${dependIndex}].`, '')\n    const listData = _get(formData, `${listPath}[${itemIndex}]`);\n\n    dependencieItem.push(listPath, itemIndex);\n\n    return getDependValues(listData, itemPath, props, dependencieItem);\n  }\n\n  dependencieItem.push(...dependPath.split('.'));\n\n  return _get(formData, dependPath);\n}\n"
  },
  {
    "path": "packages/form-render/src/render-core/FieldList/field.tsx",
    "content": "import React, { useContext, useEffect, useMemo } from 'react';\nimport { Form, message, ConfigProvider, Button } from 'antd';\nimport { isFunction, translation } from '../../utils';\nimport { getWidget } from '../../models/mapping';\nimport { transformRules } from '../../models/validates';\nimport { getParamValue } from './modules';\n\nexport default (props: any) => {\n  const {\n    form,\n    schema,\n    path,\n    parentLitPath,\n    renderCore,\n    rootPath,\n    methods,\n    upperCtx,\n    formCtx,\n    configContext,\n    setListData\n  } = props;\n \n  const { widgets, globalConfig } = configContext;\n  const configCtx = useContext(ConfigProvider.ConfigContext);\n  const t = translation(configCtx);\n\n  const defaultAddBtnProps = {\n    type: 'dashed',\n    block: true,\n    children: t('add_item'),\n  };\n\n  const defaultDelConfirmProps = {\n    title: t('confirm_delete'),\n    okText: t('confirm'),\n    cancelText: t('cancel'),\n  };\n\n  let defaultActionColumnProps = {\n    colHeaderText: t('operate')\n  };\n\n  let widgetName = schema.widget || 'cardList';\n  const Widget = getWidget(widgetName, widgets);\n  const { props: listProps, removeBtn, rules = [], ...otherSchema } = schema;\n  \n  let {\n    addBtnProps,\n    delConfirmProps,\n    actionColumnProps,\n    hideAdd,\n    hideCopy,\n    hideMove,\n    hideDelete,\n    onAdd,\n    onRemove,\n    onMove,\n    onCopy,\n    ...otherListProps\n  } = listProps || {};\n\n  const getValueFromKey = getParamValue(formCtx, upperCtx, schema);\n\n  const readOnly = getValueFromKey('readOnly');\n  const preRootPath = [...(rootPath || [])].splice(0, rootPath.length - 1);\n  const displayType = getValueFromKey('displayType');\n\n  if (hideMove === undefined && globalConfig?.listOperate?.hideMove) {\n    hideMove = globalConfig?.listOperate.hideMove;\n  }\n\n  const listData = form.getFieldValue([...preRootPath, ...path]) || [];\n  if (otherSchema?.min > 0 && listData.length <= otherSchema?.min) {\n    hideDelete = true;\n  }\n \n  if (otherSchema?.max > 0 && otherSchema?.max <= listData.length) {\n    hideAdd = true;\n  }\n\n  if (hideAdd) {\n    hideCopy = true;\n  }\n\n  if (readOnly) {\n    hideAdd = true;\n    hideCopy = true;\n    hideDelete = true;\n    hideMove = true;\n  }\n\n  const defaultValue = useMemo(() => {\n    let result = schema.default ?? schema.defaultValue;\n    if (result === undefined) {\n      result = form.getFieldValue([...preRootPath, ...path]);\n      if (!result && !['drawerList', 'list1'].includes(widgetName)) {\n        result = [{}];\n      }\n    }\n    return result;\n  }, []);\n\n  useEffect(() => {\n    setListData(defaultValue || []);\n  }, []);\n\n  const handleAdd = (add: any) => (data?: any) => {\n    let addFunc = onAdd;\n    if (typeof onAdd === 'string') {\n      addFunc = methods[onAdd];\n    }\n\n    if (isFunction(addFunc)) {\n      addFunc((funData?: any) => add(funData || data), { schema, data });\n      return;\n    }\n    add(data);\n  };\n\n  const handleRemove = (remove: any) => (index: number) => {\n    let removeFunc = onRemove;\n    if (typeof onRemove === 'string') {\n      removeFunc = methods[onRemove];\n    }\n\n    if (isFunction(removeFunc)) {\n      const data = form.getFieldValue([...preRootPath, ...path, index]);\n      removeFunc(() => remove(index), { schema, index, data });\n      return;\n    }\n\n    remove(index);\n  };\n\n  const handleMove = (move: any) => (from: number, to: number) => {\n    let moveFunc = onMove;\n    if (typeof moveFunc === 'string') {\n      moveFunc = methods[onMove];\n    }\n\n    if (isFunction(moveFunc)) {\n      moveFunc(() => move(from, to), { schema, from, to });\n      return;\n    }\n\n    move(from, to);\n  };\n\n  const handleCopy = (add: any, fields: any) => (data: any, copyIndex: number) => {\n    if (schema.max && fields.length === schema.max) {\n      return message.warning(t('copy_max_tip'));\n    }\n    let copyFunc = onCopy;\n    if (typeof onCopy === 'string') {\n      copyFunc = methods[onCopy];\n    }\n\n    if (isFunction(copyFunc)) {\n      console.log(copyIndex, 'copyIndex')\n      copyFunc((funData?: any) => add(funData || data), { schema, data, copyIndex });\n      return;\n    }\n    add(data);\n  };\n\n  const handleDelete = () => {\n    if (isFunction(removeBtn?.onClick)) {\n      removeBtn.onClick(() => {\n        form.setSchemaByPath(path, { hidden: true });\n      });\n      return;\n    }\n    form.setSchemaByPath(path, { hidden: true });\n  };\n\n  const operateBtnType = globalConfig?.listOperate?.btnType;\n\n  let ruleList: any = [];\n  if (!readOnly) {\n    ruleList = [\n      {\n        validator: async (_: any, data: any) => {\n          setListData(data);\n          if (!otherSchema?.min) {\n            return;\n          }\n          if (!data || data.length < otherSchema.min) {\n            return Promise.reject(\n              new Error(\n                otherSchema?.message?.min ||\n                `数据长度必须大于等于${otherSchema.min}`\n              )\n            );\n          }\n        }\n      },\n      ...transformRules(rules || [], methods, form)\n    ];\n  }\n\n  return (\n    <>\n      <Form.List\n        name={path}\n        initialValue={defaultValue}\n        rules={ruleList}\n      >\n        {(fields, operation, { errors }) => (\n          <>\n            <Widget\n              {...otherListProps}\n              configContext={configContext}\n              form={form}\n              schema={otherSchema}\n              fields={fields}\n              operation={operation}\n              path={path}\n              listName={path}\n              parentLitPath={parentLitPath}\n              rootPath={[...preRootPath, ...path]}\n              readOnly={readOnly}\n              methods={methods}\n              renderCore={renderCore}\n              widgets={widgets}\n              hideAdd={hideAdd}\n              hideCopy={hideCopy}\n              hideDelete={hideDelete}\n              hideMove={hideMove}\n              addItem={handleAdd(operation.add)}\n              removeItem={handleRemove(operation.remove)}\n              moveItem={handleMove(operation.move)}\n              copyItem={handleCopy(operation.add, fields)}\n              temporary={{\n                displayType\n              }}\n              addBtnProps={{\n                ...defaultAddBtnProps,\n                ...addBtnProps,\n              }}\n              delConfirmProps={{\n                ...defaultDelConfirmProps,\n                ...delConfirmProps,\n              }}\n              actionColumnProps={{\n                ...defaultActionColumnProps,\n                ...actionColumnProps,\n              }}\n              copyBtnProps={{\n                children: t('copy'),\n                btnType: operateBtnType\n              }}\n              editorBtnProps={{\n                children: t('edit'),\n                btnType: operateBtnType\n              }}\n              deleteBtnProps={{\n                children: t('delete'),\n                btnType: operateBtnType\n              }}\n              moveUpBtnProps={{\n                children: t('moveUp'),\n                btnType: operateBtnType\n              }}\n              moveDownBtnProps={{\n                children: t('moveDown'),\n                btnType: operateBtnType\n              }}\n            />\n            {errors?.length !== 0 && (\n              <div style={{ marginBottom: '12px' }}>\n                <Form.ErrorList errors={errors} />\n              </div>\n            )}\n          </>\n        )}\n      </Form.List>\n      {removeBtn && (\n        <Button\n          type='link'\n          danger\n          {...removeBtn}\n          onClick={handleDelete}\n        >\n          {removeBtn?.text || t('delete')}\n        </Button>\n      )}\n    </>\n  );\n}\n"
  },
  {
    "path": "packages/form-render/src/render-core/FieldList/index.tsx",
    "content": "import React, { useContext } from 'react';\nimport { Form } from 'antd';\n\nimport { _get } from '../../utils';\nimport { FRContext, ConfigContext } from '../../models/context';\nimport { isHasExpression, parseAllExpression } from '../../models/expression';\nimport fieldShouldUpdate from '../../models/fieldShouldUpdate';\n\nimport Main from './main';\n\nexport default (props: any) => {\n  const { schema, rootPath } = props;\n  const { items, ...listSchema } = schema || {};\n\n  const store:any = useContext(FRContext);\n  const { schema: formSchema } = store.getState();\n\n  const configCtx: any = useContext(ConfigContext);\n  const mustacheDisabled = configCtx?.globalConfig?.mustacheDisabled;\n  const dependencies = schema?.dependencies;\n\n  // No function expressions exist\n  if ((!isHasExpression(schema) && !mustacheDisabled) && (!dependencies || !dependencies?.length)) {\n    return <Main configContext={configCtx} {...props}  />;\n  }\n\n  // Need to listen to form values for dynamic rendering\n  return (\n    <Form.Item\n      noStyle\n      shouldUpdate={fieldShouldUpdate(JSON.stringify(listSchema || {}), rootPath, dependencies, true)}\n    >\n      {(form: any) => {\n       const formData = form.getFieldsValue(true);\n       const newListSchema = mustacheDisabled ? schema : parseAllExpression(listSchema, formData, rootPath, formSchema);\n        return (\n          <Main \n            configContext={configCtx} \n            {...props} \n            schema={{\n              items,\n              ...newListSchema\n            }}\n            rootPath={rootPath}\n          />\n        );\n      }}\n    </Form.Item>\n  );\n}\n"
  },
  {
    "path": "packages/form-render/src/render-core/FieldList/main.tsx",
    "content": "import React, { createContext, useContext, useState } from 'react';\nimport { Form, Col } from 'antd';\nimport { useStore } from 'zustand';\nimport { FRContext } from '../../models/context';\nimport { parseAllExpression } from '../../models/expression';\nimport { getFormListLayout, getParamValue, getLabel, getTooltip } from './modules';\nimport Main from './field';\n\nconst UpperContext = createContext(() => {});\n\nexport default (props: any) => {\n  const [_, setListData] = useState([]);\n  const { configContext } = props;\n  \n  const store: any = useContext(FRContext);\n  \n  const formCtx: any = useStore(store, (state: any) => state.context);\n  const upperCtx: any = useContext(UpperContext);\n  const { form, widgets, methods, globalConfig } = configContext;\n\n  const { displayType } = formCtx;\n  const isDisplayColumn = displayType === 'column';\n  const { schema:_schema } = props;\n\n  const formData = form.getFieldsValue(true);\n  const { schema: formSchema } = store.getState();\n\n  const { items, className, ...otherSchema } = _schema;\n  const schema = globalConfig?.mustacheDisabled ? _schema : {\n    items,\n    ...parseAllExpression(otherSchema, formData, props.rootPath, formSchema)\n  };\n  \n  const { widget } = schema;\n  let widgetName = widget || 'list1';\n\n  const getValueFromKey = getParamValue(formCtx, upperCtx, schema);\n \n  const label = getLabel(schema, displayType, widgets);\n  const tooltip = getTooltip(schema, displayType);\n  const { labelCol, fieldCol } = getFormListLayout(getValueFromKey, displayType);\n\n  let isInline = schema.display === 'inline';\n  const preRootPath = [...(props.rootPath || [])].splice(0, props.rootPath.length - 1);\n  const listData = form.getFieldValue([...preRootPath, ...props.path]);\n  if (!listData?.length && widgetName !== 'drawerList') {\n    isInline = true;\n  }\n\n  if (schema.hidden) {\n    return null;\n  }\n\n  return (\n    <Col span={24} className={className}>\n      {!isInline && !isDisplayColumn && label && (\n        <Form.Item\n          className='ant-form-item-optional-hide'\n          label={label}\n          labelAlign={'left'}\n          colon={false}\n          tooltip={tooltip}\n          style={{ marginBottom: 0 }}\n          labelCol={{ span: 24 }}\n        >\n        </Form.Item>\n      )}\n      <Form.Item\n        label={label} \n        labelCol={isDisplayColumn ? { span: 24 } : labelCol}\n        wrapperCol={fieldCol}\n        noStyle={!isInline && !isDisplayColumn}\n        tooltip={tooltip}\n      >\n        <Main\n          {...props}\n          form={form}\n          methods={methods}\n          formCtx={formCtx}\n          upperCtx={upperCtx}\n          widgets={widgets}\n          configContext={configContext}\n          setListData={setListData}\n        />\n      </Form.Item>\n    </Col>\n  );\n}"
  },
  {
    "path": "packages/form-render/src/render-core/FieldList/modules.tsx",
    "content": "import React from \"react\";\n\nexport const getParamValue = (formCtx: any, upperCtx: any, schema: any) => (valueKey: string) => {\n  return schema[valueKey] ?? upperCtx[valueKey] ?? formCtx[valueKey];\n};\n\nexport const getFormListLayout = (getValueFromKey: any, displayType: string) => {\n  const _labelCol = getValueFromKey('labelCol');\n  const _fieldCol = getValueFromKey('fieldCol');\n  const labelWidth = getValueFromKey('labelWidth')\n\n  let labelCol: any = { span: 5 };\n  let fieldCol: any = { flex: 1 };\n\n  if (labelWidth && displayType !== 'column') {\n    labelCol = { flex : labelWidth + 'px' };\n  }\n\n  if (_labelCol) {\n    labelCol = _labelCol;\n  }\n\n  if (_fieldCol) {\n    fieldCol = _fieldCol;\n  }\n\n  if (typeof _labelCol === 'number') {\n    labelCol = { span: _labelCol };\n  }\n\n  if (typeof _fieldCol === 'number') {\n    fieldCol = { span: _fieldCol };\n  }\n\n  return { labelCol, fieldCol };\n};\n\nexport const getLabel = (schema: any, displayType: string, widgets: any) => {\n  const { title, description, descWidget, labelWidget } = schema;\n  const LabelNode = widgets[labelWidget];\n\n  if (LabelNode) {\n    return <LabelNode schema={schema} />\n  }\n\n  if ((!description && !descWidget)) {\n    return title;\n  }\n\n  const RenderDesc = () => {\n    const Widget = widgets[descWidget];\n    if (Widget) {\n      return <Widget schema={schema} />;\n    }\n\n    if (description) {\n      return (\n        <span className='fr-desc'>\n          ({description})\n        </span>\n      )\n    }\n    return null;\n  };\n\n  if (displayType === 'inline') {\n    return title;\n  }\n\n  return (\n    <>\n      {title}\n      <RenderDesc />\n    </>\n  )\n};\n\nexport const getTooltip = (schema: any, displayType: string) => {\n  const { descType, description, tooltip } = schema;\n\n  if (tooltip) {\n    if (typeof tooltip === 'string') {\n      return { title: <span dangerouslySetInnerHTML={{ __html: tooltip }} /> };\n    }\n\n    return {\n      ...tooltip,\n      title: <span dangerouslySetInnerHTML={{ __html: tooltip.title }} />,\n    };\n  }\n\n  if (descType === 'widget' || !description) {\n    return null;\n  }\n\n  if (displayType === 'column' && descType === 'icon') {\n    return {\n      title: description\n    }\n  }\n\n  return null;\n};"
  },
  {
    "path": "packages/form-render/src/render-core/index.less",
    "content": "@ant-prefix: ant;\n\n.fr-inline-field {\n  display: inline-block;\n  margin-right: 16px;\n}\n\n.fr-inline-container {\n  display: inline-flex;\n  flex-wrap: wrap;\n}\n\n.ant-form-item-optional-hide {\n  padding: 0 8px;\n\n  .@{ant-prefix}-form-item-label > label {\n    font-size: 16px;\n    font-weight: #000000d9;\n    font-weight: 600;\n  }\n\n}\n\n.fr-desc {\n  word-break: break-all;\n  color: rgba(0, 0, 0, .45);\n  line-height: 22px;\n  margin-left: 5px;\n  font-weight: normal;\n  font-size: 14px;\n}\n\n.fr-item-actions {\n  margin-left: 12px;\n  display: inline-flex;\n  align-items: center;\n}"
  },
  {
    "path": "packages/form-render/src/render-core/index.tsx",
    "content": "import React from 'react';\nimport FieldItem from './FieldItem';\nimport FieldList from './FieldList';\nimport sortProperties from '../models/sortProperties';\nimport './index.less';\n\ninterface RenderCoreProps {\n  schema: any;\n  rootPath?: any[] | undefined;\n  parentPath?: any[] | undefined;\n  [key: string]: any\n};\n\ninterface RenderItemProps {\n  schema: any;\n  rootPath?: any[] | undefined;\n  path?: any[] | undefined;\n  key?: string | undefined;\n};\n\nconst renderItem = (props: RenderItemProps) => {\n  let { schema, key, path, rootPath } = props;\n\n  // render List\n  if (schema.type === 'array' && schema.items?.type === 'object') {\n    return (\n      <FieldList\n        key={key}\n        schema={schema}\n        path={path}\n        rootPath={rootPath}\n        renderCore={RenderCore}\n      />\n    );\n  }\n\n  // render Objiect | field\n  let child: React.ReactNode = null;\n\n  // has child schema\n  if (schema?.properties && schema?.widgetType !== 'field') {\n    child = RenderCore({ schema, parentPath: path, rootPath })\n    // path = undefined;\n  }\n\n  return (\n    <FieldItem\n      key={key}\n      schema={schema}\n      path={path}\n      rootPath={rootPath}\n      children={child}\n      renderCore={RenderCore}\n    />\n  );\n}\n\nconst RenderCore = (props: RenderCoreProps): any => {\n  const { schema, parentPath = [], rootPath = [] } = props;\n  if (!schema || Object.keys(schema).length === 0) {\n    return null;\n  }\n\n  // render List.item\n  if (schema?.items) {\n    return renderItem({ schema: schema.items, path: parentPath, rootPath });\n  }\n\n  // render Objiect | field\n  return sortProperties(Object.entries(schema.properties || {})).map(([key, item]) => {\n    const path = [...parentPath, key];\n\n    return renderItem({ schema: item, path, key, rootPath });\n  });\n}\n\nexport default RenderCore;\n"
  },
  {
    "path": "packages/form-render/src/type.ts",
    "content": "import { RuleItem } from 'async-validator';\nimport * as React from 'react';\nimport type { FormInstance as AntdFormInstance, FormProps as AntdFormProps, ColProps, TooltipProps } from 'antd';\nimport type { ConfigProviderProps } from 'antd/es/config-provider';\nimport type { FormProps as RcFormProps } from 'rc-field-form/lib/Form';\n\nexport type { RuleItem } from 'async-validator';\nexport type SchemaType =\n  | 'string'\n  | 'object'\n  | 'array'\n  | 'number'\n  | 'boolean'\n  | 'void'\n  | 'date'\n  | 'datetime'\n  | 'block'\n  | string;\n\nexport type ActionProps = {\n  submit: {\n    text?: string;\n    hide?: boolean;\n    [key: string]: any;\n  },\n  reset: {\n    text?: string;\n    hide?: boolean;\n    [key: string]: any;\n  }\n}\n\nexport interface SchemaBase {\n  type?: SchemaType;\n  title?: string;\n  description?: string;\n  descType?: 'text' | 'icon';\n  format?:\n  | 'image'\n  | 'textarea'\n  | 'color'\n  | 'email'\n  | 'url'\n  | 'dateTime'\n  | 'date'\n  | 'time'\n  | 'upload'\n  | (string & {});\n  default?: any;\n  /** 是否必填，支持 `'{{ formData.xxx === \"\" }}'` 形式的表达式 */\n  required?: boolean | string;\n  placeholder?: string;\n  bind?: false | string | string[];\n  dependencies?: string[];\n  /** 最小值，支持表达式 */\n  min?: number | string;\n  /** 最大值，支持表达式 */\n  max?: number | string;\n  /** 是否禁用，支持 `'{{ formData.xxx === \"\" }}'` 形式的表达式 */\n  disabled?: boolean | string;\n  /** 是否只读，支持 `'{{ formData.xxx === \"\" }}'` 形式的表达式 */\n  readOnly?: boolean | string;\n  /** 是否隐藏，隐藏的字段不会在 formData 里透出，支持 `'{{ formData.xxx === \"\" }}'` 形式的表达式 */\n  hidden?: boolean | string;\n  displayType?: 'row' | 'column' | string;\n  width?: string | number;\n  labelWidth?: number | string;\n  maxWidth?: number | string;\n  column?: number;\n  className?: string;\n  widget?: string;\n  readOnlyWidget?: string;\n  extra?: string;\n  properties?: Record<string, Schema>;\n  items?: Schema;\n  /** 多选，支持表达式 */\n  enum?: Array<string | number> | string;\n  /** 多选label，支持表达式 */\n  enumNames?: Array<string | number> | string;\n  rules?: RuleItem | RuleItem[];\n  props?: Record<string, any>;\n  /**扩展字段 */\n  'add-widget'?: string;\n  labelCol?: number | ColProps;\n  fieldCol?: number | ColProps\n  tooltip?: string | TooltipProps\n  cellSpan?: number;\n  span?: number;\n  validateTrigger?: string | string[]\n  [key: string]: any;\n}\n\nexport type Schema = Partial<SchemaBase>;\n\nexport interface Error {\n  /** 错误的数据路径 */\n  name: string;\n  /** 错误的内容 */\n  error: string[];\n}\n\nexport interface FormParams {\n  formData?: any;\n  onChange?: (data: any) => void;\n  onValidate?: (valid: any) => void;\n  showValidate?: boolean;\n  /** 数据分析接口，表单展示完成渲染时触发 */\n  logOnMount?: (stats: any) => void;\n  /** 数据分析接口，表单提交成功时触发，获得本次表单填写的总时长 */\n  logOnSubmit?: (stats: any) => void;\n}\n\nexport interface ValidateParams {\n  formData: any;\n  schema: Schema;\n  error: Error[];\n\n  [k: string]: any;\n}\n\nexport interface ResetParams {\n  formData?: any;\n  submitData?: any;\n  errorFields?: Error[];\n  touchedKeys?: any[];\n  allTouched?: boolean;\n\n  [k: string]: any;\n}\n\nexport interface FieldParams {\n  name: string;\n  error?: string[];\n  touched?: boolean;\n  validating?: boolean;\n  value?: any;\n}\n\nexport interface ListOperate {\n  /* 列表表单操作按钮样式 */\n  btnType: 'text' | 'icon';\n  /* 是否隐藏移动按钮 */\n  hideMove: boolean;\n}\n\nexport interface GlobalConfig {\n  /* 列表表单配置 */\n  listOperate: ListOperate;\n  /** 列表校验气泡模式*/\n  listValidatePopover: boolean;\n  /* 是否禁用表达式 */\n  mustacheDisabled: boolean;\n}\n\nexport interface FormInstance {\n  /*\n   * 提交表单\n   */\n  submit: () => void,\n  /**\n   *  根据路径动态设置 Schema\n   */\n  setSchemaByPath: (path: string, schema: any) => any;\n  /**\n   * 获取隐藏的表单数据\n   */\n  getHiddenValues: () => any;\n  /**\n   *  设置 Schema\n   */\n  setSchema: (schema: any, cover?: boolean) => void;\n  /**\n   * 获取表单的 schema\n   */\n  getSchema: () => any;\n  /**\n   * \n   * 获取 flatten schema\n   */\n  getFlattenSchema: (path?: string) => any;\n  /**\n   * 根据路径获取 Schema\n   */\n  getSchemaByPath: (path: string) => any;\n  /**\n   * 外部手动修改 errorFields 校验信息\n   */\n  setErrorFields: (errors: any[]) => void;\n  /**\n   * 外部手动删除某一个 path 下所有的校验信息\n   */\n  removeErrorField: (path: string) => any;\n  /**\n   * 校验表单\n   */\n  validateFields: AntdFormInstance['validateFields'];\n  /**\n   * 获取对应字段 field 的错误信息\n   */\n  getFieldError: AntdFormInstance['getFieldError'];\n  /**\n   * 获取一组字段 fields 的错误信息\n   */\n  getFieldsError: AntdFormInstance['getFieldsError'];\n  /**\n   * 检查某个表单项是否被修改过\n   */\n  isFieldTouched: AntdFormInstance['isFieldTouched'];\n  /**\n   * 检查一组表单项是否被修改过\n   */\n  isFieldsTouched: AntdFormInstance['isFieldsTouched'];\n  /**\n   * 检查某个表单项是否在校验中\n   */\n  isFieldValidating: AntdFormInstance['isFieldValidating'];\n    /**\n   * 根据路径获取表单值\n   */\n  getValueByPath: AntdFormInstance['getFieldValue'];\n  /**\n   * 根据路径修改表单值\n   */\n  setValueByPath: AntdFormInstance['setFieldValue'];\n  /**\n   * 获取表单值\n   */\n  getValues: AntdFormInstance['getFieldsValue'];\n  /**\n   * 设置表单值\n   */\n  setValues: AntdFormInstance['setFieldsValue'];\n  /**\n   * 重置表单\n   */\n  resetFields: AntdFormInstance['resetFields'];\n  /**\n   * @deprecated 即将弃用，请勿使用此 api，使用 getFieldsError\n   */\n  errorFields: AntdFormInstance['getFieldsError'];\n  /**\n   * @deprecated 即将弃用，请勿使用此 api，使用 form.isFieldsValidating\n   */\n  scrollToPath: AntdFormInstance['scrollToField'];\n  /**\n * @deprecated 即将弃用，请勿使用此 api，使用setValueByPath\n */\n  onItemChange: AntdFormInstance['setFieldValue'];\n  /**\n   * @deprecated 即将弃用，请勿使用此 api\n   */\n  init: any;\n  /**\n   * @deprecated 即将弃用，请勿使用此 api，使用 getSchema 代替\n   */\n  __schema: any;\n  /**\n   * @deprecated 内部方法不要使用\n   */\n  __initStore: (data: any) => any;\n  /**\n   * 存储 field 的 ref 对象\n   */\n  setFieldRef: (path: string, ref: any) => void;\n  /**\n   * 获取 field 的 ref 对象\n   */\n  getFieldRef: (path: string) => any;\n}\n\nexport type WatchProperties = {\n  [path: string]:\n  | {\n    handler: (value: any) => void;\n    immediate?: boolean;\n  }\n  | ((value: any) => void);\n};\n\ninterface ExtendedColProps extends ColProps {\n  // 额外的属性可以放在这里\n}\n\nexport interface FRProps extends Omit<AntdFormProps, 'form'> {\n  /**\n   * 表单顶层的className\n   */\n  className?: string;\n  /**\n   * 表单顶层的样式\n   */\n  style?: React.CSSProperties;\n  /**\n   * 表单 schema\n   */\n  schema: Schema;\n  /**\n   * form单例\n   */\n  form: FormInstance;\n  /**\n   * 组件和schema的映射规则\n   */\n  mapping?: Record<string, string>;\n  /**\n   * 自定义组件\n   */\n  widgets?: Record<string, any>;\n  /**\n   * 标签元素和输入元素的排列方式，column-分两行展示，row-同行展示，inline-自然顺排，默认`column`\n   */\n  displayType?: 'column' | 'row' | 'inline';\n  /**\n   * 表示是否显示 label 后面的冒号\n   */\n  colon?: boolean;\n  /**\n   * label 标签的文本对齐方式\n   */\n  labelAlign?: 'right' | 'left';\n  // labelCol?: number | ExtendedColProps;\n  fieldCol?: number | ColProps;\n  /**\n   *  只读模式\n   */\n  readOnly?: boolean;\n  /**\n   * 禁用模式\n   */\n  disabled?: boolean;\n  /**\n   * 标签宽度\n   */\n  labelWidth?: string | number;\n  /**\n   * antd的全局config\n   */\n  configProvider?: ConfigProviderProps;\n  /**\n   * 覆盖默认的校验信息\n   */\n  validateMessages?: RcFormProps['validateMessages'];\n  /**\n   * 显示当前表单内部状态\n   */\n  debug?: boolean;\n  /**\n   * 显示css布局提示线\n   */\n  debugCss?: boolean;\n  /**\n   * 展示语言，目前只支持中文、英文\n   */\n  locale?: 'zh-CN' | 'en-US';\n  /**\n   * 一行展示的列数\n   */\n  column?: number;\n  /**\n   * 数据会作为 beforeFinish 的第四个参数传入\n   */\n  config?: any;\n  /**\n   * 类似于 vuejs 的 watch 的用法，监控值的变化，触发 callback\n   */\n  watch?: WatchProperties;\n  /*\n   * 表单全局配置\n   */\n  globalConfig?: GlobalConfig;\n  /**\n   * 表单的全局共享属性\n   */\n  globalProps?: any;\n  /**\n   * 表单首次加载钩子\n   */\n  onMount?: () => void;\n  /**\n   * 表单提交前钩子\n   */\n  beforeFinish?: (params: ValidateParams) => Error[] | Promise<Error[]>;\n  /**\n   * 表单提交后钩子\n   */\n  onFinish?: (formData: any) => void;\n  /**\n   * 字段值更新时触发回调事件\n   */\n  onValuesChange?: (\n    changedValues: {\n      dataPath: string;\n      value: any;\n      dataIndex: number[] | unknown;\n    },\n    formData: any\n  ) => void;\n  /**\n   * 隐藏的数据是否去掉，默认不去掉\n   */\n  removeHiddenData?: boolean;\n  /**\n   * 配置自定义layout组件\n   */\n  layoutWidgets?: any;\n  /**\n   * 扩展方法\n   */\n  methods?: Record<string, Function>;\n  operateExtra?: React.ReactNode;\n  maxWidth?: number | string;\n  footer?: boolean | ((dom: React.JSX.Element[]) => React.ReactNode) | Partial<ActionProps> ;\n}\n\nexport interface SearchProps<RecordType> extends Omit<FRProps, 'form'> {\n  debug?: boolean;\n  searchBtnStyle?: React.CSSProperties;\n  searchBtnClassName?: string;\n  displayType?: any;\n  propsSchema?: any;\n  className?: string;\n  style?: React.CSSProperties;\n  hidden?: boolean;\n  searchOnMount?: boolean | unknown;\n  searchWithError?: boolean;\n  searchBtnRender?: (\n    submit: Function,\n    clearSearch: Function,\n    other: any\n  ) => React.ReactNode[];\n  searchText?: string;\n  resetText?: string;\n  onSearch?: (search: any) => any;\n  afterSearch?: (params: any) => any;\n  onReset?: (form: any) => void;\n  widgets?: any;\n  form?: any;\n  [key:string]: any\n}\n\n/** 自定义组件 props */\nexport type WidgetProps = {\n  value: any,\n  onChange: (value: any) => void,\n  schema: Schema,\n  style: React.CSSProperties,\n  id: string,\n  addons: WidgetAddonsType,\n  disabled?: boolean,\n  readOnly?: boolean,\n  [other: string]: any,\n}\n\n/** 自定义组件 addons */\nexport type WidgetAddonsType = FormInstance & {\n  globalProps: Record<string, any>,\n  dependValues: any[],\n  dataIndex: string[],\n  dataPath: string,\n  schemaPath: string,\n}\n\ndeclare const FR: React.FC<FRProps>;\n\nexport declare function useForm(params?: FormParams): FormInstance;\n\nexport type ConnectedForm<T> = T & {\n  form: FormInstance;\n};\n\nexport declare function connectForm<T extends {} = any>(\n  component: React.ComponentType<ConnectedForm<T>>\n): React.ComponentType<T>;\n\nexport default FR;\n"
  },
  {
    "path": "packages/form-render/src/utils/index.ts",
    "content": "import { isMatch, some, set, get, cloneDeep, has as _has, merge, mergeWith, isUndefined, omitBy } from 'lodash-es';\n\nexport const _set = set;\nexport const _get = get;\nexport const _cloneDeep = cloneDeep;\n// export const _has = has;\nexport { _has };\nexport const _merge = merge;\nexport const _mergeWith = mergeWith;\nexport const _isUndefined = isUndefined;\nexport const _omitBy = omitBy;\nexport const _some = some;\nexport const _isMatch = isMatch;\n\nexport const isObject = (data: any) => {\n  const str = Object.prototype.toString.call(data);\n  return str.indexOf('Object') > -1;\n}\n\nexport const isArray = (data: any) => {\n  const str = Object.prototype.toString.call(data);\n  return str.indexOf('Array') > -1;\n}\n\nexport const isFunction = (data: any) => typeof data === 'function';\n\nexport function isUrl(string: string) {\n  const protocolRE = /^(?:\\w+:)?\\/\\/(\\S+)$/;\n  // const domainRE = /^[^\\s\\.]+\\.\\S{2,}$/;\n  if (typeof string !== 'string') return false;\n  return protocolRE.test(string);\n}\n\nexport const isNumber = (str: string | number) => !isNaN(Number(str))\n\nexport const getArray = (arr, defaultValue = []) => {\n  if (Array.isArray(arr)) return arr;\n  return defaultValue;\n};\n\nexport function getFormat(format) {\n  let dateFormat;\n  switch (format) {\n    case 'date':\n      dateFormat = 'YYYY-MM-DD';\n      break;\n    case 'time':\n      dateFormat = 'HH:mm:ss';\n      break;\n    case 'dateTime':\n      dateFormat = 'YYYY-MM-DD HH:mm:ss';\n      break;\n    case 'week':\n      dateFormat = 'YYYY-w';\n      break;\n    case 'year':\n      dateFormat = 'YYYY';\n      break;\n    case 'quarter':\n      dateFormat = 'YYYY-Q';\n      break;\n    case 'month':\n      dateFormat = 'YYYY-MM';\n      break;\n    default:\n      // dateTime\n      if (typeof format === 'string') {\n        dateFormat = format;\n      } else {\n        dateFormat = 'YYYY-MM-DD';\n      }\n  }\n  return dateFormat;\n}\n\n// TODO: to support case that item is not an object\nexport function isObjType(schema: any) {\n  //return schema?.type === 'object' && schema.properties && !schema.widget;\n  return schema?.type === 'object' && schema?.properties && schema?.widgetType !== 'field';\n};\n\nexport function isListType(schema: any) {\n  return schema?.type === 'array' && isObjType(schema?.items) && schema?.enum === undefined;\n};\n\nexport function isCheckBoxType(schema: any, readOnly: boolean) {\n  if (readOnly) return false;\n  if (schema.widget === 'checkbox') return true;\n  if (schema && schema.type === 'boolean') {\n    if (schema.enum) return false;\n    if (schema.widget === undefined) return true;\n    return false;\n  }\n}\n\nexport const translation = (configCtx: any) => (key: string) => {\n  const locale = configCtx?.locale.FormRender;\n  return locale[key];\n};\n\nexport const hasFuncProperty = (obj: any) => {\n  return _some(obj, (value) => {\n    if (isFunction(value)) {\n      return true;\n    }\n    if (isObject(value)) {\n      return hasFuncProperty(value);\n    }\n    return false;\n  });\n};\n\n/**\n * 安全地获取对象的值，如果值为 null 或 undefined，则返回 defaultValue。\n *\n * @param {Object} object - 要获取值的对象。\n * @param {string|Array} path - 要获取的路径，可以是字符串或数组。\n * @param {*} [defaultValue] - 如果值为 null 或 undefined，则返回 defaultValue。\n * @returns {*} - 返回获取的值，或者默认值。\n */\nexport const safeGet = (object: any, path: string, defaultValue: any) => {\n  return get(object, path, defaultValue) ?? defaultValue;\n};\n\n\n"
  },
  {
    "path": "packages/form-render/src/widgets/ErrorSchema/index.tsx",
    "content": "import React, { useContext } from \"react\";\nimport { translation } from '../utils';\nimport { ConfigProvider } from 'antd';\n\nconst ErrorSchema = (schema: any) => {\n  const configCtx = useContext(ConfigProvider.ConfigContext);\n  const t = translation(configCtx);\n\n  return (\n    <div>\n      <div style={{ color: 'red' }}>{t('schema_not_match')}</div>\n      <div>{JSON.stringify(schema)}</div>\n    </div>\n  );\n}\n\nexport default ErrorSchema;"
  },
  {
    "path": "packages/form-render/src/widgets/boxCollapse/index.less",
    "content": "@ant-prefix: ant;\n\n.fr-obj-collapse {\n  border-radius: 4px;\n  border: 1px solid #f1f1f1;\n  margin-bottom: 24px;\n\n  .collapse-title {\n    height: 24px;\n    color: #000000e0;\n    font-weight: 600;\n    font-size: 16px;\n    line-height: 24px;\n  }\n\n  .fr-header-desc {\n    word-break: break-all;\n    color: rgba(0, 0, 0, .45);\n    margin-left: 6px;\n    font-weight: normal;\n  }\n\n  .header-item-label {\n    height: 14px;\n    margin-left: 30px;\n    color: black;\n    font-weight: 600;\n    font-size: 14px;\n    line-height: 14px;\n    span {\n      color: #141414;\n    }\n  }\n\n  .expand-icon-desc {\n    margin-left: 4px;\n  }\n\n  .@{ant-prefix}-collapse-header {\n    display: flex;\n    align-items: center !important;\n    padding: 16px !important;\n    background-color: #fff;\n    border-radius: 4px !important;\n  }\n\n  .@{ant-prefix}-collapse-header-text {\n    display: flex;\n    align-items: center;\n  }\n\n  .tag-no-data {\n    color: rgba(0, 0, 0, 0.65);\n  }\n\n  .@{ant-prefix}-collapse-content-box {\n    padding: 12px 24px !important;\n\n    .@{ant-prefix}-skeleton-title,\n    .@{ant-prefix}-skeleton-paragraph > li {\n      animation: none !important;\n    }\n  }\n}\n\n// .fr-obj-collapse:hover {\n//   box-shadow: 0 1px 2px -2px rgba(0, 0, 0, 0.16), 0 3px 6px 0 rgba(0, 0, 0, 0.12), 0 5px 12px 4px rgba(0, 0, 0, 0.09);\n// }\n"
  },
  {
    "path": "packages/form-render/src/widgets/boxCollapse/index.tsx",
    "content": "import React, { useState, FC } from 'react';\nimport { Collapse } from 'antd';\nimport { DownOutlined } from '@ant-design/icons';\nimport './index.less';\nimport BoxPanel from '../components/PanelView';\n\nconst { Panel } = Collapse;\ninterface IProps {\n  className?: any;\n  style?: object;\n  children: any;\n  title?: string;\n  description?: any;\n  collapsed?: boolean;\n  displayType?: any;\n  bordered?: boolean;\n  ghost?: boolean;\n}\n\n/**\n * 折叠面板\n */\nconst BoxCollapse: FC<IProps> = (props) => {\n  const { style, children, title, description, collapsed = true, displayType, bordered = false, ghost\t= true } = props;\n \n  if (!title) {\n    return (\n      <BoxPanel bordered={displayType !== 'inline'}>\n        {children}\n      </BoxPanel>\n    )\n  }\n  \n  const [activeKey, setActiveKey] = useState<string>(collapsed ? 'single' : '');\n  const collapseHeader = (\n    <>\n      {title && <div className='collapse-title'>{title}</div>}\n      {description && <span className='fr-header-desc '>{description}</span>}\n    </>\n  );\n\n  const renderExpandIcon = ({ isActive }: any): JSX.Element => {\n    return (\n      <div className='expand-icon-box'>\n        <DownOutlined rotate={isActive ? 0 : -90 } style={{ fontSize: '16px'}} />\n      </div>\n    );\n  };\n\n  return (\n    <Collapse\n      className='fr-obj-collapse'\n      style={style}\n      bordered={bordered}\n      ghost={ghost}\n      activeKey={[activeKey]}\n      expandIcon={renderExpandIcon}\n      onChange={() => setActiveKey(activeKey ? '' : 'single')}\n    >\n      <Panel key='single' header={collapseHeader} forceRender={true}>\n        {children}\n      </Panel>\n    </Collapse>\n  );\n}\n\nexport default BoxCollapse;\n"
  },
  {
    "path": "packages/form-render/src/widgets/boxLineTitle/index.less",
    "content": ".fr-obj-line-title {\n  .fr-obj-header {\n    line-height: 36px;\n    border-bottom: 1px solid #e9e9e9;\n  }\n  \n  .fr-header-title {\n    color: #000000e0;\n    font-size: 16px;\n    font-weight: 600;\n  }\n\n  .fr-header-desc {\n    word-break: break-all;\n    color: rgba(0, 0, 0, .45);\n    margin-left: 6px;\n    font-weight: normal;\n  }\n\n  .fr-obj-content {\n    padding: 24px 0 0 0;\n  }\n}"
  },
  {
    "path": "packages/form-render/src/widgets/boxLineTitle/index.tsx",
    "content": "import React from 'react';\nimport HeaderTitle from '../components/HeaderTitle';\nimport PanelView from '../components/PanelView';\nimport './index.less';\n\nconst FLineTitle = ({ children, title, description }) => {\n\n  if (!title) {\n    return (\n      <PanelView>\n        {children}\n      </PanelView>\n    );\n  }\n\n  return (\n    <div className='fr-obj-line-title'>\n      <div className='fr-obj-header'>\n        <span className='fr-header-title'>{title}</span>\n        {description && <span className='fr-header-desc'>{description}</span>}\n      </div>\n      <div className='fr-obj-content'>\n        {children}\n      </div>\n    </div>\n  );\n}\n\nexport default FLineTitle\n"
  },
  {
    "path": "packages/form-render/src/widgets/boxSubInline/index.less",
    "content": "@ant-prefix: ant;\n\n.fr-obj-subinline-label-hidden > .@{ant-prefix}-form-item-row > .@{ant-prefix}-form-item-label {\n  visibility: hidden;\n}\n\n.fr-obj-subinline-background > .@{ant-prefix}-form-item-row > .@{ant-prefix}-form-item-control {\n  background-color: #f6f6f6;\n  padding: 24px 24px 0 24px;\n  border-radius: 4px;\n}\n\n.fr-obj-subinline {\n  .@{ant-prefix}-form-item-row {\n    flex-wrap: nowrap;\n  }\n}\n"
  },
  {
    "path": "packages/form-render/src/widgets/boxSubInline/index.tsx",
    "content": "import React from 'react';\nimport { Form } from 'antd';\nimport classnames from 'classnames';\nimport './index.less';\nimport _ from 'lodash';\n\nconst BoxSubInline = (props: any) => {\n  const { children, title, hasBackground = true, description, tooltip, fieldCol, labelCol, labelWidth, displayType, ...rest } = props;\n\n  let _tooltip: any = null;\n  let _labelCol: any = { span: 3 };\n  let _fieldCol = { flex: 1 }\n\n  if (description) {\n    _tooltip = { title: description };\n  }\n  if (tooltip) {\n    _tooltip = tooltip;\n  }\n\n  if (labelWidth) {\n    _labelCol = { flex : labelWidth + 'px' };\n  }\n\n  if (labelCol) {\n    _labelCol = labelCol;\n  }\n\n  if (fieldCol) {\n    _fieldCol = fieldCol;\n  }\n\n  return (\n    <Form.Item\n      {...rest}\n      className={classnames('fr-obj-subinline', {\n        'fr-obj-subinline-label-hidden': !title,\n        'fr-obj-subinline-background': hasBackground\n      })}\n      label={title || 'notitle'}\n      labelCol={_labelCol}\n      wrapperCol={_fieldCol}\n      tooltip={_tooltip}\n    >\n      {children}\n    </Form.Item>\n  );\n}\n\nexport default BoxSubInline;\n"
  },
  {
    "path": "packages/form-render/src/widgets/boxcard/index.less",
    "content": "@ant-prefix: ant;\n\n.fr-obj-card {\n  border-radius: 4px;\n  border-color: #f4f4f4;\n  margin-bottom: 24px !important;\n\n  .@{ant-prefix}-card-head {\n    border: none\n  }\n\n  .@{ant-prefix}-card-body {\n    padding: 12px 24px !important;\n  }\n\n  .fr-header-desc {\n    word-break: break-all;\n    color: rgba(0, 0, 0, .45);\n    margin-left: 6px;\n    font-weight: normal;\n  }\n}\n\n"
  },
  {
    "path": "packages/form-render/src/widgets/boxcard/index.tsx",
    "content": "import React from 'react';\nimport { Card } from 'antd';\nimport BoxPanel from '../components/PanelView';\n\nimport './index.less';\n\nconst BoxCard = ({ children, title, description }) => {\n  if (!title) {\n    return (\n      <BoxPanel>\n        {children}\n      </BoxPanel>\n    )\n  }\n  return (\n    <Card\n      className='fr-obj-card'\n      title={\n        <>\n          {title}\n          {description && (\n            <span className='fr-header-desc '>\n              {description}\n            </span>\n          )}\n        </>\n      }\n      // hoverable={true}\n    >\n      {children}\n    </Card>\n  );\n}\n\nexport default BoxCard;\n"
  },
  {
    "path": "packages/form-render/src/widgets/components/DatePicker/index.tsx",
    "content": "import dayjsGenerateConfig from 'rc-picker/es/generate/dayjs';\nimport generatePicker from 'antd/es/date-picker/generatePicker';\nimport 'antd/es/date-picker/style/index';\n\n// 修复 dayjs 生成配置中缺失的毫秒相关方法\nconst enhancedDayjsConfig = {\n  ...dayjsGenerateConfig,\n  // 添加缺失的毫秒相关方法\n  getMillisecond: (date: any) => date.millisecond(),\n  setMillisecond: (date: any, millisecond: number) => date.millisecond(millisecond),\n};\n\nconst DatePicker: any = generatePicker(enhancedDayjsConfig);\n\nexport default DatePicker;\n"
  },
  {
    "path": "packages/form-render/src/widgets/components/FButton/index.tsx",
    "content": "import React from 'react';\nimport { Button } from 'antd';\n\nconst HeaderTitle = (props: any) => {\n  const { icon, children, btnType, ...otherProps } = props;\n\n  let btnProps = { ...otherProps };\n  if (btnType === 'icon') {\n    btnProps.icon = icon;\n    btnProps.size = 'small'\n  } else {\n    btnProps.children = children;\n  }\n\n  return (\n    <Button type='link' style={{ padding: 0 }} {...btnProps} />\n  );\n}\n\nexport default HeaderTitle\n"
  },
  {
    "path": "packages/form-render/src/widgets/components/HeaderTitle/index.less",
    "content": ""
  },
  {
    "path": "packages/form-render/src/widgets/components/HeaderTitle/index.tsx",
    "content": "import React from 'react';\nimport './index.less';\n\nconst HeaderTitle = (props: any) => {\n  const { title, description } = props;\n\n  return (\n    <div className='fr-obj-header'>\n      <span className='fr-header-title'>{title}</span>\n      {description && <span className='fr-header-desc'>( {description} )</span>}\n    </div>\n  );\n}\n\nexport default HeaderTitle\n"
  },
  {
    "path": "packages/form-render/src/widgets/components/PanelView/index.less",
    "content": ".fr-panel-bordered {\n  border-radius: 4px;\n  border: 1px solid #f4f4f4;\n  padding: 52px 24px 0px 24px;\n  margin-bottom: 24px;\n}"
  },
  {
    "path": "packages/form-render/src/widgets/components/PanelView/index.tsx",
    "content": "import React from 'react';\nimport classnames from 'classnames'\nimport './index.less';\n\nconst PanelView = ({ children, bordered } : any) => {\n  return <div className={classnames('fr-panel', {'fr-panel-bordered' : bordered })}>{children}</div>;\n}\n\nexport default PanelView;\n"
  },
  {
    "path": "packages/form-render/src/widgets/components/TimePicker/index.tsx",
    "content": "import * as React from 'react';\nimport DatePicker from '../DatePicker';\n\nconst TimePicker: any = React.forwardRef((props: any, ref: any) => {\n  return <DatePicker {...props} picker='time' mode={undefined} ref={ref} />;\n});\n\nTimePicker.displayName = 'TimePicker';\n\nTimePicker.RangePicker = React.forwardRef((props: any, ref: any) => {\n  return <DatePicker.RangePicker {...props} picker='time' mode={undefined} ref={ref} />;\n});\n\nexport default TimePicker;"
  },
  {
    "path": "packages/form-render/src/widgets/fields/checkbox/index.tsx",
    "content": "import React from 'react';\nimport { Checkbox } from 'antd';\nimport withFieldWrap from '../../utils/withFieldWrap';\n\ninterface Props {\n  title: string;\n  [key: string]: any;\n}\n\nconst CheckBox: React.FC<Props> = ({ title, ...rest }) => {\n  return (\n    <>\n      <Checkbox {...rest} />\n      <span style={{ marginLeft: '12px' }}>{title}</span>\n    </>\n  );\n}\n\nexport default withFieldWrap(CheckBox);\n\n"
  },
  {
    "path": "packages/form-render/src/widgets/fields/checkboxes/index.tsx",
    "content": "import React from 'react';\nimport { Checkbox, Space } from 'antd';\nimport withFieldWrap from '../../utils/withFieldWrap';\n\ninterface Option {\n  label: string;\n  value: string;\n  disabled?: boolean\n}\n\ninterface Props {\n  direction: 'row' | 'column';\n  options: Option[];\n  [key: string]: any\n}\nconst Checkboxes: React.FC<Props> = (props) => {\n  const { direction = 'row', options = [], ...rest } = props\n\n  if (direction === 'column') {\n    return <Checkbox.Group {...rest} >\n      <Space direction='vertical'>\n        {\n          options.map((item: Option) => {\n            const { value, label, ...rest } = item\n            return <Checkbox key={value} value={value} {...rest}>{label}</Checkbox>\n          })\n        }\n      </Space>\n    </Checkbox.Group>\n  }\n\n  return <Checkbox.Group {...rest} options={options} />;\n};\n\nexport default withFieldWrap(Checkboxes);\n"
  },
  {
    "path": "packages/form-render/src/widgets/fields/color/alphahexMap.ts",
    "content": "export default {\n  '1.00': 'FF',\n  0.99: 'FC',\n  0.98: 'FA',\n  0.97: 'F7',\n  0.96: 'F5',\n  0.95: 'F2',\n  0.94: 'F0',\n  0.93: 'ED',\n  0.92: 'EB',\n  0.91: 'E8',\n  '0.90': 'E6',\n  0.89: 'E3',\n  0.88: 'E0',\n  0.87: 'DE',\n  0.86: 'DB',\n  0.85: 'D9',\n  0.84: 'D6',\n  0.83: 'D4',\n  0.82: 'D1',\n  0.81: 'CF',\n  '0.80': 'CC',\n  0.79: 'C9',\n  0.78: 'C7',\n  0.77: 'C4',\n  0.76: 'C2',\n  0.75: 'BF',\n  0.74: 'BD',\n  0.73: 'BA',\n  0.72: 'B8',\n  0.71: 'B5',\n  '0.70': 'B3',\n  0.69: 'B0',\n  0.68: 'AD',\n  0.67: 'AB',\n  0.66: 'A8',\n  0.65: 'A6',\n  0.64: 'A3',\n  0.63: 'A1',\n  0.62: '9E',\n  0.61: '9C',\n  '0.60': '99',\n  0.59: '96',\n  0.58: '94',\n  0.57: '91',\n  0.56: '8F',\n  0.55: '8C',\n  0.54: '8A',\n  0.53: '87',\n  0.52: '85',\n  0.51: '82',\n  '0.50': '80',\n  0.49: '7D',\n  0.48: '7A',\n  0.47: '78',\n  0.46: '75',\n  0.45: '73',\n  0.44: '70',\n  0.43: '6E',\n  0.42: '6B',\n  0.41: '69',\n  '0.40': '66',\n  0.39: '63',\n  0.38: '61',\n  0.37: '5E',\n  0.36: '5C',\n  0.35: '59',\n  0.34: '57',\n  0.33: '54',\n  0.32: '52',\n  0.31: '4F',\n  '0.30': '4D',\n  0.29: '4A',\n  0.28: '47',\n  0.27: '45',\n  0.26: '42',\n  0.25: '40',\n  0.24: '3D',\n  0.23: '3B',\n  0.22: '38',\n  0.21: '36',\n  '0.20': '33',\n  0.19: '30',\n  0.18: '2E',\n  0.17: '2B',\n  0.16: '29',\n  0.15: '26',\n  0.14: '24',\n  0.13: '21',\n  0.12: '1F',\n  0.11: '1C',\n  '0.10': '1A',\n  0.09: '17',\n  0.08: '14',\n  0.07: '12',\n  0.06: '0F',\n  0.05: '0D',\n  0.04: '0A',\n  0.03: '08',\n  0.02: '05',\n  0.01: '03',\n  '0.00': '00'\n};\n"
  },
  {
    "path": "packages/form-render/src/widgets/fields/color/index.less",
    "content": ".fr-color-picker {\n  width: 100%;\n  display: flex;\n  flex-direction: row;\n  align-items: center;\n  color: #666;\n}\n\n.fr-color-picker .rc-color-picker-trigger {\n  margin-right: 12px;\n  height: 32px;\n  width: 60px;\n  border: 1px solid #e5e5e5;\n}\n\n.fr-color-picker > p {\n  margin: 0;\n  font-size: 14px;\n  line-height: 28px;\n}\n\n.fr-color-picker .rc-color-picker-wrap {\n  display: flex;\n}"
  },
  {
    "path": "packages/form-render/src/widgets/fields/color/index.tsx",
    "content": "import React from 'react';\nimport { Input } from 'antd';\nimport ColorPicker from 'rc-color-picker';\nimport alphaHexMap from './alphahexMap';\nimport 'rc-color-picker/assets/index.css';\nimport './index.less';\n\n// Exp: '#ffffffA6' => algha: 65\nconst getAlphaFromHex = (hex = '#ffffff') => {\n  const alphaHex = hex.slice(7);\n  let alpha = 100;\n\n  for (const key in alphaHexMap) {\n    if (alphaHexMap[key] === alphaHex.toUpperCase()) {\n      alpha = Number(key) * 100;\n    }\n  }\n  return alpha;\n};\n\nconst Color = (props: any) => {\n  const { className, schema, disabled, disabledAlpha, readOnly, value, onChange, style } = props;\n  \n  const onPickerChange = (ev: any) => {\n    if (disabled || readOnly) {\n      return;\n    }\n    const alphaHex = alphaHexMap[(ev.alpha / 100).toFixed(2)];\n    const hex = ev.color + (ev.alpha === 100 ? '' : alphaHex);\n    onChange(hex);\n  };\n\n  const onInputChange = (ev: any) => {\n    onChange(ev.target.value);\n  };\n\n\n  return (\n    <div className='fr-color-picker' style={style}>\n      {(disabled || readOnly)? (\n        <span className='rc-color-picker-trigger' style={{ backgroundColor: value || '#ffffff' }} />\n      ): (\n        <ColorPicker\n          type={schema?.format}\n          animation='slide-up'\n          color={(value && value.slice(0, 7)) || '#ffffff'}\n          alpha={getAlphaFromHex(value)}\n          enableAlpha={!disabledAlpha}\n          onChange={onPickerChange}\n          disabled={true}\n        />\n      )}\n      {readOnly ? (\n        <span>{value || '#ffffff'}</span>\n      ) : (\n        <Input\n          className={className}\n          placeholder='#ffffff'\n          disabled={disabled}\n          value={value}\n          onChange={onInputChange}\n        />\n      )}\n    </div>\n  );\n}\n\nexport default Color;\n\n\n"
  },
  {
    "path": "packages/form-render/src/widgets/fields/date/index.tsx",
    "content": "\nimport React, { useMemo } from 'react';\nimport dayjs from 'dayjs';\nimport quarterOfYear from 'dayjs/plugin/quarterOfYear';\nimport { getFormat, transformDateValue } from '../../utils';\nimport DatePicker from '../../components/DatePicker';\nimport withFieldWrap from '../../utils/withFieldWrap';\n\n\ndayjs.extend(quarterOfYear);\n\nconst DateCmpt = ({ onChange, format, value, style, ...rest }) => {\n  const dateFormat = getFormat(format);\n  \n  const valueObj = useMemo(() => {\n    return transformDateValue(value, format, dateFormat);\n  }, [value]);\n\n  const handleChange = (dateValue: any, dateString: string) => {\n    let newValue = dateString;\n    if (format === 'week' || format === 'quarter') {\n      newValue = dayjs(dateValue).format(dateFormat);\n    }\n    onChange(newValue);\n  };\n\n  const dateParams: any = {\n    value: valueObj,\n    style: { width: '100%', ...style },\n    onChange: handleChange,\n  };\n\n  // TODO: format 是在 options 里自定义的情况，是否要判断一下要不要 showTime\n  if (format === 'dateTime') {\n    dateParams.showTime = true;\n  }\n\n  if (['week', 'month', 'quarter', 'year'].indexOf(format) > -1) {\n    dateParams.picker = format;\n  }\n\n  if (dateFormat === format) {\n    dateParams.format = format;\n  }\n\n  return <DatePicker {...dateParams} {...rest} />;\n};\n\nexport default withFieldWrap(DateCmpt);\n"
  },
  {
    "path": "packages/form-render/src/widgets/fields/dateRange/index.tsx",
    "content": "/**\n * Updated by Tw93 on 2019-12-08.\n * 日历多选组件\n */\nimport React, { useMemo } from 'react';\nimport dayjs from 'dayjs';\nimport quarterOfYear from 'dayjs/plugin/quarterOfYear';\nimport DatePicker from '../../components/DatePicker';\nimport { getFormat, transformDateValue } from '../../utils';\nimport withFieldWrap from '../../utils/withFieldWrap';\n\ndayjs.extend(quarterOfYear);\nconst { RangePicker } = DatePicker;\n\nconst DateRange = ({ onChange, format, value, style, ...rest }) => {\n  const dateFormat = getFormat(format);\n\n  const valueObj = useMemo(() => {\n    if (!value) {\n      return value;\n    }\n    return value.map((item: any) => transformDateValue(item, format, dateFormat));\n  }, [value]);\n\n\n  const handleChange = (val: any[], _stringList: any[]) => {\n    let stringList = _stringList;\n    if (['week', 'quarter'].includes(format)) {\n      stringList = (val || []).map((item: any) => dayjs(item).format(dateFormat));\n    }\n\n    const isPass = stringList.every(item => !!item);\n\n    if (!isPass) {\n      stringList = null;\n    }\n\n    onChange(stringList);\n  };\n\n  let dateParams: any = {\n    value: valueObj,\n    style: { width: '100%', ...style },\n    onChange: handleChange,\n  };\n\n  // TODO: format是在options里自定义的情况，是否要判断一下要不要showTime\n  if (format === 'dateTime') {\n    dateParams.showTime = true;\n  }\n\n  if (['week', 'month', 'quarter', 'year'].indexOf(format) > -1) {\n    dateParams.picker = format;\n  }\n\n  dateParams = { ...dateParams, ...rest };\n\n  if (dateFormat === format) {\n    dateParams.format = format;\n  }\n\n  return <RangePicker {...dateParams} />;\n};\n\nexport default withFieldWrap(DateRange);\n"
  },
  {
    "path": "packages/form-render/src/widgets/fields/html/index.tsx",
    "content": "import { Image } from 'antd';\nimport React from 'react';\n\nexport default function html({ value, checked, options, schema = {} } : any) {\n  let __html = '-';\n\n  if (schema.type === 'boolean') {\n    __html = (value === true || checked === true) ? '✔' : '✘';\n  } else if (options?.length > 0) {\n    if (['string', 'number'].indexOf(typeof value) > -1) {\n      const item = options.find((item: any) => item.value === value);\n      __html = item?.label || '-';\n\n    } else if (Array.isArray(value)) {\n      let idxStr = '-';\n      value.forEach(v => {\n        const item = options.find(item => item.value === v);\n        const name = item.label;\n        if (name) {\n          idxStr += ',' + name;\n        }\n      });\n      __html = idxStr.replace('-,', '');\n    }\n  } else if (typeof value === 'number') {\n    __html = String(value);\n  } else if (typeof value === 'string') {\n    __html = value;\n  } else if (\n    schema.type === 'range' &&\n    Array.isArray(value) &&\n    value[0] &&\n    value[1]\n  ) {\n    __html = `${value[0]} - ${value[1]}`;\n  } else if (value && ['number', 'string'].indexOf(typeof value) === -1) {\n    __html = JSON.stringify(value);\n  }\n\n  if (schema.format === 'image') {\n    return (\n      <Image\n        height={56}\n        src={value}\n        {...schema.imageProps}\n      />\n    );\n  }\n  return <div dangerouslySetInnerHTML={{ __html }} />;\n}\n"
  },
  {
    "path": "packages/form-render/src/widgets/fields/imageInput/index.less",
    "content": ".fr-preview-image {\n  width: 160px;\n}\n\n.fr-preview {\n  position: relative;\n  cursor: pointer;\n}"
  },
  {
    "path": "packages/form-render/src/widgets/fields/imageInput/index.tsx",
    "content": "import React, { useContext } from 'react';\nimport { PictureOutlined } from '@ant-design/icons';\nimport { Input, Popover, ConfigProvider } from 'antd';\nimport { translation } from '../../utils';\nimport withFieldWrap from '../../utils/withFieldWrap';\nimport './index.less';\n\nconst DEFAULT_IMG =\n  'https://img.alicdn.com/tfs/TB14tSiKhTpK1RjSZFKXXa2wXXa-354-330.png';\n\ninterface PreviewNodeProps {\n  value: string;\n}\n\nconst PreviewNode = ({ value }: PreviewNodeProps) => {\n  const configCtx = useContext(ConfigProvider.ConfigContext);\n  const t = translation(configCtx);\n  \n  return (\n    <Popover\n      content={\n        <img\n          src={value || DEFAULT_IMG}\n          alt={t('img_src_error')}\n          className='fr-preview-image'\n        />\n      }\n      className='fr-preview'\n      placement='bottom'\n    >\n      <PictureOutlined />\n    </Popover>\n  );\n};\n\ninterface ImageInputProps {\n  value: string;\n}\n\nconst ImageInput = ({ value, ...rest }: ImageInputProps) => {\n  return (\n    <Input value={value} addonAfter={<PreviewNode value={value} />} {...rest} />\n  );\n}\n\nexport default withFieldWrap(ImageInput)\n\n\n\n"
  },
  {
    "path": "packages/form-render/src/widgets/fields/input/index.tsx",
    "content": "import { Input } from 'antd';\nimport withFieldWrap from '../../utils/withFieldWrap';\n\nexport default withFieldWrap(Input);"
  },
  {
    "path": "packages/form-render/src/widgets/fields/number/index.tsx",
    "content": "import { InputNumber } from 'antd';\nimport withFieldWrap from '../../utils/withFieldWrap';\n\nexport default withFieldWrap(InputNumber);"
  },
  {
    "path": "packages/form-render/src/widgets/fields/percentSlider/index.tsx",
    "content": "import * as React from 'react';\nimport { InputNumber, Slider } from 'antd';\n\ninterface Props {\n  schema: {\n    max?: number;\n    min?: number;\n    step?: number;\n  };\n  options?: {\n    hideNumber?: boolean;\n  };\n  value: string;\n  onChange: (value: string) => void;\n  disabled?: boolean;\n  readonly?: boolean;\n}\n\nconst PercentSlider: React.FC<Props> = (p) => {\n  const { max, min, step } = p.schema;\n  let setting: { max?: number; min?: number; step?: number } = {};\n  if (max || max === 0) {\n    setting = { max };\n  }\n\n  if (min || min === 0) {\n    setting = { ...setting, min };\n  }\n\n  if (step) {\n    setting = { ...setting, step };\n  }\n\n  let hideNumber = false;\n  if (p.options && p.options.hideNumber) {\n    hideNumber = true;\n  }\n\n  const isPercent = (string: string): boolean =>\n    typeof string === 'string' && string.endsWith('%');\n\n  let numberValue = 100;\n  if (isPercent(p.value)) {\n    try {\n      numberValue = Number(p.value.split('%')[0]);\n      if (Number.isNaN(numberValue)) numberValue = 100;\n    } catch (error) {}\n  }\n\n  const handleChange = (newNumber: number | undefined): void => {\n    const a = newNumber + '%';\n    p.onChange(a);\n  };\n\n  const renderNumber = p.readonly ? (\n    <span style={{ width: '80px' }}>\n      {p.value === (undefined || '') ? '-' : p.value + '%'}\n    </span>\n  ) : (\n    <InputNumber\n      {...p.options}\n      {...setting}\n      style={{ width: '80px' }}\n      value={numberValue}\n      disabled={p.disabled}\n      onChange={handleChange}\n      formatter={(value) => `${value}%`}\n      parser={(value) => Number(value.replace('%', ''))}\n    />\n  );\n\n  return (\n    <div className=\"fr-slider\">\n      <Slider\n        style={{ flexGrow: 1, marginRight: hideNumber ? 0 : 12 }}\n        {...setting}\n        onChange={handleChange}\n        max={100}\n        tooltip={{ formatter: (v) => v + '%' }}\n        value={numberValue || 100}\n        disabled={p.disabled || p.readonly}\n      />\n      {hideNumber ? null : renderNumber}\n    </div>\n  );\n};\n\nexport default PercentSlider;\n\n "
  },
  {
    "path": "packages/form-render/src/widgets/fields/radio/index.tsx",
    "content": "import React from 'react';\nimport { Radio, Space } from 'antd';\nimport withFieldWrap from '../../utils/withFieldWrap';\n\ninterface Option {\n  label: string;\n  value: string;\n  disabled?: boolean;\n}\n\ninterface Props {\n  direction: 'row' | 'column',\n  options: Option[]\n  [key: string]: any\n}\n\nconst RadioComp = (props: Props) => {\n  const { direction = 'row', options = [], ...rest } = props\n\n  if (direction === 'column') {\n    return <Radio.Group {...rest} >\n      <Space direction='vertical'>\n        {\n          options.map((item: Option) => {\n            const { value, label, ...rest } = item\n            return <Radio key={value} value={value} {...rest}>{label}</Radio>\n          })\n        }\n      </Space>\n    </Radio.Group>\n  }\n\n  return <Radio.Group {...rest} options={options} />;\n};\n\nexport default withFieldWrap(RadioComp);\n\n\n"
  },
  {
    "path": "packages/form-render/src/widgets/fields/rate/index.tsx",
    "content": "import { Rate } from 'antd';\nimport withFieldWrap from '../../utils/withFieldWrap';\n\nexport default withFieldWrap(Rate);"
  },
  {
    "path": "packages/form-render/src/widgets/fields/select/index.tsx",
    "content": "import { Select } from 'antd';\nimport withFieldWrap from '../../utils/withFieldWrap';\n\nexport default withFieldWrap(Select);"
  },
  {
    "path": "packages/form-render/src/widgets/fields/slider/index.less",
    "content": ".fr-slider {\n  display: flex;\n  width: 100%;\n  align-items: center;\n}"
  },
  {
    "path": "packages/form-render/src/widgets/fields/slider/index.tsx",
    "content": "/**\n * 滑动输入组件\n */\nimport React from 'react';\nimport { InputNumber, Slider } from 'antd';\nimport withFieldWrap from '../../utils/withFieldWrap';\nimport './index.less';\ninterface SliderWithNumberProps {\n  schema: {\n    max?: number;\n    min?: number;\n    step?: number;\n  };\n  value: number;\n  onChange: (value: number) => void;\n  hideInput?: boolean;\n  inputProps?: any;\n  style?: React.CSSProperties;\n}\n\nconst SliderWithNumber: React.FC<SliderWithNumberProps> = ({\n  schema,\n  value,\n  onChange,\n  hideInput,\n  inputProps,\n  style,\n  ...rest\n}) => {\n  const { max, min, step } = schema;\n  \n  let setting = {};\n  if (max || max === 0) {\n    setting = { max };\n  }\n\n  if (min || min === 0) {\n    setting = { ...setting, min };\n  }\n\n  if (step) {\n    setting = { ...setting, step };\n  }\n\n  return (\n    <div className='fr-slider' style={style}>\n      <Slider\n        style={{ flexGrow: 1, marginRight: hideInput ? 0 : 12 }}\n        {...setting}\n        onChange={onChange}\n        value={typeof value === 'number' ? value : min || 0}\n        {...rest}\n      />\n      {hideInput ? null : (\n        <InputNumber\n          {...setting}\n          {...inputProps}\n          style={{ width: '90px' }}\n          value={value}\n          onChange={onChange}\n        />\n      )}\n    </div>\n  );\n}\n\nexport default withFieldWrap(SliderWithNumber, ['addons', 'dependValues']);\n\n"
  },
  {
    "path": "packages/form-render/src/widgets/fields/switch/index.tsx",
    "content": "import { Switch } from 'antd';\nimport withFieldWrap from '../../utils/withFieldWrap';\n\nexport default withFieldWrap(Switch);"
  },
  {
    "path": "packages/form-render/src/widgets/fields/textArea/index.tsx",
    "content": "import React from 'react';\nimport { Input } from 'antd';\nimport withFieldWrap from '../../utils/withFieldWrap';\n\nconst TextArea = (props: any) => {\n  let finalProps = {\n    autoSize: {\n      minRows: 3,\n    },\n    ...props,\n  };\n  if (finalProps.rows) delete finalProps.autoSize;\n\n  return <Input.TextArea {...finalProps} />;\n};\n\n\nexport default withFieldWrap(TextArea)"
  },
  {
    "path": "packages/form-render/src/widgets/fields/time/index.tsx",
    "content": "import dayjs from 'dayjs';\nimport React from 'react';\n\nimport TimePicker from '../../components/TimePicker';\nimport { getFormat } from '../../utils';\nimport withFieldWrap from '../../utils/withFieldWrap';\n\nconst Time = ({ onChange, format ='time', value, style, ...rest }) => {\n  const timeFormat = getFormat(format);\n  const _value = value ? dayjs(value, timeFormat) : undefined;\n\n  const handleChange = (_: any, valueStr: string) => {\n    onChange(valueStr);\n  };\n\n  const timeParams: any = {\n    value: _value,\n    style: { width: '100%', ...style },\n    onChange: handleChange,\n    format: timeFormat,\n    ...rest,\n  };\n\n  return <TimePicker {...timeParams} />;\n};\nexport default withFieldWrap(Time);"
  },
  {
    "path": "packages/form-render/src/widgets/fields/timeRange/index.tsx",
    "content": "/**\n * Updated by Tw93 on 2019-12-08.\n * 日历多选组件\n */\nimport TimePicker from '../../components/TimePicker';\nimport dayjs from 'dayjs';\nimport React from 'react';\nimport { getFormat } from '../../utils';\n\nconst { RangePicker } = TimePicker;\n\nconst TimeRange = ({ onChange, format='time', value, style, schema }) => {\n  const timeFormat = getFormat(format);\n  let [start, end] = Array.isArray(value) ? value : [];\n\n  const _value =\n    start && end ? [dayjs(start, schema?.props?.format || timeFormat), dayjs(end, schema?.props?.format || timeFormat)] : [];\n\n  const handleChange = (_: any, stringList: any) => {\n    const emptyList1 = stringList[0] === '' || stringList[1] === '';\n    const emptyList2 =\n      stringList[0] === undefined || stringList[1] === undefined;\n    if (emptyList1 || emptyList2) {\n      onChange(undefined);\n    } else {\n      onChange(stringList);\n    }\n  };\n\n  const timeParams = {\n    style: { width: '100%', ...style },\n    value: _value,\n    onChange: handleChange,\n    ...(schema.props || {}),\n  };\n\n  return <RangePicker {...timeParams} />;\n};\n\nexport default TimeRange;\n"
  },
  {
    "path": "packages/form-render/src/widgets/fields/treeSelect/index.tsx",
    "content": "import { TreeSelect } from 'antd';\nimport withFieldWrap from '../../utils/withFieldWrap';\n\nexport default withFieldWrap(TreeSelect);"
  },
  {
    "path": "packages/form-render/src/widgets/fields/upload/index.less",
    "content": "@ant-prefix: ant;\n\n.fr-upload-mod,\n.fr-upload-file {\n  display: flex;\n}\n.fr-upload-mod {\n  align-items: center;\n}\n.fr-upload-mod .fr-upload-preview {\n  margin: 0 12px;\n}\n.fr-upload-file .@{ant-prefix}-upload-list-item {\n  margin: 5px 0 0 8px;\n}\n.fr-upload-file .@{ant-prefix}-upload-list-item-name {\n  margin-right: 6px;\n}\n.fr-upload-file .@{ant-prefix}-upload-list-item-info {\n  cursor: pointer;\n}\n.fr-upload-file .next-upload-list-text .next-upload-list-item-done,\n.fr-upload-file .next-upload-list-text .next-upload-list-item .next-icon {\n  height: 28px;\n  line-height: 28px;\n  margin-left: 12px;\n}\n\n.fr-upload-file .next-upload-list-item-name-wrap {\n  margin-top: -4px;\n}"
  },
  {
    "path": "packages/form-render/src/widgets/fields/upload/index.tsx",
    "content": "import React, { useContext } from 'react';\nimport { Button, message, Upload, ConfigProvider } from 'antd';\nimport { ButtonProps } from 'antd/es/button';\nimport { UploadOutlined } from '@ant-design/icons';\nimport { get } from 'lodash-es';\nimport { translation } from '../../utils';\n\nimport './index.less';\n\ninterface Props {\n  action: any;\n  value: string;\n  onChange: any;\n  uploadProps: any;\n  buttonProps: ButtonProps;\n  schema: any;\n}\n\nconst FrUpload = ({\n  action,\n  value,\n  onChange,\n  uploadProps,\n  buttonProps,\n  schema,\n}: Props) => {\n  const configCtx = useContext(ConfigProvider.ConfigContext);\n  const t = translation(configCtx);\n\n  const props = {\n    name: 'file',\n    type: 'file',\n    action, // 旧的兼容\n    onChange(info: any) {\n      if (info.file.status === 'done') {\n        message.success(`${info.file.name} ${t('upload_success')}`);\n        const path = get(schema, 'props.path', '');\n        const url = path\n          ? get(info.file.response, path)\n          : info.file.response.url;\n        onChange(url);\n      } else if (info.file.status === 'error') {\n        message.error(`${info.file.name} ${t('upload_fail')}`);\n      }\n    },\n    onRemove() {\n      onChange('');\n    },\n    ...uploadProps,\n  };\n\n  const defaultBtnProps = {\n    icon: <UploadOutlined />,\n    children: t('upload'),\n  };\n\n  const btnProps = {\n    ...defaultBtnProps,\n    ...buttonProps,\n  };\n\n  return (\n    <div className='fr-upload-mod'>\n      <Upload {...props} className='fr-upload-file'>\n        <Button {...btnProps} />\n      </Upload>\n      {value && (\n        <a\n          href={value}\n          target='_blank'\n          rel='noopener noreferrer'\n          className='fr-upload-preview'\n        >\n          {t('uploaded_address')}\n        </a>\n      )}\n    </div>\n  );\n}\n\nexport default FrUpload;\n"
  },
  {
    "path": "packages/form-render/src/widgets/fields/urlInput/index.tsx",
    "content": "import React, { useContext } from 'react';\nimport { Input, ConfigProvider } from 'antd';\nimport { isUrl, translation } from '../../utils';\nimport withFieldWrap from '../../utils/withFieldWrap';\ninterface UrlNodeProps {\n  value: string;\n  addonText?: string;\n}\n\nconst UrlNode: React.FC<UrlNodeProps> = (props) => {\n  const configCtx = useContext(ConfigProvider.ConfigContext);\n  const t = translation(configCtx);\n\n  const { value, addonText = t('test_src')} = props;\n  const useUrl = isUrl(value);\n\n  if (useUrl) {\n    return (\n      <a target=\"_blank\" href={value}>\n        {addonText}\n      </a>\n    );\n  }\n\n  return <div>{addonText}</div>;\n};\n\ninterface UrlInputProps {\n  value?: string;\n  prefix?: string;\n  suffix?: string;\n  addonText?: string;\n  onChange?: (value: string) => void;\n}\n\nconst UrlInput: React.FC<UrlInputProps> = ({\n  value,\n  prefix,\n  suffix,\n  addonText,\n  onChange,\n  ...rest\n}) => {\n  let _value = value || '';\n\n  if (prefix) {\n    _value = _value.replace(prefix, '');\n  }\n\n  if (suffix) {\n    _value = _value.replace(suffix, '');\n  }\n\n  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n    let _value = e.target.value;\n    if (!_value) {\n      onChange?.(_value);\n      return;\n    }\n    if (prefix) {\n      _value = prefix + _value;\n    }\n    if (suffix) {\n      _value = _value + suffix;\n    }\n    onChange?.(_value);\n  };\n\n  return (\n    <Input\n      value={_value}\n      prefix={prefix}\n      suffix={suffix}\n      onChange={handleChange}\n      addonAfter={\n        <UrlNode\n          value={value}\n          addonText={addonText}\n        />\n      }\n      {...rest}\n    />\n  );\n}\n\nexport default withFieldWrap(UrlInput);\n\n"
  },
  {
    "path": "packages/form-render/src/widgets/index.ts",
    "content": "export { default as Input } from './fields/input';\nexport { default as InputNumber } from './fields/number';\nexport { default as TextArea } from './fields/textArea';\n\nexport { default as Select } from './fields/select';\nexport { default as MultiSelect } from './fields/select';\nexport { default as Switch } from './fields/switch';\nexport { default as Radio } from './fields/radio';\nexport { default as Rate } from './fields/rate';\nexport { default as TreeSelect } from './fields/treeSelect';\nexport { default as Checkbox } from './fields/checkbox';\nexport { default as Checkboxes } from './fields/checkboxes';\n\nexport { default as Color } from './fields/color';\nexport { default as DatePicker } from './fields/date';\nexport { default as DateRange } from './fields/dateRange';\nexport { default as TimePicker } from './fields/time';\nexport { default as TimeRange } from './fields/timeRange';\nexport { default as ImageInput } from './fields/imageInput';\nexport { default as UrlInput } from './fields/urlInput';\nexport { default as Slider } from './fields/slider';\nexport { default as Upload } from './fields/upload';\nexport { default as Html } from './fields/html';\nexport { default as PercentSlider } from './fields/percentSlider';\n\nexport { default as Card } from './boxcard';\nexport { default as Collapse } from './boxCollapse';\nexport { default as SubInline } from './boxSubInline';\nexport { default as LineTitle } from './boxLineTitle';\n\nexport { default as SimpleList } from './listSimple';\nexport { default as CardList } from './listCard';\nexport { default as TableList } from './listTable';\nexport { default as DrawerList } from './listDrawer';\nexport { default as VirtualList } from './listVirtual';\nexport { default as TabList } from './listTab';\n\nexport { default as VoidTitle } from './voidTitle';\nexport { default as ErrorSchema } from './ErrorSchema';\n"
  },
  {
    "path": "packages/form-render/src/widgets/listCard/index.less",
    "content": ".fr-list-card {\n  margin-bottom: 24px;\n  \n  .fr-list-item {\n    display: flex;\n    align-items: flex-start;\n    position: relative;\n  }\n\n  .fr-list-item-operate {\n    padding: 0 20px;\n    height: 32px;\n    gap: 0 !important;\n  }\n\n  .fr-list-item-operate-fixed {\n    position: absolute;\n    right: 8px;\n  }\n\n  .fr-list-add-btn {\n    padding: 0 8px;\n  }\n}"
  },
  {
    "path": "packages/form-render/src/widgets/listCard/index.tsx",
    "content": "import React from 'react';\nimport { Button, Space, Popconfirm, Divider } from 'antd';\nimport { PlusOutlined, CloseOutlined, ArrowUpOutlined, ArrowDownOutlined, CopyOutlined } from '@ant-design/icons';\nimport classnames from 'classnames';\nimport FButton from '../components/FButton';\nimport { cloneDeep } from 'lodash-es';\nimport './index.less';\n\nconst getOperateFixed = (schema: any) => {\n  let fixed = true;\n\n  if (schema?.items?.props?.extra) {\n    fixed = false;\n  }\n  return fixed;\n};\n\nconst getOperateStyle = (schema: any) => {\n  let style: any = {};\n  const widgetName = schema?.items?.theme || schema?.items?.widget || 'collapse';\n  if (['card', 'collapse', 'lineTitle'].includes(widgetName) && !schema?.items?.props?.extra) {\n    style.top = '14px';\n    if (['lineTitle'].includes(widgetName)) {\n      style.top = '3px';\n      style.padding = 0;\n    }\n\n    if (!schema?.items?.title) {\n      style.right = '0';\n    }\n  }\n  return style;\n};\n\nconst CardList = (props: any) => {\n  const {\n    form,\n    schema,\n    fields,\n    rootPath,\n    renderCore,\n    hasBackground,\n    operateBtnType,\n\n    addBtnProps,\n    delConfirmProps,\n    copyBtnProps,\n    deleteBtnProps,\n    moveUpBtnProps,\n    moveDownBtnProps,\n\n    hideDelete,\n    hideCopy,\n    hideMove,\n    hideAdd,\n\n    addItem,\n    copyItem,\n    moveItem,\n    removeItem\n  } = props;\n\n  const handleCopy = (name: number) => {\n    const value = form.getFieldValue(rootPath.concat(name));\n    copyItem(value, name);\n  };\n\n  return (\n    <>\n      <div className={classnames('fr-list-card', { 'fr-list-card-background': hasBackground })}>\n        {fields.map(({ key, name }) => {\n          const length = fields.length;\n          const newSchema = cloneDeep(schema);\n          if (newSchema.items.title && typeof newSchema.items.title === 'string') {\n            newSchema.items.title += ` ${name + 1}`;\n          }\n          return (\n            <div key={key} className='fr-list-item'>\n              <div style={{ width: 0, flex: 1 }}>\n                {renderCore({ schema: newSchema, parentPath: [name], rootPath: [...rootPath, name] })}\n              </div>\n              <Space\n                className={classnames('fr-list-item-operate', { 'fr-list-item-operate-fixed': getOperateFixed(schema) })}\n                style={getOperateStyle(schema)}\n                split={operateBtnType !== 'icon' && <Divider type='vertical' />}\n              >\n                {!hideMove && (\n                  <>\n                    <FButton\n                      disabled={name === 0}\n                      onClick={() => moveItem(name, name - 1)}\n                      icon={<ArrowUpOutlined />}\n                      {...moveUpBtnProps}\n                    />\n                    <FButton\n                      disabled={name === length - 1}\n                      onClick={() => moveItem(name, name + 1)}\n                      icon={<ArrowDownOutlined />}\n                      {...moveDownBtnProps}\n                    />\n                  </>\n                )}\n                {!hideDelete && (\n                  <Popconfirm\n                    onConfirm={() => removeItem(name)}\n                    {...delConfirmProps}\n                  >\n                    <FButton\n                      icon={<CloseOutlined />}\n                      {...deleteBtnProps}\n                    />\n                  </Popconfirm>\n                )}\n                {!hideCopy && (\n                  <FButton\n                    onClick={() => handleCopy(name)}\n                    icon={<CopyOutlined />}\n                    {...copyBtnProps}\n                  />\n                )}\n              </Space>\n            </div>\n          );\n        })}\n        {(!schema.max || fields.length < schema.max) && !hideAdd && (\n          <div className=\"fr-list-add-btn\">\n            <Button\n              {...addBtnProps}\n              onClick={() => addItem()}\n              icon={<PlusOutlined />}\n              block={fields.length > 0 ? true : false}\n            />\n          </div>\n        )}\n      </div>\n    </>\n  );\n}\n\nexport default CardList;"
  },
  {
    "path": "packages/form-render/src/widgets/listDrawer/drawerForm.tsx",
    "content": "import React, { useContext } from 'react';\nimport { Button, Drawer, Space, ConfigProvider } from 'antd';\nimport { translation } from '../utils';\n\nconst DrawerForm = (props: any) => {\n  const { children, onConfirm, onClose, ...ret } = props;\n\n  const configCtx = useContext(ConfigProvider.ConfigContext);\n  const t = translation(configCtx);\n\n  let extraProps: any = { ...ret, open: true };\n  if ((window as any).antdVersion === 'v4') {\n    extraProps = { ...ret, visible: true };\n  }\n\n  return (\n    <Drawer\n      width={600}\n      title={t('operate')}\n      {...extraProps}\n      onClose={onClose}\n      extra={\n        <Space>\n          <Button onClick={onClose}>{t('cancel')}</Button>\n          <Button type=\"primary\" onClick={onConfirm}>\n            {t('confirm')}\n          </Button>\n        </Space>\n      }\n    >\n      {children}\n    </Drawer>\n  );\n};\n\nexport default DrawerForm;\n"
  },
  {
    "path": "packages/form-render/src/widgets/listDrawer/index.less",
    "content": "@ant-prefix: ant;\n\n.fr-list-drawer {\n  margin-bottom: 24px;\n\n  &-table-header {\n    width: 100%;\n    display: flex;\n    justify-content: right;\n    align-items: center;\n    margin-bottom: 10px;\n  }\n\n  .@{ant-prefix}-form-item {\n    margin-bottom: 0;\n  }\n\n  .fr-list-item-operate {\n    gap: 0 !important;\n  }\n}"
  },
  {
    "path": "packages/form-render/src/widgets/listDrawer/index.tsx",
    "content": "import React, { useState, useRef } from 'react';\nimport { Space, Table, Form, Button, Popconfirm, Tooltip, Divider, Collapse } from 'antd';\nimport { ArrowDownOutlined, ArrowUpOutlined, PlusOutlined, InfoCircleOutlined, CloseOutlined, CopyOutlined } from '@ant-design/icons';\nimport type { FormListFieldData, FormListOperation, TableColumnsType } from 'antd';\nimport sortProperties from '../../models/sortProperties';\nimport FormDrawer from './drawerForm';\nimport FButton from '../components/FButton';\nimport './index.less';\n\ninterface Props {\n  schema: any;\n  fields: FormListFieldData[];\n  operation: FormListOperation;\n  prefix: string;\n  [key: string]: any\n};\n\nconst getTooltip = (tooltip: any) => {\n  if (!tooltip) {\n    return;\n  }\n\n  if (typeof tooltip === 'string') {\n    return { title: <span dangerouslySetInnerHTML={{ __html: tooltip }} /> };\n  }\n\n  return {\n    ...tooltip,\n    title: <span dangerouslySetInnerHTML={{ __html: tooltip.title }} />,\n  };\n};\n\nconst TableList: React.FC<Props> = (props: any) => {\n  const {\n    form,\n    schema,\n    fields,\n    rootPath,\n    renderCore,\n    readOnly,\n    widgets,\n    pagination,\n\n    operateBtnType,\n    addBtnProps,\n    delConfirmProps,\n    copyBtnProps,\n    deleteBtnProps,\n    moveUpBtnProps,\n    moveDownBtnProps,\n    actionColumnProps,\n    editorBtnProps,\n    drawerProps,\n\n    hideOperate,\n    hideDelete,\n    hideCopy,\n    hideMove,\n    hideAdd,\n    hideEdit,\n    hideColumnNestedObject,\n\n    operation,\n    addItem,\n    copyItem,\n    moveItem,\n    removeItem,\n    configContext,\n  } = props;\n\n  const { colHeaderText, ...otherActionColumnProps } = actionColumnProps;\n  const paginationConfig = {\n    size: 'small',\n    hideOnSinglePage: true,\n    ...pagination,\n  };\n  const columnSchema = schema?.items?.properties || {};\n\n  const [visible, setVisible] = useState(false);\n  const [itemData, setItemData] = useState(null);\n  const indexRef = useRef<any>(null);\n\n  const handleCopy = (name: number) => {\n    const value = form.getFieldValue(rootPath.concat(name));\n    copyItem(value, name);\n  };\n\n  const handleAdd = () => {\n    setVisible(true);\n    addItem();\n  };\n\n  const handleRepeal = () => {\n    if (!indexRef.current && indexRef.current !== 0) {\n      operation.remove(fields.length - 1);\n    } else {\n      form.setFieldValue([...rootPath, indexRef.current], itemData);\n    }\n    handleCloseDrawer();\n  };\n\n  const handleCloseDrawer = () => {\n    setItemData(null);\n    setVisible(false);\n    indexRef.current = null;\n  };\n\n  const columns: any = sortProperties(Object.entries(columnSchema))\n    .map(([dataIndex, item]) => {\n      const { required, title, tooltip, width, columnHidden } = item;\n      if (columnHidden) {\n        return null;\n      }\n\n      const tooltipProps = getTooltip(tooltip);\n      return {\n        dataIndex,\n        width,\n        title: (\n          <>\n            {required && (\n              <span style={{ color: 'red', marginRight: '3px' }}>*</span>\n            )}\n            <span>{title}</span>\n            {tooltipProps && (\n              <Tooltip placement=\"top\" {...tooltipProps}>\n                <InfoCircleOutlined style={{ marginLeft: 6 }} />\n              </Tooltip>\n            )}\n          </>\n        ),\n        render: (_, field) => {\n          const fieldSchema = {\n            type: 'object',\n            properties: {\n              [dataIndex]: {\n                ...columnSchema[dataIndex],\n                noStyle: true,\n                readOnly: true,\n              }\n            }\n          };\n          const fieldDataIndex = fieldSchema.properties[dataIndex];\n          const renderColumn = renderCore({\n            schema: fieldSchema,\n            parentPath: [field.name],\n            rootPath: [...rootPath, field.name],\n          });\n          if (\n            (fieldDataIndex?.type === 'array' &&\n              fieldDataIndex?.items?.type === 'object') ||\n            fieldDataIndex?.type === 'object'\n          ) {\n            if (hideColumnNestedObject === 'hide') {\n              return '-';\n            } else if (hideColumnNestedObject === 'collapse') {\n              return (\n                <Collapse\n                  ghost\n                  items={[\n                    {\n                      key: 'detail',\n                      label: '查看详情',\n                      children: renderColumn,\n                    },\n                  ]}\n                />\n              );\n            } else {\n              return renderColumn;\n            }\n          }\n\n          return renderColumn;\n        },\n      };\n    })\n    .filter(item => item);\n\n  if (!readOnly && !hideOperate) {\n    columns.push({\n      title: colHeaderText,\n      width: '190px',\n      fixed: 'right',\n      ...otherActionColumnProps,\n      render: (_, field) => (\n        <Form.Item>\n          <Space className='fr-list-item-operate' split={operateBtnType !== 'icon' && <Divider type='vertical'/>}>\n            {!hideMove && (\n              <>\n                <FButton\n                  disabled={field.name === 0}\n                  onClick={() => moveItem(field.name, field.name - 1)}\n                  icon={<ArrowUpOutlined/>}\n                  {...moveUpBtnProps}\n                />\n                <FButton\n                  disabled={field.name === fields.length - 1}\n                  onClick={() => moveItem(field.name, field.name + 1)}\n                  icon={<ArrowDownOutlined/>}\n                  {...moveDownBtnProps}\n                />\n              </>\n            )}\n            {!hideDelete && (\n              <Popconfirm\n                onConfirm={() => removeItem(field.name)}\n                {...delConfirmProps}\n              >\n                <FButton\n                  icon={<CloseOutlined/>}\n                  btnType={operateBtnType}\n                  {...deleteBtnProps}\n                />\n              </Popconfirm>\n            )}\n            {!hideCopy && (\n              <FButton\n                onClick={() => handleCopy(field.name)}\n                icon={<CopyOutlined/>}\n                {...copyBtnProps}\n              />\n            )}\n            {!hideEdit && (\n              <FButton\n                onClick={() => {\n                  setVisible(true);\n                  indexRef.current = field.name;\n                  setItemData(form.getFieldValue(rootPath.concat(field.name)));\n                }}\n                icon={<CopyOutlined/>}\n                {...editorBtnProps}\n              />\n            )}\n          </Space>\n        </Form.Item>\n      )\n    });\n  }\n\n  const drawerIndex = indexRef.current ?? (fields.length - 1);\n\n  const hanldeConfirm = () => {\n    const path = [...rootPath, drawerIndex]?.join('.');\n    form\n      .validateFields([path], { recursive: true })\n      .then(res => {\n        handleCloseDrawer();\n      })\n      .catch(error => {\n        console.log('表单校验错误', error);\n      });\n  };\n\n\n  return (\n    <div className='fr-list-drawer'>\n      <Table\n        size='middle'\n        dataSource={fields}\n        columns={columns}\n        style={{ marginBottom: '12px' }}\n        scroll={{ x: 'max-content' }}\n        pagination={paginationConfig}\n      />\n      {(!schema.max || fields.length < schema.max) && !hideAdd && (\n        <Button\n          icon={<PlusOutlined />}\n          onClick={handleAdd}\n          {...addBtnProps}\n        />\n      )}\n      {visible && (\n        <FormDrawer\n          {...drawerProps}\n          schema={schema}\n          data={itemData}\n          widgets={widgets}\n          configContext={configContext}\n          onClose={handleRepeal}\n          onConfirm={hanldeConfirm}\n        >\n          {renderCore({ schema: schema.items, parentPath: [drawerIndex], rootPath: [...rootPath, drawerIndex] })}\n        </FormDrawer>\n      )}\n    </div>\n  );\n}\n\nexport default TableList;\n"
  },
  {
    "path": "packages/form-render/src/widgets/listSimple/index.less",
    "content": ".fr-list-simple {\n  display: inline-block;\n\n  .fr-inline-field {\n    min-width: 220px;\n    margin-bottom: 24px !important;\n  }\n\n  .fr-list-item {\n    display: flex;\n    align-items: flex-start;\n    position: relative;\n  }\n\n  .fr-list-item-operate {\n    height: 32px;\n    gap: 0 !important;\n  }\n}\n\n.fr-list-simple-background {\n  padding: 24px 24px 0 24px;\n  background-color: #f6f6f6;\n  border-radius: 4px;\n\n  .add-btn {\n    margin-bottom: 24px;\n  }\n}\n\n.fr-list-simple-column .fr-list-item {\n  align-items: center;\n}"
  },
  {
    "path": "packages/form-render/src/widgets/listSimple/index.tsx",
    "content": "import React from 'react';\nimport { Button, Space, Popconfirm, Divider } from 'antd';\nimport { PlusOutlined, CloseOutlined, ArrowUpOutlined, ArrowDownOutlined, CopyOutlined } from '@ant-design/icons';\nimport classnames from 'classnames';\nimport FButton from '../components/FButton';\n\nimport './index.less';\n\nconst getHasBackground = (fields: any[], hasBackground: boolean) => {\n  let result = hasBackground;\n  if (fields.length === 0) {\n    result = false;\n  }\n  return result;\n}\n\nconst SimpleList = (props: any) => {\n  const {\n    form,\n    schema: _schema,\n    fields,\n    rootPath,\n    renderCore,\n    hasBackground,\n    operateBtnType,\n    addBtnProps,\n    delConfirmProps,\n    copyBtnProps,\n    deleteBtnProps,\n    moveUpBtnProps,\n    moveDownBtnProps,\n\n    hideDelete,\n    hideCopy,\n    hideMove,\n    hideAdd,\n\n    addItem,\n    copyItem,\n    moveItem,\n    removeItem,\n    temporary\n  } = props;\n\n  const schema = {..._schema, items: { ..._schema.items }};\n\n  if (!schema.items.displayType) {\n    schema.items.displayType = 'inline';\n    schema.items.inlineMode = true;\n  }\n\n  const handleCopy = (name: number) => {\n    const value = form.getFieldValue(rootPath.concat(name));\n    copyItem(value, name);\n  };\n\n  const isColumm = temporary.displayType === 'column';\n \n  return (\n    <div className={classnames('fr-list-simple', { 'fr-list-simple-background': getHasBackground(fields, hasBackground), 'fr-list-simple-column':isColumm})}>\n      {fields.map(({ key, name }) => {\n        const length = fields.length;\n        return (\n          <div key={key} className='fr-list-item'>\n            {renderCore({ schema, parentPath: [name], rootPath: [...rootPath, name] })}\n            <Space \n              className={classnames('fr-list-item-operate')}\n              split={operateBtnType !== 'icon' && <Divider type='vertical' />}\n            >\n              {!hideMove && (\n                <>\n                  <FButton \n                    disabled={name === 0}\n                    onClick={() => moveItem(name, name - 1)}\n                    icon={<ArrowUpOutlined/>}\n                    {...moveUpBtnProps}\n                  />\n                  <FButton \n                    disabled={name === length - 1}\n                    onClick={() => moveItem(name, name + 1)}\n                    icon={<ArrowDownOutlined/>}\n                    children='下移'\n                    {...moveDownBtnProps}\n                  />\n                </>\n              )}\n              {!hideDelete && (\n                <Popconfirm\n                  onConfirm={() => removeItem(name)}\n                  {...delConfirmProps}\n                >\n                  <FButton\n                    icon={<CloseOutlined/>}\n                    children='删除'\n                    btnType={operateBtnType}\n                    {...deleteBtnProps}\n                  />\n                </Popconfirm>\n              )}\n              {!hideCopy && (\n                <FButton \n                  onClick={() => handleCopy(name)}\n                  icon={<CopyOutlined/>}\n                  children='复制'\n                  {...copyBtnProps}\n                />\n              )}\n            </Space>\n\n          </div>\n        );\n      })}\n      {(!schema.max || fields.length < schema.max) && !hideAdd && (\n        <Button\n          className='add-btn'\n          icon={<PlusOutlined />}\n          onClick={() => addItem()}\n          block={fields.length > 0 ? true : false}\n          {...addBtnProps}\n        />\n      )}\n    </div>\n  );\n}\n\nexport default SimpleList;\n"
  },
  {
    "path": "packages/form-render/src/widgets/listTab/index.less",
    "content": ""
  },
  {
    "path": "packages/form-render/src/widgets/listTab/index.tsx",
    "content": "import React, { useState, useContext, useMemo, useEffect } from 'react';\nimport { Popconfirm, Tabs, ConfigProvider } from 'antd';\nimport { CloseOutlined } from '@ant-design/icons';\nimport type { FormListFieldData } from 'antd';\nimport { translation } from '../utils';\nimport './index.less';\n\ninterface ListTabProps {\n  fields: FormListFieldData[];\n  schema: any;\n  delConfirmProps: any;\n  renderCore: any;\n  rootPath: any;\n  [key: string]: any;\n};\n\nconst TabPaneContent = (props: any) => {\n  const { renderCore, name, schema, rootPath } = props;\n\n  return useMemo(() => (\n   <div style={{ flex: 1 }}>\n      {renderCore({ schema, parentPath: [name], rootPath: [...rootPath, name] })}\n    </div>\n  ), [JSON.stringify(props)]);\n};\n\nconst TabList: React.FC<ListTabProps> = (props) => {\n  const {\n    schema,\n    fields,\n    rootPath,\n    renderCore,\n    readOnly,\n    delConfirmProps,\n    tabName,\n    hideDelete,\n    hideAdd,\n    addItem,\n    removeItem,\n    tabItemProps = {},\n    activeKey: _activeKey,\n    ...retProps\n  } = props;\n\n  const [activeKey, setActiveKey] = useState<string>('0');\n  const configCtx = useContext(ConfigProvider.ConfigContext);\n  const t = translation(configCtx);\n\n  useEffect(() => {\n    setActiveKey(_activeKey || '0');\n  }, [_activeKey]);\n\n  const getTabPaneName = (index: number) => {\n    return tabName instanceof Array ? tabName[index] || index + 1 : `${tabName || t('item')} ${index + 1}`;\n  };\n\n  const handleDelete = (targetKey: number) => {\n    removeItem(targetKey);\n    setActiveKey(`${targetKey > 1 ? targetKey - 1 : 0}`);\n  }\n\n  const handleEdit = (_: any, action: any) => {\n    if (action === 'add') {\n      if ((!schema.max || fields.length < schema.max) && !readOnly && !hideAdd) {\n        addItem();\n        const currentKey = fields.length;\n        setActiveKey(`${currentKey}`);\n      }\n    }\n  };\n\n  const renderClose = (name: number) => {\n    return !readOnly && !hideDelete ? (\n      <Popconfirm\n        onConfirm={() => handleDelete(name)}\n        {...delConfirmProps}\n      >\n        <CloseOutlined />\n      </Popconfirm>\n    ) : <></>\n  };\n\n  return (\n    <Tabs\n      className='fr-tab-list'\n      type='editable-card'\n      {...retProps}\n      onChange={setActiveKey}\n      activeKey={`${activeKey}`}\n      onEdit={handleEdit}\n      hideAdd={readOnly || hideAdd}\n    >\n      {fields.map(({ key, name }) => {\n        return (\n          <Tabs.TabPane \n            key={key}\n            className='fr-list-item'\n            {...tabItemProps}\n            tab={getTabPaneName(name)}\n            closeIcon={renderClose(name)}\n          >\n            <TabPaneContent \n              name={name} \n              rootPath={rootPath} \n              schema={schema} \n              renderCore={renderCore} \n            />\n          </Tabs.TabPane>\n        )\n      })}\n    </Tabs>\n  );\n}\n\nexport default TabList;\n"
  },
  {
    "path": "packages/form-render/src/widgets/listTable/index.less",
    "content": "@ant-prefix: ant;\n\n.fr-table-list {\n  margin-bottom: 24px;\n\n  .@{ant-prefix}-form-item {\n    margin-bottom: 0;\n  }\n  \n  .@{ant-prefix}-form-item-label {\n    display: none;\n  }\n\n  .@{ant-prefix}-form-item-explain-error {\n    display: none;\n  }\n  \n  .fr-list-item-operate {\n    gap: 0 !important;\n  }\n}\n\n.fr-popover-error {\n  .@{ant-prefix}-popover-inner-content {\n    color: #ff4d4f !important\n  }\n}\n\n.fr-table-list-no-popover {\n  .@{ant-prefix}-table-tbody {\n    .@{ant-prefix}-table-cell {\n      padding: 24px 8px 0px 8px !important;\n    }\n  }\n\n  .@{ant-prefix}-form-item {\n    margin-bottom: 24px;\n  }\n\n  .@{ant-prefix}-form-item-explain-error {\n    display: block;\n    font-size: 10px;\n  }\n}"
  },
  {
    "path": "packages/form-render/src/widgets/listTable/index.tsx",
    "content": "import React from 'react';\nimport { Table, Form, Space, Popconfirm, Button, Divider, Tooltip } from 'antd';\nimport type { FormListFieldData, TableColumnsType } from 'antd';\nimport { ArrowDownOutlined, ArrowUpOutlined, PlusOutlined, CloseOutlined, CopyOutlined, InfoCircleOutlined } from '@ant-design/icons';\nimport classnames from 'classnames';\nimport TableCell from './tableCell';\nimport FButton from '../components/FButton';\nimport sortProperties from '../../models/sortProperties';\n\nimport './index.less';\n\ninterface ListTableProps {\n  fields: FormListFieldData[];\n  schema: any;\n  delConfirmProps: any;\n  renderCore: any;\n  rootPath: any;\n  /*\n   * 没有数据时是否隐藏表格\n   */\n  hideEmptyTable?: boolean;\n  [key: string]: any;\n};\n\nconst getTooltip = (tooltip: any) => {\n  if (!tooltip) {\n    return;\n  }\n\n  if (typeof tooltip === 'string') {\n    return { title: <span dangerouslySetInnerHTML={{ __html: tooltip }} /> };\n  }\n\n  return {\n    ...tooltip,\n    title: <span dangerouslySetInnerHTML={{ __html: tooltip.title }} />,\n  };\n};\n\nconst TableList: React.FC<ListTableProps> = (props) => {\n  const {\n    form,\n    schema,\n    fields,\n    rootPath,\n    renderCore,\n    readOnly,\n\n    operateBtnType,\n    addBtnProps,\n    delConfirmProps,\n    copyBtnProps,\n    deleteBtnProps,\n    moveUpBtnProps,\n    moveDownBtnProps,\n    actionColumnProps,\n    pagination,\n\n    hideDelete,\n    hideCopy,\n    hideMove,\n    hideAdd,\n    hideOperate,\n    hideEmptyTable,\n    addItem,\n    copyItem,\n    moveItem,\n    removeItem,\n    configContext,\n    validatePopover,\n    ...retProps\n  } = props;\n\n  const { globalConfig } = configContext;\n  const islidatePopover = validatePopover ?? globalConfig?.listValidatePopover ?? true;\n\n  const { colHeaderText, ...otherActionColumnProps } = actionColumnProps;\n  const itemSchema = schema?.items?.properties || {};\n\n  const paginationConfig = {\n    size: 'small',\n    hideOnSinglePage: true,\n    ...pagination,\n  };\n\n  const handleCopy = (name: number) => {\n    const value = form.getFieldValue(rootPath.concat(name));\n    copyItem(value, name);\n  };\n\n  const columns: any = sortProperties(Object.entries(itemSchema)).map(([dataIndex, item]) => {\n    const { required, title, width, tooltip, columnHidden } = item;\n    if (columnHidden) {\n      return null;\n    }\n\n    const tooltipProps = getTooltip(tooltip);\n    return {\n      dataIndex,\n      width,\n      title: (\n        <>\n          {required && <span style={{ color: 'red', marginRight: '3px' }}>*</span>}\n          <span>{title}</span>\n          {tooltipProps && (\n            <Tooltip placement='top' {...tooltipProps}>\n              <InfoCircleOutlined style={{ marginLeft: 6 }} />\n            </Tooltip>\n          )}\n        </>\n      ),\n      render: (_, field) => {\n        const fieldSchema = {\n          type: 'object',\n          properties: {\n            [dataIndex]: {\n              ...itemSchema[dataIndex],\n              fieldCol: 24,\n            }\n          }\n        };\n\n        if (!islidatePopover) {\n          return (\n            <div className='fr-table-cell-content'>\n              {renderCore({ parentPath: [field.name], rootPath: [...rootPath, field.name], schema: fieldSchema })}\n            </div>\n          )\n        }\n\n        return (\n          <TableCell\n            renderCore={renderCore}\n            schema={fieldSchema}\n            parentPath={[field.name]}\n            rootPath={[...rootPath, field.name]}\n            dataIndex={dataIndex}\n          />\n        );\n      }\n    };\n  }).filter(item => item);\n\n  if (!readOnly && !hideOperate) {\n    columns.push({\n      title: colHeaderText,\n      width: '190px',\n      fixed: 'right',\n      ...otherActionColumnProps,\n      render: (_, field) => (\n        <Form.Item>\n          <Space className='fr-list-item-operate' split={operateBtnType !== 'icon' && <Divider type='vertical' />}>\n            {!hideMove && (\n              <>\n                <FButton\n                  disabled={field.name === 0}\n                  onClick={() => moveItem(field.name, field.name - 1)}\n                  icon={<ArrowUpOutlined />}\n                  {...moveUpBtnProps}\n                />\n                <FButton\n                  disabled={field.name === fields.length - 1}\n                  onClick={() => moveItem(field.name, field.name + 1)}\n                  icon={<ArrowDownOutlined />}\n                  {...moveDownBtnProps}\n                />\n              </>\n            )}\n            {!hideDelete && (\n              <Popconfirm\n                onConfirm={() => removeItem(field.name)}\n                {...delConfirmProps}\n              >\n                <FButton\n                  icon={<CloseOutlined />}\n                  btnType={operateBtnType}\n                  {...deleteBtnProps}\n                />\n              </Popconfirm>\n            )}\n            {!hideCopy && (\n              <FButton\n                onClick={() => handleCopy(field.name)}\n                icon={<CopyOutlined />}\n                {...copyBtnProps}\n              />\n            )}\n          </Space>\n        </Form.Item>\n      )\n    });\n  }\n\n  const showTable = fields.length > 0 ? true : !hideEmptyTable;\n\n  return (\n    <div className={classnames('fr-table-list', { 'fr-table-list-no-popover': !islidatePopover })}>\n      {showTable && (\n        <Table\n          size='middle'\n          scroll={{ x: 'max-content' }}\n          style={{ marginBottom: '12px' }}\n          {...retProps}\n          columns={columns}\n          dataSource={fields}\n          pagination={paginationConfig}\n        />\n      )}\n      {(!schema.max || fields.length < schema.max) && !hideAdd && (\n        <Button\n          icon={<PlusOutlined />}\n          onClick={() => addItem()}\n          {...addBtnProps}\n        />\n      )}\n    </div>\n  );\n}\n\nexport default TableList;\n"
  },
  {
    "path": "packages/form-render/src/widgets/listTable/tableCell.tsx",
    "content": "import React, { useState, useRef } from 'react';\nimport { Popover } from 'antd';\n\nconst TableCell = (props: any) => {\n  const { renderCore, schema, dataIndex, ...otherProps } = props;\n  const [errorMsg, setErrorMsg] = useState(null);\n  const [visible, setVisible] = useState<boolean>();\n  const mouseRef = useRef<any>(null);\n\n  const onStatusChange = (_: any, errors: any[]) => {\n    const message = errors[0] || null;\n    setErrorMsg(message);\n   \n    if (mouseRef.current && message) {\n      setVisible(true);\n    }\n  };\n\n  if (!schema.properties[dataIndex].onStatusChange) {\n    schema.properties[dataIndex].onStatusChange = onStatusChange;\n  }\n\n  const popoverVisible = visible && errorMsg;\n  let popoverProps: any = {\n    open: popoverVisible\n  };\n  if ((window as any).antdVersion === 'v4')  {\n    popoverProps = {\n      visible: popoverVisible\n    };\n  }\n  \n  return (\n    <div\n      className='fr-table-cell-content'\n      onMouseEnter={() => {\n        mouseRef.current = true;\n        setVisible(true);\n      }}\n      onMouseLeave={() => {\n        mouseRef.current = false;\n        setVisible(false);\n      }}\n    >\n      <Popover\n        overlayClassName='fr-popover-error'\n        content={errorMsg}\n        placement='topRight'\n        {...popoverProps}\n      >\n        {renderCore({ ...otherProps, schema })}\n      </Popover>\n    </div>\n  );\n};\n\nexport default TableCell;\n"
  },
  {
    "path": "packages/form-render/src/widgets/listVirtual/index.less",
    "content": "@ant-prefix: ant;\n\n.fr-virtual-list {\n  margin-bottom: 12px;\n\n  .@{ant-prefix}-form-item {\n    margin-bottom: 0;\n  }\n  \n  .@{ant-prefix}-form-item-label {\n    display: none;\n  }\n\n  .@{ant-prefix}-form-item-explain-error {\n    display: none;\n  }\n  \n  .fr-list-item-operate {\n    gap: 0 !important;\n  }\n}\n\n.fr-popover-error {\n  .@{ant-prefix}-popover-inner-content {\n    color: #ff4d4f !important\n  }\n}\n\n.fr-virtual-list-no-popover {\n  .@{ant-prefix}-table-tbody {\n    .@{ant-prefix}-table-cell {\n      padding: 24px 8px 0px 8px !important;\n    }\n  }\n\n  .@{ant-prefix}-form-item {\n    margin-bottom: 24px;\n  }\n\n  .@{ant-prefix}-form-item-explain-error {\n    display: block;\n    font-size: 10px;\n  }\n}"
  },
  {
    "path": "packages/form-render/src/widgets/listVirtual/index.tsx",
    "content": "import React from 'react';\nimport { Table, Form, Space, Popconfirm, Button, Divider, Tooltip } from 'antd';\nimport type { FormListFieldData, TableColumnsType } from 'antd';\nimport { ArrowDownOutlined, ArrowUpOutlined, PlusOutlined, CloseOutlined, CopyOutlined, InfoCircleOutlined } from '@ant-design/icons';\nimport classnames from 'classnames';\nimport VirtualCell from './virtualCell';\nimport { useVT } from 'virtualizedtableforantd4';\nimport FButton from '../components/FButton';\nimport sortProperties from '../../models/sortProperties';\n\nimport './index.less';\n\ninterface ListVirtualProps {\n  fields: FormListFieldData[];\n  schema: any;\n  delConfirmProps: any;\n  renderCore: any;\n  rootPath: any;\n  /*\n   * 没有数据时是否隐藏表格\n   */\n  hideEmptyTable?: boolean;\n  [key: string]: any;\n};\n\nconst getTooltip = (tooltip: any) => {\n  if (!tooltip) {\n    return;\n  }\n\n  if (typeof tooltip === 'string') {\n    return { title: <span dangerouslySetInnerHTML={{ __html: tooltip }} /> };\n  }\n\n  return {\n    ...tooltip,\n    title: <span dangerouslySetInnerHTML={{ __html: tooltip.title }} />,\n  };\n};\n\nconst VirtualList: React.FC<ListVirtualProps> = (props) => {\n  const {\n    form,\n    schema,\n    fields,\n    rootPath,\n    renderCore,\n    readOnly,\n    \n    operateBtnType,\n    addBtnProps,\n    delConfirmProps,\n    copyBtnProps,\n    deleteBtnProps,\n    moveUpBtnProps,\n    moveDownBtnProps,\n    actionColumnProps,\n    \n    scrollY = 600,\n    hideDelete,\n    hideCopy,\n    hideMove,\n    hideAdd,\n    hideOperate,\n    hideEmptyTable,\n\n    addItem,\n    copyItem,\n    moveItem,\n    removeItem,\n    configContext,\n    validatePopover,\n  } = props;\n\n  const { globalConfig } = configContext;\n  const islidatePopover = validatePopover ?? globalConfig?.listValidatePopover ?? true;\n\n  const { colHeaderText, ...otherActionColumnProps } = actionColumnProps;\n\n  const itemSchema = schema?.items?.properties || {};\n\n  const [vt, set_components] = useVT(() => ({ scroll: { y: scrollY } }), []);\n\n  const handleCopy = (name: number) => {\n    const value = form.getFieldValue(rootPath.concat(name));\n    copyItem(value, name);\n  };\n\n  const columns: TableColumnsType<FormListFieldData> = sortProperties(Object.entries(itemSchema)).map(([dataIndex, item]) => {\n    const { required, title, width, tooltip } = item;\n    const tooltipProps = getTooltip(tooltip);\n    return {\n      dataIndex,\n      width,\n      title: (\n        <>\n          {required && <span style={{ color: 'red', marginRight: '3px' }}>*</span>}\n          <span>{title}</span>\n          {tooltipProps && (\n            <Tooltip placement='top' {...tooltipProps}>\n              <InfoCircleOutlined style={{ marginLeft: 6 }} />\n            </Tooltip>\n          )}\n        </>\n      ),\n      render: (_, field) => {\n        const fieldSchema = {\n          type: 'object',\n          properties: {\n            [dataIndex]: {\n              ...itemSchema[dataIndex],\n              fieldCol: 24,\n            }\n          }\n        };\n\n        if (!islidatePopover) {\n          return (\n            <div className='fr-table-cell-content'>\n              {renderCore({ parentPath: [field.name], rootPath: [...rootPath, field.name], schema: fieldSchema })}\n            </div>\n          )\n        }\n\n        return (\n          <VirtualCell\n            renderCore={renderCore}\n            schema={fieldSchema}\n            parentPath={[field.name]}\n            rootPath={[...rootPath, field.name]}\n            dataIndex={dataIndex}\n          />\n        );\n      }\n    };\n  });\n\n  if (!readOnly && !hideOperate) {\n    columns.push({\n      title: colHeaderText,\n      width: '190px',\n      fixed: 'right',\n      ...otherActionColumnProps,\n      render: (_, field) => (\n        <Form.Item>\n          <Space className='fr-list-item-operate' split={operateBtnType !== 'icon' && <Divider type='vertical'/>}>\n            {!hideMove && (\n              <>\n                <FButton \n                  disabled={field.name === 0}\n                  onClick={() => moveItem(field.name, field.name - 1)}\n                  icon={<ArrowUpOutlined/>}\n                  {...moveUpBtnProps}\n                />\n                <FButton \n                  disabled={field.name === fields.length - 1}\n                  onClick={() => moveItem(field.name, field.name + 1)}\n                  icon={<ArrowDownOutlined/>}\n                  {...moveDownBtnProps}\n                />\n              </>\n            )}\n            {!hideDelete && (\n              <Popconfirm\n                onConfirm={() => removeItem(field.name)}\n                {...delConfirmProps}\n              >\n                <FButton\n                  icon={<CloseOutlined/>}\n                  btnType={operateBtnType}\n                  {...deleteBtnProps}\n                />\n              </Popconfirm>\n            )}\n            {!hideCopy && (\n              <FButton \n                onClick={() => handleCopy(field.name)}\n                icon={<CopyOutlined/>}\n                {...copyBtnProps}\n              />\n            )}\n          </Space>\n        </Form.Item>\n      )\n    });\n  }\n\n  const showTable = fields.length > 0 ? true : !hideEmptyTable;\n\n  return (\n    <>\n      {showTable && (\n        <Table\n          className={classnames('fr-virtual-list', { 'fr-virtual-list-no-popover': !islidatePopover })}\n          size='middle'\n          columns={columns}\n          dataSource={fields}\n          pagination={false}\n          scroll={{ y: scrollY }}\n          components={vt}\n        />\n      )}\n      {(!schema.max || fields.length < schema.max) && !hideAdd && (\n        <Button\n          icon={<PlusOutlined />}\n          onClick={() => addItem()}\n          {...addBtnProps}\n        />\n      )}\n    </>\n  );\n}\n\n\nexport default VirtualList;\n"
  },
  {
    "path": "packages/form-render/src/widgets/listVirtual/virtualCell.tsx",
    "content": "import React, { useState, useRef } from 'react';\nimport { Popover } from 'antd';\n\nconst VirtualCell = (props: any) => {\n  const { renderCore, schema, dataIndex, ...otherProps } = props;\n  const [errorMsg, setErrorMsg] = useState(null);\n  const [visible, setVisible] = useState<boolean>();\n  const mouseRef = useRef<any>(null);\n\n  const onStatusChange = (_: any, errors: any[]) => {\n    const message = errors[0] || null;\n    setErrorMsg(message);\n   \n    if (mouseRef.current && message) {\n      setVisible(true);\n    }\n  };\n\n  if (!schema.properties[dataIndex].onStatusChange) {\n    schema.properties[dataIndex].onStatusChange = onStatusChange;\n  }\n  \n  const popoverVisible = visible && errorMsg;\n  let popoverProps: any = {\n    open: popoverVisible\n  };\n  if ((window as any).antdVersion === 'v4') {\n    popoverProps = {\n      visible: popoverVisible\n    };\n  }\n\n  return (\n    <div \n      onMouseEnter={() => {\n        mouseRef.current = true;\n        setVisible(true);\n      }}\n      onMouseLeave={() => {\n        mouseRef.current = false;\n        setVisible(false);\n      }}\n    >\n      <Popover\n        overlayClassName='fr-popover-error'\n        content={errorMsg}\n        placement='topRight'\n        {...popoverProps}\n      >\n        {renderCore({ ...otherProps, schema })}\n      </Popover>\n    </div>\n  );\n};\n\nexport default VirtualCell;\n"
  },
  {
    "path": "packages/form-render/src/widgets/utils/hooks.ts",
    "content": "import { message } from 'antd';\nimport { useReducer, useRef, useEffect, useState } from 'react';\n\nexport function usePrevious(value: any) {\n  const ref = useRef(null);\n  useEffect(() => {\n    ref.current = value;\n  }, [value]);\n  return ref.current;\n}\n\n// 类似于class component的setState\nexport const useSet = (initState: any) => {\n  return useReducer((state: any, newState: any) => {\n    return { ...state, ...newState };\n  }, initState);\n};"
  },
  {
    "path": "packages/form-render/src/widgets/utils/index.ts",
    "content": "import dayjs from 'dayjs';\nimport weekOfYear from 'dayjs/plugin/weekOfYear';\n\ndayjs.extend(weekOfYear); // 启用 weekOfYear 插件\n\nexport function isUrl(str: string) {\n  const protocolRE = /^(?:\\w+:)?\\/\\/(\\S+)$/;\n  // const domainRE = /^[^\\s\\.]+\\.\\S{2,}$/;\n  if (typeof str !== 'string') return false;\n  return protocolRE.test(str);\n}\n\nexport function getFormat(format: string) {\n  let dateFormat: string;\n\n  switch (format) {\n    case 'date':\n      dateFormat = 'YYYY-MM-DD';\n      break;\n    case 'time':\n      dateFormat = 'HH:mm:ss';\n      break;\n    case 'dateTime':\n      dateFormat = 'YYYY-MM-DD HH:mm:ss';\n      break;\n    case 'week':\n      dateFormat = 'YYYY-w';\n      break;\n    case 'year':\n      dateFormat = 'YYYY';\n      break;\n    case 'quarter':\n      dateFormat = 'YYYY-Q';\n      break;\n    case 'month':\n      dateFormat = 'YYYY-MM';\n      break;\n    default:\n      // dateTime\n      if (typeof format === 'string') {\n        dateFormat = format;\n      } else {\n        dateFormat = 'YYYY-MM-DD';\n      }\n  }\n  \n  return dateFormat;\n}\n\nexport const transformDateValue = (value: string, format: string, dateFormat: string) => {\n  let result: any = value || undefined;\n\n  if (typeof value === 'string') {\n    if (format === 'week') {\n      const [years, week] = value.split('-');\n      result = dayjs(years)?.week(Number(week));\n    }\n    if (format === 'quarter') {\n      const [yearx, quarter]: any = value.split('-');\n      result = dayjs(yearx).quarter(quarter);\n    }\n  }\n\n  if (result) {\n    result = dayjs(result, dateFormat);\n  }\n  return result;\n}\n\nexport const translation = (configCtx: any) => (key: string) => {\n  const locale = configCtx?.locale?.FormRender;\n  return locale[key];\n}\n"
  },
  {
    "path": "packages/form-render/src/widgets/utils/withFieldWrap.tsx",
    "content": "import React from 'react';\n\nconst getProps = (props: any, filter: any[]) => {\n  const result = {};\n  \n  Object.keys(props).forEach(key => {\n    if (filter.includes(key)) {\n      return;\n    }\n    result[key] = props[key];\n  });\n  \n  return result;\n}\n\nexport default (Field: any, filterProps = ['addons', 'schema', 'dependValues']) => {\n  return (props: any) => {\n    return <Field {...getProps(props, filterProps)} />;\n  };\n}"
  },
  {
    "path": "packages/form-render/src/widgets/voidTitle/index.less",
    "content": ".fr-void-title {\n  height: 24px;\n  color: #000000e0;\n  font-weight: 600;\n  font-size: 16px;\n  line-height: 24px;\n}\n\n"
  },
  {
    "path": "packages/form-render/src/widgets/voidTitle/index.tsx",
    "content": "import React from 'react';\nimport classnames from 'classnames';\nimport './index.less';\n\nexport default ({ schema }) => {\n  return (\n    <div className={classnames('fr-void-title', { [schema?.className] : !! schema?.className })}>\n      {schema.title}\n    </div>\n  )\n}\n"
  },
  {
    "path": "packages/form-render/src/withProvider.tsx",
    "content": "import React, { useEffect, useRef } from 'react';\nimport { ConfigProvider } from 'antd';\nimport dayjs from 'dayjs';\nimport { useUnmount } from 'ahooks';\n\nimport zhCN from 'antd/lib/locale/zh_CN';\nimport enUS from 'antd/lib/locale/en_US';\nimport locales from './locales';\nimport 'dayjs/locale/zh-cn';\n\nimport { createStore } from './models/store';\nimport { FRContext, ConfigContext } from './models/context';\nimport { validateMessagesEN, validateMessagesCN } from './models/validateMessage';\n\nexport default function withProvider<T>(Element: React.ComponentType<T>, defaultWidgets?: any) : React.ComponentType<T> {\n  return (props: any) => {\n    const {\n      configProvider,\n      locale = 'zh-CN',\n      widgets,\n      methods,\n      form,\n      validateMessages,\n      globalProps={},\n      globalConfig = {},\n      ...otherProps\n    } = props;\n  \n    const storeRef = useRef(createStore());\n    const store: any = storeRef.current;\n  \n    useEffect(() => {\n      if (locale === 'en-US') {\n        dayjs.locale('en');\n        return;\n      }\n      dayjs.locale('zh-cn');\n    }, [locale]);\n\n    useUnmount(() => {\n      form.resetFields();\n    });\n  \n    if (!form) {\n      console.warn('Please provide a form instance to FormRender');\n      return null;\n    }\n  \n    const antdLocale = locale === 'zh-CN' ? zhCN : enUS;\n    const formValidateMessages = locale === 'zh-CN' ? validateMessagesCN : validateMessagesEN;\n    const configContext = {\n      locale,\n      widgets: { ...defaultWidgets, ...widgets },\n      methods,\n      form,\n      globalProps,\n      globalConfig\n    };\n  \n    const langPack: any = { \n      ...antdLocale,\n      'FormRender': locales[locale],\n      ...configProvider?.locale\n    };\n\n    return (\n      <ConfigProvider\n        {...configProvider}\n        locale={langPack}\n        form={{\n          validateMessages: {\n            ...formValidateMessages,\n            ...validateMessages\n          }\n        }}\n      >\n        <ConfigContext.Provider value={configContext}>\n          <FRContext.Provider value={store}>\n            <Element form={form} {...otherProps} />\n          </FRContext.Provider>\n        </ConfigContext.Provider>\n      </ConfigProvider>\n    );\n  };\n}"
  },
  {
    "path": "packages/form-render/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"ES2015\",\n    \"module\": \"ES2015\",\n    \"moduleResolution\": \"node\",\n    \"importHelpers\": true,\n    \"jsx\": \"react\",\n    \"esModuleInterop\": true,\n    \"sourceMap\": true,\n    \"baseUrl\": \"./\",\n    \"skipLibCheck\": true,\n    // \"strict\": true,\n    \"declaration\": true,\n    \"paths\": {\n      \"@/*\": [\n        \"src/*\"\n      ],\n      \"@@/*\": [\n        \"src/.umi/*\"\n      ],\n      \"form-render\": [\n        \"packages/form-render/src/*\"\n      ],\n    },\n    \"allowJs\": true,\n    \"allowSyntheticDefaultImports\": true,\n    \"noImplicitAny\": false,\n    \"resolveJsonModule\": true\n  },\n  \"exclude\": [\n    \"node_modules\",\n    \"lib\",\n    \"es\",\n    \"dist\",\n    \"typings\",\n    \"**/__test__\",\n    \"test\",\n    \"tests\",\n    \"docs\",\n    \"**/*.js\"\n  ]\n}\n"
  },
  {
    "path": "packages/form-render-mobile/.fatherrc.js",
    "content": "import copy from 'rollup-plugin-copy';\n\nexport default {\n  cjs: 'babel',\n  esm: {\n    type: 'babel',\n    importLibToEs: true,\n  },\n  lessInBabelMode: true,\n  extraBabelPlugins: [\n    [\n      'import',\n      {\n        libraryName: 'antd-mobile',\n        libraryDirectory: 'es/components',\n        style: false,\n      },\n      'antd-mobile',\n    ],\n    // [\n    //   'import',\n    //   {\n    //     libraryName: '@ant-design/icons',\n    //     libraryDirectory: 'lib/icons',\n    //     camel2DashComponentName: false,\n    //   },\n    //   '@ant-design/icons',\n    // ],\n  ],\n};\n"
  },
  {
    "path": "packages/form-render-mobile/CHANGELOG.md",
    "content": "# 更新日志\n\n### 1.0.15\n- [!] 修复 removeHidden 配置不生效\n\n### 1.0.14\n- [+] labelWidget、descWidget 增加 addons 访问属性\n- [-] 兼容 widget 大小写配置\n### 1.0.13\n- [+] 增加输入控件单独配置布局 layout = 'row' | 'column'\n- [+] 补齐 tooltip 字段，与 PC 端保持一致\n- [!] 修复 title 不存在时，校验信息不提示\n### 1.0.5\n- [+] 增加 form.getFieldRef API，自定义组件可以暴露实例，供 getFieldRef 使用\n### 1.0.0\n\n- [+] form-render-mobile 1.0 正式发版\n"
  },
  {
    "path": "packages/form-render-mobile/CONTRIBUTING.md",
    "content": "# 如何贡献代码\n\n欢迎给 FormRender 提优化建议，或者修复已有 Bug，共促其发展\n\n## Branch 管理\n\n```\nmaster\n ↑\ndev         <--- Develop/PR\n```\n\n- `dev` 分支\n  - 所有的开发均在 dev 分支进行\n  - 提 PR 时候请提交到 dev 分支\n- `master` 分支\n  - `master` 是稳定不改的分支，不会在上面进行代码开发\n  - 在 dev 分支 publish 后会 merge 到 master，同时打对应 tag\n\n## Commit 格式\n\n```\n[{action}] {description}\n```\n\n- `{action}`\n  - `+` 新增功能\n  - `!` 更新或者修复 bug\n  - `-` 移除功能\n- `{description}`\n  - 尽可能详细的描述就好\n\nfor example:\n\n- [+] 列表选项新增拖拽功能\n- [!] 修复输入框长按闪烁的问题\n\n## 更多\n\n- 很推荐在提交 PR 前，先在钉钉群里进行讨论，已防止此功能已经有同学在开发了\n- 但是如果是想修复文档和明显代码错误，直接提交 PR 就好\n\n<img src=https://img.alicdn.com/imgextra/i2/O1CN01RoUHQF1EoKLWyotFn_!!6000000000398-0-tps-750-990.jpg width=250/>\n"
  },
  {
    "path": "packages/form-render-mobile/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2019-present XRender Team\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "packages/form-render-mobile/README.md",
    "content": "<div style=\"display:flex;align-items:center;margin-bottom:24px\">\n  <img src=\"https://img.alicdn.com/tfs/TB17UtINiLaK1RjSZFxXXamPFXa-606-643.png\" alt=\"logo\" width=\"48px\"/>\n  <h4 style=\"font-size:30px;font-weight:600;display:inline-block;margin-left:12px\">FormRender Mobile</h4>\n</div>\n<p style=\"display:flex;justify-content:space-between;width:440px\">\n  <a href=\"https://www.npmjs.com/package/form-render-mobile?_blank\">\n    <img alt=\"npm\" src=\"https://img.shields.io/npm/v/form-render-mobile.svg?maxAge=3600&style=flat-square\">\n  </a>\n  <a href=\"https://npmjs.org/package/form-render-mobile\">\n    <img alt=\"NPM downloads\" src=\"https://img.shields.io/npm/dm/fform-render-mobile.svg?style=flat-square\">\n  </a>\n  <a href=\"https://npmjs.org/package/form-render-mobile\">\n    <img alt=\"NPM all downloads\" src=\"https://img.shields.io/npm/dt/form-render-mobile.svg?style=flat-square\">\n  </a>\n  <a>\n    <img alt=\"PRs Welcome\" src=\"https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square\">\n  </a>\n</p>\n\n\n<p align=\"center\">\n  <a href=\"https://xrender.fun/form-render-mobile\">Get started</a> | \n  <a href=\"https://xrender.fun/form-render-mobile/api\">API</a> |\n  <a href=\"https://xrender.fun/playground\">Playground</a>\n</p>\n\n## ✨ 简介\n\nFormRender Mobile 是为移动端设置的开箱即用的表单解决方案，通过 JsonSchema 协议动态渲染表单。基于 [FormRender2.0](https://xrender.fun/form-render) 和 [Ant Design Mobile](https://mobile.ant.design/zh/components/form/) 实现。API 与 FormRender2.0 基本一致，如果你熟悉 FromRender2.0 那么你就已经会使用 FormRender Mobile 了。\n\n## ⚙️ 安装\n\nFormRender Mobile 依赖 Ant Design Mobile，单独使用不要忘记同时安装 `antd-mobile`\n\n```shell\nnpm i form-render-mobile --save\n```\n\n## 🚀 快速上手\n\n```jsx\nimport React from 'react';\nimport FormRender, { useForm } from 'form-render-mobile';\n\nconst schema = {\n  type: 'object',\n  displayType: 'row',\n  properties: {\n    input: {\n      title: '输入框',\n      type: 'string',\n      widget: 'input'\n    },\n    radio: {\n      title: '单选',\n      type: 'string',\n      widget: 'radio',\n      props: {\n        options: [\n          { label: '早', value: 'a' },\n          { label: '中', value: 'b' },\n          { label: '晚', value: 'c' }\n        ]\n      }\n    }\n  }\n};\n\n\nexport default () => {\n  const form = useForm();\n\n  const onFinish = (formData) => {\n    console.log('formData:', formData);\n  };\n\n  return (\n    <FormRender \n      form={form} \n      schema={schema} \n      onFinish={onFinish}\n    />\n  );\n}\n```\n"
  },
  {
    "path": "packages/form-render-mobile/package.json",
    "content": "{\n  \"name\": \"form-render-mobile\",\n  \"version\": \"1.0.16\",\n  \"description\": \"通过 JSON Schema 生成标准 Form，常用于自定义搭建配置界面生成\",\n  \"keywords\": [\n    \"Form\",\n    \"FormRenderMobile\",\n    \"Render\",\n    \"XRender\",\n    \"React\",\n    \"Json Schema\",\n    \"Ant Design\"\n  ],\n  \"homepage\": \"https://xrender.fun/form-render-mobile\",\n  \"bugs\": {\n    \"url\": \"https://github.com/alibaba/x-render/issues\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git@github.com:alibaba/form-render.git\"\n  },\n  \"license\": \"MIT\",\n  \"contributors\": [\n    {\n      \"name\": \"Tw93\",\n      \"email\": \"tw93@qq.com\"\n    },\n    {\n      \"name\": \"mankaiviky\",\n      \"email\": \"mankaiviky@163.com\"\n    }\n  ],\n  \"main\": \"lib/index.js\",\n  \"module\": \"es/index.js\",\n  \"files\": [\n    \"es\",\n    \"lib\",\n    \"package.json\"\n  ],\n  \"scripts\": {\n    \"beta\": \"npm publish --tag beta\",\n    \"build\": \"father-build\",\n    \"next\": \"npm publish --tag next\",\n    \"prepare\": \"npm run build\",\n    \"prettier\": \"prettier --write \\\"**/*.{js,jsx,tsx,ts,less,md,json}\\\"\",\n    \"postpublish\": \"git push --tags\",\n    \"test:ui\": \"vitest --ui\"\n  },\n  \"lint-staged\": {\n    \"*.{js,jsx,less,md,json}\": [\n      \"prettier --write\"\n    ],\n    \"*.ts?(x)\": [\n      \"prettier --parser=typescript --write\"\n    ]\n  },\n  \"dependencies\": {\n    \"antd-mobile-icons\": \"^0.3.0\",\n    \"async-validator\": \"^3.5.1\",\n    \"classnames\": \"^2.3.1\",\n    \"color\": \"^3.1.2\",\n    \"lodash-es\": \"^4.17.21\",\n    \"dayjs\": \"^1.11.7\",\n    \"rc-color-picker\": \"^1.2.6\",\n    \"virtualizedtableforantd4\": \"^1.1.2\",\n    \"zustand\": \"^4.1.5\",\n    \"ahooks\": \"3.7.5\",\n    \"form-render\": \"^2.3.6\"\n  },\n  \"devDependencies\": {\n    \"deep-equal\": \"^2.0.3\",\n    \"rollup-plugin-copy\": \"^3.4.0\"\n  },\n  \"peerDependencies\": {\n    \"antd-mobile\": \"4.x | 5.x\",\n    \"react\": \">=16.8.0\",\n    \"react-dom\": \">=16.8.0\"\n  },\n  \"gitHooks\": {\n    \"pre-commit\": \"lint-staged\"\n  },\n  \"sideEffect\": false\n}"
  },
  {
    "path": "packages/form-render-mobile/src/form-core/connectForm.tsx",
    "content": "import React, { forwardRef } from 'react';\nimport useForm from '../models/useForm';\n\nexport default (Component: React.FC<any>) => {\n  return forwardRef((props, ref) => {\n    const form = useForm();\n\n    return <Component ref={ref} {...props} form={form} />;\n  });\n}"
  },
  {
    "path": "packages/form-render-mobile/src/form-core/index.less",
    "content": ".frm-form {\n  .adm-list-item {\n    padding: 0 16px;\n  }\n\n  .adm-collapse-panel-content-inner > .adm-list-item {\n    padding: 0;\n\n    .adm-list-item-content {\n      padding-right: 0;\n    }\n  }\n  \n  .adm-list-body {\n    background: transparent;\n  }\n}\n\n.frm-form-card {\n  .adm-list-body {\n    border: none !important;\n  }\n}\n"
  },
  {
    "path": "packages/form-render-mobile/src/form-core/index.tsx",
    "content": "import React, { useEffect, useContext } from 'react';\nimport { Form, Grid } from 'antd-mobile';\nimport { useStore } from 'zustand';\nimport classnames from 'classnames';\nimport { cloneDeep } from 'lodash-es';\nimport { parseValuesToBind } from 'form-render/es/models/bindValues';\nimport filterValuesUndefined from 'form-render/es/models/filterValuesUndefined';\nimport filterValuesHidden from 'form-render/es/models/filterValuesHidden';\n\nimport { valueRemoveUndefined, _cloneDeep, isFunction } from '../utils';\nimport { FRContext } from '../models/context';\nimport transformProps from '../models/transformProps';\n\nimport {\n  valuesWatch,\n  immediateWatch,\n  yymmdd,\n  msToTime,\n  getSessionItem,\n  setSessionItem\n} from 'form-render/es/models/formCoreUtils';\nimport RenderCore from '../render-core';\n\nimport './index.less';\n\nconst FormCore = (props: any) => {\n  const store: any = useContext(FRContext);\n  const schema = useStore(store, (state: any) => state.schema);\n  const flattenSchema = useStore(store, (state: any) => state.flattenSchema);\n  const setContext = useStore(store, (state: any) => state.setContext);\n  const isCardMode = useStore(store, (state: any) => state.isCardMode);\n\n  const { type, properties, ...schemaProps } = schema || {};\n  const {\n    formProps,\n    displayType,\n    beforeFinish,\n    watch,\n    onMount,\n    column,\n    labelWidth,\n    labelCol,\n    fieldCol,\n    maxWidth,\n    form,\n    onFinish,\n    onFinishFailed,\n    readOnly,\n    removeHiddenData,\n    logOnMount,\n    logOnSubmit,\n    className,\n    id,\n    validateMessages,\n  } = transformProps({ ...props, ...schemaProps });\n\n  useEffect(() => {\n    form.__initStore(store);\n    setTimeout(initial, 0);\n  }, []);\n\n  useEffect(() => {\n    form.setSchema(props.schema, true);\n  }, [JSON.stringify(props.schema || {})]);\n\n  useEffect(() => {\n    store.setState({ removeHiddenData });\n  }, [removeHiddenData]);\n\n  useEffect(() => {\n    const context = {\n      column,\n      readOnly,\n      labelWidth,\n      displayType,\n      labelCol,\n      fieldCol,\n      maxWidth\n    };\n    setContext(context);\n  }, [column, labelCol, fieldCol, displayType, labelWidth, maxWidth, readOnly]);\n\n  const initial = async () => {\n    onMount && await onMount();\n    onMountLogger();\n    setTimeout(() => {\n      const values = form.getValues();\n      immediateWatch(watch, values);\n    }, 0);\n  };\n\n  const onMountLogger = () => {\n    const start = new Date().getTime();\n    if (isFunction(logOnMount)|| isFunction(logOnSubmit)) {\n      setSessionItem('FORM_MOUNT_TIME', start);\n      setSessionItem('FORM_START', start);\n    }\n    if (isFunction(logOnMount)) {\n      const logParams: any = {\n        schema: props.schema,\n        url: location.href,\n        formData: JSON.stringify(form.getValues()),\n        formMount: yymmdd(start),\n      };\n      if (id) {\n        logParams.id = id;\n      }\n      logOnMount(logParams);\n    }\n    // 如果是要计算时间，在 onMount 时存一个时间戳\n    if (isFunction(logOnSubmit)) {\n      setSessionItem('NUMBER_OF_SUBMITS', 0);\n      setSessionItem('FAILED_ATTEMPTS', 0);\n    }\n  };\n\n  const onSubmitLogger = (params: any) => {\n    if (!isFunction(logOnSubmit)) {\n      return;\n    }\n\n    const start = getSessionItem('FORM_START');\n    const mount = getSessionItem('FORM_MOUNT_TIME');\n\n    const numberOfSubmits = getSessionItem('NUMBER_OF_SUBMITS') + 1;\n    const end = new Date().getTime();\n\n    let failedAttempts = getSessionItem('FAILED_ATTEMPTS');\n    if (params.errorFields.length > 0) {\n      failedAttempts = failedAttempts + 1;\n    }\n    const logParams: any = {\n      formMount: yymmdd(mount),\n      ms: end - start,\n      duration: msToTime(end - start),\n      numberOfSubmits: numberOfSubmits,\n      failedAttempts: failedAttempts,\n      url: location.href,\n      formData: JSON.stringify(params.values),\n      errors: JSON.stringify(params.errorFields),\n      schema: JSON.stringify(schema),\n    };\n    if (id) {\n      logParams.id = id;\n    }\n    logOnSubmit(logParams);\n    setSessionItem('FORM_START', end);\n    setSessionItem('NUMBER_OF_SUBMITS', numberOfSubmits);\n    setSessionItem('FAILED_ATTEMPTS', failedAttempts);\n  };\n\n  const handleValuesChange = (changedValues: any, _allValues: any) => {\n    const allValues = valueRemoveUndefined(_allValues, true);\n    valuesWatch(changedValues, allValues, watch);\n  };\n\n  const transFormValues = (_values: any) => {\n    let values = cloneDeep(_values);\n    values = removeHiddenData ? filterValuesHidden(values, flattenSchema) : cloneDeep(form.getFieldsValue(true));\n    values = parseValuesToBind(values, flattenSchema);\n    values = filterValuesUndefined(values);\n    return values;\n  };\n\n  const handleFinish = async (_values: any) => {\n    const values = transFormValues(_values);\n    const fieldsError = beforeFinish ? await beforeFinish({ data: values, schema, errors: [] }) : null;\n    // console.log(values, form.getValues(true), _values);\n    if (fieldsError?.length > 0) {\n      form.setFields(fieldsError);\n      return;\n    }\n    onSubmitLogger({ values });\n    onFinish && onFinish(values, []);\n  };\n\n  const handleFinishFailed = async (params: any) => {\n    const values = transFormValues(params.values);\n    onSubmitLogger({ ...params, values });\n    if (!onFinishFailed) {\n      return;\n    }\n    onFinishFailed({ ...params, values });\n  };\n\n  return (\n    <Form\n      {...formProps}\n      className={classnames('frm-form', className, { ['frm-form-card']: isCardMode })}\n      form={form}\n      onFinish={handleFinish}\n      onFinishFailed={handleFinishFailed}\n      onValuesChange={handleValuesChange}\n      validateMessages={validateMessages}\n    >\n      <Grid columns={1}>\n        <RenderCore schema={schema} />\n      </Grid>\n    </Form>\n  );\n}\n\nexport default FormCore;\n"
  },
  {
    "path": "packages/form-render-mobile/src/index.tsx",
    "content": "import FormCore from './form-core';\nimport withProvider from './withProvider';\n\nexport * as widgets from './widgets';\n\nexport { default as useForm } from './models/useForm';\nexport { default as connectForm } from './form-core/connectForm';\n\nexport type {\n  default as FR,\n  Schema,\n  FRProps,\n  FormInstance,\n  FormParams,\n  FieldParams,\n  WatchProperties,\n  SchemaType,\n  SchemaBase,\n  ValidateParams,\n  ResetParams,\n  RuleItem,\n} from './type';\n\nexport default withProvider(FormCore);"
  },
  {
    "path": "packages/form-render-mobile/src/locales/en_US.ts",
    "content": "export default {\n  \"copy_max_tip\": \"The maximum number of table items has been reached and cannot be copied\",\n  \"copy\": \"Copy\",\n  \"add_item\": \"Add a new line\",\n  \"confirm_delete\": \"Are you sure to delete?\",\n  \"confirm\": \"Yes\",\n  \"cancel\": \"No\",\n  \"operate\": \"Operate\",\n  \"delete\": \"Delete\",\n  \"edit\": \"Edit\",\n  \"img_src_error\": \"Image address error\",\n  \"upload\": \"Upload\",\n  \"upload_success\": \"upload success\",\n  \"upload_fail\": \"upload failed\",\n  \"uploaded_address\": \"Uploaded address\",\n  \"test_src\": \"Test address\",\n  \"schema_not_match\": \"Schema does not match the display component：\",\n  \"item\": \"Item\",\n  \"search\": \"Search\",\n  \"reset\": \"Reset\",\n  \"expand\": \"Expand\",\n  \"fold\": \"Fold\",\n  \"submit\": \"Submit\",\n  \"moveDown\": \"Move Down\",\n  \"moveUp\": \"Move Up\"\n}"
  },
  {
    "path": "packages/form-render-mobile/src/locales/index.ts",
    "content": "import enUS from './en_US';\nimport zhCN from './zh_CN';\n\nexport default {\n  'en-US':  enUS,\n  'zh-CN': zhCN,\n}"
  },
  {
    "path": "packages/form-render-mobile/src/locales/zh_CN.ts",
    "content": "export default {\n  \"copy_max_tip\": \"已达表单项数量上限，无法复制！\",\n  \"copy\": \"复制\",\n  \"add_item\": \"新增一条\",\n  \"confirm_delete\": \"确定删除?\",\n  \"confirm\": \"确定\",\n  \"cancel\": \"取消\",\n  \"operate\": \"操作\",\n  \"delete\": \"删除\",\n  \"edit\": \"编辑\",\n  \"img_src_error\": \"图片地址错误\",\n  \"upload\": \"上传\",\n  \"upload_success\": \"上传成功\",\n  \"upload_fail\": \"上传失败\",\n  \"uploaded_address\": \"已上传地址\",\n  \"test_src\": \"测试链接\",\n  \"schema_not_match\": \"schema未匹配到展示组件：\",\n  \"item\": \"项目\",\n  \"search\": \"查询\",\n  \"reset\": \"重置\",\n  \"expand\": \"展开\",\n  \"fold\": \"收起\",\n  \"submit\": \"提交\",\n  \"moveDown\": \"下移\",\n  \"moveUp\": \"上移\"\n};"
  },
  {
    "path": "packages/form-render-mobile/src/models/context.ts",
    "content": "import { createContext } from 'react';\n\nexport const FRContext = createContext(null);\n\nexport const ConfigContext = createContext(null);"
  },
  {
    "path": "packages/form-render-mobile/src/models/store.ts",
    "content": "import { createStore as createx } from 'zustand';\n\ntype FormStore = {\n  schema?: any;\n  flattenSchema: any;\n  context?: any;\n  initialized: boolean,\n  isCardMode: boolean,\n  init?: (schema: FormStore['schema']) => any;\n  setContext: (context: any) => any;\n  setIsCardMode: (mode:boolean) => void;\n};\n\n// 将 useStore 改为 createStore， 并把它改为 create 方法\nexport const createStore = () => createx<FormStore>((setState: any, get: any) => ({\n  initialized: false,\n  schema: {},\n  flattenSchema: {},\n  context: {},\n  isCardMode: false,\n  init: data => {\n    return setState({ \n      initialized: true, \n      ...data\n    });\n  },\n  setContext: context => {\n    return setState({ context });\n  },\n  setIsCardMode: (mode) => setState({ isCardMode: mode }),\n}));"
  },
  {
    "path": "packages/form-render-mobile/src/models/transformProps.ts",
    "content": "import { _get } from '../utils';\n\nconst displayTypeEnum = {\n  column: 'vertical',\n  row: 'horizontal',\n  inline: 'inline',\n};\n\nconst transformProps =  (props: any) => {\n  const {\n    schema,\n    beforeFinish,\n    onMount,\n    displayType = 'column',\n    watch,\n    removeHiddenData = true,\n    readOnly,\n    column = 1,\n    locale,\n    configProvider,\n    validateMessages,\n    debug,\n    id,\n    labelWidth,\n    maxWidth,\n    form,\n    onFinish,\n    onFinishFailed,\n    logOnMount,\n    logOnSubmit,\n    labelCol,\n    fieldCol,\n    className,\n    ...otherProps\n  } = props;\n\n  const formProps = {\n    ...otherProps,\n  };\n\n  if (displayType) {\n    formProps.layout = displayTypeEnum[displayType] || 'horizontal';\n  }\n\n  return {\n    formProps,\n    schema,\n    displayType,\n    onFinish,\n    beforeFinish, // form 没有这个 api, 感觉找不到时机\n    onMount,\n    watch,\n    readOnly,\n    column,\n    className,\n    locale,\n    configProvider,\n    form,\n    labelWidth,\n    validateMessages,\n    id,\n    onFinishFailed,\n    removeHiddenData,\n    logOnMount,\n    logOnSubmit,\n    labelCol,\n    fieldCol,\n    maxWidth\n  };\n};\n\nexport default transformProps;\n"
  },
  {
    "path": "packages/form-render-mobile/src/models/useForm.ts",
    "content": "import { useRef } from 'react';\nimport { Form } from 'antd-mobile';\nimport { isMatch, cloneDeep } from 'lodash-es';\n\nimport { transformFieldsData, getSchemaFullPath } from 'form-render/es/models/formCoreUtils';\nimport { parseBindToValues, parseValuesToBind } from 'form-render/es/models/bindValues';\nimport { flattenSchema as flatten } from 'form-render/es/models/flattenSchema';\nimport filterValuesUndefined from 'form-render/es/models/filterValuesUndefined';\nimport filterValuesHidden from 'form-render/es/models/filterValuesHidden';\nimport { _set, _get, _has, _merge, _mergeWith, isFunction, isObject, isArray, _isUndefined, valueRemoveUndefined, hasFuncProperty } from '../utils';\n\nimport type { FormInstance } from '../type';\n\nconst updateSchemaByPath = (_path: string, _newSchema: any, formSchema: any) => {\n  const path = getSchemaFullPath(_path, formSchema);\n  const currSchema = _get(formSchema, path, {});\n  const newSchema = isFunction(_newSchema) ? _newSchema(currSchema) : _newSchema;\n\n  const result = {\n    ...currSchema,\n    ...newSchema,\n  }\n\n  if (newSchema.props) {\n    result.props = {\n      ...currSchema?.props,\n      ...newSchema.props\n    }\n  }\n  \n  _set(formSchema, path, result);\n};\n\nconst getFieldName = (_path: any): any => {\n  if (!_path) {\n    return undefined;\n  }\n\n  if (typeof _path === 'boolean') {\n    return _path;\n  }\n\n  let result: any[] = [];\n\n  if (isArray(_path)) {\n    result = _path.map((item: any) => {\n      return item.split('.').map((ite: any) => {\n        if (!isNaN(Number(ite))) {\n          return ite * 1;\n        }\n        return ite;\n      });\n    });\n  }\n\n  result = _path.split('.').map((item: any) => {\n    if (!isNaN(Number(item))) {\n      return item * 1;\n    }\n    return item;\n  });\n\n  result = result.map(item => {\n    if (typeof item === 'string' && item?.indexOf('[') === 0  && item?.indexOf(']') === item?.length -1) {\n      return Number(item.substring(1, item.length-1));\n    }\n    return item;\n  });\n \n  return result;\n};\n\nconst useForm = () => {\n  const [form] = Form.useForm() as [FormInstance];\n  const flattenSchemaRef = useRef({});\n  const storeRef: any = useRef(null);\n  const schemaRef = useRef({});\n  const fieldRefs = useRef({});\n\n  const { \n    getFieldError, \n    getFieldsError,\n    setFields,\n    isFieldsTouched,\n    isFieldTouched,\n    isFieldValidating,\n    resetFields,\n    validateFields,\n    ...otherForm\n  } = form;\n  \n  const xform: any = otherForm;\n\n\n  const setStoreData = (data: any) => {\n    const { setState } = storeRef.current;\n    if (!setState) {\n      setTimeout(() => {\n        setState({ schema: schemaRef.current, flattenSchema: flattenSchemaRef.current });\n      }, 0);\n    }\n    setState(data);\n  };\n\n  const handleSchemaUpdate = (newSchema: any) => {\n    // form.__schema = Object.freeze(newSchema);\n    flattenSchemaRef.current = flatten(newSchema) || {};\n    schemaRef.current = newSchema;\n    setStoreData({ schema: newSchema, flattenSchema: flattenSchemaRef.current });\n  };\n\n  xform.setSchema = (obj: any, cover = false) => {\n    if (!isObject(obj)) {\n      return;\n    }\n\n    if (cover) {\n      handleSchemaUpdate(obj);\n      return;\n    }\n\n    const schema = cloneDeep(schemaRef.current);\n    Object.keys(obj || {}).forEach(path => {\n      updateSchemaByPath(path, obj[path], schema);\n    });\n\n    handleSchemaUpdate(schema);\n  }\n\n  // 设置某个字段的协议\n  xform.setSchemaByPath = (_path: string, _newSchema: any) => {\n    // diff 判断是否需要更新，存在函数跳过\n    if (!hasFuncProperty(_newSchema) && isMatch(_newSchema, xform.getSchemaByPath(_path))) {\n      return;\n    }\n\n    const schema = cloneDeep(schemaRef.current);\n    updateSchemaByPath(_path, _newSchema, schema);\n    handleSchemaUpdate(schema);\n  }\n\n  // form.setSchemaByFullPath = (path: string, newSchema: any) => {\n  //   const schema = _cloneDeep(schemaRef.current);\n  //   const currSchema = _get(schema, path, {});\n\n  //   const result = _mergeWith(currSchema, newSchema, (objValue, srcValue, key) => {\n  //     return srcValue;\n  //   });\n\n  //   _set(schema, path, result);\n  //   handleSchemaUpdate(schema);\n  // }\n\n  // 设置表单数据\n  xform.setValues = (_values: any) => {\n    const values = parseBindToValues(_values, flattenSchemaRef.current);\n    form.setFieldsValue(values);\n  }\n\n  // 获取表单数据\n  xform.getValues = (nameList?: any, filterFunc?: any) => {\n    let values = cloneDeep(form.getFieldsValue(getFieldName(nameList), filterFunc));\n    const { removeHiddenData } = storeRef.current?.getState() || {};\n    if (removeHiddenData) {\n      values = filterValuesHidden(values, flattenSchemaRef.current);\n    }\n    values = filterValuesUndefined(values);\n    return parseValuesToBind(values, flattenSchemaRef.current);\n  }\n\n  xform.setValueByPath = (path: any, value: any) => {\n    const name = getFieldName(path);\n    form.setFieldValue(name, value);\n  }\n\n  xform.getValueByPath = (path: string) => {\n    const name = getFieldName(path);\n    return form.getFieldValue(name);\n  }\n\n  xform.getSchemaByPath = (_path: string) => {\n    if (typeof _path !== 'string') {\n      console.warn('请输入正确的路径');\n    }\n    const path = getSchemaFullPath(_path, schemaRef.current);\n    return _get(schemaRef.current, path);\n  };\n\n  xform.getSchema = () => {\n    return schemaRef.current;\n  };\n\n  // 设置一组字段错误\n  xform.setErrorFields = (fieldsError: any[]) => {\n    const fieldsData = transformFieldsData(fieldsError, getFieldName);\n    if (!fieldsData) {\n      return;\n    }\n\n    setFields(fieldsData);\n  };\n\n  // 清空某个字段的错误\n  xform.removeErrorField = (path: any) => {\n    setFields([{ name: getFieldName(path), errors: [] }]);\n  };\n  \n  // 获取对应字段名的错误信息\n  xform.getFieldError = (path: string) => {\n    const name = getFieldName(path);\n    return form.getFieldError(name);\n  }\n\n  // 获取一组字段名对应的错误信息，返回为数组形式\n  xform.getFieldsError = (path: string[]) => {\n    const name = getFieldName(path);\n    return getFieldsError(name);\n  }\n  \n  // 获取隐藏字段数据\n  xform.getHiddenValues = () => {\n    const values = xform.getValues();\n    const allValues = xform.getValues(true);\n    const hiddenValues = {};\n\n    const recursion = (obj1: any, obj2: any, path: any) => {\n      Object.keys(obj1).forEach((key: string) => {\n        const value = obj1[key];\n        const _path = path ? `${path}.${key}` : key;\n        if (!obj2.hasOwnProperty(key)) {\n          _set(hiddenValues, _path, value);\n          return;\n        }\n\n        if (isObject(value)) {\n          recursion(value, obj2[key], _path);\n        }\n\n        if (isArray(value)) {\n          value.map((item: any, index: number) => {\n            recursion(item, _get(obj2, `${key}[${index}]`, []), `${_path}[${index}]`)\n          });\n        }\n      });\n    };\n\n    recursion(allValues, values, null);\n    return hiddenValues;\n  }\n\n  // 设置一组字段状态\n  xform.setFields = (nameList: any[]) => {\n    const fieldsData = transformFieldsData(nameList, getFieldName);\n    if (!fieldsData) {\n      return;\n    }\n    setFields(fieldsData);\n  }\n\n  // 检查一组字段是否被用户操作过，allTouched 为 true 时检查是否所有字段都被操作过\n  xform.isFieldsTouched = (pathList?: string[], allTouched?: boolean) => {\n    const nameList = (pathList || []).map(path => getFieldName(path));\n    return isFieldsTouched(nameList, allTouched);\n  }\n\n  // 检查对应字段是否被用户操作过\n  xform.isFieldTouched = (path: string) => {\n    const name = getFieldName(path);\n    return isFieldTouched(name);\n  }\n\n  // 检查对应字段是否被用户操作过\n  xform.isFieldValidating = (path: string) => {\n    const name = getFieldName(path);\n    return isFieldValidating(name);\n  }\n\n  xform.resetFields = (pathList?: string[]) => {\n    const nameList = (pathList || []).map(path => getFieldName(path));\n    if (nameList.length > 0) {\n      resetFields(nameList);\n    } else {\n      resetFields();\n    }\n  }\n\n  // 触发表单验证\n  xform.validateFields = (pathList?: string[]) => {\n    const nameList = (pathList || []).map(path => getFieldName(path));\n    if (nameList.length > 0) {\n      return validateFields(nameList);\n    }\n    return validateFields();\n  }\n\n  // 获取扁平化 schema\n  xform.getFlattenSchema = (path?: string) => {\n    if (!path) {\n      return flattenSchemaRef.current;\n    }\n    return flattenSchemaRef.current?.[path];\n  }\n\n  xform.__initStore = (store: any) => {\n    storeRef.current = store;\n  }\n\n  xform.setFieldRef = (path: string, ref: any) => {\n    if (!path) {\n      return;\n    }\n    fieldRefs.current[path] = ref;\n  }\n\n  xform.getFieldRef = (path: string) => {\n    return fieldRefs.current[path];\n  }\n \n  return xform;\n};\n\nexport default useForm;\n"
  },
  {
    "path": "packages/form-render-mobile/src/render-core/FieldItem/field.tsx",
    "content": "import React from 'react';\nimport { useUpdateEffect } from 'ahooks';\n\nconst FieldWrapper = (props: any) => {\n  const { Field, fieldProps, defaultValue, ...otherProps } = props;\n \n  useUpdateEffect(() => {\n    otherProps.onChange(defaultValue);\n  }, [JSON.stringify(defaultValue)]);\n\n  return (\n    <Field \n      {...otherProps} \n      {...fieldProps}\n    />\n  );\n}\n\nexport default FieldWrapper; "
  },
  {
    "path": "packages/form-render-mobile/src/render-core/FieldItem/index.tsx",
    "content": "import React, { useContext } from 'react';\nimport { Form } from 'antd-mobile';\n\nimport { _get } from '../../utils';\nimport { FRContext } from '../../models/context';\nimport { isHasExpression, parseAllExpression } from 'form-render/es/models/expression';\nimport { getDependValues } from './module';\nimport Main from './main';\n\nexport default (props: any) => {\n  const { schema, rootPath, ...otherProps } = props;\n\n  const store: any = useContext(FRContext);\n  const { schema: formSchema } = store.getState();\n  const dependencies = schema?.dependencies;\n\n  // No function expressions exist\n  if (!isHasExpression(schema) && !schema?.dependencies) {\n    return <Main {...props} store={store} />;\n  }\n\n  // Need to listen to form values for dynamic rendering\n  return (\n    <Form.Item\n      noStyle\n      //dependencies={schema.dependencies}\n      shouldUpdate={(prevValues, curValues) => {\n        // Observe whether the value of a function expression dependency has changed\n        // TODO 进行优化\n        return true;\n      }}\n    >\n      {(form: any) => {\n        const formData = form.getFieldsValue(true);\n\n        const formDependencies: any[] = [];\n        const dependValues = (dependencies || []).map((depPath: string) => {\n          const item:any[] = [];\n          formDependencies.push(item);\n          return getDependValues(formData, depPath, props, item);\n        });\n        \n        const newSchema =  parseAllExpression(schema, formData, rootPath, formSchema);\n\n        return (\n          <Main \n            schema={{\n              ...newSchema,\n              dependencies: formDependencies\n            }} \n            rootPath={rootPath} \n            {...otherProps}\n            dependValues={dependValues} \n            store={store} \n          />\n        );\n      }}\n    </Form.Item>\n  );\n}"
  },
  {
    "path": "packages/form-render-mobile/src/render-core/FieldItem/main.tsx",
    "content": "import React, { createContext, useContext, useRef, useEffect, useState } from 'react';\nimport { Form, Grid, FormItemProps } from 'antd-mobile';\nimport { useStore } from 'zustand';\nimport classnames from 'classnames';\n\nimport { _get, getWidget } from '../../utils';\nimport { ConfigContext } from '../../models/context';\nimport getRuleList from 'form-render/es/models/validates';\nimport FieldWrapper from './field';\nimport { \n  getParamValue, \n  getFieldProps,\n  getPath,\n  getLabel,\n  getExtraView,\n} from './module';\n\nconst UpperContext: any = createContext(() => {});\nconst valuePropNameMap = {\n  checkbox: 'checked',\n  switch: 'checked',\n  Checkbox: 'checked',\n  Switch: 'checked'\n};\n\nexport default (props: any) => {\n  const { store, schema, path, children, dependValues, rootPath, renderCore } = props;\n\n  if (schema?.hidden) {\n    return null;\n  }\n\n  const [needOnClick, setNeedOnClick] = useState(false);\n\n  const fieldRef: any = useRef(null);\n  const formCtx: any = useStore(store, (state: any) => state.context);\n  const upperCtx: any = useContext(UpperContext);\n  const configCtx = useContext(ConfigContext);\n  \n  const { form, widgets, methods, globalProps }: any = configCtx;\n  const { hidden, properties, dependencies, inlineMode: _inlineMode, remove, removeText, visible = true, layout, ...otherSchema } = schema;\n  const getValueFromKey = getParamValue(formCtx, upperCtx, schema);\n\n  useEffect(() => {\n    form.setFieldRef(fieldProps.addons.dataPath, fieldRef);\n  }, []);\n\n  useEffect(() => {\n    if (fieldRef?.current?.open) {\n      setNeedOnClick(true);\n    }\n  }, [fieldRef.current]);\n  \n\n  let Widget = getWidget(widgets, schema.widget, schema)\n\n  const fieldProps = getFieldProps(schema, {\n    widgets,\n    methods,\n    form,\n    dependValues,\n    globalProps,\n    path: getPath(path),\n    rootPath,\n    fieldRef\n  });\n\n  const displayType = getValueFromKey('displayType');\n  const labelWidth = getValueFromKey('labelWidth');\n\n  if (['collapse'].includes(schema.widget)) {\n    return <Widget {...fieldProps} renderCore={renderCore} />\n  }\n\n  if (children) {\n    fieldProps.children = (\n      <Grid columns={1}>\n        {children}\n      </Grid>\n    );\n    \n    return (\n      <UpperContext.Provider\n        value={{\n          column: schema.column,\n          labelCol: schema.labelCol,\n          fieldCol: schema.fieldCol,\n          displayType: schema.displayType,\n          labelWidth: schema.labelWidth,\n          noStyle: schema.noStyle,\n          exist: true,\n        }}\n      >\n        <Widget \n          labelWidth={labelWidth} \n          displayType={schema.displayType} \n          {...fieldProps} \n          {...otherSchema} \n        />\n      </UpperContext.Provider>\n    );\n  }\n\n  // Render field components\n  let label = getLabel(schema, displayType, widgets, fieldProps.addons);\n  let noStyle = getValueFromKey('noStyle');\n\n  const extra = getExtraView('extra', schema, widgets);\n  const help = getExtraView('help', schema, widgets);\n  const tooltip = getExtraView('tooltip', schema, widgets);\n  const ruleList = getRuleList(schema, form, methods, fieldRef);\n  const readOnly = getValueFromKey('readOnly');\n  const valuePropName = schema.valuePropName || valuePropNameMap[schema.widget] || undefined;\n\n  if (readOnly) {\n    fieldProps.readOnly = readOnly;\n  }\n\n  if (readOnly) {\n    Widget = getWidget(widgets, schema.readOnlyWidget, schema, true);\n  }\n\n  const defaultValue = schema.default ?? schema.defaultValue;\n\n  const itemProps: FormItemProps = {\n    label,\n    valuePropName,\n    hidden,\n    extra,\n    help: tooltip || help,\n    noStyle,\n    dependencies,\n    name: path,\n    initialValue: defaultValue,\n    rules: readOnly ? [] : ruleList,\n    className:classnames('fr-field', {'fr-field-visibility': !visible})\n  };\n\n  if (layout) {\n    itemProps.layout = {\n      column: 'vertical',\n      row: 'horizontal',\n    }[layout];\n  }\n\n  if (!readOnly && needOnClick) {\n    itemProps.onClick = () => {\n      fieldRef.current.open();\n    };\n  }\n\n  return (\n    <Grid.Item>\n      <Form.Item {...itemProps}>\n        <FieldWrapper\n          Field={Widget}\n          fieldProps={fieldProps}\n          defaultValue={defaultValue}\n        />\n      </Form.Item>\n    </Grid.Item>\n  );\n}\n"
  },
  {
    "path": "packages/form-render-mobile/src/render-core/FieldItem/module.tsx",
    "content": "import React from 'react';\nimport { _get, isObject, getArray, isArray, isNumber } from '../../utils';\n\n// return dataIndex、dataPath、schemaPath\nconst getPathObj = ({ rootPath = [], path }) => {\n\n  const pathList = (path || '').split('.');\n  const dataIndex: any[] = [];\n  const schemaIndex: any[] = [];\n  const dataPathList: any[] = [];\n\n  // dataIndex\n  rootPath.forEach((item: any, index: number) => {\n    if (isNumber(item)) {\n      dataIndex.push(item);\n      return;\n    }\n\n    if (isNumber(rootPath[index+1])) {\n      schemaIndex.push(`${item}[]`);\n    } else {\n      schemaIndex.push(item);\n    }\n  });\n\n  // dataPath\n  let list: any[] = [...rootPath];\n  list.pop();\n  list = [...list, ...pathList];\n\n  list.forEach((item: any, index: number) => {\n    if (isNumber(item)) {\n      dataPathList.push(`[${item}]`)\n    } else {\n      dataPathList.push(item)\n    }\n  });\n\n  const dataPath = dataPathList.join('.');\n\n  // schemaPath\n  const _path = pathList;\n  if (_path[0] && isNumber(_path[0])) {\n    _path.splice(0, 1);\n  }\n  const schemaPath = [...schemaIndex, _path].join('.');\n\n  // console.log(path, rootPath, '-------', dataIndex, dataPath, schemaPath);\n\n  return {\n    dataIndex,\n    dataPath,\n    schemaPath\n  };\n};\n\nexport const getPath = (path: any) => {\n  if (!path) {\n    return null;\n  }\n  if (isArray(path)) {\n    return path.join('.');\n  }\n\n  return path;\n};\n\nexport const getLabel = (schema: any, displayType: string, widgets: any, addons: any) => {\n  const { title, description, descWidget, labelWidget } = schema;\n\n  const LabelNode = widgets[labelWidget];\n\n  if (LabelNode) {\n    return <LabelNode schema={schema} addons={addons} />\n  }\n\n  if ((!description && !descWidget)) {\n    return title;\n  }\n\n  const RenderDesc = () => {\n    const Widget = widgets[descWidget];\n    if (Widget) {\n      return <Widget schema={schema} addons={addons} />;\n    }\n\n    if (description) {\n      return (\n        <span className='fr-desc ml2'>\n          ({description})\n        </span>\n      )\n    }\n    return null;\n  };\n\n  if (displayType === 'inline') {\n    return title;\n  }\n\n  return (\n    <>\n      {title}\n      <RenderDesc />\n    </>\n  )\n};\n\n\nexport const getExtraView = (extraKey: string, schema: any, widgets: any) => {\n  const extra = schema[extraKey];\n  if (!extra) {\n    return;\n  }\n\n  // extra 自定义\n  const widgetName = extra?.widget;\n  if (widgetName) {\n    const Widget = widgets[widgetName];\n    if (!Widget) {\n      return;\n    }\n    return <Widget schema={schema} />;\n  }\n\n\n  let __html = '';\n  if (typeof extra === 'string') {\n    __html = extra;\n  }\n  // 内部BU使用的口子，这个api不对外，也没有必要\n  if (extra?.text) {\n    __html = extra.text;\n  }\n\n  if (!__html) {\n    return;\n  }\n\n  return (\n    <div\n      className='fr-form-item-extra'\n      dangerouslySetInnerHTML={{ __html }}\n    />\n  )\n}\n\n\nexport const getParamValue = (formCtx: any, upperCtx: any, schema: any) => (valueKey: string, isTop = true) => {\n  if (isTop) {\n    return schema[valueKey] ?? upperCtx[valueKey] ?? formCtx[valueKey];\n  }\n  return schema[valueKey] ?? upperCtx[valueKey];\n};\n\nexport const getFieldProps = (schema: any, { widgets, methods, form, dependValues, globalProps, path, rootPath, fieldRef }) => {\n  const pathObj = getPathObj({ path, rootPath });\n \n  const fieldProps = {\n    ...schema.props,\n    addons: {\n      ...form,\n      globalProps,\n      dependValues,\n      fieldRef,\n      ...pathObj\n    }\n  };\n\n  if (dependValues?.length > 0) {\n    fieldProps.dependValues = dependValues;\n  }\n\n  ['placeholder', 'disabled', 'format', 'className'].forEach(key => {\n    if (schema[key]) {\n      fieldProps[key] = schema[key];\n    }\n  });\n\n  // 兼容 1.0 版本逻辑 enum => options\n  if (schema.enum && !schema.props?.options) {\n    const { enum: enums, enumNames } = schema;\n    fieldProps.options = getArray(enums).map((item: any, index: number) => {\n      let label = enumNames && Array.isArray(enumNames) ? enumNames[index] : item;\n      const isHtml = typeof label === 'string' && label[0] === '<';\n      if (isHtml) {\n        label = <span dangerouslySetInnerHTML={{ __html: label }} />;\n      }\n      return { label, value: item };\n    });\n  }\n\n  // 以 props 结尾的属性，直接透传\n  Object.keys(schema).forEach(key => {\n    if (\n      typeof key === 'string' &&\n      key.toLowerCase().indexOf('props') > -1 &&\n      key.length > 5\n    ) {\n      fieldProps[key] = schema[key];\n    }\n  });\n\n  // 支持 addonAfter 为自定义组件的情况\n  if (isObject(fieldProps.addonAfter) && fieldProps.addonAfter.widget) {\n    const AddonAfterWidget = widgets[fieldProps.addonAfter.widget];\n    fieldProps.addonAfter = <AddonAfterWidget {...schema} />;\n  }\n\n  // Dynamic Mapping of Methods\n  if (isObject(schema.methods)) {\n    Object.keys(schema.methods).forEach(key => {\n      const name = schema.methods[key];\n      fieldProps[key] = methods[name];\n    });\n  }\n\n  fieldProps.schema = schema;\n  return fieldProps;\n};\n\n\n/*\n   * Get depend values\n   *\n   * 1. normal path\n   * Just get value of path in formData\n   *\n   * 2. list path\n   * Like `list[].foo`.`[]` means the same index as the current item.\n   * You can pass `[index]` to get specific item at the index of list, such as `list[1].foo`.\n   * Support more complex path like `list[].foo[].bar`\n   */\nexport const getDependValues = (formData: any, dependPath: string, props: any, dependencieItem: any[]) => {\n  const indexReg =/\\[[0-9]*\\]/;\n\n  if (indexReg.test(dependPath)) {\n    const currentIndex = _get(props, 'path.0')\n    const dependIndex = dependPath\n      .match(indexReg)[0]\n      .replace('[', '')\n      .replace(']', '')\n\n    const listPath = dependPath.split(indexReg)[0];\n    const itemIndex = dependIndex || currentIndex;\n    const itemPath = dependPath.replace(`${listPath}[${dependIndex}].`, '')\n    const listData = _get(formData, `${listPath}[${itemIndex}]`);\n\n    dependencieItem.push(listPath, itemIndex);\n\n    return getDependValues(listData, itemPath, props, dependencieItem);\n  }\n\n  dependencieItem.push(...dependPath.split('.'));\n\n  return _get(formData, dependPath);\n}\n"
  },
  {
    "path": "packages/form-render-mobile/src/render-core/FieldList/index.less",
    "content": ".frm-list {\n  .frm-widget-card {\n    margin-left: 0;\n    margin-right: 0;\n    margin-top: 0;\n  }\n}\n"
  },
  {
    "path": "packages/form-render-mobile/src/render-core/FieldList/index.tsx",
    "content": "import { Form, Grid } from 'antd-mobile';\nimport { AddCircleOutline } from 'antd-mobile-icons';\nimport { parseAllExpression } from 'form-render/es/models/expression';\nimport React, { createContext, useContext, useEffect, useState } from 'react';\nimport { useStore } from 'zustand';\nimport { ConfigContext, FRContext } from '../../models/context';\nimport { isFunction } from '../../utils';\nimport './index.less';\n\nconst UpperContext = createContext(() => {});\nconst getParamValue =\n  (formCtx: any, upperCtx: any, schema: any) => (valueKey: string) => {\n    return schema[valueKey] ?? upperCtx[valueKey] ?? formCtx[valueKey];\n  };\n\nexport default (props: any) => {\n  const { schema: _schema, path, renderCore, rootPath: _rootPath } = props;\n\n  const store: any = useContext(FRContext);\n  const formCtx: any = useStore(store, (state: any) => state.context);\n  const upperCtx: any = useContext(UpperContext);\n  const { form, methods }: any = useContext(ConfigContext);\n\n  const formData = form.getFieldsValue(true);\n  const { schema: formSchema } = store.getState();\n\n  const { items, ...otherSchema } = _schema;\n  const schema = {\n    items,\n    ...parseAllExpression(otherSchema, formData, _rootPath, formSchema),\n  };\n\n  const defaultValue = schema.default ?? (schema.defaultValue || [{}]);\n  const { onAdd, onRemove } = schema.props || {};\n  const [fieldLength, setFieldLength] = useState<number>();\n\n  useEffect(() => {\n    const fieldsLength = getFieldsLength();\n    setFieldLength(fieldsLength);\n  }, []);\n\n  const getFieldsLength = () => {\n    const fieldValue = form.getFieldValue(path);\n    return Array.isArray(fieldValue) ? fieldValue.length : 0;\n  };\n\n  const handleAdd = (add: any, data?: any) => {\n    let addFunc = onAdd;\n    if (typeof onAdd === 'string') {\n      addFunc = methods[onAdd];\n    }\n\n    if (isFunction(addFunc)) {\n      addFunc((funData?: any) => add(funData || data), { schema, data });\n      return;\n    }\n    add(data);\n    const fieldsLength = getFieldsLength();\n    setFieldLength(fieldsLength);\n  };\n\n  const handleRemove = (remove: any, index: number) => {\n    let removeFunc = onRemove;\n    if (typeof onRemove === 'string') {\n      removeFunc = methods[onRemove];\n    }\n\n    if (isFunction(removeFunc)) {\n      removeFunc(() => remove(index), { schema, index });\n      return;\n    }\n\n    remove(index);\n\n    const fieldsLength = getFieldsLength();\n    setFieldLength(fieldsLength);\n  };\n\n  const getValueFromKey = getParamValue(formCtx, upperCtx, schema);\n\n  const readOnly = getValueFromKey('readOnly');\n\n  if (schema.hidden) {\n    return null;\n  }\n\n  const preRootPath = [...(_rootPath || [])].splice(0, _rootPath.length - 1);\n  const rootPath = [...preRootPath, ...path];\n\n  return (\n    <Grid.Item className=\"frm-list\">\n      <Form.Array\n        name={path}\n        initialValue={defaultValue}\n        renderAdd={\n          !readOnly && (!schema.max || fieldLength < schema.max)\n            ? () => (\n                <span>\n                  <AddCircleOutline /> 添加\n                </span>\n              )\n            : undefined\n        }\n        onAdd={({ add }) => handleAdd(add)}\n        renderHeader={({ index }, { remove }) => (\n          <>\n            {schema.title && (\n              <span>\n                {schema.title} {index + 1}\n              </span>\n            )}\n            {!readOnly && (!schema.min || fieldLength > schema.min) && (\n              <a\n                onClick={() => handleRemove(remove, index)}\n                style={{ float: 'right' }}\n              >\n                删除\n              </a>\n            )}\n          </>\n        )}\n      >\n        {fields =>\n          fields.map(({ index }) => {\n            return renderCore({\n              schema,\n              parentPath: [index],\n              rootPath: [...rootPath, index],\n            });\n          })\n        }\n      </Form.Array>\n    </Grid.Item>\n  );\n};\n"
  },
  {
    "path": "packages/form-render-mobile/src/render-core/index.tsx",
    "content": "import React from 'react';\nimport FieldItem from './FieldItem';\nimport FieldList from './FieldList';\nimport sortProperties from 'form-render/es/models/sortProperties';\ninterface RenderCoreProps {\n  schema: any;\n  rootPath?: any[] | undefined;\n  parentPath?: any[] | undefined;\n  [key: string]: any\n};\n\ninterface RenderItemProps {\n  schema: any;\n  rootPath?: any[] | undefined;\n  path?: any[] | undefined;\n  key?: string | undefined;\n};\n\nconst renderItem = (props: RenderItemProps) => {\n  let { schema, key, path, rootPath } = props;\n\n  // render List\n  if (schema.type === 'array' && schema.items?.type === 'object') {\n    return (\n      <FieldList\n        key={key}\n        schema={schema}\n        path={path}\n        rootPath={rootPath}\n        renderCore={RenderCore}\n      />\n    );\n  }\n\n  // render Object | field\n  let child: React.ReactNode = null;\n\n  // has child schema\n  if (schema?.properties) {\n    child = RenderCore({ schema, parentPath: path, rootPath })\n    path = undefined;\n  }\n\n  return (\n    <FieldItem\n      key={key}\n      schema={schema}\n      path={path}\n      rootPath={rootPath}\n      children= {child}\n      renderCore={RenderCore}\n    />\n  );\n}\n\nconst RenderCore = (props: RenderCoreProps) : any => {\n  const { schema, parentPath = [], rootPath = [] } = props;\n  if (!schema || Object.keys(schema).length === 0) {\n    return null;\n  }\n \n  // render List.item\n  if (schema?.items) {\n    return renderItem({ schema: schema.items, path: parentPath, rootPath });\n  }\n\n  // render Objiect | field\n  return sortProperties(Object.entries(schema.properties || {})).map(([key, item]) => {\n    const path = [...parentPath, key];\n   \n    return renderItem({ schema: item, path, key, rootPath });\n  });\n}\n\nexport default RenderCore;"
  },
  {
    "path": "packages/form-render-mobile/src/type.ts",
    "content": "import { RuleItem } from 'async-validator';\nimport * as React from 'react';\nimport type { FormProps as AntdFormProps } from 'antd-mobile';\nimport type { ConfigProviderProps } from 'antd/es/config-provider';\n\ntype AntdFormInstance = Exclude<AntdFormProps['form'], undefined>\n\nexport type { RuleItem } from 'async-validator';\nexport type SchemaType =\n  | 'string'\n  | 'object'\n  | 'array'\n  | 'number'\n  | 'boolean'\n  | 'void'\n  | 'date'\n  | 'datetime'\n  | 'block'\n  | string;\n\nexport interface SchemaBase {\n  type?: SchemaType;\n  title?: string;\n  description?: string;\n  descType?: 'text' | 'icon';\n  format?:\n  | 'image'\n  | 'textarea'\n  | 'color'\n  | 'email'\n  | 'url'\n  | 'dateTime'\n  | 'date'\n  | 'time'\n  | 'upload'\n  | (string & {});\n  default?: any;\n  /** 是否必填，支持 `'{{ formData.xxx === \"\" }}'` 形式的表达式 */\n  required?: boolean | string;\n  placeholder?: string;\n  bind?: false | string | string[];\n  dependencies?: string[];\n  /** 最小值，支持表达式 */\n  min?: number | string;\n  /** 最大值，支持表达式 */\n  max?: number | string;\n  /** 是否禁用，支持 `'{{ formData.xxx === \"\" }}'` 形式的表达式 */\n  disabled?: boolean | string;\n  /** 是否只读，支持 `'{{ formData.xxx === \"\" }}'` 形式的表达式 */\n  readOnly?: boolean | string;\n  /** 是否隐藏，隐藏的字段不会在 formData 里透出，支持 `'{{ formData.xxx === \"\" }}'` 形式的表达式 */\n  hidden?: boolean | string;\n  displayType?: 'row' | 'column' | string;\n  width?: string | number;\n  labelWidth?: number | string;\n  maxWidth?: number | string;\n  column?: number;\n  className?: string;\n  widget?: string;\n  readOnlyWidget?: string;\n  extra?: string;\n  properties?: Record<string, Schema>;\n  items?: Schema;\n  /** 多选，支持表达式 */\n  enum?: Array<string | number> | string;\n  /** 多选label，支持表达式 */\n  enumNames?: Array<string | number> | string;\n  rules?: RuleItem | RuleItem[];\n  props?: Record<string, any>;\n  /**扩展字段 */\n  'add-widget'?: string;\n}\n\nexport type Schema = Partial<SchemaBase>;\n\nexport interface Error {\n  /** 错误的数据路径 */\n  name: string;\n  /** 错误的内容 */\n  error: string[];\n}\n\nexport interface FormParams {\n  formData?: any;\n  onChange?: (data: any) => void;\n  onValidate?: (valid: any) => void;\n  showValidate?: boolean;\n  /** 数据分析接口，表单展示完成渲染时触发 */\n  logOnMount?: (stats: any) => void;\n  /** 数据分析接口，表单提交成功时触发，获得本次表单填写的总时长 */\n  logOnSubmit?: (stats: any) => void;\n}\n\nexport interface ValidateParams {\n  formData: any;\n  schema: Schema;\n  error: Error[];\n\n  [k: string]: any;\n}\n\nexport interface ResetParams {\n  formData?: any;\n  submitData?: any;\n  errorFields?: Error[];\n  touchedKeys?: any[];\n  allTouched?: boolean;\n\n  [k: string]: any;\n}\n\nexport interface FieldParams {\n  name: string;\n  error?: string[];\n  touched?: boolean;\n  validating?: boolean;\n  value?: any;\n}\n\nexport interface ListOperate {\n  btnType: 'text' | 'icon';\n  hideMove: boolean;\n}\n\nexport interface GlobalConfig {\n  listOperate: ListOperate\n}\n\nexport interface FormInstance extends AntdFormInstance {\n  init: any;\n  __schema: any;\n  __initStore: (data: any) => any;\n  setSchemaByFullPath: (path: string, schema: any) => any;\n  /**\n   *  根据路径动态设置 Schema\n   */\n  setSchemaByPath: (path: string, schema: any) => any;\n  /**\n   * 获取隐藏的表单数据\n   */\n  getHiddenValues: () => any;\n  /**\n   *  设置 Schema\n   */\n  setSchema: (schema: any, cover?: boolean) => void;\n  /** \n   * 根据路径获取 Schema\n   */\n  getSchemaByPath: (path: string) => any;\n  /**\n   * 外部手动修改 errorFields 校验信息\n   */\n  setErrorFields: (errors: any[]) => void;\n  /**\n   * 外部手动删除某一个 path 下所有的校验信息\n   */\n  removeErrorField: (path: string) => any;\n  /** \n   * 根据路径修改表单值\n   */\n  setValueByPath: FormInstance['setFieldValue'];\n  /** \n   * 获取表单值\n   */\n  getValues: FormInstance['getFieldsValue'];\n  /**\n   * 表单校验错误的数组\n   */\n  errorFields: FormInstance['getFieldsError'];\n  /** \n   * 设置表单值\n   */\n  setValues: FormInstance['setFieldsValue'];\n  /**\n   * 获取表单 schema\n   */\n  getSchema: () => any;\n  /**\n   * 存储 field 的 ref 对象\n   */\n  setFieldRef: (path: string, ref: any) => void;\n  /**\n   * 获取 field 的 ref 对象\n   */\n  getFieldRef: (path: string) => any;\n}\n\nexport type WatchProperties = {\n  [path: string]:\n  | {\n    handler: (value: any) => void;\n    immediate?: boolean;\n  }\n  | ((value: any) => void);\n};\n\nexport interface FRProps extends AntdFormProps {\n  /** \n   * 表单顶层的className\n   */\n  className?: string;\n  /** \n   * 表单顶层的样式\n   */\n  style?: React.CSSProperties;\n  /** \n   * 表单 schema\n   */\n  schema: Schema;\n  /** \n   * form单例\n   */\n  form: FormInstance;\n  /** \n   * 自定义组件\n   */\n  widgets?: Record<string, any>;\n  /** \n   * 标签元素和输入元素的排列方式，column-分两行展示，row-同行展示，inline-自然顺排，默认`column`\n   */\n  displayType?: 'column' | 'row';\n  /**\n   *  只读模式\n   */\n  readOnly?: boolean;\n  /** \n   * 禁用模式\n   */\n  disabled?: boolean;\n  /** \n   * antd的全局config\n   */\n  configProvider?: ConfigProviderProps;\n  /** \n   * 覆盖默认的校验信息\n   */\n  validateMessages?: ConfigProviderProps['form']['validateMessages'];\n  /**\n   * 展示语言，目前只支持中文、英文\n   */\n  locale?: 'zh-CN' | 'en-US';\n  /**\n   * 数据会作为 beforeFinish 的第四个参数传入\n   */\n  config?: any;\n  /**\n   * 类似于 vuejs 的 watch 的用法，监控值的变化，触发 callback\n   */\n  watch?: WatchProperties;\n  /** \n  * 表单全局配置\n  */\n  globalConfig?: GlobalConfig;\n  /** \n   * 表单的全局共享属性\n   */\n  globalProps?: any;\n  /** \n   * 表单首次加载钩子\n   */\n  onMount?: () => void;\n  /** \n   * 表单提交前钩子\n   */\n  beforeFinish?: (params: ValidateParams) => Error[] | Promise<Error[]>;\n  /** \n   * 表单提交后钩子\n   */\n  onFinish?: (formData: any) => void;\n  /** \n   * 字段值更新时触发回调事件\n   */\n  onValuesChange?: (\n    changedValues: {\n      dataPath: string;\n      value: any;\n      dataIndex: number[] | unknown;\n    },\n    formData: any\n  ) => void;\n  /** \n   * 隐藏的数据是否去掉，默认不去掉\n   */\n  removeHiddenData?: boolean;\n  /** \n   * 扩展方法\n   */\n  methods?: Record<string, Function>;\n}\n\nexport interface SearchProps<RecordType> extends Omit<FRProps, 'form'> {\n  debug?: boolean;\n  searchBtnStyle?: React.CSSProperties;\n  searchBtnClassName?: string;\n  displayType?: any;\n  propsSchema?: any;\n  className?: string;\n  style?: React.CSSProperties;\n  hidden?: boolean;\n  searchOnMount?: boolean | unknown;\n  searchWithError?: boolean;\n  searchBtnRender?: (\n    submit: Function,\n    clearSearch: Function,\n    other: any\n  ) => React.ReactNode[];\n  searchText?: string;\n  resetText?: string;\n  onSearch?: (search: any) => any;\n  afterSearch?: (params: any) => any;\n  widgets?: any;\n  form?: any;\n  [key: string]: any\n}\n\n\ndeclare const FR: React.FC<FRProps>;\n\nexport declare function useForm(params?: FormParams): FormInstance;\n\nexport type ConnectedForm<T> = T & {\n  form: FormInstance;\n};\n\nexport declare function connectForm<T extends {} = any>(\n  component: React.ComponentType<ConnectedForm<T>>\n): React.ComponentType<T>;\n\nexport default FR;\n"
  },
  {
    "path": "packages/form-render-mobile/src/utils/index.ts",
    "content": "import { set, get, cloneDeep, has as _has, merge, isUndefined, omitBy, mergeWith, some } from 'lodash-es';\n\nexport const _set = set;\nexport const _get = get;\nexport const _cloneDeep = cloneDeep;\n// export const _has = has;\nexport { _has };\nexport const _merge = merge;\nexport const _isUndefined = isUndefined;\nexport const _omitBy = omitBy;\nexport const _mergeWith = mergeWith;\n\n// 首字母转大写\nconst strToUpperCase = (str: string) => {\n  if (!str) {\n    return '';\n  }\n  return str.charAt(0).toUpperCase() + str.slice(1);\n}\n\n// 首字母转小写\nconst strToLowerCase = (str: string) => {\n  if (!str) {\n    return '';\n  }\n  return str.charAt(0).toLowerCase() + str.slice(1);\n}\n\nexport const isObject = (data: any) => {\n  const str = Object.prototype.toString.call(data);\n  return str.indexOf('Object') > -1;\n}\n\nexport const isArray = (data: any) => {\n  const str = Object.prototype.toString.call(data);\n  return str.indexOf('Array') > -1;\n}\n\nexport const isFunction = (data: any) => typeof data === 'function';\n\nexport function isUrl(string: string) {\n  const protocolRE = /^(?:\\w+:)?\\/\\/(\\S+)$/;\n  // const domainRE = /^[^\\s\\.]+\\.\\S{2,}$/;\n  if (typeof string !== 'string') return false;\n  return protocolRE.test(string);\n}\n\nexport const isNumber = (str: string | number) => !isNaN(Number(str))\n\nexport const getArray = (arr, defaultValue = []) => {\n  if (Array.isArray(arr)) return arr;\n  return defaultValue;\n};\n\nexport function getFormat(format) {\n  let dateFormat;\n  switch (format) {\n    case 'date':\n      dateFormat = 'YYYY-MM-DD';\n      break;\n    case 'time':\n      dateFormat = 'HH:mm:ss';\n      break;\n    case 'dateTime':\n      dateFormat = 'YYYY-MM-DD HH:mm:ss';\n      break;\n    case 'week':\n      dateFormat = 'YYYY-w';\n      break;\n    case 'year':\n      dateFormat = 'YYYY';\n      break;\n    case 'quarter':\n      dateFormat = 'YYYY-Q';\n      break;\n    case 'month':\n      dateFormat = 'YYYY-MM';\n      break;\n    default:\n      // dateTime\n      if (typeof format === 'string') {\n        dateFormat = format;\n      } else {\n        dateFormat = 'YYYY-MM-DD';\n      }\n  }\n  return dateFormat;\n}\n\n// TODO: to support case that item is not an object\nexport function isObjType(schema: any) {\n  //return schema?.type === 'object' && schema.properties && !schema.widget;\n  return schema?.type === 'object' && schema.properties;\n};\n\nexport function isListType(schema: any) {\n  return schema?.type === 'array' && isObjType(schema?.items) && schema?.enum === undefined;\n};\n\nexport function isCheckBoxType(schema, readOnly) {\n  if (readOnly) return false;\n  if (schema.widget === 'checkbox') return true;\n  if (schema && schema.type === 'boolean') {\n    if (schema.enum) return false;\n    if (schema.widget === undefined) return true;\n    return false;\n  }\n}\n\nexport const valueRemoveUndefined = (values: any, notFilter?: boolean) => {\n  const recursionArray = (list: any[]) => {\n    let result = list.map(item => {\n      if (isObject(item)) {\n        return recursionObj(item);\n      }\n\n      if (isArray(item)) {\n        return recursionArray(item);\n      }\n\n      return item;\n    });\n\n    // 数组会变成对象，感觉 underfined 不能剔除，会影响顺序\n    // result = omitBy(result, isUndefined);\n\n    if (Object.keys(result).length === 0) {\n      return undefined;\n    }\n    return result;\n  };\n\n  const recursionObj = (_data: any) => {\n    let data =  omitBy(_data, isUndefined);\n\n    Object.keys(data).forEach(key => {\n      const item = data[key];\n      if (isObject(item)) {\n        data[key] = recursionObj(item);\n      }\n\n      if (isArray(item)) {\n        const result = recursionArray(item) || [];\n        data[key] = notFilter ? result : result.filter((item: any) => item !== undefined);\n      }\n    });\n\n    data = omitBy(data, isUndefined);\n\n    if (Object.keys(data).length === 0) {\n      return undefined;\n    }\n    return data;\n  }\n\n  return recursionObj(values) || {};\n}\n\nexport const translation = (configCtx: any) => (key: string) => {\n  const locale = configCtx?.locale.FormRender;\n  return locale[key];\n}\n\nexport const warn = (str:string, ...args: any[]) => {\n  if (process.env.NODE_ENV === 'development') {\n    console.error('[form-render-mobile]:', str, ...args);\n  }\n}\n\nexport const getWidget = (widgets: any, widgetName: string, schema: any, readOnly?: boolean) => {\n  let widget = widgets[strToLowerCase(widgetName)];\n  if (!widget) {\n    widget = widgets[strToUpperCase(widgetName)];\n  }\n\n  if (!widget) {\n    widget = widgets['Html'] || widgets['html'];\n    if (!readOnly) {\n      warn(`Can not find widget component named ${widgetName}, please check the schema and widgets`, schema);\n    }\n  }\n\n  return widget || null;\n}\n\nexport const hasFuncProperty = (obj: any) => {\n  return some(obj, (value) => {\n    if (isFunction(value)) {\n      return true;\n    }\n    if (isObject(value)) {\n      return hasFuncProperty(value);\n    }\n    return false;\n  });\n};\n\n"
  },
  {
    "path": "packages/form-render-mobile/src/widgets/Card/index.less",
    "content": ".frm-widget-card {\n  border-radius: 4px;\n  margin: 0 12px;\n\n  margin-top: 12px;\n\n  .adm-card-header {\n    border-bottom: none !important;\n  }\n\n  .adm-card-body {\n    padding: 0;\n  }\n\n  .adm-form-item {\n    padding-left: 4px;\n  }\n\n  &-title {\n    margin-right: 6px;\n    padding-left: 4px;\n  }\n\n  &-desc {\n    font-size: 12px;\n    word-break: break-all;\n    color: rgba(0, 0, 0, .45);\n  }\n\n  &-no-title {\n    .adm-grid-item:first-of-type {\n      .adm-list-item-content {\n        border-top: none;\n      }\n    }\n  }\n}\n\n"
  },
  {
    "path": "packages/form-render-mobile/src/widgets/Card/index.tsx",
    "content": "import React from 'react';\nimport { Card } from 'antd-mobile';\nimport { FRContext } from '../../models/context';\nimport {useStore} from 'zustand'\nimport cx from 'classnames';\n\nimport './index.less';\n\nconst prefix = 'frm-widget-card';\n\nconst BoxCard = (props: any) => {\n  const { children, description, title } = props;\n  const context = React.useContext(FRContext);\n  const setIsCardMode = useStore(context, (store: any) => store.setIsCardMode);\n\n  React.useEffect(() => {\n    setIsCardMode(true);\n  }, []);\n\n  let titleNode = (\n    <>\n      {title && <span className={`${prefix}-title`}>{title}</span>}\n      {description && (\n        <span className={`${prefix}-desc`}>\n          {description}\n        </span>\n      )}\n    </>\n  )\n\n  const noTitle = !title && !description;\n\n  const className = cx(prefix, {\n    [`${prefix}-no-title`]: noTitle,\n  })\n  \n  return (\n    <Card\n      className={className}\n      title={noTitle ? null : titleNode}\n    >\n      {children}\n    </Card>\n  );\n}\n\nexport default BoxCard;\n"
  },
  {
    "path": "packages/form-render-mobile/src/widgets/Cascader/index.tsx",
    "content": "import React, { useRef, useImperativeHandle} from 'react';\nimport { Cascader } from 'antd-mobile';\nimport { omit } from 'lodash-es';\n\nexport default (props: any) => {\n  const { \n    placeholder = '请选择',\n    value,\n    onChange,\n    options,\n    ...rest\n  } = omit(props, ['addons', 'schema']);\n\n  const pickerRef: any = useRef(null);\n  \n  // 使用useImperativeHandle暴露方法给外部\n  useImperativeHandle(props.addons.fieldRef, () => ({\n    ...pickerRef?.current\n  }));\n \n  return (\n    <Cascader\n      {...rest}\n      ref={pickerRef}\n      value={value}\n      options={options}\n      onConfirm={onChange}\n    >\n      {items => {\n        if (items.every(i => i === null)) {\n          return <span style={{ color: '#ccc' }}>{placeholder}</span>;\n        } else {\n          return items.map(i => i?.label ?? '未选择').join('-')\n        }\n      }}\n    </Cascader>\n  );\n}\n"
  },
  {
    "path": "packages/form-render-mobile/src/widgets/Collapse/index.tsx",
    "content": "import React from 'react';\nimport { Collapse } from 'antd-mobile';\n\nexport default ({ schema, addons, renderCore, ...props }) => {\n  const { items } = schema;\n\n  return (\n    <Collapse defaultActiveKey={['1']} {...props}>\n      {Object.keys(items).map((key: string) => {\n        const { type, properties, ...other } = items[key];\n        return (\n          <Collapse.Panel key={key} {...other}>\n            {renderCore({ schema: { type, properties }, parentPath: [key] })}\n          </Collapse.Panel>\n        );\n      })}\n    </Collapse>\n  );\n}\n"
  },
  {
    "path": "packages/form-render-mobile/src/widgets/DatePicker/index.tsx",
    "content": "import React, { useRef, useImperativeHandle} from 'react';\nimport { DatePicker as AntdDatePicker } from 'antd-mobile';\nimport { getFormat } from '../utils';\nimport dayjs from 'dayjs';\nimport formatPlugin from 'dayjs/plugin/advancedFormat';\nimport weekOfYearPlugin from 'dayjs/plugin/weekOfYear';\nimport updateLocalePlugin from 'dayjs/plugin/updateLocale';\nimport { omit } from 'lodash-es';\n\ndayjs.extend(updateLocalePlugin);\ndayjs.extend(formatPlugin);\ndayjs.extend(weekOfYearPlugin);\n\ndayjs.updateLocale(\"en\",{\n  weekStart:1,\n})\n\nexport default (props: any) => {\n  const { \n    value, \n    onChange, \n    precision = 'day', \n    placeholder = '请选择日期',\n    format,\n    ...restProps\n  } = omit(props, ['addons', 'schema'])\n  \n  const pickerRef: any = useRef(null);\n  \n  // 使用useImperativeHandle暴露方法给外部\n  useImperativeHandle(props.addons.fieldRef, () => ({\n    ...pickerRef?.current\n  }));\n\n  const dateFormat = format || getFormat(precision);\n  \n  const handleChange = (date: Date) => {\n    const dateString = dayjs(date).format(dateFormat);\n    onChange(dateString);\n  }\n\n  return (\n    <AntdDatePicker\n      ref={pickerRef}\n      value={dayjs(value, dateFormat).toDate()}\n      onConfirm={handleChange}\n      precision={precision}\n      {...restProps}\n    >\n      {date => (\n        <div>{(date && value) ? dayjs(date).format(dateFormat) : <span style={{ color: '#ccc' }}>{placeholder}</span>}</div>\n      )}\n    </AntdDatePicker>\n  )\n}\n"
  },
  {
    "path": "packages/form-render-mobile/src/widgets/Group/index.less",
    "content": ".frm-widget-group {\n\n  &-title {\n    font-size: 15px;\n    color: #999;\n    padding: 6px 16px;\n    border-top: 1px solid rgb(238, 238, 238);\n    border-bottom: 1px solid rgb(238, 238, 238);\n  }\n  \n  .adm-grid-item:first-of-type {\n    .adm-list-item-content {\n      border-top: none;\n    }\n  }\n}\n"
  },
  {
    "path": "packages/form-render-mobile/src/widgets/Group/index.tsx",
    "content": "import React from 'react';\nimport './index.less';\n\nconst prefix = 'frm-widget-group';\n\nexport default (props: any) => {\n  const { children, title } = props;\n\n  return (\n    <div className={prefix}>\n      <div className={`${prefix}-title`}>{title}</div>\n      {children}\n    </div>\n  )\n}\n"
  },
  {
    "path": "packages/form-render-mobile/src/widgets/Html/index.tsx",
    "content": "import React from 'react';\nimport { getFormat } from '../utils';\nimport dayjs from 'dayjs';\n\ninterface IProps {\n  value: any;\n  options: any[];\n  schema: any;\n}\n\n// 首字母转大写\nconst strToUpperCase = (str: string) => {\n  if (!str) {\n    return '';\n  }\n  return str.charAt(0).toUpperCase() + str.slice(1);\n};\n\nconst findLabels = (value: any[], options: any[]) => {\n  if (!isValidateArray(value) || !isValidateArray(options)) return [];\n\n  return value.map(v => options.find(o => o.value === v)?.label);\n};\n\nconst flatCascaderOptions = (options: any[]) => {\n  const result = [];\n\n  const walk = (list: any[]) => {\n    list.forEach(i => {\n      result.push(i);\n      if (isValidateArray(i.children)) {\n        walk(i.children);\n      }\n    });\n  };\n\n  walk(options);\n  return result;\n};\n\nconst isValidateArray = (list: unknown) =>\n  Array.isArray(list) && list.length > 0;\n\nexport default (props: IProps & Record<string, any>) => {\n  const { value, options, schema = {} } = props;\n  let __html: string;\n\n  switch (strToUpperCase(schema.widget)) {\n    case 'Input':\n    case 'TextArea':\n    case 'Rate':\n    case 'Stepper':\n      __html = value;\n      break;\n    case 'Slider':\n      if (isValidateArray(value)) {\n        __html = value.join('-');\n      } else {\n        __html = value;\n      }\n      break;\n    case 'Selector':\n      if (isValidateArray(value)) {\n        __html = findLabels(value, options).join('，');\n      }\n      break;\n    case 'Switch':\n      const { uncheckedText = '否', checkedText = '是' } = props;\n      __html = value ? checkedText : uncheckedText;\n      break;\n    case 'Radio':\n      __html = options.find(o => o.value === value)?.label;\n      break;\n    case 'DatePicker':\n      const { format, precision } = props;\n      if (!value) return '-';\n      const dateFormat = format || getFormat(precision);\n      __html = dayjs(value).format(dateFormat);\n      break;\n    case 'Cascader':\n      const flatOptions = flatCascaderOptions(options);\n      __html = findLabels(value, flatOptions).join('-') || '-';\n      break;\n    case 'Picker': {\n      const { columns, options } = props;\n      if (options && options.length) {\n        __html = findLabels(value, options).join('-') || '-';\n      } else {\n        const labels = value?.map((i: string, index: number) => {\n          return columns[index].find((j: any) => j.value === i)?.label;\n        });\n        __html = labels ? labels.join('-') : '-';\n      }\n      break;\n    }\n    default:\n      __html = '-';\n  }\n\n  __html ??= '-';\n\n  return <div dangerouslySetInnerHTML={{ __html }} />;\n};\n"
  },
  {
    "path": "packages/form-render-mobile/src/widgets/Picker/index.tsx",
    "content": "import React, { useRef, useImperativeHandle} from 'react';\nimport { Picker } from 'antd-mobile';\nimport { omit } from 'lodash-es';\n\nexport default (props: any) => {\n  const { \n    value, \n    onChange, \n    placeholder = '请选择',\n    options,\n    columns,\n    ...restProps\n  } = omit(props, ['addons', 'schema']);\n\n  const pickerRef: any = useRef(null);\n  \n  // 使用useImperativeHandle暴露方法给外部\n  useImperativeHandle(props.addons.fieldRef, () => ({\n    ...pickerRef?.current\n  }));\n\n  // 只有一列的场景更多，这里兼容下\n  const finalColumns = React.useMemo(() => {\n    if (columns && columns.length > 0) {\n      return columns;\n    } else {\n      return [options]\n    }\n  }, [options, columns]);\n\n  return (\n    <Picker\n      value={value}\n      onConfirm={(val: any) => onChange(val)}\n      ref={pickerRef}\n      columns={finalColumns}\n      {...restProps}\n    >\n      {items => {\n        if (items.every(i => i === null)) {\n          return <span style={{color: '#ccc'}}>{placeholder}</span>;\n        } else {\n          return items.map(i => i?.label ?? '未选择').join('-')\n        }\n      }}\n    </Picker>\n  )\n}\n"
  },
  {
    "path": "packages/form-render-mobile/src/widgets/Radio/index.tsx",
    "content": "import React from 'react';\nimport { Radio, Space } from 'antd-mobile';\nimport { omit } from 'lodash-es';\n\nexport default (props: any) => {\n  const { options, ...rest } = omit(props, ['addons', 'schema'])\n  return (\n    <Radio.Group  {...rest}>\n      <Space direction='horizontal' wrap={true}>\n        {options.map((item: any) => {\n          return <Radio value={item.value} key={item.value}>{item.label}</Radio>\n        })}\n      </Space>\n    </Radio.Group>\n  )\n}"
  },
  {
    "path": "packages/form-render-mobile/src/widgets/index.less",
    "content": ""
  },
  {
    "path": "packages/form-render-mobile/src/widgets/index.tsx",
    "content": "import React from 'react';\nimport { \n  Input as AntdInput,\n  Slider as AntdSlider,\n  TextArea as AntdTextArea,\n  Rate as AntdRate,\n  Selector as AntdSelector,\n  Switch as AntdSwitch,\n  Stepper as AntdStepper,\n} from 'antd-mobile';\nimport { omit } from 'lodash-es';\n\nconst widgetHoc = (Widget: any) => (props: any) => {\n  const widgetProps = omit(props, ['addons', 'schema']);\n  return <Widget {...widgetProps} />\n};\n\nexport const Input = widgetHoc(AntdInput);\nexport const Slider = widgetHoc(AntdSlider);\nexport const TextArea = widgetHoc(AntdTextArea);\nexport const Rate = widgetHoc(AntdRate);\nexport const Selector = widgetHoc(AntdSelector);\nexport const Switch = widgetHoc(AntdSwitch);\nexport const Stepper = widgetHoc(AntdStepper);\n\nexport { default as Radio } from './Radio';\nexport { default as DatePicker } from './DatePicker';\nexport { default as Cascader } from './Cascader';\nexport { default as Html } from './Html';\nexport { default as Picker } from './Picker';\n\n// layout widget\nexport { default as Group } from './Group';\nexport { default as Card } from './Card';\n// export { default as Collapse } from './Collapse';\n"
  },
  {
    "path": "packages/form-render-mobile/src/widgets/utils.ts",
    "content": "export function getFormat(format) {\n  switch (format) {\n    case 'date': return 'YYYY-MM-DD'\n    case 'year': return 'YYYY';\n    case 'month': return 'YYYY-MM';\n    case 'week': return 'YYYY-w';\n    case 'hour': return 'YYYY-MM-DD hh';\n    case 'minute': return 'YYYY-MM-DD hh:mm';\n    case 'second': return 'YYYY-MM-DD hh:mm:ss';\n    case 'week-day': return 'w-d';\n    default: return 'YYYY-MM-DD';\n  }\n}\n\nexport const translation = (configCtx) => (key) => {\n  const locale = configCtx?.locale.FormRender;\n  return locale[key];\n}\n"
  },
  {
    "path": "packages/form-render-mobile/src/withProvider.tsx",
    "content": "import React, { useEffect, useRef } from 'react';\nimport { ConfigProvider } from 'antd-mobile';\nimport dayjs from 'dayjs';\nimport { useUnmount } from 'ahooks';\n\nimport enUS from 'antd-mobile/es/locales/en-US'\nimport zhCN from 'antd-mobile/es/locales/zh-CN'\nimport locales from './locales';\nimport 'dayjs/locale/zh-cn';\n\nimport { createStore } from './models/store';\nimport { FRContext, ConfigContext } from './models/context';\nimport { validateMessagesEN, validateMessagesCN } from 'form-render/es/models/validateMessage';\nimport * as defaultWidgets from './widgets';\nimport { FRProps } from './type';\n\nexport default function withProvider<T>(Element: React.ComponentType<T>): React.FC<FRProps> {\n  return (props: any) => {\n\n    const {\n      configProvider,\n      locale = 'zh-CN',\n      widgets,\n      methods,\n      form,\n      validateMessages,\n      globalProps = {},\n      globalConfig = {},\n      ...otherProps\n    } = props;\n\n    const storeRef = useRef(createStore());\n    const store: any = storeRef.current;\n\n    useEffect(() => {\n      if (locale === 'en-US') {\n        dayjs.locale('en');\n        return;\n      }\n      dayjs.locale('zh-cn');\n    }, [locale]);\n\n    useUnmount(() => {\n      form.resetFields();\n    });\n\n    if (!form) {\n      console.warn('Please provide a form instance to FormRender');\n      return null;\n    }\n\n    const antdLocale = locale === 'zh-CN' ? zhCN : enUS;\n    const formValidateMessages = locale === 'zh-CN' ? validateMessagesCN : validateMessagesEN;\n\n    const configContext = {\n      locale,\n      widgets: { ...defaultWidgets, ...widgets },\n      methods,\n      form,\n      globalProps,\n      globalConfig\n    };\n\n    const langPack: any = {\n      ...antdLocale,\n      \"FormRender\": locales[locale],\n      ...configProvider?.locale\n    };\n\n    return (\n      <ConfigProvider\n        {...configProvider}\n        locale={langPack}\n      >\n        <ConfigContext.Provider value={configContext}>\n          <FRContext.Provider value={store}>\n            <Element\n              form={form}\n              validateMessages={{\n                ...formValidateMessages,\n                ...validateMessages\n              }}\n              {...otherProps}\n            />\n          </FRContext.Provider>\n        </ConfigContext.Provider>\n      </ConfigProvider>\n    );\n  };\n}\n"
  },
  {
    "path": "packages/form-render-mobile/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"ES2015\",\n    \"module\": \"ES2015\",\n    \"moduleResolution\": \"node\",\n    \"importHelpers\": true,\n    \"jsx\": \"react\",\n    \"esModuleInterop\": true,\n    \"sourceMap\": true,\n    \"baseUrl\": \"./\",\n    \"skipLibCheck\": true,\n    // \"strict\": true,\n    \"declaration\": true,\n    \"paths\": {\n      \"@/*\": [\n        \"src/*\"\n      ],\n      \"@@/*\": [\n        \"src/.umi/*\"\n      ],\n      \"form-render\": [\n        \"packages/form-render-mobile/src/*\"\n      ],\n    },\n    \"allowJs\": true,\n    \"allowSyntheticDefaultImports\": true,\n    \"noImplicitAny\": false,\n    \"resolveJsonModule\": true\n  },\n  \"exclude\": [\n    \"node_modules\",\n    \"lib\",\n    \"es\",\n    \"dist\",\n    \"typings\",\n    \"**/__test__\",\n    \"test\",\n    \"tests\",\n    \"docs\",\n    \"**/*.js\"\n  ]\n}\n"
  },
  {
    "path": "packages/table-render/.fatherrc.ts",
    "content": "export default {\n  cjs: { type: 'rollup' },\n  esm: {\n    type: 'rollup',\n    importLibToEs: true,\n  },\n  extraBabelPlugins: [\n    [\n      'import',\n      {\n        libraryName: 'antd',\n        libraryDirectory: 'es',\n        style: true,\n      },\n      'antd',\n    ],\n    // [\n    //   'import',\n    //   {\n    //     libraryName: '@ant-design/icons',\n    //     libraryDirectory: 'lib/icons',\n    //     camel2DashComponentName: false,\n    //   },\n    //   '@ant-design/icons',\n    // ],\n    // ['transform-remove-console', { exclude: ['error', 'warn'] }],\n  ],\n};\n"
  },
  {
    "path": "packages/table-render/CHANGELOG.md",
    "content": "# 更新日志\n\n### 2.1.4\n-[!] 修复 onSearch 方法缺失\n\n### 2.0.18\n-[!] 修复 table-render 切换 pageSize 无效\n### 2.0.17\n-[!] 修复 table-render 配置 pagesize 查询无效\n### 2.0.16\n-[+] 工具栏增加列字段筛选显示\n\n### 2.0.13\n-[+] 增加 tableWrapper 属性，可以通过 tableWrapper 在搜索栏和表格之间增加一些内容\n### 2.0.9\n-[+] Search 增加极简模式 mode='simple'配置，直接通过onChange触发查询事件，并且去除查询按钮\n-[+] 增加 autoSize 是否自适应布局\n\n### 2.0.8\n-[+] valueType 新增 image、tag、tags、progress、dateRange、dateTimeRange 类型\n-[+] 支持列头配置气泡 tooltip 提示\n\n### 2.0.5\n-[!] 修复 DatePicker 组件显示默认是英文提示\n### 2.0.1\n-[+] table-render 新版发布上线，props 调整\n### 2.0.0\n\n- [+] table-render 2.0 正式发版\n"
  },
  {
    "path": "packages/table-render/CONTRIBUTION.md",
    "content": "## 如何贡献代码\n\n```\n# fork项目 & git clone\n\n# 安装依赖\ntnpm i\n\n# 切到 dev 分支\ngit checkout dev\n\n# 跑起来\nnpm start\n\n# 发布\n## 1. 更新版本\nnpm version patch (major, minor)\n\n## 2. 修改 README.md / CHANGELOG.md\n\n## 3. 发布\ntnpm publish\n```\n"
  },
  {
    "path": "packages/table-render/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2019-present XRender Team\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "packages/table-render/README.md",
    "content": "<div style=\"display:flex;align-items:center;margin-bottom:24px\">\n  <img src=\"https://img.alicdn.com/tfs/TB17UtINiLaK1RjSZFxXXamPFXa-606-643.png\" alt=\"logo\" width=\"48px\"/>\n  <h4 style=\"font-size:30px;font-weight:600;display:inline-block;margin-left:12px\">TableRender</h4>\n</div>\n\n<p style=\"display:flex;justify-content:space-between;width:440px\">\n  <a href=\"https://www.npmjs.com/package/table-render?_blank\">\n    <img alt=\"npm\" src=\"https://img.shields.io/npm/v/table-render.svg?maxAge=3600&style=flat-square\">\n  </a>\n  <a href=\"https://npmjs.org/package/table-render\">\n    <img alt=\"NPM downloads\" src=\"https://img.shields.io/npm/dm/table-render.svg?style=flat-square\">\n  </a>\n  <a href=\"https://npmjs.org/package/table-render\">\n    <img alt=\"NPM all downloads\" src=\"https://img.shields.io/npm/dt/table-render.svg?style=flat-square\">\n  </a>\n  <a>\n    <img alt=\"PRs Welcome\" src=\"https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square\">\n  </a>\n</p>\n\n> 易用且轻量的中后台**列表解决方案**，常用于**搜索列表页**快速生成\n\n## 官网\n\n<https://xrender.fun/table-render>\n\n## 优势\n1. **开箱即用**：以最简单的方式配置 API 请求和表头字段，就能生成一个好用的搜索列表。\n2. **XRender 生态**：搜索部分集成 FormRender，以最小成本快速生成搜索面板。\n3. **无缝对接**：表格部分沿用 Ant Design Table， API 无缝对接，降低用户使用成本。\n4. **数据模版**：表格列内置多种数据展示模版，减少自定义 Render 函数配置。\n5. **多种形态**：支持搜索栏、工具栏、表格内容，根据业务需求相互组合展示多种形态。\n\n## 何时使用\n\n1. 用于查看和处理多条结构类似的数据，可对数据进行排序、筛选、对比或其他自定义操作，常有导航到详情页面的作用。\n2. 表格列表建议将重要信息和操作展示出来，不重要信息直接收起，可以帮助用户更高效的查看、处理、查找数据。\n\n## 如何使用\n\n### 安装\n\ntable-render 依赖 ant design，单独使用不要忘记安装～\n\n```sh\nnpm i table-render --save\n```\n\n### 代码演示\n\n```jsx\n/**\n * transform: true\n * defaultShowCode: true\n * background: 'rgb(245,245,245)'\n */\nimport React from 'react';\nimport TableRender from 'table-render';\nimport { Button } from 'antd';\nimport { InfoCircleOutlined, PlusOutlined } from '@ant-design/icons';\n\nconst dataSource = [];\nfor (let i = 0; i < 6; i++) {\n  dataSource.push({\n    id: i.toString(),\n    title: `标题${i + 1}`,\n    created_at: new Date().getTime(),\n  });\n}\n\nconst schema = {\n  type: 'object',\n  labelWidth: 70,\n  properties: {\n    title: {\n      title: '标题',\n      type: 'string'\n    },\n    created_at: {\n      title: '创建时间',\n      type: 'string',\n      format: 'date'\n    }\n  }\n};\n\nconst columns = [\n  {\n    title: '标题',\n    dataIndex: 'title',\n  },\n  {\n    title: '创建时间',\n    key: 'since',\n    dataIndex: 'created_at',\n    valueType: 'date',\n  },\n  {\n    title: '操作',\n    render: (row, record) => <a onClick={() => alert(row.title)}>编辑</a>,\n  }\n];\n\nconst Demo = () => {\n  \n  const api = () => {\n    return {\n      data: dataSource,\n      total: dataSource.length\n    };\n  };\n\n  return (\n    <TableRender\n      search={{ schema }}\n      request={api}\n      columns={columns}\n      title='最简表格'\n      toolbarRender={ \n        <>\n          <Button>查看日志</Button>\n          <Button>导出数据</Button>\n          <Button type='primary'>\n            <PlusOutlined />\n            新增\n          </Button>\n        </>\n      }\n    />\n  );\n}\n\nexport default Demo;\n```"
  },
  {
    "path": "packages/table-render/__tests__/utils.spec.ts",
    "content": "import { describe, expect, test } from 'vitest';\nimport { getDate, getDateTime, getMoneyType, isObj } from '../src/utils';\n\ndescribe('Test TableRender valueType', () => {\n  test('Test getDate', () => {\n    const current = new Date().getTime();\n    expect(getDate(current)).toHaveLength(10);\n  });\n  test('Test getDateTime', () => {\n    const current = new Date().getTime();\n    expect(getDateTime(current)).toHaveLength(19);\n  });\n  test('Test getMoneyType', () => {\n    expect(getMoneyType(10000)).toEqual('¥10,000');\n  });\n  test('Test isObj object', () => {\n    expect(isObj({})).toBeTruthy();\n  });\n  test('Test isObj array', () => {\n    expect(isObj([])).toBeFalsy();\n  });\n  test('Test isObj string', () => {\n    expect(isObj('')).toBeFalsy();\n  });\n  test('Test isObj number', () => {\n    expect(isObj('')).toBeFalsy();\n  });\n});\n"
  },
  {
    "path": "packages/table-render/package.json",
    "content": "{\n  \"name\": \"table-render\",\n  \"version\": \"2.1.5-beta.1\",\n  \"description\": \"中后台表格解决方案\",\n  \"keywords\": [\n    \"TableRender\",\n    \"Render\",\n    \"XRender\",\n    \"React\",\n    \"Json Schema\",\n    \"Ant Design\"\n  ],\n  \"homepage\": \"https://xrender.fun/table-render\",\n  \"bugs\": {\n    \"url\": \"https://github.com/alibaba/x-render/issues\"\n  },\n  \"license\": \"MIT\",\n  \"main\": \"dist/index.js\",\n  \"module\": \"dist/index.esm.js\",\n  \"typings\": \"dist/src/index.d.ts\",\n  \"scripts\": {\n    \"analyze\": \"ANALYZE=1 dumi dev\",\n    \"beta\": \"npm publish --tag beta\",\n    \"build\": \"father-build\",\n    \"predeploy\": \"npm run site\",\n    \"deploy\": \"gh-pages -d docs-dist\",\n    \"prepare\": \"npm run build\",\n    \"prettier\": \"prettier --write \\\"**/*.{js,jsx,tsx,ts,less,md,json}\\\"\",\n    \"postpublish\": \"git push --tags\",\n    \"site\": \"dumi build\",\n    \"start\": \"dumi dev\",\n    \"test:ui\": \"vitest --ui\"\n  },\n  \"lint-staged\": {\n    \"*.{js,jsx,less,md,json}\": [\n      \"prettier --write\"\n    ],\n    \"*.ts?(x)\": [\n      \"prettier --parser=typescript --write\"\n    ]\n  },\n  \"dependencies\": {\n    \"@ant-design/icons\": \"^5.1.4\",\n    \"dayjs\": \"^1.11.7\",\n    \"form-render\": \"^2.2.15\",\n    \"lodash.get\": \"^4.4.2\",\n    \"lodash.omit\": \"^4.4.2\",\n    \"lodash.defaults\": \"^4.2.0\",\n    \"zustand\": \"^4.1.5\",\n    \"classnames\": \"^2.3.1\",\n    \"@dnd-kit/core\": \"^6.0.8\",\n    \"@dnd-kit/sortable\": \"^7.0.2\",\n    \"@dnd-kit/utilities\": \"^3.2.1\",\n    \"ahooks\": \"^3.7.5\"\n  },\n  \"devDependencies\": {\n    \"less\": \"^3.0.0\",\n    \"babel-plugin-transform-remove-console\": \"^6.9.4\",\n    \"umi-request\": \"^1.3.5\"\n  },\n  \"peerDependencies\": {\n    \"antd\": \"4.x || 5.x\",\n    \"react\": \">=16.8.0\"\n  },\n  \"gitHooks\": {\n    \"pre-commit\": \"lint-staged\"\n  }\n}"
  },
  {
    "path": "packages/table-render/src/core/ErrorBoundary/index.tsx",
    "content": "import { Result } from 'antd';\nimport React, { ErrorInfo } from 'react';\n\nclass ErrorBoundary extends React.Component<\n  {},\n  { hasError: boolean; errorInfo: string }\n> {\n  state = { hasError: false, errorInfo: '' };\n\n  static getDerivedStateFromError(error: Error) {\n    return { hasError: true, errorInfo: error.message };\n  }\n\n  componentDidCatch(error: any, errorInfo: ErrorInfo) {\n    // eslint-disable-next-line no-console\n    console.log(error, errorInfo);\n  }\n\n  render() {\n    if (this.state.hasError) {\n      // You can render any custom fallback UI\n      return (\n        <Result\n          status=\"error\"\n          title=\"Something went wrong.\"\n          extra={this.state.errorInfo}\n        />\n      );\n    }\n    //@ts-ignore\n    return this.props.children;\n  }\n}\n\nexport default ErrorBoundary;\n"
  },
  {
    "path": "packages/table-render/src/core/SearchView/index.tsx",
    "content": "import React from 'react';\nimport { SearchForm } from 'form-render';\nimport { isFunction, _debounce } from '../../utils';\nimport { SearchProps } from '../../types';\n\nconst Search: <RecordType extends object = any>(\n  props: SearchProps<RecordType>\n) => React.ReactElement = props => {\n\n  const {\n    refresh,\n    getState,\n    onMount,\n    onSearch,\n    watch: _watch,\n    mode,\n    form,\n    ...otherProps\n  }  = props;\n\n  const { loading, sorter }: any = getState();\n\n  let watch = { ..._watch };\n  if (mode === 'simple') {\n    watch = {\n      '#': _debounce((value) => {\n        form.submit();\n        const callBack: any = _watch?.['#'];\n        if (isFunction(callBack)) {\n          callBack(value);\n        }\n      }, 300),\n      ..._watch,\n    }\n  }\n\n  const handleMount = async () => {\n    if (typeof onMount === 'function') {\n      await onMount();\n    }\n  };\n\n  const handleSearch = (data: any) => {\n    if (typeof onSearch === 'function') {\n      onSearch(data);\n    }\n    refresh({ ...data, sorter });\n  };\n\n  return (\n    <SearchForm\n      {...otherProps}\n      mode={mode}\n      form={form}\n      watch={watch}\n      loading={loading} \n      onSearch={handleSearch}\n      onMount={handleMount}\n      schema={otherProps.schema || {}}\n    />\n  );\n}\n\nexport default Search;\n"
  },
  {
    "path": "packages/table-render/src/core/TableView/copy.tsx",
    "content": "import React, { useContext } from 'react';\nimport { message, Typography, ConfigProvider } from 'antd';\nimport { translation } from '../../utils';\n\nconst Copy = ({ item, text }) => {\n  const configCtx = useContext(ConfigProvider.ConfigContext);\n  const t = translation(configCtx);\n  \n  return (\n    <Typography.Text\n      style={{\n        maxWidth: '100%',\n        margin: 0,\n        padding: 0,\n      }}\n      copyable={\n        item.copyable && text\n          ? {\n            text,\n            onCopy: () => message.success(t('copy_success')),\n          }\n          : undefined\n      }\n      ellipsis={item.ellipsis || false}\n    >\n      {text}\n    </Typography.Text>\n  );\n};\n\nexport default Copy;\n"
  },
  {
    "path": "packages/table-render/src/core/TableView/field.tsx",
    "content": "import { Tooltip, Progress, Space, Tag, Image, Typography } from 'antd';\nimport React from 'react';\nimport Copy from './copy';\nimport { isObject, isArray, isFunction } from '../../utils';\n\n\nconst renderLink = (value: string, item: any, { record, index } : { record: any, index: number }) =>  {\n  const { ellipsis, valueTypeProps } = item;\n  const { type, href, onClick } = valueTypeProps || {};\n\n  const handleClick = () => {\n    if (onClick) {\n      onClick(value, record, index);\n      return;\n    }\n\n    let linkUrl = href || value;\n    if (typeof href === 'function') {\n      linkUrl = href(value, record, index);\n    }\n\n    if (type === '_self') {\n      window.location.href = linkUrl;\n      return;\n    }\n\n    window.open(linkUrl);\n  };\n\n  // 省略\n  if (ellipsis) {\n    return (\n      <Typography.Text\n        style={{ width: '100%', color: '#1890ff', cursor: 'pointer' }}\n        ellipsis={{ tooltip: value }}\n        onClick={() => handleClick()}\n      >\n        {value}\n      </Typography.Text>\n    )\n  }\n\n  return (\n    <Typography.Link onClick={handleClick}>\n      {value}\n    </Typography.Link>\n  )\n};\n\nexport const renderEllipsis = (\n  dom: JSX.Element,\n  text: any,\n  item: { ellipsis: any }\n) => {\n  if (!item.ellipsis) {\n    return (\n      <span>\n        <>{dom}</>\n      </span>\n    );\n  }\n  return <Tooltip title={text}>{dom}</Tooltip>;\n};\n\nexport const renderCopyable = (\n  text: any,\n  item: { copyable: any; ellipsis: any }\n) => {\n  if (item.copyable || item.ellipsis) {\n    return <Copy item={item} text={text} />\n  }\n  return text;\n};\n\nexport const renderCode = (code: string) => {\n  return code ? (\n    <pre\n      style={{\n        padding: 16,\n        overflow: 'auto',\n        fontSize: '85%',\n        lineHeight: 1.45,\n        backgroundColor: '#f6f8fa',\n        borderRadius: 3,\n      }}\n    >\n      <code style={{ whiteSpace: 'pre-wrap' }}>{code}</code>\n    </pre>\n  ) : null;\n};\n\nexport const renderProgress = (value: any, props: any) => {\n  return (<Progress percent={value} {...props} />)\n}\n\nexport const renderTag = (value: any, props: any) => {\n  return (\n    <Tag color='blue' {...props} key={value}>{value}</Tag>\n  );\n}\n\nexport const renderTags = (_value: any, item: any) => {\n  let value = _value;\n  if (!Array.isArray(value)) {\n    value = `${_value}`.split(',');\n  }\n  return (\n    <Space style={{ flexWrap: 'wrap' }}>\n      {(value || [])?.map((ite: any) => {\n        let data = ite;\n        if (typeof ite === 'string') {\n          data = {\n            name: `${ite}`,\n          };\n          if (isObject(item.valueTypeProps)) {\n            data = {\n              name: `${ite}`,\n              ...item.valueTypeProps\n            };\n          }\n        }\n        if (isFunction(item.valueTypeProps)) {\n          data = item.valueTypeProps(ite);\n        }\n        const { name, ...otherProps } = data;\n        return renderTag(name, otherProps);\n      })}\n    </Space>\n  );\n}\n\nexport const renderImage = (value, props) => {\n  return (\n    <Image width={80} src={value} {...props}\n    />\n  );\n}\n\n// 渲染单元格\nexport const renderDom = (value: any, item: any, extra ?: { record: any, index: number }) => {\n  const { record, index } = extra || {};\n\n  let val = value;\n\n  if (item.enum && !isObject(val) && !isArray(val)) {\n    val = item.enum[value] ?? (item.enum.default || value);\n  }\n\n  if (item.valueType === 'tags') {\n    return renderTags(val, item);\n  }\n\n  if (typeof val === 'object') {\n    return;\n  }\n\n  if (item.valueType === 'code') {\n    return renderCode(val);\n  }\n\n  if (item.valueType === 'progress') {\n    return renderProgress(val, item.valueTypeProps);\n  }\n\n  if (item.valueType === 'tag') {\n    return renderTag(val, item.valueTypeProps)\n  }\n\n  if (item.valueType === 'image') {\n    return renderImage(val, item.valueTypeProps);\n  }\n\n  if (item.valueType === 'link') {\n    return renderLink(val, item, { record, index });\n  }\n\n  const copyHoc = renderCopyable(val, item);\n  const ellipsisHoc = renderEllipsis(copyHoc, val, item);\n  return ellipsisHoc;\n};\n"
  },
  {
    "path": "packages/table-render/src/core/TableView/index.tsx",
    "content": "import React, { useMemo } from 'react';\nimport { Table, TableProps, Tooltip } from 'antd';\nimport { InfoCircleOutlined } from '@ant-design/icons';\nimport { useTableStore } from '../store';\n\nimport { getDate, getDateTime, getMoneyType, getDateRange, isObject, isFunction, getColumnKey } from '../../utils';\nimport { renderDom } from './field';\nimport { ColumnsSettingValueType } from '../../types';\n\nconst TableView: <RecordType extends object = any>(\n  props: TableProps<RecordType> & {\n    doSearch: (...arg: any[]) => void,\n    locale?: 'zh-CN' | 'en-US';\n  }\n) => React.ReactElement = props => {\n  //@ts-ignore\n  if (props.dataSource) {\n    console.error(\n      '设置table-render的数据请使用api，具体使用可参考：https://form-render.github.io/table-render/guide/demo#%E5%9F%BA%E6%9C%AC-demo'\n    );\n  }\n\n  const { doSearch, pageChangeWithRequest = true, ...otherProps }: any = props;\n  const dataSource = useTableStore((store) => store.dataSource);\n  const loading = useTableStore((store) => store.loading);\n  const pagination = useTableStore((store) => store.pagination);\n  const tableSize = useTableStore((store) => store.tableSize);\n  const columns = useTableStore((store) => store.columns);\n  const columnsSetting = useTableStore((store) => store.columnsSetting);\n  const setState = useTableStore((store) => store.setState);\n\n  const handleChange = ({ current, pageSize }, filters, sorter, extra) => {\n    if (extra?.action === 'filter') {\n      setState({ pagination: { ...pagination, current, pageSize, total: extra.currentDataSource.length }, sorter });\n    } else {\n      setState({ pagination: { ...pagination, current, pageSize }, sorter });\n    }\n    \n    if (!pageChangeWithRequest || extra?.action === 'filter') {\n      return;\n    }\n    doSearch({ current, pageSize, sorter });\n  };\n\n  const getProColumns = (columns: any[]) => {\n    if (!columns) return [];\n\n    return columns.map((item) => {\n      const { tooltip, ...otherItem } = item;\n      const result = otherItem;\n\n      // 兼容 tooltip 模式\n      if (typeof result.title === 'string' && tooltip) {\n        let tooltipProps = isObject(tooltip) ? tooltip : { title: tooltip };\n        if (isFunction(tooltip)) {\n          tooltipProps = tooltip();\n        }\n        result.title = (\n          <>\n            {result.title}\n            <Tooltip placement='top' {...tooltipProps}>\n              <InfoCircleOutlined style={{ marginLeft: 6 }} />\n            </Tooltip>\n          </>\n        );\n      }\n\n      // 用户在columns中自定义的render会覆盖tr的预设render\n      if (result.render) {\n        return result;\n      }\n\n      if (isFunction(result.valueTypeProps)) {\n        result.render = (value: any, record: any) => {\n          if (result.valueType === 'tags') {\n            return renderDom(value, { ...item });\n          }\n\n          const { type, ...domProps } = result.valueTypeProps(value, record);\n          return renderDom(value, { ...item, valueTypeProps: domProps });\n        }\n        return result;\n      }\n\n      switch (result.valueType) {\n        case 'link':\n          result.render = (value: any, record: any, index: number) => renderDom(value, result, { record, index });\n          break;\n        case 'date':\n          result.render = (value: any) => renderDom(getDate(value, result.valueTypeProps?.format), result);\n          break;\n        case 'dateTime':\n          result.render = (value: any) => renderDom(getDateTime(value, result.valueTypeProps?.format), result);\n          break;\n        case 'dateRange':\n          result.render = (value: any, record: any) => renderDom(getDateRange(value, { result, record }), result);\n          break;\n        case 'dateRangeTime':\n          result.render = (value: any, record: any) => renderDom(getDateRange(value, { result, record }, 'YYYY-MM-DD HH:mm:ss'), result);\n          break;\n        case 'money':\n          result.render = (value: any) => renderDom(getMoneyType(value), result);\n          break;\n        case 'text':\n        default:\n          result.render = (value: any) => renderDom(value, result);\n      }\n      return result;\n    })\n  }\n\n\n  // 应用 columnsSetting 到 columns\n  const setColumns = (columnsSetting: ColumnsSettingValueType, proColumns: any[]) => {\n    return columnsSetting\n      .filter(i => !i.hidden)\n      .map(i => {\n        const column = proColumns.find((j, jIndex) => getColumnKey(j, jIndex) === i.key);\n        if (column) {\n          return {\n            ...column,\n            ...i,\n          };\n        }\n        return i;\n      });\n  };\n\n  const proColumns = useMemo(() => {\n    const proColumns = getProColumns(columns);\n    if (columnsSetting && columnsSetting.length > 0) {\n      return setColumns(columnsSetting, proColumns)\n    }\n    return proColumns;\n  }, [columns, columnsSetting]);\n\n  const tableProps: TableProps<typeof dataSource[number]> = {\n    rowKey: 'id',\n    ...otherProps,\n    columns: proColumns,\n    onChange: handleChange,\n    dataSource,\n    pagination: props.pagination === false ? false : {\n      size: 'small',\n      ...props.pagination,\n      ...pagination,\n      // pageSize: props.pagination?.pageSize || pagination.pageSize,\n      // total: props.pagination?.total || pagination.total,\n      // current: props.pagination?.current || pagination.current,\n    },\n    loading,\n    size: tableSize,\n  };\n \n  // 需要判断一下，否则影响 table 的某些属性初始化渲染异常\n  if (columns?.length === 0) {\n    return null;\n  }\n  return <Table {...tableProps} />\n}\n\nexport default TableView;\n"
  },
  {
    "path": "packages/table-render/src/core/TableView/widgets.tsx",
    "content": "import { Tag, Tooltip } from 'antd';\nimport React from 'react';\nimport { getDate, getDateTime } from '../../utils';\n\nconst PrompText = ({ text = '', prompText = '' }) => (\n  <Tooltip title={prompText || text}>\n    <span>{text}</span>\n  </Tooltip>\n);\n\nexport default {\n  tags: tags => (\n    <div>\n      {tags.map(tag => {\n        return (\n          <Tag color=\"volcano\" key={tag}>\n            {tag.toUpperCase()}\n          </Tag>\n        );\n      })}\n    </div>\n  ),\n  dateTime: value => getDateTime(value),\n  date: value => getDate(value),\n  tooltip: text => <PrompText text={text} />,\n  status: value => {\n    switch (value.status) {\n      case 'Default':\n        return <Tag color=\"blue\">{value.text}</Tag>;\n      case 'Error':\n        return <Tag color=\"red\">{value.text}</Tag>;\n      case 'Success':\n        return <Tag color=\"green\">{value.text}</Tag>;\n      default:\n        return <Tag color=\"blue\">{value.text}</Tag>;\n    }\n  },\n};\n"
  },
  {
    "path": "packages/table-render/src/core/ToolbarView/InteriorTool/ColumnSetting/index.less",
    "content": ".tr-toolbar-column-setting {\n  display: flex;\n  flex-direction: column;\n  background: #fff;\n  box-shadow: 0 3px 6px -4px rgb(0 0 0 / 12%), 0 6px 16px 0 rgb(0 0 0 / 8%),\n    0 9px 28px 8px rgb(0 0 0 / 5%);\n\n  &-header {\n    padding: 10px;\n    display: flex;\n    align-items: center;\n    justify-content: space-between;\n  }\n}\n\n.tr-toolbar-column-setting .ant-dropdown-menu {\n  box-shadow: none;\n}\n\n.tr-toolbar-column-setting-item .ant-dropdown-menu-item {\n  display: flex;\n  align-items: center;\n}\n\n.tr-toolbar-column-setting-item-can-fixed {\n\n  &:hover {\n    .tr-toolbar-column-setting-item-pin {\n      opacity: 1;\n    }\n  }\n}\n\n.tr-toolbar-column-setting-item-fixed {\n  display: flex;\n  align-items: center;\n  background: #e6f7ff !important;\n\n  .tr-toolbar-column-setting-item-pin {\n    opacity: 1;\n    transform: rotate(-45deg);\n  }\n}\n\n\n.tr-toolbar-column-setting-item-pin {\n  opacity: 0;\n  margin-left: auto;\n}\n"
  },
  {
    "path": "packages/table-render/src/core/ToolbarView/InteriorTool/ColumnSetting/index.tsx",
    "content": "import React, { useContext, useEffect, useMemo, useRef, useState } from 'react';\nimport { Tooltip, ConfigProvider, Dropdown, Checkbox, Divider, Button } from 'antd';\nimport { CloseOutlined, SettingOutlined, UndoOutlined } from '@ant-design/icons';\nimport { DndContext, DragOverlay } from '@dnd-kit/core';\nimport { SortableContext, verticalListSortingStrategy, arrayMove } from '@dnd-kit/sortable';\nimport { useTableStore } from '../../../store';\nimport { getColumnKey, translation } from '../../../../utils';\nimport { ToolbarActionConfig } from '@/types';\nimport Item from './item';\nimport { cancelFixed, fixItem, Setting } from './utils';\nimport clx from 'classnames'\nimport './index.less';\n\nconst prefix = 'tr-toolbar-column-setting';\n\nconst ColumnSetting: React.FC<Pick<ToolbarActionConfig, 'columnsSettingValue' | 'onColumnsSettingChange'>> = ({\n  columnsSettingValue,\n  onColumnsSettingChange\n}) => {\n  const configCtx = useContext(ConfigProvider.ConfigContext);\n  const t = translation(configCtx);\n\n  const columns = useTableStore(store => store.columns);\n  const columnsSetting = useTableStore((store) => store.columnsSetting);\n  const setColumnsSetting = useTableStore(store => store.setColumnsSetting);\n\n  const [open, setOpen] = useState(false);\n  const [activeId, setActiveId] = useState<string | null>(null);\n\n  const inited = useRef(false);\n\n  useEffect(() => {\n    if (columnsSettingValue) {\n      setColumnsSetting(columnsSettingValue);\n    }\n  }, [columnsSettingValue]);\n\n  useEffect(() => {\n    if (open && columns.length > 0 && !inited.current && columnsSetting.length === 0) {\n      init();\n      inited.current = true;\n      return;\n    }\n  }, [open, columns, columnsSetting])\n\n  const init = () => {\n    const initSetting = columns.map((i, index) => ({\n      key: getColumnKey(i, index),\n      hidden: false,\n    }))\n    handleChange(initSetting);\n  }\n\n  const findIndex = (key: any) => {\n    return columnsSetting.findIndex(i => i.key === key)\n  }\n\n  const handleChange = (setting: Setting) => {\n    setColumnsSetting(setting);\n    onColumnsSettingChange?.(setting);\n  }\n\n  const getItems = (setting: Setting) => {\n    if (!setting) return [];\n    return setting.map(i => ({\n      className: clx(`${prefix}-item`, {\n        [`${prefix}-item-fixed`]: i.fixed,\n      }),\n      key: i.key,\n      label: (\n        <Item\n          {...i}\n          onFixItem={onFixItem}\n          onUnfixItem={onUnfixItem}\n          columnKey={String(i.key)}\n        />\n      ),\n    }))\n  }\n\n  const onReset = () => {\n    init();\n  }\n\n  /** 固定某一列 */\n  const onFixItem = (key: string) => {\n    const fixedSetting = fixItem(columnsSetting, key);\n    const finalSetting = cancelFixed(fixedSetting);\n    handleChange(finalSetting);\n  }\n\n  /** 取消固定某一列 */\n  const onUnfixItem = (key: string) => {\n    const canceledSetting = columnsSetting.map(i => ({\n      ...i,\n      fixed: i.key === key ? undefined : i.fixed,\n    }))\n    const finalSetting = cancelFixed(canceledSetting);\n    handleChange(finalSetting);\n  }\n\n  /** 列的显示和隐藏 */\n  const onColumnsCheckChange = (val: string[]) => {\n    const finalSetting = columnsSetting.map(i => ({\n      ...i,\n      hidden: !val.includes(String(i.key))\n    }))\n    handleChange(finalSetting);\n  }\n\n  /** 移动某一列 */\n  const onDragEnd = (activeId: any, overId: any) => {\n    const newSetting = arrayMove(columnsSetting, findIndex(activeId), findIndex(overId));\n    const activeItem = newSetting.find(i => i.key === activeId);\n\n    if (activeItem.fixed) {\n      const fixedSetting = fixItem(newSetting, activeId);\n      const finalSetting = cancelFixed(fixedSetting);\n      handleChange(finalSetting);\n    } else {\n      const finalSetting = cancelFixed(newSetting);\n      handleChange(finalSetting);\n    }\n  }\n\n  const items = useMemo(() => getItems(columnsSetting), [columnsSetting]);\n  const activeItem = useMemo(() => columnsSetting.find(i => i.key === activeId), [columnsSetting, activeId]);\n  const keyList = useMemo(() => columnsSetting.map(i => i.key), [columnsSetting]);\n  const value = useMemo(() => columnsSetting.filter(i => !i.hidden).map(i => i.key), [columnsSetting]);\n\n  return (\n    <Dropdown\n      menu={{ items }}\n      open={open}\n      trigger={['click']}\n      onOpenChange={(open) => setOpen(open)}\n      getPopupContainer={(node) => (node.parentNode as HTMLElement) || document.body}\n      dropdownRender={(menu) => {\n        return (\n          <div className={prefix}>\n            <div className={`${prefix}-header`}>\n              <CloseOutlined\n                onClick={() => setOpen(false)}\n                style={{\n                  fontSize: 16,\n                  color: '#999',\n                  cursor: 'pointer'\n                }}\n              />\n              <Button\n                icon={<UndoOutlined />}\n                type=\"primary\"\n                size=\"small\"\n                onClick={onReset}\n              >\n                {t('reset')}\n              </Button>\n            </div>\n            <Divider style={{ margin: 0 }} />\n            <DndContext\n              onDragEnd={({ over, active }) => {\n                if (over) {\n                  onDragEnd(active.id, over.id)\n                }\n              }}\n              onDragStart={({ active }) => {\n                setActiveId(String(active.id));\n              }}\n            >\n              <SortableContext items={keyList} strategy={verticalListSortingStrategy}>\n                <Checkbox.Group value={value} onChange={onColumnsCheckChange}>\n                  {menu}\n                </Checkbox.Group>\n                <DragOverlay>\n                  {activeId && (\n                    <Item\n                      {...activeItem}\n                      isOverlay\n                      isChecked={value.includes(activeId)}\n                      columnKey={activeId}\n                    />\n                  )}\n                </DragOverlay>\n              </SortableContext>\n            </DndContext>\n          </div>\n        )\n      }}\n    >\n      <Tooltip title={t('column_setting')}>\n        <SettingOutlined style={{ cursor: 'pointer' }} onClick={() => setOpen(true)} />\n      </Tooltip>\n    </Dropdown>\n  );\n}\n\nexport default ColumnSetting;\n"
  },
  {
    "path": "packages/table-render/src/core/ToolbarView/InteriorTool/ColumnSetting/item.tsx",
    "content": "import React, { FC, useMemo, } from 'react';\nimport { Checkbox, } from 'antd';\nimport { HolderOutlined, PushpinOutlined, } from '@ant-design/icons';\nimport { useSortable } from '@dnd-kit/sortable';\nimport { CSS } from '@dnd-kit/utilities';\nimport { useTableStore } from '../../../store';\nimport clx from 'classnames';\nimport './index.less';\nimport { getColumnKey } from '../../../../utils';\nimport { getStatus, Setting } from './utils';\n\nconst prefix = 'tr-toolbar-column-setting-item';\n\nconst Item: FC<Setting[number] & {\n  columnKey: string,\n  isOverlay?: boolean,\n  isChecked?: boolean,\n  onFixItem?: (columnKey: string) => void,\n  onUnfixItem?: (columnKey: string) => void,\n}> = (props) => {\n  const { columnKey, isChecked, isOverlay, onFixItem, onUnfixItem } = props;\n  const { setNodeRef, attributes, listeners, transition, transform, isDragging, setActivatorNodeRef } = useSortable({\n    id: columnKey,\n  });\n\n  const columns = useTableStore(store => store.columns);\n  const columnsSetting = useTableStore(store => store.columnsSetting);\n\n  const {\n    isFirstOne,\n    isLastOne,\n    isFixed,\n    preFixed,\n    nextFixed\n  } = useMemo(\n    () => getStatus(columnsSetting, columnKey),\n    [columnsSetting, columnKey]\n  );\n\n  const canFix = isFirstOne || isLastOne || preFixed || nextFixed;\n\n  const style: React.CSSProperties = {\n    transition,\n    transform: CSS.Translate.toString(transform),\n    opacity: isDragging ? '0.6' : 'unset',\n  };\n\n  // TODO 测试下函数的场景\n  const title = columns.find((i, index) => getColumnKey(i, index) === columnKey).title;\n  const label = useMemo(() => typeof title === 'function' ? title({}) : title, [title]);\n\n  /** 取消固定当前项 */\n  const onCancelFix = () => {\n    if (!canFix) return;\n    onUnfixItem?.(columnKey);\n  }\n\n  /** 固定当前项 */\n  const onFix = () => {\n    if (!canFix) return;\n    onFixItem?.(columnKey);\n  }\n\n  const className = clx({\n    [`${prefix}-can-fixed`]: canFix,\n  })\n\n  return (\n    <div\n      style={style}\n      ref={setNodeRef}\n      className={className}\n      {...attributes}\n    >\n      <HolderOutlined\n        {...listeners}\n        ref={setActivatorNodeRef}\n        style={{\n          marginRight: 8,\n          color: '#666',\n          cursor: isOverlay ? 'grabbing' : 'grab'\n        }}\n      />\n      {isOverlay ? (\n        <Checkbox checked={isChecked}>{label}</Checkbox>\n      ) : (\n        <Checkbox value={columnKey}>{label}</Checkbox>\n      )}\n      <PushpinOutlined\n        className={`${prefix}-pin`}\n        onClick={isFixed ? onCancelFix : onFix}\n      />\n    </div>\n  )\n}\n\nexport default Item;\n"
  },
  {
    "path": "packages/table-render/src/core/ToolbarView/InteriorTool/ColumnSetting/utils.ts",
    "content": "import { ToolbarActionConfig } from \"@/types\";\nimport omit from 'lodash.omit';\n\nexport type Setting = ToolbarActionConfig['columnsSettingValue'];\n\n\n/**\n * 固定某一列\n */\nexport const fixItem: (setting: Setting, fixKey: string) => Setting = (setting, fixKey) => {\n  return setting.map(i => {\n    if (i.key === fixKey) {\n      const { onFirstPart, preFixed, nextFixed, isFirstOne, isLastOne, index } = getStatus(setting, i.key);\n      let fixed;\n\n      if (preFixed && !nextFixed && !isLastOne) {\n        fixed = setting[index - 1].fixed\n      } else if (!preFixed && nextFixed && !isFirstOne) {\n        fixed = setting[index + 1].fixed\n      } else if (onFirstPart) {\n        fixed = 'left'\n      } else {\n        fixed = 'right';\n      }\n\n      return {\n        ...i,\n        fixed,\n      }\n    }\n    return i;\n  })\n}\n\n\n/**\n * 取消固定不应该的固定的那些列\n * \n * @param setting 排序或固定之后的 setting 数组\n * @returns newSetting 新 setting 数组\n */\nexport const cancelFixed: (setting: Setting) => Setting = (setting) => {\n  return setting.map((i) => {\n    if (i.fixed) {\n      const { haveBackwardUnFixed, haveForwardUnFixed, isFirstOne, isLastOne } = getStatus(setting, i.key);\n\n      if (haveForwardUnFixed && haveBackwardUnFixed && !isLastOne && !isFirstOne) {\n        return omit(i, 'fixed');\n      }\n    }\n    return i;\n  })\n}\n\n\n/**\n * 获取当前项的位置状态\n */\nexport const getStatus = (setting: Setting, key: string) => {\n  const length = setting.length;\n  const index = setting.findIndex(i => i.key === key);\n  const isFixed = !!setting[index].fixed;\n  const isFirstOne = index === 0;\n  const isLastOne = index === length - 1;\n  const preFixed = !isFirstOne && !!setting[index - 1]?.fixed;\n  const nextFixed = !isLastOne && !!setting[index + 1]?.fixed;\n  const haveForwardUnFixed = setting.slice(0, index).some(i => !i.fixed);\n  const haveBackwardUnFixed = setting.slice(index).some(i => !i.fixed);\n  const onFirstPart = index + 1 < (length / 2);\n\n  return {\n    index,\n    /** 当前项是固定的 */\n    isFixed,\n    /** 是第一个 */\n    isFirstOne,\n    /** 是最后一个 */\n    isLastOne,\n    /** 前一个是固定的 */\n    preFixed,\n    /** 后一个是固定的 */\n    nextFixed,\n    /** 前面存在未固定的 */\n    haveForwardUnFixed,\n    /** 后面存在未固定的 */\n    haveBackwardUnFixed,\n    /** 在前半部分 */\n    onFirstPart,\n  }\n}\n"
  },
  {
    "path": "packages/table-render/src/core/ToolbarView/InteriorTool/DensityIcon.tsx",
    "content": "import React, { useRef, useContext } from 'react';\nimport { Dropdown, ConfigProvider, Tooltip } from 'antd';\nimport { ColumnHeightOutlined } from '@ant-design/icons';\nimport { useTableStore } from '../../store';\nimport { translation } from '../../../utils';\n\nexport type DensitySize = 'middle' | 'small' | 'default' | undefined;\n\nconst DesityIcon = () => {\n  const configCtx = useContext(ConfigProvider.ConfigContext);\n  const t = translation(configCtx);\n\n  const dropRef = useRef<any>(null); // class组件用 React.createRef()\n  const tableSize = useTableStore((state) => state.tableSize);\n  const setState = useTableStore((state) => state.setState);\n\n  const items = [\n    {\n      key: 'large',\n      label: t('default')\n    },\n    {\n      key: 'middle',\n      label: t('middle')\n    },\n    {\n      key: 'small',\n      label: t('small')\n    },\n  ]\n\n  return (\n    <div ref={dropRef}>\n      <Dropdown\n        getPopupContainer={() => dropRef.current}\n        menu={{\n          selectedKeys: [tableSize || 'large'],\n          onClick: ({ key }) => {\n            setState({ tableSize: key as DensitySize });\n          },\n          style: { width: 80 },\n          items,\n        }}\n        trigger={['click']}\n      >\n        <Tooltip title={t('table_density')}>\n          <ColumnHeightOutlined />\n        </Tooltip>\n      </Dropdown>\n    </div>\n  );\n};\n\nexport default DesityIcon;\n"
  },
  {
    "path": "packages/table-render/src/core/ToolbarView/InteriorTool/FullScreenIcon.tsx",
    "content": "import React, { useState, useContext } from 'react';\nimport { message, Tooltip, ConfigProvider } from 'antd';\nimport { FullscreenExitOutlined, FullscreenOutlined } from '@ant-design/icons';\nimport { translation } from '../../../utils';\n\nconst FullScreenIcon: React.FC<{\n  fullScreen: () => Promise<void>;\n}> = props => {\n  const configCtx = useContext(ConfigProvider.ConfigContext);\n  const t = translation(configCtx);\n  const [isFullScreen, setFullScreen] = useState(false);\n  const { fullScreen } = props;\n\n  return isFullScreen ? (\n    <Tooltip title={t('exit_full_screen')}>\n      <FullscreenExitOutlined\n        onClick={() => {\n          document.exitFullscreen();\n          setFullScreen(false);\n        }}\n      />\n    </Tooltip>\n  ) : (\n    <Tooltip title={t('full_screen')}>\n      <FullscreenOutlined\n        onClick={() => {\n          if (!document.fullscreenEnabled) {\n            message.warning(t('cannot_full_screen'));\n            return;\n          }\n          if (!document.fullscreenElement) {\n            setFullScreen(true);\n            fullScreen().catch((err: any) => message.error(err.message));\n          }\n        }}\n      />\n    </Tooltip>\n  );\n};\n\nexport default FullScreenIcon;\n"
  },
  {
    "path": "packages/table-render/src/core/ToolbarView/InteriorTool/ReloadIcon.tsx",
    "content": "import React, { useContext} from 'react';\nimport { Tooltip, ConfigProvider } from 'antd';\nimport { ReloadOutlined } from '@ant-design/icons';\nimport { translation } from '../../../utils';\n\nconst ReloadIcon = ({ refresh }) => {\n  const configCtx = useContext(ConfigProvider.ConfigContext);\n  const t = translation(configCtx);\n  return (\n    <Tooltip title={t('reload')}>\n      <ReloadOutlined onClick={() => refresh()} />\n    </Tooltip>\n  );\n};\n\nexport default ReloadIcon;\n"
  },
  {
    "path": "packages/table-render/src/core/ToolbarView/InteriorTool/index.tsx",
    "content": "import { Space } from 'antd';\nimport React from 'react';\nimport DensityIcon from './DensityIcon';\nimport FullScreenIcon from './FullScreenIcon';\nimport ReloadIcon from './ReloadIcon';\nimport ColumnSetting from './ColumnSetting';\nimport { ToolbarActionConfig } from '@/types';\nimport defaults from 'lodash.defaults';\n\nconst defaultConfig: ToolbarActionConfig = {\n  enabled: ['columnsSetting', 'density', 'fullScreen', 'refresh']\n}\n\nconst ToolBar: React.FC<{\n  fullScreen: () => Promise<void>,\n  refresh: () => void,\n  toolbarAction: ToolbarActionConfig,\n}> = ({ refresh, fullScreen, toolbarAction = false }) => {\n\n\n  const toolbarActionConfig = (typeof toolbarAction === 'boolean' && toolbarAction)\n    ? defaultConfig\n    : defaults(toolbarAction, defaultConfig)\n\n  const { columnsSettingValue, onColumnsSettingChange, enabled } = toolbarActionConfig;\n\n  if (!toolbarAction) return null;\n\n  return (\n    <Space size={14} style={{ fontSize: 18 }}>\n      {enabled.includes('refresh') && <ReloadIcon refresh={refresh} />}\n      {enabled.includes('density') && <DensityIcon />}\n      {enabled.includes('fullScreen') && <FullScreenIcon fullScreen={fullScreen} />}\n      {enabled.includes('columnsSetting') && <ColumnSetting columnsSettingValue={columnsSettingValue} onColumnsSettingChange={onColumnsSettingChange} />}\n    </Space>\n  );\n};\n\nexport default ToolBar;\n"
  },
  {
    "path": "packages/table-render/src/core/ToolbarView/TitleView.tsx",
    "content": "import React from 'react';\nimport { Radio } from 'antd';\nimport { isFunction, isArray } from '../../utils';\n\nconst TitleView = (props: any) => {\n  const { title, request, setState, doSearch, onTabChange, currentTab } = props;\n  const tabIndex = currentTab || 0;\n\n  const handleTabChange = (ev: any) => {\n    const _tabIndex = ev.target.value;\n    if (isFunction(onTabChange)) {\n      onTabChange(_tabIndex, ev);\n    }\n    setState({ tab: _tabIndex });\n  };\n\n  if (isArray(request) && request.length > 1) {\n    return (\n      <>\n        <Radio.Group onChange={handleTabChange} value={tabIndex}>\n          {request.map((item: any, index: any) => {\n            return (\n              <Radio.Button key={index.toString()} value={index}>\n                {item.name}\n              </Radio.Button>\n            );\n          })}\n        </Radio.Group>\n        {title && <div className='tr-extra-tab'>{title}</div>}\n      </>\n    );\n  }\n\n  let content = title;\n  if (isArray(request)) {\n    content = request[0].name;\n  }\n  return <div className='tr-single-tab'>{content || ''}</div>;\n};\n\nexport default TitleView;\n"
  },
  {
    "path": "packages/table-render/src/core/ToolbarView/index.less",
    "content": ".tr-toolbar {\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n  height: 64px;\n}\n\n.tr-toolbar-nohead {\n  height: 24px;\n}\n\n\n.tr-toolbar-right {\n  display: flex;\n  align-items: flex-start;\n}\n\n"
  },
  {
    "path": "packages/table-render/src/core/ToolbarView/index.tsx",
    "content": "import React from 'react';\nimport { Space } from 'antd';\nimport classNames from 'classnames';\n\nimport { _get, isFunction, isArray } from '../../utils';\nimport InteriorTool from './InteriorTool';\n\nimport TitleView from './TitleView';\nimport './index.less';\n\nconst ToolbarView = props => {\n  const {\n    setState,\n    title,\n    toolbarRender,\n    toolbarAction = false,\n    request,\n    refresh,\n    fullScreen,\n    currentTab,\n    onTabChange\n  } = props;\n\n  const content = isFunction(toolbarRender) ? toolbarRender() : (toolbarRender || []);\n  const isTopHead = title || (!!content && content?.length !== 0) || (isArray(request) && request.length > 1);\n\n  return (\n    <div className={classNames('tr-toolbar', { 'tr-toolbar-nohead': !isTopHead && !toolbarAction })}>\n      <div className='tr-toolbar-left'>\n        <TitleView title={title} setState={setState} request={request} currentTab={currentTab} onTabChange={onTabChange} />\n      </div>\n      <div className='tr-toolbar-right'>\n        <Space>\n          <Space>{content}</Space>\n          <InteriorTool toolbarAction={toolbarAction} fullScreen={fullScreen} refresh={refresh} />\n        </Space>\n      </div>\n    </div>\n  );\n}\n\nexport default ToolbarView;\n"
  },
  {
    "path": "packages/table-render/src/core/index.less",
    "content": ".tr-table-wrapper {\n  background: #fff;\n  padding: 0 24px 24px;\n  width: 100%;\n  overflow: auto;\n  box-sizing: border-box;\n}\n\n.tr-action-list {\n  display: flex;\n  justify-content: center;\n}\n\n.tr-single-tab {\n  color: rgba(0, 0, 0, 0.85);\n  font-size: 16px;\n  font-weight: 500;\n  line-height: 24px;\n  opacity: 0.85;\n}\n\n.tr-extra-tab {\n  display: inline-block;\n  margin-left: 16px;\n}\n\n.tr-search-btn.fr-field {\n  padding-right: 0;\n}\n\n.mb2 {\n  margin-bottom: 0.5rem;\n}\n\n.mr {\n  margin-right: 8px;\n}\n\n.flex {\n  display: flex;\n}\n\n.justify-end {\n  justify-content: flex-end;\n}\n\n.w-100 {\n  width: 100%;\n}\n"
  },
  {
    "path": "packages/table-render/src/core/index.tsx",
    "content": "import React, { useEffect, useRef, useImperativeHandle } from 'react';\nimport { useForm } from 'form-render';\nimport { useUpdateEffect } from 'ahooks';\n\nimport { useTableStore } from './store';\nimport { _get, isFunction, isArray } from '../utils';\nimport { TableRenderProps } from '../types';\n\nimport ErrorBoundary from './ErrorBoundary';\nimport SearchView from './SearchView';\nimport ToolbarView from './ToolbarView';\nimport TableView from './TableView';\n\nimport './index.less';\n\ntype ISearchParams = {\n  current?: number;\n  tab?: number | string;\n  pageSize?: number;\n  sorter?: any;\n};\n\nconst RenderCore: React.FC<TableRenderProps & { tableRef: any }> = props => {\n  const {\n    search: searchProps,\n    debug, className,\n    style,\n    title,\n    toolbarRender,\n    toolbarAction,\n    tableRef,\n    request: api,\n    size,\n    tableWrapper,\n    autoRequest = true,\n    columns,\n    onTabChange,\n    ...tableProps\n  } = props;\n\n  const { hidden: hiddenSearch } = searchProps || { hidden: true };\n\n  const form = useForm();\n  const rootRef = useRef<HTMLDivElement>(null);\n\n  const inited = useTableStore((state) => state.inited);\n  const currentTab = useTableStore((state) => state.tab);\n  const tableSize = useTableStore((state) => state.tableSize);\n  const pagination = useTableStore((state) => state.pagination);\n  const setState = useTableStore((state) => state.setState);\n  const getState = useTableStore((state) => state.getState);\n  const setColumns = useTableStore((state) => state.setColumns);\n\n  useEffect(() => {\n    const initState = {\n      tableSize: size,\n      inited: true,\n      pagination\n    };\n\n    if (typeof tableProps?.pagination === 'object') {\n      if (tableProps?.pagination?.current) {\n        initState.pagination.current = tableProps.pagination.current;\n      }\n      if (tableProps?.pagination?.pageSize) {\n        initState.pagination.pageSize = tableProps.pagination.pageSize;\n      }\n    }\n    setState(initState);\n  }, []);\n\n  useEffect(() => {\n    if (columns) {\n      setColumns(columns);\n    }\n  }, [columns]);\n\n  useEffect(() => {\n    if (inited && hiddenSearch && autoRequest) {\n      refresh();\n    }\n  }, [inited]);\n\n  useUpdateEffect(() => {\n    refresh();\n  }, [currentTab])\n\n  useImperativeHandle(tableRef, () => ({\n    doSearch,\n    refresh,\n    changeTab,\n    form,\n    getState: () => ({\n      ...getState(),\n      search: form.getValues(true)\n    }),\n    setState,\n  }));\n\n  const fullScreen = () => {\n    return Promise.resolve(rootRef.current?.requestFullscreen());\n  };\n\n  const doSearch = (params: ISearchParams, customSearch?: Record<string, any>) => {\n    const { current, pageSize, tab, sorter, ...extraSearch } = params || {};\n  \n    const _pageNum = current || 1;\n    const _pageSize = pageSize || 10;\n\n    let _tab: any = currentTab;\n    if (['string', 'number'].indexOf(typeof tab) > -1) {\n      _tab = tab;\n    }\n    const _pagination = { current: _pageNum, pageSize: _pageSize };\n\n    const getTableData = (_api: any) => {\n      setState({ loading: true });\n      let _params = {\n        ...form.getValues(true),\n        ...customSearch,\n        ...extraSearch,\n        ..._pagination,\n      };\n\n      Promise.resolve(_api(_params, sorter, { tab: _tab }))\n        .then(res => {\n          // TODO：这里校验res是否规范\n          const { rows, data, total, pageSize, ...extraData } = res;\n\n          setState({\n            loading: false,\n            dataSource: data || rows,\n            ...extraData,\n            pagination: {\n              ..._pagination,\n              total,\n              pageSize: pageSize || _pageSize,\n            },\n          });\n\n          searchProps?.afterSearch?.({ data, total, pageSize, ...extraData });\n        })\n        .catch(err => {\n          setState({ loading: false });\n        });\n    };\n\n    if (isFunction(api)) {\n      getTableData(api);\n      return;\n    }\n    if (isArray(api)) {\n      const _api = _get(api, `[${_tab}].api`);\n      if (isFunction(_api)) {\n        getTableData(_api);\n        return;\n      }\n    }\n    console.warn('api 不是函数，检查 <Search /> 的 props');\n  };\n\n  const refresh = (params?: { tab?: string | number; stay?: boolean }, moreSearch?: any) => {\n    const _stay = (params && params.stay) || false;\n    const _tab = params && params.tab;\n    const _search = moreSearch || {};\n   \n    doSearch(\n      {\n        ...params,\n        current: _stay ? pagination.current : 1,\n        tab: _tab,\n        pageSize: pagination.pageSize,\n      },\n      _search\n    );\n  };\n\n  const changeTab = (tab: string | number) => {\n    if (['string', 'number'].indexOf(typeof tab) > -1) {\n      setState({ tab });\n      // refresh({ tab });\n    } else {\n      console.error('changeTab的入参必须是number或string');\n    }\n  };\n\n  const tableNode = (\n    <div\n      ref={rootRef}\n      className={`tr-table-wrapper ${className}`}\n      style={style}\n    >\n      <ToolbarView\n        request={api}\n        doSearch={doSearch}\n        refresh={refresh}\n        fullScreen={fullScreen}\n        title={title}\n        tableSize={tableSize}\n        currentTab={currentTab}\n        onTabChange={onTabChange}\n        toolbarAction={toolbarAction}\n        toolbarRender={toolbarRender}\n        setState={setState}\n        getState={getState}\n      />\n      <TableView\n        {...tableProps}\n        doSearch={doSearch}\n      />\n    </div>\n  );\n\n  const renderTable = () => {\n    if (isFunction(tableWrapper)) {\n      return tableWrapper(tableNode);\n    }\n\n    return tableNode;\n  };\n\n  return (\n    <div>\n      <SearchView\n        {...searchProps}\n        form={form}\n        refresh={refresh}\n        getState={getState}\n        hidden={hiddenSearch}\n      />\n      <ErrorBoundary>\n        {renderTable()}\n      </ErrorBoundary>\n    </div>\n  );\n}\n\nexport default RenderCore;\n"
  },
  {
    "path": "packages/table-render/src/core/store.ts",
    "content": "import React from 'react';\nimport { create, useStore } from 'zustand';\nimport { ToolbarActionConfig, ProColumnsType } from '../types';\n\nexport type TableRenderStoreType = {\n  loading: boolean;\n  api: null,\n  /**\n   * 如果api是数组，需要在最顶层感知tab，来知道到底点击搜索调用的是啥api\n   */\n  tab: 0,\n  /** \n   * 表格列定义\n   */\n  columns: ProColumnsType<any>,\n  dataSource: any[],\n  /**\n   * 需要用到的 dataSource 以外的扩展返回值\n   */\n  extraData: null,\n  extraParams: {},\n  pagination: {\n    current: number,\n    pageSize: number,\n    total: number,\n  },\n  tableSize: 'default',\n  schema: any,\n  inited: boolean,\n  init?: (schema: TableRenderStoreType['schema']) => any;\n  getState: () => any;\n  setState: (state: any) => void;\n  /**\n   * 更新列数据\n   */\n  setColumns: (columns: ProColumnsType<any>) => void;\n  /** \n   * 动态设置列状态\n   */\n  columnsSetting: ToolbarActionConfig['columnsSettingValue'];\n  setColumnsSetting: (setting: ToolbarActionConfig['columnsSettingValue']) => void;\n};\n\nexport const StoreContext = React.createContext(null);\n\nexport const createStore = (defaultProps?: Partial<TableRenderStoreType>) => create<TableRenderStoreType>()((set, get) => ({\n  ...defaultProps,\n  loading: false,\n  api: null,\n  tab: 0,\n  dataSource: [],\n  extraData: null,\n  extraParams: {},\n  pagination: {\n    current: 1,\n    pageSize: 10,\n    total: 0,\n  },\n  tableSize: 'default',\n  schema: {},\n  columns: [],\n  inited: false,\n  columnsSetting: [],\n  setState: (state) => set({ ...state }),\n  getState: () => get(),\n  setColumns: (columns) => set({ columns }),\n  setColumnsSetting: (setting) => set({ columnsSetting: setting }),\n}));\n\nexport const useTableStore = <T>(\n  selector: (store: TableRenderStoreType) => T,\n  equalityFn?: (left: T, right: T) => boolean\n) => {\n  const store = React.useContext(StoreContext);\n  return useStore(store, selector, equalityFn);\n}"
  },
  {
    "path": "packages/table-render/src/index.tsx",
    "content": "import React, { useEffect } from 'react';\nimport { ConfigProvider } from 'antd';\nimport dayjs from 'dayjs';\nimport zhCN from 'antd/lib/locale/zh_CN';\nimport enUS from 'antd/lib/locale/en_US';\nimport 'dayjs/locale/zh-cn';\nimport locales from './locales';\nimport { StoreContext, createStore } from './core/store';\nimport { UseBoundStore, StoreApi } from 'zustand'\n\nimport RenderCore from './core';\nimport { TableContext, TableRenderProps } from './types';\n\nexport type {\n  TableRenderProps,\n  ProColumnsType,\n  SearchProps,\n  TableContext,\n} from './types';\n\nconst TableRender = React.forwardRef<TableContext, TableRenderProps>((props, ref) => {\n  const {\n    configProvider,\n    locale = 'zh-CN',\n    ...otherProps\n  } = props;\n\n  const storeRef = React.useRef<UseBoundStore<StoreApi<TableRenderProps>>>(null);\n  if (!storeRef.current) {\n    storeRef.current = createStore();\n  }\n\n  useEffect(() => {\n    if (locale === 'en-US') {\n      dayjs.locale('en');\n    } else {\n      dayjs.locale('zh-cn');\n    }\n  }, [locale]);\n\n  const antdLocale = locale === 'zh-CN' ? zhCN : enUS;\n  if (otherProps.search && locale) {\n    otherProps.search.locale = locale;\n  }\n\n  const langPack: any = {\n    ...antdLocale,\n    \"TableRender\": locales[locale],\n    ...configProvider?.locale\n  };\n\n  return (\n    <ConfigProvider\n      {...configProvider}\n      locale={langPack}\n    >\n      <StoreContext.Provider value={storeRef.current}>\n        <RenderCore {...otherProps} tableRef={ref} />\n      </StoreContext.Provider>\n    </ConfigProvider>\n  );\n});\n\nexport default TableRender;"
  },
  {
    "path": "packages/table-render/src/locales/en_US.ts",
    "content": "export default {\n  \"search\": \"Search\",\n  \"reset\": \"Reset\",\n  \"copy_success\": \"Copy Success\",\n  \"column_setting\": \"ColumnSetting\",\n  \"default\": \"Default\",\n  \"middle\": \"Middle\",\n  \"small\": \"small\",\n  \"table_density\": \"Table Density\",\n  \"exit_full_screen\": \"Exit FullScreen\",\n  \"full_screen\": \"FullScreen\",\n  \"cannot_full_screen\": \"The current page does not support full screen function\",\n  \"reload\": \"Reload\"\n}"
  },
  {
    "path": "packages/table-render/src/locales/index.ts",
    "content": "import enUS from './en_US';\nimport zhCN from './zh_CN';\n\nexport default {\n  'en-US':  enUS,\n  'zh-CN': zhCN,\n}"
  },
  {
    "path": "packages/table-render/src/locales/zh_CN.ts",
    "content": "export default {\n  \"search\": \"查询\",\n  \"reset\": \"重置\",\n  \"copy_success\": \"复制成功\",\n  \"column_setting\": \"列设置\",\n  \"default\": \"默认\",\n  \"middle\": \"中等\",\n  \"small\": \"紧凑\",\n  \"table_density\": \"表格密度\",\n  \"exit_full_screen\": \"退出全屏\",\n  \"full_screen\": \"全屏\",\n  \"cannot_full_screen\": \"当前页面不支持全屏功能\",\n  \"reload\": \"刷新\"\n};"
  },
  {
    "path": "packages/table-render/src/types.ts",
    "content": "import { TableProps } from 'antd';\nimport type { TableColumnType } from 'antd';\nimport { FRProps, FormInstance } from 'form-render';\nimport type { ConfigProviderProps } from 'antd/es/config-provider';\nimport type { TableRenderStoreType } from './core/store';\n\nexport type ColumnsSettingValueType = Array<{\n  /** 列的 key */\n  key: string,\n  /** 当前列是否隐藏 */\n  hidden: boolean,\n  /** 当前列是否固定 */\n  fixed?: 'right' | 'left'\n}>\n\nexport type ToolbarActionConfig = {\n  /** 开启的功能，默认 all，全部开启 */\n  enabled?: Array<'refresh' | 'columnsSetting' | 'fullScreen' | 'density'>,\n  /** 列设置的状态 */\n  columnsSettingValue?: ColumnsSettingValueType\n  /** 列设置状态改变时的回调 */\n  onColumnsSettingChange?: (val: ColumnsSettingValueType) => void;\n}\nexport type DoSearchType = (\n  params: {\n    current?: number;\n    tab?: number | string;\n    pageSize?: number;\n    sorter?: any;\n  },\n  customSearch?: any\n) => Promise<void>\n\nexport type RefreshType = (\n  params?: { stay?: boolean; tab?: number | string },\n  search?: any\n) => Promise<void>\n\nexport type ChangeTabType = (tab: number | string) => Promise<void>;\n\nexport interface TableContext {\n  doSearch: DoSearchType,\n  refresh: RefreshType,\n  changeTab: ChangeTabType,\n  form: FormInstance,\n  getState: () => TableRenderStoreType & { search: Record<string, any> },\n}\n\nexport type ProColumnsType<T extends object = any> = Array<\n  TableColumnType<T> & {\n    dataIndex?: string;\n    /** 是否支持复制 */\n    copyable?: boolean;\n    /** 值的类型 */\n    valueType?: 'text' | 'money' | 'date' | 'dateTime' | 'code' | 'tag' | 'tags' | 'progress' | 'dateRange' | 'dateTimeRange' | 'image';\n    /** 当前列值的枚举 */\n    enum?: Record<string, string>;\n  }\n>;\n\nexport interface TableState<RecordType> {\n  loading: boolean;\n  api: ApiType<RecordType>;\n  tab: number;\n  dataSource: Array<RecordType>;\n  extraData: any;\n  extraParams: Record<string, any>;\n  pagination: {\n    current: number;\n    pageSize: number;\n    total: number;\n  };\n  tableSize: TableProps<any>['size'];\n  sorter: any;\n}\n\n// TODO这里FR的props应该去FR里写，这里继承就好了\nexport interface SearchProps<RecordType> extends Omit<FRProps, 'form'> {\n  debug?: boolean;\n  searchBtnStyle?: React.CSSProperties;\n  searchBtnClassName?: string;\n  displayType?: any;\n  propsSchema?: any;\n  className?: string;\n  style?: React.CSSProperties;\n  hidden?: boolean;\n  searchOnMount?: boolean | unknown;\n  searchWithError?: boolean;\n  searchBtnRender?: (\n    submit: Function,\n    clearSearch: Function,\n    other: any\n  ) => React.ReactNode[];\n  searchText?: string;\n  resetText?: string;\n  onSearch?: (search: any) => any;\n  afterSearch?: (params: any) => any;\n  widgets?: any;\n  form?: any;\n  [key: string]: any\n}\n\ntype ApiType<RecordType> =\n  | SearchApi<RecordType>\n  | Array<{ api: SearchApi<RecordType>; name: string }>;\n\nexport type SearchApi<RecordType> = (\n  params: Record<string, any> & {\n    current: number;\n    pageSize: number;\n    tab?: number;\n  },\n  sorter?: any\n) => Promise<{\n\n  /**\n   * @deprecated 即将弃用，请使用 data 返回\n   */\n  rows?: Array<RecordType>;\n  data: Array<RecordType>;\n  total: number;\n  pageSize?: number;\n}>;\n\nexport interface TablePropsC<RecordType extends Object = any>\n  extends Omit<TableProps<RecordType>, 'columns' | 'dataSource' | 'title'> {\n  /** 列定义，除了支持antd的所有配置，还额外增加一些语法糖 */\n  columns: ProColumnsType<RecordType>;\n  /** title */\n  title?: string | React.ReactNode;\n}\n\nexport interface TableRenderProps<RecordType extends Object = any>\n  extends Omit<TablePropsC<RecordType>, 'locale'> {\n  /**\n   * 开启 debug 模式，时时显示内部状态\n  */\n  debug?: boolean;\n  /** 表格主体右上方的控件，例如“添加”按钮 */\n  toolbarRender?: React.ReactNode;\n  /** \n   * 显示在表格主体右上方的 Icon 列表，内置了刷新、调整密度、全屏显示 等功能\n   * \n   * 可以通过传入一个对象进行更具体的配置\n   */\n  toolbarAction?: boolean | Pick<ToolbarActionConfig, 'enabled'>;\n  /** 切换分页时是否需要请求接口 */\n  pageChangeWithRequest?: boolean;\n  onTabChange?: () => any;\n  search?: SearchProps<RecordType>;\n  locale?: 'zh-CN' | 'en-US';\n  /**\n   * antd的全局config\n   */\n  configProvider?: ConfigProviderProps;\n  /**\n   * 自定义渲染表格\n   */\n  tableWrapper?: (tableNode: React.ReactNode) => React.ReactNode;\n  request?: ApiType<RecordType>;\n  // 自动请求\n  autoRequest?: boolean;\n}\n"
  },
  {
    "path": "packages/table-render/src/utils/index.ts",
    "content": "import dayjs from 'dayjs';\nimport { set, get, cloneDeep, has as _has, merge, isUndefined, omitBy, debounce } from 'lodash-es';\nimport { ProColumnsType } from '..';\n\nexport const _set = set;\nexport const _get = get;\nexport const _cloneDeep = cloneDeep;\n// export const _has = has;\nexport { _has };\nexport const _merge = merge;\nexport const _isUndefined = isUndefined;\nexport const _omitBy = omitBy;\nexport const _debounce = debounce;\n\nexport const getDateTime = (time: any, format?: string) => {\n  if (!time) return null;\n  return dayjs(time).format(format || 'YYYY-MM-DD HH:mm:ss');\n};\n\nexport const getDate = (time: any, format?: string) => {\n  if (!time) return null;\n  return dayjs(time).format(format || 'YYYY-MM-DD');\n};\n\nexport const getDateRange = (value: any, { result, record }, _format?: string) => {\n  let data = value;\n  const { bind, format } = result.valueTypeProps || {}\n  if (bind) {\n    data = [record?.[bind[0]], record?.[bind[1]]];\n  }\n\n\n  if (!isArray(data)) return null;\n  const start = getDate(data[0], _format || format);\n  const end = getDate(data[1], _format || format);\n\n  if (start && end) {\n    return `${start} ~ ${end}`;\n  }\n\n  if (start) {\n    return `${start}（开始时间）`;\n  }\n\n  if (end) {\n    return `${end}（结束时间）`;\n  }\n};\n\n// 格式化千分符\nexport const getMoneyType = (num: any) => {\n  if (!num) return null;\n  return `¥${num}`.replace(/(\\d)(?=(\\d{3})+(?!\\d))/g, '$1,');\n};\n\n// 如果是函数，则解析，如果不是，直接返回值\nexport const parseFunctionValue = (value, params, cb) => {\n  let _value = value;\n  if (typeof _value === 'function') {\n    _value = _value(params);\n  } else {\n    cb && typeof cb === 'function' && cb();\n  }\n  return _value;\n};\n\nexport const isObject = (data: any) => {\n  const str = Object.prototype.toString.call(data);\n  return str.indexOf('Object') > -1;\n}\n\nexport const isArray = (data: any) => {\n  const str = Object.prototype.toString.call(data);\n  return str.indexOf('Array') > -1;\n}\n\nexport const isFunction = (data: any) => typeof data === 'function';\n\nexport const translation = (configCtx: any) => (key: string) => {\n  const locale = configCtx?.locale.TableRender;\n  return locale[key];\n}\n\n\n/**\n * 从 column 中获取 key\n *\n * 优先级：key > dataIndex > title\n *\n * @param column 表格列\n * @param fallback 备选值\n * @returns key\n */\nexport const getColumnKey = (column: ProColumnsType<any>[number], fallback: any) => {\n  if (column.key) return String(column.key);\n  if (column.dataIndex) return String(column.dataIndex);\n  if (column.title && typeof column.title === 'string') return column.title;\n  console.warn('[Table Render]: column must have a key or dataIndex or title');\n  return String(fallback);\n}\n"
  },
  {
    "path": "packages/table-render/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"ES2015\",\n    \"module\": \"ES2015\",\n    \"moduleResolution\": \"node\",\n    \"importHelpers\": true,\n    \"jsx\": \"react\",\n    \"esModuleInterop\": true,\n    \"sourceMap\": true,\n    \"baseUrl\": \"./\",\n    // \"strict\": true,\n    \"declaration\": true,\n    \"paths\": {\n      \"@/*\": [\"src/*\"],\n      \"@@/*\": [\"src/.umi/*\"]\n    },\n    \"allowJs\": true,\n    \"allowSyntheticDefaultImports\": true,\n    \"noImplicitAny\": false,\n    \"resolveJsonModule\": true\n  },\n  \"exclude\": [\n    \"node_modules\",\n    \"lib\",\n    \"es\",\n    \"dist\",\n    \"typings\",\n    \"**/__test__\",\n    \"test\",\n    \"tests\",\n    \"docs\"\n  ]\n}\n"
  },
  {
    "path": "packages/table-render/开发文档.md",
    "content": "# 开发文档\n\n## 启动\n\n```\n# 安装依赖\nyarn\n# 起项目\nnpm start\n```\n\n## 开发\n\n小需求 & 简单的 bug fix，在 dev 分支开发，大改动另起一个分支，分支名表达改动内容就行\n\n## 发布\n\n```\n# tag版本号\nnpm version 1.0.0 或者 npm version patch\n# 发版\nnpm publish\n# 部署文档\nnpm run deploy\n```\n"
  },
  {
    "path": "packages/x-flow/.fatherrc.js",
    "content": "import copy from 'rollup-plugin-copy';\n\nexport default {\n  cjs: 'babel',\n  esm: {\n    type: 'babel',\n    importLibToEs: true,\n  },\n  lessInBabelMode: true,\n  extraRollupPlugins: [\n    copy({\n      targets: [{ src: 'src/index.d.ts', dest: 'dist/' }],\n    }),\n  ],\n  extraBabelPlugins: [\n    [\n      'import',\n      {\n        libraryName: 'antd',\n        libraryDirectory: 'es',\n        style: true,\n      },\n      'antd',\n    ],\n  ],\n};\n"
  },
  {
    "path": "packages/x-flow/CHANGELOG.md",
    "content": "# 更新日志\n\n### 1.0.3\n- [+] xflow 初始版本发布\n\n### 1.0.4\n- [+] 增加自定义组件完整案例，修复下拉框在全屏模式下无法显示的问题，增加撤销与重做按钮的隐藏配置\n\n### 1.0.5\n- [+] 补充节点图标配置文档，以及更新图标的SVG形式的用法\n"
  },
  {
    "path": "packages/x-flow/CONTRIBUTING.md",
    "content": "# 如何贡献代码\n\n欢迎给 XFlow 提优化建议，或者修复已有 Bug，共促其发展\n\n## Branch 管理\n\n```\nmaster\n ↑\ndev         <--- Develop/PR\n```\n\n- `dev` 分支\n  - 所有的开发均在 dev 分支进行\n  - 提 PR 时候请提交到 dev 分支\n- `master` 分支\n  - `master` 是稳定不改的分支，不会在上面进行代码开发\n  - 在 dev 分支 publish 后会 merge 到 master，同时打对应 tag\n\n## Commit 格式\n\n```\n[{action}] {description}\n```\n\n- `{action}`\n  - `+` 新增功能\n  - `!` 更新或者修复 bug\n  - `-` 移除功能\n- `{description}`\n  - 尽可能详细的描述就好\n\nfor example:\n\n- [+] 列表选项新增拖拽功能\n- [!] 修复输入框长按闪烁的问题\n\n## 更多\n\n- 很推荐在提交 PR 前，先在钉钉群里进行讨论，已防止此功能已经有同学在开发了\n- 但是如果是想修复文档和明显代码错误，直接提交 PR 就好\n\n<img src=https://img.alicdn.com/imgextra/i2/O1CN01RoUHQF1EoKLWyotFn_!!6000000000398-0-tps-750-990.jpg width=250/>\n"
  },
  {
    "path": "packages/x-flow/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2019-present XRender Team\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "packages/x-flow/README.md",
    "content": "<div style=\"display:flex;align-items:center;margin-bottom:24px\">\n  <img src=\"https://img.alicdn.com/tfs/TB17UtINiLaK1RjSZFxXXamPFXa-606-643.png\" alt=\"logo\" width=\"48px\"/>\n  <span style=\"font-size:30px;font-weight:600;display:inline-block;margin-left:12px\">XFlow</span>\n</div>\n<p style=\"display:flex;justify-content:space-between;width:440px\">\n  <a href=\"https://www.npmjs.com/package/@xrenders/xflow\" target=\"_blank\">\n    <img alt=\"npm\" src=\"https://img.shields.io/npm/v/@xrenders/xflow.svg?maxAge=3600&style=flat-square\">\n  </a>\n  <a href=\"https://npmjs.org/package/@xrenders/xflow\" target=\"_blank\">\n    <img alt=\"NPM downloads\" src=\"https://img.shields.io/npm/dm/@xrenders/xflow.svg?style=flat-square\">\n  </a>\n  <a href=\"https://npmjs.org/package/@xrenders/xflow\" target=\"_blank\">\n    <img alt=\"NPM all downloads\" src=\"https://img.shields.io/npm/dt/@xrenders/xflow.svg?style=flat-square\">\n  </a>\n  <a>\n    <img alt=\"PRs Welcome\" src=\"https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square\">\n  </a>\n</p>\n\n画布流程编排解决方案\n\n## 安装\n\n```bash\nnpm install @xrenders/xflow\n```\n\n## 基础使用\n\n### 1. 引入组件\n\n```js\nimport XFlow from '@xrenders/xflow';\n```\n\n### 2. 配置节点菜单以及节点面板\n\n```js\n const nodeSettings = [\n    {\n      title: '开始',\n      type: 'Start',\n      hidden: true,\n      targetHandleHidden: true,\n      icon: {\n        type: 'icon-start',\n        bgColor: '#17B26A',\n      },\n      settingSchema: {\n        type: 'object',\n        properties: {\n          input: {\n            title: '变量一',\n            type: 'string',\n            widget: 'input',\n          },\n        },\n      },\n    },\n    {\n      title: '结束',\n      type: 'End',\n      hidden: true,\n      sourceHandleHidden: true,\n      icon: {\n        type: 'icon-end',\n        bgColor: '#F79009',\n      },\n      settingSchema: {\n        type: \"object\",\n        properties: {\n          input: {\n            title: '变量一',\n            type: 'string',\n            widget: 'input',\n          },\n        }\n      }\n    },\n    {\n      title: 'LLM',\n      type: 'LLM',\n      desc: '调用大语言模型回答问题或者对自然语言进行处理',\n      icon: {\n        type: 'icon-model',\n        bgColor: '#6172F3',\n      },\n      settingSchema: {\n        type: 'object',\n        properties: {\n          model: {\n            title: '模型',\n            type: 'string',\n            enum: ['gpt-3.5', 'gpt-4'],\n            default: 'gpt-3.5'\n          },\n          temperature: {\n            title: '温度',\n            type: 'number',\n            default: 0.7,\n            minimum: 0,\n            maximum: 1\n          }\n        }\n      }\n    },\n    {\n      title: 'Prompt',\n      type: 'Prompt',\n      description: '通过精心设计提示词，提升大语言模型回答效果',\n      icon: {\n        type: 'icon-prompt',\n        bgColor: '#17B26A',\n      },\n    },\n    {\n      title: '知识库',\n      type: 'knowledge',\n      description: '允许你从知识库中查询与用户问题相关的文本内容',\n      icon: {\n        type: 'icon-knowledge',\n        bgColor: '#6172F3',\n      },\n    },\n  ];\n```\n\n\n### 3. 使用组件\n\n```js\nconst Demo = () => {\n  return (\n    <XFlow\n      settings={nodeSettings}\n      initialValues={{\n        nodes: [],\n        edges: []\n      }}\n    />\n  );\n};\n```\n\n## 完整示例\n\n<code src=\"./demo/quickStart/index.tsx\"></code>\n\n## 注意事项\n1. 确保项目中已安装必要的依赖：\n   - React\n   - XFlow\n2. 组件需要设置固定高度，建议使用百分比或视口单位\n3. 节点配置中的 `settingSchema` 需要符合 FormRender 的协议规范\n4. 初始值 `initialValues` 中的节点数据需要包含必要的字段：id、type、data、position\n"
  },
  {
    "path": "packages/x-flow/package.json",
    "content": "{\n  \"name\": \"@xrenders/xflow\",\n  \"version\": \"1.0.8-beta.33\",\n  \"description\": \"一款功能强大、易用灵活的流程编辑器框架，帮助你轻松构建复杂的工作流和流程产品\",\n  \"keywords\": [\n    \"xflow\"\n  ],\n  \"homepage\": \"https://xrender.fun/xflow\",\n  \"bugs\": {\n    \"url\": \"https://github.com/alibaba/x-render/issues\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git@github.com:alibaba/form-render.git\"\n  },\n  \"license\": \"MIT\",\n  \"contributors\": [\n    {\n      \"name\": \"lhbxs\",\n      \"email\": \"596850703@qq.com\"\n    }\n  ],\n  \"main\": \"lib/index.js\",\n  \"module\": \"es/index.js\",\n  \"files\": [\n    \"es\",\n    \"lib\",\n    \"package.json\"\n  ],\n  \"scripts\": {\n    \"beta\": \"npm publish --tag beta\",\n    \"build\": \"father-build\",\n    \"next\": \"npm publish --tag next\",\n    \"prepare\": \"npm run build\",\n    \"prettier\": \"prettier --write \\\"**/*.{js,jsx,tsx,ts,less,md,json}\\\"\",\n    \"postpublish\": \"git push --tags\",\n    \"test:ui\": \"vitest --ui\"\n  },\n  \"lint-staged\": {\n    \"*.{js,jsx,less,md,json}\": [\n      \"prettier --write\"\n    ],\n    \"*.ts?(x)\": [\n      \"prettier --parser=typescript --write\"\n    ]\n  },\n  \"dependencies\": {\n    \"@ant-design/icons\": \"^4.0.2\",\n    \"@codemirror/lang-json\": \"^6.0.1\",\n    \"@dagrejs/dagre\": \"^1.1.3\",\n    \"@uiw/react-codemirror\": \"^4.23.7\",\n    \"@xyflow/react\": \"^12.3.2\",\n    \"ahooks\": \"^3.7.5\",\n    \"braft-editor\": \"^2.3.9\",\n    \"classnames\": \"^2.3.1\",\n    \"dayjs\": \"^1.11.7\",\n    \"form-render\": \"^2.3.4\",\n    \"immer\": \"^10.1.1\",\n    \"lodash-es\": \"^4.17.21\",\n    \"tinycolor2\": \"^1.6.0\",\n    \"use-context-selector\": \"^1.4.1\",\n    \"zundo\": \"^2.1.0\",\n    \"zustand\": \"^4.5.4\"\n  },\n  \"devDependencies\": {\n    \"deep-equal\": \"^2.0.3\",\n    \"rollup-plugin-copy\": \"^3.4.0\"\n  },\n  \"peerDependencies\": {\n    \"antd\": \"4.x || 5.x\",\n    \"react\": \">=16.9.0\",\n    \"react-dom\": \">=16.9.0\"\n  },\n  \"gitHooks\": {\n    \"pre-commit\": \"lint-staged\"\n  },\n  \"sideEffect\": false\n}\n"
  },
  {
    "path": "packages/x-flow/src/XFlow.tsx",
    "content": "import {\n  Background,\n  BackgroundVariant,\n  MarkerType,\n  ReactFlow,\n  useReactFlow,\n} from '@xyflow/react';\nimport '@xyflow/react/dist/style.css';\nimport { useEventListener, useMemoizedFn } from 'ahooks';\nimport { produce, setAutoFreeze } from 'immer';\nimport { isFunction, isString } from 'lodash';\nimport type { FC } from 'react';\nimport React, {\n  memo,\n  useContext,\n  useEffect,\n  useMemo,\n  useRef,\n  useState,\n} from 'react';\nimport CandidateNode from './components/CandidateNode';\nimport CustomEdge from './components/CustomEdge';\nimport PanelContainer from './components/PanelContainer';\nimport PanelStatusLogContainer from './components/PanelStatusLogContainer';\nimport { useEventEmitterContextContext } from './models/event-emitter';\n\nimport CustomNodeComponent from './components/CustomNode';\nimport { useFlow } from './hooks/useFlow';\nimport { useStore, useStoreApi } from './hooks/useStore';\n\nimport Operator from './operator';\nimport FlowProps from './types';\nimport { isTruthy, uuid, uuid4 } from './utils';\nimport autoLayoutNodes from './utils/autoLayoutNodes';\n\nimport { message } from 'antd';\nimport { shallow } from 'zustand/shallow';\nimport NodeEditor from './components/NodeEditor';\nimport NodeLogPanel from './components/NodeLogPanel';\nimport { useTemporalStore } from './hooks/useTemporalStore';\nimport './index.less';\nimport { ConfigContext } from './models/context';\n\nconst CustomNode = memo(CustomNodeComponent);\nconst edgeTypes = { buttonedge: memo(CustomEdge) };\n\n/***\n *\n * XFlow 入口\n *\n */\nconst XFlow: FC<FlowProps> = memo(props => {\n  const workflowContainerRef = useRef<HTMLDivElement>(null);\n  const storeApi = useStoreApi();\n  const { zoomTo } = useReactFlow();\n  const {\n    layout,\n    nodes,\n    edges,\n    setNodes,\n    setEdges,\n    panOnDrag,\n    onNodesChange,\n    onEdgesChange,\n    onConnect,\n    setCandidateNode,\n    isAddingNode,\n    setMousePosition,\n    copyNodes,\n  } = useStore(\n    s => ({\n      nodes: s.nodes,\n      edges: s.edges,\n      setNodes: s.setNodes,\n      setEdges: s.setEdges,\n      layout: s.layout,\n      panOnDrag: s.panOnDrag,\n      setMousePosition: s.setMousePosition,\n      isAddingNode: s.isAddingNode,\n      setCandidateNode: s.setCandidateNode,\n      onNodesChange: s.onNodesChange,\n      onEdgesChange: s.onEdgesChange,\n      onConnect: s.onConnect,\n      copyNodes: s.copyNodes,\n    }),\n    shallow\n  );\n  const { record } = useTemporalStore();\n  const [activeNode, setActiveNode] = useState<any>(null);\n  const { settingMap, globalConfig, readOnly, logPanel } =\n    useContext(ConfigContext);\n  const [openPanel, setOpenPanel] = useState<boolean>(true);\n  const [openLogPanel, setOpenLogPanel] = useState<boolean>(true);\n  const {\n    onNodeClick,\n    onEdgeClick,\n    onPasteCompleted,\n    onCopyCompleted,\n    zoomOnScroll = true,\n    panOnScroll = false,\n    preventScrolling = true,\n    connectionLineComponent,\n  } = props;\n  const nodeEditorRef = useRef(null);\n  const { copyNode, pasteNodeSimple, copyFLowNodes, pasteFLowNodes } = useFlow();\n  const { undo, redo } = useTemporalStore();\n\n  useEffect(() => {\n    zoomTo(0.8);\n    setAutoFreeze(false);\n    return () => {\n      setAutoFreeze(true);\n      const { copyTimeoutId } = storeApi.getState();\n      if (copyTimeoutId) {\n        clearTimeout(copyTimeoutId);\n      }\n    };\n  }, []);\n\n  const handleKeyDown = useMemoizedFn((e: KeyboardEvent) => {\n    if ((e.key === 'd' || e.key === 'D') && (e.ctrlKey || e.metaKey))\n      e.preventDefault();\n    if ((e.key === 'z' || e.key === 'Z') && (e.ctrlKey || e.metaKey)) {\n      e.preventDefault();\n      undo();\n    }\n    if ((e.key === 'y' || e.key === 'Y') && (e.ctrlKey || e.metaKey)) {\n      e.preventDefault();\n      redo();\n    }\n    if ((e.key === 's' || e.key === 'S') && (e.ctrlKey || e.metaKey))\n      e.preventDefault();\n    if ((e.key === 'c' || e.key === 'C') && (e.ctrlKey || e.metaKey)) {\n      const latestNodes = storeApi.getState().nodes;\n      // let isNodeCopyEvent = false;\n      // if (e.target instanceof HTMLElement) {\n      //   const target = e.target as HTMLElement;\n\n      //   if (\n      //     (isString(target.tagName) &&\n      //       target.tagName.toLowerCase() === 'body') ||\n      //     (target.tagName.toLowerCase() === 'div' &&\n      //       target.classList &&\n      //       isFunction(target.classList.contains) &&\n      //       (target.classList.contains('ant-drawer') ||\n      //         target.classList.contains('react-flow__node') ||\n      //         target.id === 'xflow-container')) ||\n      //     (target.tagName.toLowerCase() === 'div' && target.className === 'react-flow__nodesselection-rect')\n      //   ) {\n      //     isNodeCopyEvent = true;\n      //   }\n      // }\n      const selectedNodes = latestNodes?.filter(node => node.selected);\n      if ((document.activeElement === workflowContainerRef.current || document.activeElement.className === 'react-flow__nodesselection-rect') && selectedNodes.length > 0) {\n        const flag = selectedNodes.some(selectedNode => {\n          const nodeType = selectedNode?.data?._nodeType;\n          if (isString(nodeType) && nodeType) {\n            const nodeConfig = settingMap[nodeType];\n            if (nodeConfig?.disabledShortcutCopy) {\n              message.warning(\n                `${selectedNode.data?.title || selectedNode.id}节点不允许复制`\n              );\n\n              return true;\n            }\n          }\n        });\n        if (flag) {\n          return;\n        }\n        //copyNode(selectedNode.id);\n        // 复制节点\n        e.preventDefault();\n        copyFLowNodes(selectedNodes,onCopyCompleted);\n      }\n    } else if ((e.key === 'v' || e.key === 'V') && (e.ctrlKey || e.metaKey)) {\n      if(document.activeElement === workflowContainerRef.current){\n        e.preventDefault();\n        //pasteNodeSimple();\n        pasteFLowNodes(onPasteCompleted)\n      }\n    } else if (e.key === 'Escape') {\n      setOpenPanel(false);\n      workflowContainerRef.current?.focus();\n    }\n  });\n  useEventListener('keydown', handleKeyDown, {\n    target: workflowContainerRef,\n  });\n\n  useEventListener(\n    'mousemove',\n    e => {\n      const containerClientRect =\n        workflowContainerRef.current?.getBoundingClientRect();\n      if (containerClientRect) {\n        setMousePosition({\n          pageX: e.clientX,\n          pageY: e.clientY,\n          elementX: e.clientX - containerClientRect.left,\n          elementY: e.clientY - containerClientRect.top,\n        });\n      }\n    },\n    {\n      target: workflowContainerRef.current,\n      enable: isAddingNode,\n      // enable: true, // 复制粘贴的时候需要监听鼠标位置\n    }\n  );\n\n  const { eventEmitter } = useEventEmitterContextContext();\n  eventEmitter?.useSubscription(async (v: any) => {\n    // 整理画布\n    if (v.type === 'auto-layout-nodes') {\n      const newNodes: any = autoLayoutNodes(\n        storeApi.getState().nodes,\n        edges,\n        layout\n      );\n      setNodes(newNodes, false);\n\n      // 整理画布完成后执行回调\n      const onAutoLayoutCompleted = globalConfig?.controls?.onAutoLayoutCompleted;\n      if (onAutoLayoutCompleted) {\n        await onAutoLayoutCompleted(newNodes);\n      }\n    }\n\n    if (v.type === 'deleteNode') {\n      setActiveNode(null);\n    }\n  });\n\n  // 新增节点\n  const handleAddNode = (data: any) => {\n    const title = settingMap[data?._nodeType]?.title || data?._nodeType;\n    const newNode = {\n      id: uuid(),\n      type: 'custom',\n      data: {\n        title: `${title}_${uuid4()}`,\n        ...data,\n      },\n      position: {\n        x: 0,\n        y: 0,\n      },\n    };\n    setCandidateNode(newNode);\n  };\n\n\n  const hoveredEdgeIdRef = useRef<string | null>(null);// edge 移入/移出效果\n\n  const getUpdateEdgeConfig = useMemoizedFn((edgeId: string, color: string, shouldCheckColor = false, allowedColors?: string[]) => {\n    const currentEdges = storeApi.getState().edges;\n    const currEdge = currentEdges.find(e => e.id === edgeId);\n\n    // 如果需要检查颜色，只有在允许的颜色范围内才更新\n    if (shouldCheckColor && allowedColors && currEdge?.style?.stroke) {\n      if (!allowedColors.includes(currEdge.style.stroke)) {\n        return; // 如果是自定义颜色，不更新\n      }\n    }\n\n    const newEdges = produce(currentEdges, draft => {\n      const draftEdge: any = draft.find(e => e.id === edgeId);\n      if (draftEdge) {\n        draftEdge.style = {\n          ...draftEdge.style,\n          stroke: color,\n        };\n        draftEdge.markerEnd = {\n          ...draftEdge.markerEnd,\n          color,\n        };\n      }\n    });\n    setEdges(newEdges);\n  });\n\n  const nodeTypes = useMemo(() => {\n    return {\n      custom: (props: any) => {\n        const { data, id, ...rest } = props;\n        const { _nodeType, _status, ...restData } = data || {};\n        const nodeSetting = settingMap[_nodeType] || {};\n        const showPanel = nodeSetting?.nodePanel?.showPanel ?? true;\n        return (\n          <CustomNode\n            {...rest}\n            id={id}\n            data={{ ...restData }}\n            type={_nodeType}\n            layout={layout}\n            status={_status}\n            onClick={async e => {\n              if (nodeEditorRef?.current?.validateForm) {\n                const result = await nodeEditorRef?.current?.validateForm();\n                if (!result) {\n                  message.error('请检查必填项！');\n                  return;\n                }\n              }\n              setActiveNode({\n                id,\n                _nodeType,\n                values: { ...restData },\n                _status,\n              });\n              if (!showPanel) {\n                setOpenPanel(false);\n              } else {\n                setOpenPanel(true);\n              }\n              setOpenLogPanel(true);\n            }}\n            onDelete={() => {\n              // 删除节点并关闭弹窗\n              setActiveNode(null);\n            }}\n          />\n        );\n      },\n    };\n  }, [layout]);\n\n  const NodeEditorWrap = useMemo(() => {\n    return (\n      <NodeEditor\n        ref={nodeEditorRef}\n        data={activeNode?.values}\n        nodeType={activeNode?._nodeType}\n        id={activeNode?.id}\n      />\n    );\n    // JSON.stringify(activeNode)\n  }, [activeNode?.id]);\n\n  const NodeLogWrap = useMemo(() => {\n    return (\n      <NodeLogPanel\n        data={activeNode?.values}\n        // onChange={handleNodeValueChange}\n        nodeType={activeNode?._nodeType}\n        id={activeNode?.id}\n        node={activeNode}\n        onTrackCollapseChange={data => {\n          if (data) {\n            setActiveNode(data);\n            setOpenPanel(true);\n          }\n        }}\n      />\n    );\n  }, [activeNode?.id]);\n\n  const deletable = globalConfig?.edge?.deletable ?? true;\n  const strokeWidth = globalConfig?.edge?.strokeWidth ?? 1.5;\n  const panelonClose = globalConfig?.nodePanel?.onClose;\n\n  const handleClosePanel = useMemoizedFn(async () => {\n    // 面板关闭校验表单\n    const result = await nodeEditorRef?.current?.validateForm();\n    if (!result) {\n      return;\n    }\n    setOpenPanel(false);\n    workflowContainerRef.current?.focus();\n\n    // 如果日志面板关闭\n    if (!isTruthy(activeNode?._status) || !openLogPanel) {\n      setActiveNode(null);\n    }\n    if (isFunction(panelonClose)) {\n      panelonClose(activeNode?.id);\n    }\n  });\n\n  const handleCloseLogPanel = useMemoizedFn(() => {\n    setOpenLogPanel(false);\n    !openPanel && setActiveNode(null);\n    workflowContainerRef.current?.focus();\n  });\n\n  // 点击空白处关闭抽屉\n  const handlePaneClick = useMemoizedFn(() => {\n    if (openPanel && activeNode) {\n      handleClosePanel();\n    }\n    if (openLogPanel && activeNode) {\n      handleCloseLogPanel();\n    }\n  });\n\n  return (\n    <div\n      id=\"xflow-container\"\n      ref={workflowContainerRef}\n      tabIndex={0}\n    >\n      <ReactFlow\n        panOnDrag={panOnDrag}\n        nodeTypes={nodeTypes}\n        edgeTypes={edgeTypes}\n        nodes={nodes}\n        edges={edges}\n        minZoom={0.3}\n        zoomOnScroll={zoomOnScroll}\n        panOnScroll={panOnScroll} // 禁用滚动平移\n        preventScrolling={preventScrolling} // 允许页面滚动\n        connectionLineComponent={connectionLineComponent}\n        connectionRadius={100}\n        defaultEdgeOptions={{\n          type: 'buttonedge',\n          style: {\n            strokeWidth, // 线粗细\n          },\n          markerEnd: {\n            type: MarkerType.ArrowClosed, // 箭头\n            width: 18,\n            height: 18,\n          },\n          deletable: deletable, //默认连线属性受此项控制\n        }}\n        onBeforeDelete={async elements => {\n          if (readOnly) {\n            return false;\n          }\n          const nodesToDelete = elements?.nodes || [];\n          const blockedNodes = nodesToDelete?.filter(node => {\n            const nodeConfig = settingMap[node?.data?._nodeType as string];\n            return nodeConfig?.hasOwnProperty('disabledShortcutDelete')\n              ? Boolean(nodeConfig?.disabledShortcutDelete)\n              : false;\n          });\n          if (blockedNodes?.length > 0) {\n            message.warning(\n              `${blockedNodes\n                .map(n => n.data?.title || n.id)\n                .join(', ')}节点不允许删除！`\n            );\n            return false;\n          }\n          return true;\n        }}\n        onConnect={onConnect}\n        onNodesChange={changes => {\n          changes.forEach(change => {\n            if (change.type === 'remove') {\n              record(() => {\n                onNodesChange([change]);\n              });\n            } else {\n              onNodesChange([change]);\n            }\n          });\n        }}\n        onEdgesChange={changes => {\n          changes.forEach(change => {\n            if (change.type === 'remove') {\n              record(() => {\n                onEdgesChange([change]);\n              });\n            } else {\n              onEdgesChange([change]);\n            }\n          });\n        }}\n        onEdgeMouseEnter={(_, edge: any) => {\n          // 如果之前有 hover 的 edge，先重置它的颜色（只重置我们设置过的颜色）\n          if (hoveredEdgeIdRef.current && hoveredEdgeIdRef.current !== edge.id) {\n            getUpdateEdgeConfig(hoveredEdgeIdRef.current, '#c9c9c9', true, ['#2970ff', '#c9c9c9']);\n          }\n          hoveredEdgeIdRef.current = edge.id;\n          // 设置当前 edge 为高亮色（只有当没有自定义颜色时才更新）\n          const currentEdges = storeApi.getState().edges;\n          const currentEdge = currentEdges.find(e => e.id === edge.id);\n          const currentStroke = currentEdge?.style?.stroke;\n          // 只有当没有设置颜色或是默认灰色时才设置为高亮色\n          if (!currentStroke || currentStroke === '#c9c9c9') {\n            getUpdateEdgeConfig(edge.id, '#2970ff');\n          }\n        }}\n        onEdgeMouseLeave={(_, edge) => {\n          if (hoveredEdgeIdRef.current === edge.id) {\n            // 重置当前 edge 的颜色（只重置我们设置过的颜色）\n            hoveredEdgeIdRef.current = null;\n          }\n          getUpdateEdgeConfig(edge.id, '#c9c9c9', true, ['#2970ff', '#c9c9c9']);\n        }}\n        onNodesDelete={() => {\n          setActiveNode(null);\n        }}\n        onNodeClick={(event, node) => {\n          onNodeClick && onNodeClick(event, node);\n        }}\n        deleteKeyCode={globalConfig?.deleteKeyCode}\n        onEdgeClick={(event, edge) => {\n          onEdgeClick && onEdgeClick(event, edge);\n        }}\n        onPaneClick={handlePaneClick}\n      >\n        <CandidateNode />\n        <Operator addNode={handleAddNode} xflowRef={workflowContainerRef} />\n        <Background\n          gap={[16, 16]}\n          size={0.6}\n          color=\"black\"\n          variant={BackgroundVariant.Dots}\n        />\n        {activeNode && openPanel && (\n          <PanelContainer\n            id={activeNode?.id}\n            nodeType={activeNode?._nodeType}\n            onClose={handleClosePanel}\n            node={activeNode}\n            data={activeNode?.values}\n            openLogPanel={openLogPanel}\n          >\n            {NodeEditorWrap}\n          </PanelContainer>\n        )}\n        {isTruthy(activeNode?._status) &&\n          openLogPanel &&\n          Boolean(logPanel?.enable ?? true) && (\n            <PanelStatusLogContainer\n              id={activeNode?.id}\n              nodeType={activeNode?._nodeType}\n              onClose={handleCloseLogPanel}\n              data={activeNode?.values}\n            >\n              {NodeLogWrap}\n            </PanelStatusLogContainer>\n          )}\n      </ReactFlow>\n    </div>\n  );\n});\n\nexport default XFlow;\n"
  },
  {
    "path": "packages/x-flow/src/components/CandidateNode/index.tsx",
    "content": "import { useReactFlow, useViewport } from '@xyflow/react';\nimport { useEventListener } from 'ahooks';\nimport React, { memo } from 'react';\nimport { shallow } from 'zustand/shallow';\nimport { useStore } from '../../hooks/useStore';\nimport CustomNode from '../CustomNode';\nimport { useFlow } from '../../hooks/useFlow';\n\nconst CandidateNode = () => {\n  const { zoom } = useViewport();\n  const reactflow = useReactFlow();\n  const { candidateNode, mousePosition, setIsAddingNode, setCandidateNode } = useStore(\n    (s: any) => ({\n      nodes: s.nodes,\n      edges: s.edges,\n      candidateNode: s.candidateNode,\n      setIsAddingNode: s.setIsAddingNode,\n      mousePosition: s.mousePosition,\n      setCandidateNode: s.setCandidateNode,\n      onNodesChange: s.onNodesChange,\n      onEdgesChange: s.onEdgesChange,\n    }),\n    shallow\n  );\n  const { addNodes } = useFlow();\n\n  useEventListener('click', ev => {\n    if (!candidateNode) {\n      return;\n    }\n    ev.preventDefault();\n    const { screenToFlowPosition } = reactflow;\n    const { x, y } = screenToFlowPosition({\n      x: mousePosition.pageX,\n      y: mousePosition.pageY,\n    });\n\n    const newNodes = {\n      ...candidateNode,\n      data: {\n        ...candidateNode.data,\n        _isCandidate: false,\n      },\n      position: { x, y },\n    };\n    addNodes(newNodes);\n    setIsAddingNode(false)\n    setCandidateNode(null);\n  });\n\n  if (!candidateNode) {\n    return null;\n  }\n\n  return (\n    <div\n      className=\"candidate-node\"\n      style={{\n        left: mousePosition?.elementX,\n        top: mousePosition?.elementY,\n        transform: `scale(${zoom})`,\n        transformOrigin: '0 0',\n        position: 'absolute',\n        zIndex: 10000,\n      }}\n    >\n      <CustomNode\n        {...(candidateNode as any)}\n        type={candidateNode?.data?._nodeType}\n      />\n    </div>\n  );\n};\n\nexport default memo(CandidateNode);\n"
  },
  {
    "path": "packages/x-flow/src/components/CustomEdge/index.less",
    "content": "\n.custom-edge-line {\n  position: absolute;\n  z-index: 1000;\n  pointer-events: all;\n\n  .line-content {\n    width: 60px;\n    display: flex;\n    justify-content: space-around;\n    align-items: center;\n  }\n\n  .line-icon-box {\n    width: 16px;\n    height: 16px;\n    border-radius: 16px;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    background: #296dff;\n    visibility: visible;\n  }\n}"
  },
  {
    "path": "packages/x-flow/src/components/CustomEdge/index.tsx",
    "content": "import { CloseOutlined, PlusOutlined } from '@ant-design/icons';\nimport {\n  BezierEdge,\n  EdgeLabelRenderer,\n  getBezierPath,\n  useReactFlow,\n} from '@xyflow/react';\nimport React, { memo, useContext, useState } from 'react';\nimport { shallow } from 'zustand/shallow';\nimport { useFlow } from '../../hooks/useFlow';\nimport { useStore } from '../../hooks/useStore';\nimport { ConfigContext } from '../../models/context';\nimport { uuid, uuid4 } from '../../utils';\nimport NodeSelectPopover from '../NodesPopover';\nimport './index.less';\n\nexport default memo((edge: any) => {\n  const {\n    id,\n    selected,\n    sourceX,\n    sourceY,\n    targetX,\n    targetY,\n    source,\n    target,\n    sourceHandleId,\n  } = edge;\n\n  const reactflow = useReactFlow();\n  const [isHovered, setIsHovered] = useState(false);\n  const [edgePath, labelX, labelY] = getBezierPath({\n    sourceX,\n    sourceY,\n    targetX,\n    targetY,\n  });\n\n  const { globalConfig, settingMap, readOnly } = useContext(ConfigContext);\n  const hideEdgeAddBtn = globalConfig?.edge?.hideEdgeAddBtn ?? false;\n  const hideEdgeDelBtn = globalConfig?.edge?.hideEdgeDelBtn ?? false;\n  const deletable = globalConfig?.edge?.deletable ?? true;\n\n  const { nodes, edges, addEdges, mousePosition, onEdgesChange, layout } =\n    useStore(\n      (state: any) => ({\n        layout: state.layout,\n        nodes: state.nodes,\n        edges: state.edges,\n        mousePosition: state.mousePosition,\n        addEdges: state.addEdges,\n        onEdgesChange: state.onEdgesChange,\n      }),\n      shallow\n    );\n  const { addNodes } = useFlow();\n\n  const handleAddNode = (data: any) => {\n    const { getNode, screenToFlowPosition } = reactflow;\n    const sourceNode = getNode(source);\n    const targetNode = getNode(target);\n\n    // 节点默认尺寸\n    const defaultNodeWidth = 204;\n    const defaultNodeHeight = 45;\n\n    let x, y;\n\n    // 如果源节点和目标节点都存在，将新节点放在边的中点位置\n    if (sourceNode && targetNode) {\n      const sourceX = sourceNode.position.x + (sourceNode.width || defaultNodeWidth) / 2;\n      const sourceY = sourceNode.position.y + (sourceNode.height || defaultNodeHeight) / 2;\n      const targetX = targetNode.position.x + (targetNode.width || defaultNodeWidth) / 2;\n      const targetY = targetNode.position.y + (targetNode.height || defaultNodeHeight) / 2;\n\n      // 计算中点位置\n      x = (sourceX + targetX) / 2 - defaultNodeWidth / 2;\n      y = (sourceY + targetY) / 2 - defaultNodeHeight / 2;\n    } else {\n      // 如果节点不存在，使用鼠标位置作为后备方案\n      const fallbackPos = screenToFlowPosition({\n        x: mousePosition.pageX,\n        y: mousePosition.pageY,\n      });\n      x = fallbackPos.x;\n      y = fallbackPos.y;\n    }\n\n    const targetId = uuid();\n    const title = settingMap[data?._nodeType]?.title || data?._nodeType;\n\n    const newNodes = {\n      id: targetId,\n      type: 'custom',\n      data: {\n        title: `${title}_${uuid4()}`,\n        ...data,\n      },\n      position: { x, y },\n    };\n\n    const newEdges = [\n      {\n        id: uuid(),\n        source,\n        target: targetId,\n        deletable: deletable,\n        ...(sourceHandleId && { sourceHandle: sourceHandleId }),\n      },\n      {\n        id: uuid(),\n        source: targetId,\n        deletable: deletable,\n        target,\n      },\n    ];\n\n    addNodes(newNodes as any);\n    addEdges(newEdges);\n    onEdgesChange([{ id, type: 'remove' }]);\n  };\n\n  let edgeExtra: any = {\n    sourceX: edge.sourceX - 15,\n    targetX: edge.targetX + 10,\n  };\n  if (layout === 'TB') {\n    edgeExtra = {\n      sourceY: edge.sourceY - 15,\n      targetY: edge.targetY + 13,\n    };\n  }\n\n  return (\n    <g\n      onMouseEnter={() => setIsHovered(true)}\n      onMouseLeave={() => setIsHovered(false)}\n    >\n      <BezierEdge\n        {...edge}\n        {...edgeExtra}\n        edgePath={edgePath}\n        label={\n          isHovered && (\n            <EdgeLabelRenderer>\n              <div\n                className=\"custom-edge-line\"\n                style={{\n                  transform: `translate(-50%, -50%) translate(${labelX}px,${labelY}px)`,\n                }}\n              >\n                <div className=\"line-content\">\n                  {!hideEdgeDelBtn && !readOnly && (\n                    <div\n                      className=\"line-icon-box\"\n                      onClick={(ev: any) => {\n                        ev.stopPropagation();\n                        if (readOnly) {\n                          return;\n                        }\n                        onEdgesChange([{ id, type: 'remove' }]);\n                      }}\n                    >\n                      <CloseOutlined style={{ color: '#fff', fontSize: 10 }} />\n                    </div>\n                  )}\n                  {!hideEdgeAddBtn && !readOnly && (\n                    <NodeSelectPopover\n                      placement=\"right\"\n                      addNode={handleAddNode}\n                    >\n                      <div className=\"line-icon-box\">\n                        <PlusOutlined style={{ color: '#fff', fontSize: 10 }} />\n                      </div>\n                    </NodeSelectPopover>\n                  )}\n                </div>\n              </div>\n            </EdgeLabelRenderer>\n          )\n        }\n      />\n    </g>\n  );\n});\n"
  },
  {
    "path": "packages/x-flow/src/components/CustomNode/index.less",
    "content": ".xflow-node-container {\n  border: 2px solid var(--nodeBorderColor,#fff);\n  border-radius: 14px;\n  position: relative;\n  background: #ffffff;\n\n  .react-flow__edge-path,\n  .react-flow__connection-path {\n    stroke: #d0d5dc;\n    stroke-width: 2px;\n  }\n  .xflow-node-actions-container {\n    cursor: pointer;\n    position: absolute;\n    top: -4px;\n    background: #ffffff;\n    transform: translate(0, -100%);\n    right: 6px;\n    padding: 0 2px;\n    border-radius: 4px;\n    box-shadow: 0px 1px 2px 0px rgba(16, 24, 40, .05);\n    opacity: 0;\n    transition: all .5s;\n  }\n  &:hover{\n    box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);\n    .xflow-node-actions-container{\n      opacity: 1;\n    }\n  }\n  .react-flow__handle {\n    width: 30px;\n    height: 30px;\n    background: transparent;\n    border-radius: 0;\n    border: none;\n  }\n\n  .react-flow__handle::after {\n    content: '';\n    --tw-bg-opacity: 1;\n    background-color: var(--nodeBorderColor,#2970ff);  // 把手颜色\n    width: 2px;\n    height: 10px;\n    display: block;\n    margin: 11px 0 8px 17px;\n  }\n\n  .handle-connected-target::after{\n      margin: 11px 0 8px 11px;\n  }\n\n  // .handle-disconnected {\n  //   &::after {\n  //     background-color: transparent !important; // 未连接时透明\n  //   }\n  // }\n\n  .react-flow__handle:hover {\n    .xflow-node-add-box {\n      scale: 130%;\n    }\n  }\n\n  .xflow-node-add-box {\n    position: absolute;\n    top: 8px;\n    right: 6px;\n    width: 16px;\n    height: 16px;\n    border-radius: 16px;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    background-color: #2970ff;\n    pointer-events: none;\n    transition: all 0.3s;\n  }\n\n  .xflow-node-switch-title {\n    position: absolute;\n    left: -45px;\n    top: 6px;\n    font-weight: 600;\n    text-align: right;\n    width: 35px;\n  }\n}\n\n.xflow-node-container-tb {\n  .react-flow__handle::after {\n    content: '';\n    --tw-bg-opacity: 1;\n    background-color: #2970ff;\n    width: 8px;\n    height: 2px;\n    display: block;\n    margin: 15px 0 0 12px;\n  }\n}\n\n.xflow-node-container-selected {\n  border: 2px solid #296dff;\n\n  .react-flow__handle::after {\n    display: none;\n  }\n}\n\n\n.xflow-node-container-note{\n  border: none;\n  padding: 2px 6px 6px 6px;\n}\n"
  },
  {
    "path": "packages/x-flow/src/components/CustomNode/index.tsx",
    "content": "import { MoreOutlined } from '@ant-design/icons';\nimport { Handle, Position, useReactFlow } from '@xyflow/react';\nimport { Dropdown, Menu, message } from 'antd';\nimport { ItemType } from 'antd/es/menu/interface';\nimport classNames from 'classnames';\nimport { isFunction } from 'lodash';\nimport React, { Fragment, memo, useCallback, useContext, useMemo, useState } from 'react';\nimport { shallow } from 'zustand/shallow';\nimport { useStore } from '../../hooks/useStore';\nimport { ConfigContext } from '../../models/context';\nimport {\n  capitalize,\n  isTruthy,\n  transformNodeStatus,\n  uuid,\n  uuid4,\n} from '../../utils';\nimport './index.less';\nimport SourceHandle from './sourceHandle';\nimport { useFlow } from '../../hooks/useFlow';\n\nexport default memo((props: any) => {\n  const { id, type, data, layout, isConnectable, selected, onClick, status, onDelete } =\n    props;\n  const {\n    widgets,\n    settingMap,\n    globalConfig,\n    onMenuItemClick,\n    antdVersion,\n    readOnly,\n  } = useContext(ConfigContext);\n  const deletable = globalConfig?.edge?.deletable ?? true;\n  const disabledCopy = settingMap[type]?.disabledCopy ?? false;\n  const disabledDelete = settingMap[type]?.disabledDelete ?? false;\n  const switchExtra = settingMap[type]?.switchExtra || {};\n  const handleProps = globalConfig?.handle || {}\n  const renderHandle = settingMap[type]?.renderHandle || void(0)\n  // const isConnectableStart = globalConfig?.handle?.isConnectableStart ?? true;\n  // const isConnectableEnd = globalConfig?.handle?.isConnectableEnd ?? true;\n\n  const NodeWidget =\n    widgets[`${capitalize(type)}Node`] || widgets['CommonNode'];\n  const [isHovered, setIsHovered] = useState(false);\n  const reactflow = useReactFlow();\n  const { edges,nodes,addEdges, mousePosition } =\n    useStore(\n      (state: any) => ({\n        nodes: state.nodes,\n        edges: state.edges,\n        mousePosition: state.mousePosition,\n        addEdges: state.addEdges,\n        onEdgesChange: state.onEdgesChange,\n      }),\n      shallow\n    );\n  const { addNodes, pasteNode, copyNode, deleteNode } = useFlow();\n  const isNote = type === 'Note';\n  const isEnd = type === 'End';\n  const isSwitchNode = type === 'Switch' || type === 'Parallel' || isNote; // 判断是否为条件节点/并行节点/注释节点\n  const connectable = readOnly ? false : isConnectable;\n  const nodeSetting = settingMap[type] || {};\n  const nodeClassName = nodeSetting?.className || '';\n  const disabledShortcutDelete = nodeSetting?.disabledShortcutDelete || false;\n\n  // 判断左侧Handle是否已连接\n  const isTargetHandleConnected = useMemo(() => {\n    return (edges || [])?.some(edge => edge.target === id);\n  }, [edges, id]);\n\n  const isSourceHandleConnected = useMemo(() => {\n    if (isSwitchNode) {\n      // 对于Switch节点，需要检查每个sourceHandle是否已连接\n      if (type === 'Switch' && Array.isArray(data.list)) {\n        return data.list.some(item =>\n          edges.some(edge => edge.source === id && edge.sourceHandle === item._id)\n        );\n      }\n      // 对于Parallel节点，需要检查每个sourceHandle是否已连接\n      if (type === 'Parallel' && Array.isArray(data.list)) {\n        return data.list.some(item =>\n          edges.some(edge => edge.source === id && edge.sourceHandle === item._id)\n        );\n      }\n      return false;\n    }\n    // 对于普通节点，检查是否有从该节点出发的边\n    return edges.some(edge => edge.source === id);\n  }, [edges, id, type, data.list, isSwitchNode]);\n\n  // 增加节点并进行联系\n  const handleAddNode = (data: any, sourceHandle?: string) => {\n    const { getNode } = reactflow;\n    const currentNode = getNode(id);\n    if (!currentNode) return;\n\n    // 节点默认尺寸\n    const defaultNodeWidth = 204;\n    const defaultNodeHeight = 45;\n    const nodeSpacing = 50; // 节点之间的最小间距\n\n    // 获取当前节点的位置和尺寸\n    const currentNodeX = currentNode.position.x;\n    const currentNodeY = currentNode.position.y;\n    const currentNodeWidth = currentNode.width || defaultNodeWidth;\n    const currentNodeHeight = currentNode.height || defaultNodeHeight;\n\n    // 根据布局方向计算新节点的初始位置\n    // LR布局：新节点在右侧，从上到下堆叠（垂直方向堆叠）\n    // TB布局：新节点在下方，从左到右堆叠（水平方向堆叠）\n    const isTBLayout = layout === 'TB';\n\n    // 新节点的尺寸（假设与当前节点相同，实际可能需要根据节点类型调整）\n    const newNodeWidth = defaultNodeWidth;\n    const newNodeHeight = defaultNodeHeight;\n\n    // 检测是否有其他节点遮挡\n    const checkCollision = (x: number, y: number, width: number, height: number) => {\n      return nodes.some((node: any) => {\n        if (node.id === id) return false; // 排除当前节点\n        const nodeX = node.position?.x || 0;\n        const nodeY = node.position?.y || 0;\n        const nodeWidth = node.width || defaultNodeWidth;\n        const nodeHeight = node.height || defaultNodeHeight;\n\n        // 检查是否重叠\n        return !(\n          x + width < nodeX ||\n          x > nodeX + nodeWidth ||\n          y + height < nodeY ||\n          y > nodeY + nodeHeight\n        );\n      });\n    };\n\n    // 计算新节点的初始位置\n    let newX: number;\n    let newY: number;\n\n    if (isTBLayout) {\n      // TB布局：新节点在下方，从左到右堆叠\n      // 初始位置：x 从当前节点左侧开始，y 在当前节点下方\n      newX = currentNodeX;\n      newY = currentNodeY + currentNodeHeight + nodeSpacing;\n    } else {\n      // LR布局：新节点在右侧，从上到下堆叠\n      // 初始位置：x 在当前节点右侧，y 从当前节点顶部开始\n      newX = currentNodeX + currentNodeWidth + nodeSpacing;\n      newY = currentNodeY;\n    }\n\n    // 如果有遮挡，根据布局方向移动找到空位置\n    const maxAttempts = 50; // 最多尝试50次，确保能找到空位置\n    let attempts = 0;\n    while (checkCollision(newX, newY, newNodeWidth, newNodeHeight) && attempts < maxAttempts) {\n      if (isTBLayout) {\n        // TB布局：往右堆叠（水平方向移动）\n        newX += newNodeWidth + nodeSpacing;\n      } else {\n        // LR布局：往下堆叠（垂直方向移动）\n        newY += newNodeHeight + nodeSpacing;\n      }\n      attempts++;\n    }\n\n    // 如果尝试次数过多，使用原始逻辑（基于鼠标位置）\n    if (attempts >= maxAttempts) {\n      const { screenToFlowPosition } = reactflow;\n      const fallbackPos = screenToFlowPosition({\n        x: mousePosition.pageX + 100,\n        y: mousePosition.pageY + 100,\n      });\n      newX = fallbackPos.x;\n      newY = fallbackPos.y;\n    }\n\n    const targetId = uuid();\n    const title = settingMap[data?._nodeType]?.title || data?._nodeType;\n    const newNodes = {\n      id: targetId,\n      type: 'custom',\n      data: {\n        title: `${title}_${uuid4()}`,\n        ...data,\n      },\n      position: { x: newX, y: newY },\n    };\n    const newEdges = {\n      id: uuid(),\n      source: id,\n      target: targetId,\n      deletable,\n      ...(sourceHandle && { sourceHandle }),\n    };\n    addNodes(newNodes as any);\n    addEdges(newEdges);\n  };\n\n  let targetPosition = Position.Left;\n  let sourcePosition = Position.Right;\n  if (layout === 'TB') {\n    targetPosition = Position.Top;\n    sourcePosition = Position.Bottom;\n  }\n\n  const handleCopyNode = useCallback(() => {\n    copyNode(id);\n    message.success('复制成功');\n  }, [copyNode, id]);\n\n  const handlePasteNode = useCallback(\n    (data?: { sourceHandle: string }) => {\n      pasteNode(id, data);\n    },\n    [pasteNode, id]\n  );\n\n  const handleDeleteNode = useCallback(() => {\n    deleteNode(id);\n    onDelete(id)\n  }, [deleteNode, id]);\n\n  const defaultAction = (e, sourceHandle) => {\n    if (e.key === 'copy') {\n      handleCopyNode();\n    } else if (e.key === 'paste') {\n      handlePasteNode();\n    } else if (e.key === 'delete') {\n      handleDeleteNode();\n    } else if (e.key.startsWith('paste-')) {\n      if (sourceHandle) {\n        handlePasteNode({\n          sourceHandle,\n        });\n      } else {\n        handlePasteNode();\n      }\n    }\n  };\n\n  const itemClick = e => {\n    if (!e.key) {\n      return;\n    }\n\n    const sourceHandle = e.item.props?.sourcehandle;\n    if (isFunction(onMenuItemClick)) {\n      const data: Record<string, string> = {\n        key: e.key,\n        nodeId: id,\n      };\n      if (type === 'Switch' && e.key.startsWith('paste-') && sourceHandle) {\n        data['sourceHandle'] = sourceHandle;\n      }\n      onMenuItemClick(data as any, () => {\n        defaultAction(e, sourceHandle);\n      });\n    } else {\n      defaultAction(e, sourceHandle);\n    }\n  };\n\n  const menuItem: ItemType[] = useMemo(() => {\n    if (type === 'Switch') {\n      let list = [];\n      if (Array.isArray(data.list)) {\n        const len = data.list.length;\n        list = data.list.map((r, i) => {\n          if (i === 0) {\n            return {\n              label: `粘贴到第${i + 1}个出口`,\n              key: 'paste-' + i,\n              index: i,\n              id: id,\n              sourcehandle: r._id,\n            };\n          } else {\n            return {\n              label: `粘贴到第${i + 1}个出口`,\n              key: 'paste-' + i,\n              id: id,\n              index: i,\n              sourcehandle: r._id,\n            };\n          }\n        });\n      }\n      const defaultElse = switchExtra?.hideElse\n        ? []\n        : [\n            {\n              label: `粘贴到第${list.length + 1}个出口`,\n              key: 'paste-' + (list.length + 1),\n              id: id,\n              index: list.length + 1,\n              sourcehandle: 'id_else',\n            },\n          ];\n      return [...list, ...defaultElse];\n    }\n    return [\n      {\n        label: '粘贴',\n        key: 'paste',\n      },\n    ];\n  }, [type, data, isEnd]);\n\n  // 节点状态处理\n  const statusObj = transformNodeStatus(globalConfig?.nodeView?.status || []);\n  const nodeBorderColor = statusObj[status]?.color;\n\n  const menu = (\n    <Menu onClick={itemClick}>\n      <Menu.Item key={'copy'} disabled={disabledCopy}>\n        复制\n      </Menu.Item>\n      {!isEnd ? menuItem.map((r: any) => {\n        return (\n          <Menu.Item {...r} key={r.key}>\n            {r.label}\n          </Menu.Item>\n        );\n      }) : null}\n      <Menu.Item key={'delete'} danger={true} disabled={disabledDelete}>\n        删除\n      </Menu.Item>\n    </Menu>\n  );\n\n  const dropdownVersionProps = useMemo(() => {\n    if (antdVersion === 'V5') {\n      return {\n        menu: {\n          items: [\n            {\n              label: '复制',\n              key: 'copy',\n              disabled: disabledCopy,\n            },\n            ...(isEnd ? [] : menuItem),\n            {\n              label: '删除',\n              key: 'delete',\n              danger: true,\n              disabled: disabledDelete,\n            },\n          ],\n          onClick: itemClick,\n        },\n      };\n    }\n    // V4\n    return {\n      overlay: menu,\n    };\n  }, [menuItem, isEnd]);\n\n  const renderHandleMemo = useCallback(()=>{\n    if(renderHandle){\n      return renderHandle(\n        SourceHandle,\n        {\n          position:sourcePosition,\n          isConnectable:connectable,\n          selected:selected,\n          isHovered:isHovered,\n          handleAddNode:handleAddNode\n        },\n        {\n          id,\n          type,\n          data,\n          layout,\n          isConnectable,\n          readOnly\n        }\n      )\n    }\n    return null\n  },[renderHandle,SourceHandle,sourcePosition,connectable,selected,isHovered,handleAddNode,id,type,data,layout,isConnectable,readOnly])\n\n  return (\n    <div\n      className={classNames('xflow-node-container', {\n        ['xflow-node-container-selected']: !!selected,\n        ['xflow-node-container-tb']: layout === 'TB',\n        ['xflow-node-container-note']: isNote,\n        [`xflow-node-container-status-${status}`]: isTruthy(status),\n        [nodeClassName]: !!nodeClassName\n      })}\n      onMouseEnter={() => setIsHovered(true)}\n      onMouseLeave={() => setIsHovered(false)}\n      style={{ '--nodeBorderColor': nodeBorderColor } as React.CSSProperties}\n    >\n      {!settingMap?.[type]?.targetHandleHidden && !isNote && (\n        <Handle\n          isValidConnection={(edge)=>{\n            if(handleProps.isValidConnection){\n              return handleProps.isValidConnection(edge,'target',type)\n            }\n            return true\n          }}\n          type=\"target\"\n          position={targetPosition}\n          isConnectable={connectable}\n          className={classNames({\n            'handle-connected': isTargetHandleConnected,\n            'handle-disconnected': !isTargetHandleConnected,\n            \"handle-connected-target\":true\n          })}\n          // isConnectableStart={isConnectableStart}\n          // isConnectableEnd={isConnectableEnd}\n        />\n      )}\n      {!readOnly && !disabledShortcutDelete && (\n        <Dropdown\n          disabled={readOnly}\n          {...dropdownVersionProps}\n          //trigger={['click', 'contextMenu']}\n        >\n          <div className=\"xflow-node-actions-container\">\n            <MoreOutlined\n              style={{ transform: 'rotateZ(90deg)', fontSize: '20px' }}\n            ></MoreOutlined>\n          </div>\n        </Dropdown>\n      )}\n      <NodeWidget\n        id={id}\n        type={type}\n        data={data}\n        onClick={() => onClick(data)}\n        position={sourcePosition}\n        isConnectable={connectable}\n        selected={selected}\n        isHovered={isHovered}\n        handleAddNode={handleAddNode}\n      />\n      {typeof renderHandle === 'function' ?\n        <div onClick={()=>{\n          onClick(data)\n        }}>\n          {renderHandleMemo()}\n        </div>\n        :\n        <Fragment>\n          {!settingMap?.[type]?.sourceHandleHidden && !isSwitchNode && (\n            <>\n              <SourceHandle\n                position={sourcePosition}\n                isConnectable={connectable}\n                selected={selected}\n                isHovered={isHovered}\n                handleAddNode={handleAddNode}\n                isConnected={isSourceHandleConnected}\n                nodeType={type}\n                // isConnectableStart={isConnectableStart}\n                // isConnectableEnd={isConnectableEnd}\n              />\n            </>\n          )}\n        </Fragment>\n      }\n    </div>\n  );\n});\n"
  },
  {
    "path": "packages/x-flow/src/components/CustomNode/sourceHandle.tsx",
    "content": "import { PlusOutlined } from '@ant-design/icons';\nimport { Handle } from '@xyflow/react';\nimport { Tooltip } from 'antd';\nimport classNames from 'classnames';\nimport React, { ComponentProps, memo, useContext, useEffect, useMemo, useRef, useState } from 'react';\nimport NodeSelectPopover from '../NodesPopover';\nimport { ConfigContext } from '../../models/context';\nimport './index.less';\n\nexport type HandleProps = ComponentProps<typeof Handle>\n\nexport default memo((props:Partial<HandleProps> &  Record<string,any> ) => {\n  const {\n    position,\n    isConnectable,\n    selected,\n    isHovered,\n    handleAddNode,\n    switchTitle,\n    isConnected, // 是否有连接的节点\n    nodeType,\n    ...rest\n  } = props;\n  const [isShowTooltip, setIsShowTooltip] = useState(false);\n  const [openNodeSelectPopover, setOpenNodeSelectPopover] = useState(false);\n  const popoverRef = useRef(null);\n  const { antdVersion, globalConfig } = useContext(ConfigContext);\n  const handleProps = globalConfig?.handle || {};\n\n  const toolTipVersionProps = useMemo(() => {\n    if (antdVersion === 'V5') {\n      return {\n        open: isShowTooltip,\n      };\n    }\n    // V4\n    return {\n      visible: isShowTooltip,\n    };\n  }, [isShowTooltip]);\n\n  return (\n    <Handle\n      isValidConnection={(edge)=>{\n        if(handleProps.isValidConnection){\n          return handleProps.isValidConnection(edge,'source',nodeType)\n        }\n        return true\n      }}\n      type=\"source\"\n      position={position}\n      isConnectable={isConnectable}\n      onMouseEnter={() => setIsShowTooltip(true)}\n      onMouseLeave={() => setIsShowTooltip(false)}\n      onClick={e => {\n        e.stopPropagation();\n        popoverRef?.current?.changeOpen(true);\n        setIsShowTooltip(false);\n        setOpenNodeSelectPopover(true);\n      }}\n      {...rest}\n      className={classNames(\n        {\n          'handle-disconnected': !isConnected,\n        },\n        rest.className\n      )}\n    >\n      {(selected || isHovered || openNodeSelectPopover) && (\n        <>\n          {switchTitle && (\n            <div className=\"xflow-node-switch-title\">{switchTitle}</div>\n          )}\n          {isConnectable && (\n            <div className=\"xflow-node-add-box\">\n              <NodeSelectPopover\n                placement=\"right\"\n                addNode={handleAddNode}\n                ref={popoverRef}\n                onNodeSelectPopoverChange={val => setOpenNodeSelectPopover(val)}\n                nodeType={nodeType}\n              >\n                <Tooltip\n                  title=\"点击添加节点\"\n                  //  arrow={false}\n                  overlayInnerStyle={{\n                    background: '#fff',\n                    color: '#354052',\n                    fontSize: '12px',\n                  }}\n                  color=\"#fff\"\n                  {...toolTipVersionProps}\n                  getPopupContainer={() =>\n                    document.getElementById('xflow-container') as HTMLElement\n                  }\n                >\n                  <PlusOutlined style={{ color: '#fff', fontSize: 10 }} />\n                </Tooltip>\n              </NodeSelectPopover>\n            </div>\n          )}\n        </>\n      )}\n    </Handle>\n  );\n});\n"
  },
  {
    "path": "packages/x-flow/src/components/FAutoComplete/index.tsx",
    "content": "import React, { useEffect, useState } from 'react';\nimport { AutoComplete, InputNumber } from 'antd';\nimport _ from 'lodash';\n\nconst FAutoComplete: React.FC<any> = (props) => {\n  const {\n    value,\n    onChange,\n    placeholder,\n    optionList,\n    width = '100%',\n    disabled,\n  } = props;\n  const [options, setOptions] = useState<{ value: string }[]>(optionList);\n\n  useEffect(() => {\n    setOptions(optionList);\n  }, [optionList]);\n  const handleSearch = async (value: string) => {\n    if (!props.request) {\n      return;\n    }\n    const res = await props.request(value);\n    setOptions(res);\n  };\n\n  let customDisabled = false;\n  const dependValues = props.addons.dependValues;\n  if (dependValues) {\n    // 知识库组件的特殊处理\n    if (dependValues.length > 1) {\n      customDisabled = dependValues[1] === 'vector_weight' && dependValues[0];\n    }\n    if (dependValues[1] === 'vector_weight') {\n      const onNumberChange = (val: any) => {\n        let newValue: string | number = val;\n        if (val === null || val === undefined) {\n          newValue = '';\n        }\n        onChange(newValue);\n      };\n      return (\n        <InputNumber\n          value={value || ''}\n          onChange={onNumberChange}\n          disabled={customDisabled}\n          style={{ width }}\n          min={0}\n          max={1}\n          step={0.01}\n        />\n      );\n    }\n    // if (\n    //   ['search_attachment', 'with_associated_documents'].includes(\n    //     dependValues[1],\n    //   )\n    // ) {\n    //   return (\n    //     <Checkbox\n    //       checked={value}\n    //       onChange={(e) => onChange(e.target.checked)}\n    //       style={{ width }}\n    //       disabled={disabled}\n    //     />\n    //   );\n    // }\n  }\n  const values = props.addons.getValues();\n  const dataPath = props.addons.dataPath;\n  const record = _.get(values, dataPath.slice(0, dataPath.lastIndexOf('.')));\n  const pathName = dataPath.slice(dataPath.lastIndexOf('.') + 1);\n\n  return (\n    <AutoComplete\n      value={value}\n      options={options}\n      onSearch={handleSearch}\n      style={{ width }}\n      status={record?.required && !record?.[pathName] ? 'error' : ''}\n      onChange={(val) => {\n        onChange(val);\n      }}\n      disabled={disabled}\n      placeholder={placeholder}\n    />\n  );\n};\nexport default FAutoComplete;\n"
  },
  {
    "path": "packages/x-flow/src/components/FlowProvider/index.tsx",
    "content": "import { ReactFlowProvider } from '@xyflow/react';\nimport React, { memo, ReactNode, useContext, useEffect, useState } from 'react';\n\nimport { useStore } from '../../hooks/useStore';\nimport StoreContext, { Provider } from '../../models/context';\nimport { createStore } from '../../models/store';\nimport { transformNodes, transformSwitchNodes } from '../../utils';\n\nexport const FlowProvider = memo<{\n  initialNodes?: any[];\n  initialEdges?: any[];\n  children: ReactNode;\n  layout?: 'LR' | 'TB';\n}>(\n  ({\n    initialNodes: nodes = [],\n    initialEdges: edges = [],\n    children,\n    layout = 'LR',\n  }) => {\n    const [store] = useState(() =>\n      createStore({\n        nodes,\n        edges,\n        layout,\n      })\n    );\n\n    return (\n      <ReactFlowProvider>\n        <Provider value={store}>{children}</Provider>\n      </ReactFlowProvider>\n    );\n  }\n);\n\nconst InitialProvider = ({ nodes, edges, layout, children }) => {\n  const { setNodes, setEdges, setLayout } = useStore(s => ({\n    setNodes: s.setNodes,\n    setEdges: s.setEdges,\n    setLayout: s.setLayout,\n  }));\n  useEffect(() => {\n    setNodes(nodes);\n    setLayout(layout);\n    setEdges(edges);\n  }, []);\n\n  // we need to wrap it with a fragment because it's not allowed for children to be a ReactNode\n  // https://github.com/DefinitelyTyped/DefinitelyTyped/issues/18051\n  return <>{children}</>;\n};\n\nexport const FlowProviderWrapper = ({\n  children,\n  nodes,\n  edges,\n  layout,\n}: {\n  children: React.ReactNode;\n  nodes: any[];\n  edges: any[];\n  layout?: 'LR' | 'TB';\n}) => {\n  const isWrapped = useContext(StoreContext);\n\n  if (isWrapped) {\n    return (\n      <InitialProvider nodes={transformSwitchNodes(nodes)} edges={edges} layout={layout}>\n        {children}\n      </InitialProvider>\n    );\n  }\n\n  return (\n    <FlowProvider\n      initialNodes={transformNodes(nodes)}\n      initialEdges={edges}\n      layout={layout}\n    >\n      {children}\n    </FlowProvider>\n  );\n};\n"
  },
  {
    "path": "packages/x-flow/src/components/IconView/index.tsx",
    "content": "import { createFromIconfontCN } from '@ant-design/icons';\n\n/**\n * icon 图标库\n * 图标库资源变动需要更新 scriptUrl 资源路径\n * https://www.iconfont.cn/manage/index?manage_type=myprojects&projectId=4201076\n */\n\nconst Icon = createFromIconfontCN({\n  scriptUrl: '//at.alicdn.com/t/a/font_4069358_dd524fgnynb.js',\n});\n\nexport default Icon;\n"
  },
  {
    "path": "packages/x-flow/src/components/NodeContainer/TitleMenuTooltip.tsx",
    "content": "import React, { useMemo } from 'react';\nimport createIconFont from '../../utils/createIconFont';\n\nconst TitleMenuTooltip = ({ icon, nodeSettingTitle, description, iconFontUrl, iconSvg }: any) => {\n  const IconBox = useMemo(() => createIconFont(iconFontUrl), [iconFontUrl]);\n\n  return (\n    <div className='xflow-node-title-menu-tooltip'>\n      <div className='header-wrap'>\n      <div className='icon-box-max' style={{ background: icon?.bgColor || '#F79009', marginRight: '8px' }}>\n        {iconSvg ? iconSvg :<IconBox type={icon?.type} style={{ color: '#fff', fontSize: 13, ...icon?.style }} />}\n      </div>\n      <div className='title'>\n          {nodeSettingTitle}\n        </div>\n      </div>\n      <div className='description'>\n        {description}\n      </div>\n    </div>\n  )\n};\n\nexport default TitleMenuTooltip;\n"
  },
  {
    "path": "packages/x-flow/src/components/NodeContainer/index.less",
    "content": ".custom-node-container {\n  width: 240px;\n  padding: 13px 12px 5px 12px;\n  background: #fff;\n  border-radius: 12px;\n  background: '#fff';\n  position: 'relative';\n\n  .node-gradient-header{\n      position: absolute;\n      left: 0;\n      top: 0;\n      width: 100%;\n      height: var(--gradient-height);\n      border-radius: 12px;\n      pointer-events: none;\n      z-index: 0;\n  }\n\n  .node-title {\n    display: flex;\n    height: 24px;\n    margin-bottom: 8px;\n    align-items: center;\n    font-size: 13px;\n    color: #1D2939;\n    span {\n      font-weight: bold;\n    }\n  }\n\n  .node-body{\n    position: relative;\n    z-index: 1;\n  }\n  .node-body-desc{\n    position: relative;\n    z-index: 1;\n  }\n\n  .node-desc {\n    color: #676f83;\n    font-size: 12px;\n    // padding-top: 4px;\n    margin-top: 10px;\n    max-width: 100%;\n  }\n\n  .icon-box {\n    width: 24px;\n    height: 24px;\n    border-radius: 6px;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n  }\n\n  .node-widget {\n    width: 100%;\n  }\n}\n\n.xflow-node-title-menu-tooltip {\n  width: 200px;\n\n  .header-wrap {\n    display: flex;\n    align-items: flex-start;\n    justify-content: flex-start;\n  }\n\n  .title {\n    color: #101828;\n    display: inline-flex;\n  }\n\n  .description {\n    font-size: 12px;\n    font-weight: normal;\n    color: #101828;\n    margin-top: 3px;\n  }\n\n  .icon-box-max {\n    width: 24px;\n    height: 24px;\n    min-width: 24px;\n    min-height: 24px;\n    border-radius: 6px;\n    display: inline-flex;\n    align-items: center;\n    justify-content: center;\n  }\n}\n\n\n.switch-node-code-bottom{\n  min-width: 240px;\n  width:min-content;\n}\n"
  },
  {
    "path": "packages/x-flow/src/components/NodeContainer/index.tsx",
    "content": "import { Popover } from 'antd';\nimport classNames from 'classnames';\nimport React, { memo, useMemo } from 'react';\nimport createIconFont from '../../utils/createIconFont';\nimport TextEllipsis from '../TextEllipsis';\nimport './index.less';\nimport TitleMenuTooltip from './TitleMenuTooltip';\n\nexport default memo((props: any) => {\n  const {\n    className,\n    onClick,\n    children,\n    icon,\n    title,\n    desc,\n    hideDesc,\n    NodeWidget,\n    iconFontUrl,\n    iconSvg,\n    hideTitleTips,\n    isSwitchBottom,\n    nodeSettingTitle,\n    style,\n    gradientHeight: _gradientHeight,\n  } = props;\n  const IconBox = useMemo(() => createIconFont(iconFontUrl), [iconFontUrl]);\n  const containerRef = React.useRef<HTMLDivElement>(null);\n\n  const renderDesc = () => (\n    <>\n      {!hideDesc && !!desc && (\n        <TextEllipsis\n          text={desc}\n          rows={2}\n          type=\"paragraph\"\n          className=\"node-desc\"\n        />\n      )}\n    </>\n  );\n\n  const renderDescAndNodeWidget = () => {\n    if (isSwitchBottom) {\n      // 条件节点且为TB布局\n      return (\n        <>\n          {renderDesc()}\n          {NodeWidget && <div className=\"node-widget\">{NodeWidget}</div>}\n        </>\n      );\n    } else {\n      return (\n        <>\n          {NodeWidget && <div className=\"node-widget\">{NodeWidget}</div>}\n          {renderDesc()}\n        </>\n      );\n    }\n  };\n\n  const hasBody = !!children;\n  const hasDesc = !!desc && !hideDesc;\n  const gradientHeight = _gradientHeight || (hasBody || hasDesc || NodeWidget ? '20%' : '100%');\n\n  return (\n    <div\n      ref={containerRef}\n      className={classNames('custom-node-container', {\n        [className]: !!className,\n      })}\n      onClick={onClick}\n    >\n      {/* 渐变头部，动态高度 */}\n      <div\n        className=\"node-gradient-header\"\n        style={{\n          '--gradient-height': gradientHeight,\n          ...style,\n        }}\n      />\n      <div className=\"node-title\" style={{ position: 'relative', zIndex: 1 }}>\n        {!hideTitleTips ? (\n          <Popover\n            overlayClassName=\"nodes-popover\"\n            content={<TitleMenuTooltip {...props} />}\n            placement=\"bottomLeft\"\n            trigger=\"hover\"\n            getPopupContainer={() =>\n              document.getElementById('xflow-container') as HTMLElement\n            }\n            overlayInnerStyle={{ padding: '12px 16px' }}\n          >\n            <span className=\"icon-box\" style={{ background: icon?.bgColor }}>\n              {iconSvg ? iconSvg : <IconBox {...icon} />}\n            </span>\n          </Popover>\n        ) : (\n          <span className=\"icon-box\" style={{ background: icon?.bgColor }}>\n            {iconSvg ? iconSvg : <IconBox {...icon} />}\n          </span>\n        )}\n        <TextEllipsis text={title} style={{ width: 188, marginLeft: '8px' }} />\n      </div>\n      <div className=\"node-body\">{children}</div>\n      <div className=\"node-body-desc\">{renderDescAndNodeWidget()}</div>\n    </div>\n  );\n});\n"
  },
  {
    "path": "packages/x-flow/src/components/NodeEditor/index.tsx",
    "content": "import FormRender, { Schema, useForm } from 'form-render';\nimport { produce } from 'immer';\nimport { debounce, isFunction } from 'lodash';\nimport React, {\n  FC,\n  forwardRef,\n  useContext,\n  useEffect,\n  useImperativeHandle,\n  useRef,\n  useState,\n} from 'react';\nimport { shallow } from 'zustand/shallow';\nimport { useStore } from '../../hooks/useStore';\nimport { ConfigContext } from '../../models/context';\nimport { safeJsonStringify, uuid } from '../../utils';\n\ninterface INodeEditorProps {\n  data: any;\n  nodeType: string;\n  id: string;\n  ref?: React.Ref<any>; // 添加 ref 属性\n  // activeNode?: any;\n}\n\nconst NodeEditor: FC<INodeEditorProps> = forwardRef((props, ref: any) => {\n  const { data, nodeType, id } = props;\n  const form = useForm();\n  // // 1.获取节点配置信息\n  const { settingMap, widgets, readOnly } = useContext(ConfigContext);\n  const nodeSetting = settingMap[nodeType] || {};\n  const [customVal, setCustomVal] = useState(data);\n  const CustomSettingWidget = widgets[`${nodeType}NodeSettingWidget`]; // 内置setting组件\n  const NodeWidget = widgets[nodeSetting?.settingWidget]; // 自定义面板配置组件\n  const getSettingSchema = nodeSetting['getSettingSchema'];\n  const [asyncSchema, setAsyncSchema] = useState<Schema>({});\n  const nodeWidgetRef = useRef(null);\n  const { nodes, setNodes } = useStore(\n    (state: any) => ({\n      nodes: state.nodes,\n      setNodes: state.setNodes,\n    }),\n    shallow\n  );\n\n\n  useImperativeHandle(ref, () => ({\n    validateForm: async () => {\n      let result = true;\n      if (\n        nodeSetting?.settingSchema ||\n        (isFunction(getSettingSchema) && Object.keys(asyncSchema).length > 0)\n      ) {\n        result = await form\n          .validateFields()\n          .then(() => {\n            return true;\n          })\n          .catch(err => {\n            return false;\n          });\n      } else if (\n        nodeSetting?.settingWidget &&\n        nodeWidgetRef.current?.validateForm\n      ) {\n        result = await nodeWidgetRef.current.validateForm();\n      }\n      return result;\n    },\n  }));\n\n  async function getSchema() {\n    const shema = await getSettingSchema(\n      id,\n      nodeType,\n      nodeSetting,\n      data,\n      form\n    ).catch(() => ({}));\n    setAsyncSchema(shema);\n  }\n\n  useEffect(() => {\n    if (isFunction(getSettingSchema)) {\n      getSchema();\n    }\n  }, []);\n\n  useEffect(() => {\n    if (nodeSetting?.settingSchema) {\n      // 自定义Schema\n      form.resetFields();\n      form.setValues(data || {});\n    } else if (nodeSetting?.settingWidget) {\n      // 自定义组件\n      setCustomVal(data);\n    } else {\n      //可能为内置schema或者是没有\n    }\n  }, [safeJsonStringify(data), id]);\n\n  const handleNodeValueChange = debounce((data: any) => {\n    const newNodes = produce(nodes, draft => {\n      let node = null;\n      // 反向查询ID，因为有多个ID相同的元素\n      for (let i = draft?.length - 1; i >= 0; i--) {\n        if (draft[i].id === id) {\n          node = draft[i];\n          break;\n        }\n      }\n      if (node) {\n        // 更新节点的 data\n        if (\n          node?.data?._nodeType === 'Switch' ||\n          node?.data?._nodeType === 'Parallel'\n        ) {\n          data['list'] = (data?.list || [])?.map((item, index) => {\n            if (item?._id) {\n              return item;\n            } else {\n              // if (node?.data?.list?.length && node?.data?.list[index]?._id) {\n              //   return {\n              //     ...item,\n              //     _id: node?.data?.list[index]?._id,\n              //   };\n              // } else {\n              return { ...item, _id: `id_${uuid()}` };\n              // }\n            }\n          });\n        }\n        const { _nodeType, _status, _isCandidate, title, desc } = node?.data;\n        node.data = { _nodeType, _status, _isCandidate, title, desc, ...data }; // form-render的list如果为空，不会返回list相应的字段，只能全部替换data\n      }\n    });\n    setNodes(newNodes, false);\n  }, 300);\n\n  const watch = {\n    '#': (allValues: any) => {\n      handleNodeValueChange({ ...allValues });\n    },\n  };\n\n  if (nodeSetting?.settingWidget && NodeWidget) {\n    return (\n      <NodeWidget\n        {...nodeSetting?.settingWidgetProps}\n        value={customVal}\n        onChange={values => {\n          setCustomVal(values);\n          handleNodeValueChange({ ...values });\n        }}\n        nodeId={id}\n        readOnly={readOnly}\n        ref={nodeWidgetRef}\n      />\n    );\n  } else if (nodeSetting?.settingSchema) {\n    return (\n      <FormRender\n        schema={nodeSetting?.settingSchema}\n        form={form}\n        widgets={widgets}\n        watch={watch}\n        globalProps={{\n          nodeId:id\n        }}\n        readOnly={readOnly}\n        onMount={() => {\n          const initialValues = form.getValues();\n          handleNodeValueChange(initialValues);\n        }}\n        configProvider={{\n          getPopupContainer: triggerNode => triggerNode.parentElement,\n        }}\n      />\n    );\n  } else if (\n    isFunction(getSettingSchema) &&\n    Object.keys(asyncSchema).length > 0\n  ) {\n    return (\n      <FormRender\n        schema={asyncSchema}\n        form={form}\n        widgets={widgets}\n        watch={watch}\n        size={'small'}\n        globalProps={{\n          nodeId:id\n        }}\n        readOnly={readOnly}\n        configProvider={{\n          getPopupContainer: triggerNode => triggerNode.parentElement,\n        }}\n      />\n    );\n  } else if (CustomSettingWidget) {\n    // 内置节点\n    return (\n      <CustomSettingWidget\n        onChange={val => {\n          handleNodeValueChange({ ...val });\n        }}\n        value={data}\n        nodeId={id}\n        readOnly={readOnly}\n      />\n    );\n  } else {\n    return null;\n  }\n});\n\nexport default NodeEditor;\n"
  },
  {
    "path": "packages/x-flow/src/components/NodeLogPanel/components/CodePanel.tsx",
    "content": "import {\n  ArrowsAltOutlined,\n  CheckOutlined,\n  CopyOutlined,\n  ShrinkOutlined,\n} from '@ant-design/icons';\nimport { json } from '@codemirror/lang-json';\nimport { EditorView } from '@codemirror/view';\nimport CodeMirror from '@uiw/react-codemirror';\nimport classNames from 'classnames';\nimport { isString } from 'lodash';\nimport React, { memo, useState } from 'react';\nimport TextEllipsis from '../../TextEllipsis';\n\nexport default memo((props: any) => {\n  const { codeData, onFullScreenChange, isShowFullScreen = true } = props;\n  const [isCopy, setIsCopy] = useState(false);\n  const isRenderTitle = isString(codeData?.title);\n  const [isFullScreen, setIsFullScreen] = useState(false);\n\n  const copyCode = () => {\n    navigator.clipboard\n      .writeText(codeData?.code)\n      .then(() => {\n        setIsCopy(true);\n        setTimeout(() => {\n          setIsCopy(false);\n        }, 1000);\n      })\n      .catch(err => {\n        console.error('Failed to copy: ', err);\n      });\n  };\n\n  return (\n    <div\n      className={classNames('log-code-panel', {\n        ['log-code-panel-full']: isFullScreen,\n      })}\n    >\n      <div className=\"log-code-title\">\n        {isRenderTitle ? (\n          <TextEllipsis\n            text={codeData?.title}\n            className=\"log-code-title-text\"\n          />\n        ) : (\n          <>{codeData?.title}</>\n        )}\n        <div>\n          {isCopy ? (\n            <CheckOutlined className=\"log-code-copy\" />\n          ) : (\n            <CopyOutlined className=\"log-code-copy\" onClick={copyCode} />\n          )}\n          {isFullScreen\n            ? isShowFullScreen && (\n                <ShrinkOutlined\n                  onClick={() => {\n                    setIsFullScreen(false);\n                    onFullScreenChange && onFullScreenChange(false);\n                  }}\n                  className=\"log-code-copy\"\n                />\n              )\n            : isShowFullScreen && (\n                <ArrowsAltOutlined\n                  onClick={() => {\n                    setIsFullScreen(true);\n                    onFullScreenChange && onFullScreenChange(true);\n                  }}\n                  className=\"log-code-copy\"\n                />\n              )}\n        </div>\n      </div>\n      <CodeMirror\n        value={codeData?.code}\n        className=\"log-code-editor\"\n        extensions={[json(), EditorView.lineWrapping]}\n        minHeight=\"172px\"\n        maxHeight=\"58vh\"\n        width=\"100%\"\n        theme=\"none\"\n        readOnly={true}\n        editable={false}\n      />\n    </div>\n  );\n});\n"
  },
  {
    "path": "packages/x-flow/src/components/NodeLogPanel/components/DetailPanel.tsx",
    "content": "import classNames from 'classnames';\nimport { isEmpty, isObject } from 'lodash-es';\nimport React, { memo, useContext, useState } from 'react';\nimport { ConfigContext } from '../../../models/context';\nimport '../index.less';\nimport CodePanel from './CodePanel';\nimport StatusPanel from './StatusPanel';\n\nexport default memo((props: any) => {\n  const { detailData, currentStatus } = props;\n  const isRenderStatus =\n    isObject(detailData?.statusPanel) && !isEmpty(detailData?.statusPanel);\n  const [isFullScreen, setIsFullScreen] = useState(false);\n  const { widgets, logPanel } = useContext(ConfigContext);\n  const CustomTitleWidget = widgets[detailData?.groupTitle]; // 自定义标题组件\n  const DetailLogWidget = widgets[logPanel?.detailLogWidget]; // 自定义组件\n  const isShowDetailLogWidget =Boolean(detailData?.showDetailLogWidget!==false)&&Boolean(DetailLogWidget);\n\n  return (\n    <div\n      className={classNames('log-detail-panel', {\n        ['log-detail-panel-code-full']: isFullScreen,\n      })}\n    >\n      {Boolean(CustomTitleWidget) ? (\n        <CustomTitleWidget logData={detailData} currentStatus={currentStatus} />\n      ) : (\n        detailData?.groupTitle && (\n          <div className=\"log-detail-panel-title\">\n            <span className=\"log-detail-panel-title-text\">\n              {detailData.groupTitle}\n            </span>\n            <div className=\"log-detail-panel-title-line\" />\n          </div>\n        )\n      )}\n      {Boolean(isRenderStatus) && (\n        <StatusPanel\n          currentStatus={currentStatus}\n          statusPanelData={detailData?.statusPanel}\n        />\n      )}\n      {(detailData?.codePanel || [])?.map((item, index) => (\n        <CodePanel\n          codeData={item}\n          key={index}\n          onFullScreenChange={isFullScreen => {\n            setIsFullScreen(isFullScreen);\n          }}\n        />\n      ))}\n      {Boolean(isShowDetailLogWidget) && <DetailLogWidget data={detailData} logList={logPanel?.logList} currentStatus={currentStatus} logPanel={logPanel} />}\n    </div>\n  );\n});\n"
  },
  {
    "path": "packages/x-flow/src/components/NodeLogPanel/components/StatusPanel.tsx",
    "content": "import { Badge, Divider, Space } from 'antd';\nimport classNames from 'classnames';\nimport { isString } from 'lodash';\nimport React, { memo, useContext } from 'react';\nimport { ConfigContext } from '../../../models/context';\nimport { getTransparentColor, transformNodeStatus } from '../../../utils';\nimport TextEllipsis from '../../TextEllipsis';\nimport '../index.less';\n\nconst StatusItem = ({ title, content, isBadge, color, colorLabel }) => {\n  return (\n    <div className=\"log-status-item\">\n      <Space style={{ width: '100%' }} direction=\"vertical\" size={2}>\n        <TextEllipsis text={title} className=\"log-status-item-title\" />\n        {isBadge ? (\n          <Badge\n            color={color}\n            text={content || colorLabel}\n            className=\"log-status-item-badge\"\n          />\n        ) : (\n          <TextEllipsis text={content} className=\"log-status-item-content\" />\n        )}\n      </Space>\n    </div>\n  );\n};\n\nexport default memo((props: any) => {\n  const { currentStatus, statusPanelData: renderData } = props;\n  const { globalConfig } = useContext(ConfigContext);\n  const {\n    nodeView: { status = [] },\n  } = globalConfig;\n  const statusObj = transformNodeStatus(status || []);\n  const statusColor = statusObj[currentStatus]?.color;\n  const bgColor = getTransparentColor(statusColor, 0.1);\n  const boxShadowColor = getTransparentColor(statusColor, 0.2);\n\n  return (\n    <div\n      className=\"log-status-panel\"\n      style={\n        {\n          '--status-color': statusColor,\n          '--status-bg-color': bgColor,\n          '--status-box-shadow': boxShadowColor,\n        } as React.CSSProperties\n      }\n    >\n      <div\n        className={classNames('log-status-panel-wrap', {\n          'log-status-panel-single': renderData?.status?.length == 1,\n        })}\n      >\n        {(renderData?.status || [])?.map((item, index) => (\n          <StatusItem\n            title={item?.label}\n            content={item?.value}\n            key={index}\n            isBadge={item?.isBadge || false}\n            color={statusColor}\n            colorLabel={statusObj[currentStatus]?.name}\n          />\n        ))}\n      </div>\n      {renderData?.status?.length && renderData?.extra && (\n        <Divider style={{ margin: '6px 0' }} />\n      )}\n      {renderData?.extra && (\n        <div className=\"log-status-panel-extra\">\n          {isString(renderData?.extra) ? (\n            <TextEllipsis\n              text={renderData?.extra}\n              className=\"log-status-panel-extra-text\"\n              type=\"paragraph\"\n              rows={3}\n            />\n          ) : (\n            <>{renderData?.extra}</>\n          )}\n        </div>\n      )}\n    </div>\n  );\n});\n"
  },
  {
    "path": "packages/x-flow/src/components/NodeLogPanel/components/TrackNodeItem.tsx",
    "content": "import { Badge, Collapse, Empty } from 'antd';\nimport React, { memo, useContext, useMemo } from 'react';\nimport { ConfigContext } from '../../../models/context';\nimport { transformNodeStatus } from '../../../utils';\nimport createIconFont from '../../../utils/createIconFont';\nimport TextEllipsis from '../../TextEllipsis';\nimport CodePanel from './CodePanel';\n\nconst { Panel } = Collapse;\ninterface ITrackNodeItemProps {\n  nodeType: string;\n  nodeStatus: string;\n  node: any;\n  logTrackList?: []; // 默认的追踪数据\n  onTrackCollapseChange: (data: any) => void;\n}\n\nexport default memo((props: ITrackNodeItemProps) => {\n  const { nodeType, nodeStatus, node, logTrackList, onTrackCollapseChange } =\n    props;\n\n  const { settingMap, iconFontUrl, globalConfig, widgets }: any =\n    useContext(ConfigContext);\n  const Icon = useMemo(() => createIconFont(iconFontUrl), [iconFontUrl]);\n  const nodeSetting = settingMap[nodeType] || {};\n  const { iconSvg } = nodeSetting;\n  const {\n    nodeView: { status = [] },\n    logPanel,\n  } = globalConfig;\n  const statusObj = transformNodeStatus(status || []);\n  const statusData = statusObj[nodeStatus];\n  const SVGWidget = widgets[nodeSetting?.iconSvg];\n\n  return (\n    <div className=\"log-track-node\">\n      <Collapse\n        className=\"log-track-collapse\"\n        onChange={arr => {\n          if (node) {\n            const { _nodeType, _status, ...rest } = node?.data;\n            onTrackCollapseChange({\n              id: node?.id,\n              values: { ...rest },\n              _nodeType,\n              _status,\n            });\n          }\n        }}\n      >\n        <Panel\n          header={\n            <div className=\"track-collapse-header\">\n              <span\n                className=\"track-icon-box\"\n                style={{\n                  background: nodeSetting?.icon?.bgColor || '#F79009',\n                }}\n              >\n                {iconSvg ? (\n                  <SVGWidget setting={nodeSetting} />\n                ) : (\n                  <Icon\n                    style={{ fontSize: 14, color: '#fff' }}\n                    type={nodeSetting?.icon?.type}\n                  />\n                )}\n              </span>\n\n              <TextEllipsis\n                text={node?.data?.title || nodeSetting?.title}\n                style={{ width: '100%', fontSize: '12px' }}\n              />\n            </div>\n          }\n          key={node?.id}\n          className=\"log-track-collapse-panel\"\n          extra={\n            statusData ? (\n              <Badge\n                color={statusData?.color}\n                text={statusData?.name}\n                className=\"track-extra-badge\"\n              />\n            ) : (\n              ''\n            )\n          }\n        >\n          {Boolean(logTrackList?.length) ? (\n            (logTrackList || [])?.map((item: any, index) => (\n              <div key={index}>\n                {Boolean(item?.groupTitle) && (\n                  <div\n                    className=\"log-detail-panel-title\"\n                    style={{ marginTop: 10 }}\n                  >\n                    <span className=\"log-detail-panel-title-text\">\n                      {item?.groupTitle}\n                    </span>\n                    <div className=\"log-detail-panel-title-line\" />\n                  </div>\n                )}\n                {Boolean((item?.codePanel || [])?.length) ? (\n                  (item?.codePanel || [])?.map((codeItem, codeIndex) => (\n                    <CodePanel\n                      codeData={codeItem}\n                      key={codeIndex}\n                      isShowFullScreen={false}\n                    />\n                  ))\n                ) : (\n                  <Empty\n                    image={Empty.PRESENTED_IMAGE_SIMPLE}\n                    description=\"暂无日志信息\"\n                    style={{ fontSize: '12px' }}\n                  />\n                )}\n              </div>\n            ))\n          ) : (\n            <Empty\n              image={Empty.PRESENTED_IMAGE_SIMPLE}\n              description=\"暂无日志信息\"\n              style={{ fontSize: '12px' }}\n            />\n          )}\n        </Panel>\n      </Collapse>\n    </div>\n  );\n});\n"
  },
  {
    "path": "packages/x-flow/src/components/NodeLogPanel/components/TrackPanel.tsx",
    "content": "import { Empty } from 'antd';\nimport React, { memo } from 'react';\nimport { shallow } from 'zustand/shallow';\nimport { useStore } from '../../../hooks/useStore';\nimport { isTruthy } from '../../../utils';\nimport TrackNodeItem from './TrackNodeItem';\n\n// 追踪面板，默认值展示有状态的节点\nexport default memo((props: any) => {\n  const { logList, onTrackCollapseChange } = props;\n  const { nodes, setNodes } = useStore(\n    (state: any) => ({\n      nodes: state.nodes,\n      setNodes: state.setNodes,\n    }),\n    shallow\n  );\n\n  const statusNode = (nodes || [])?.filter(item =>\n    isTruthy(item?.data?._status)\n  );\n  const trackList = (statusNode || [])?.map(node => {\n    const logTrackList = logList?.filter(item => item?.nodeId == node?.id)||[];\n    return { ...node, logTrackList };\n  });\n\n  return (\n    <div className=\"log-track-panel\">\n      {trackList?.length ? (\n        (trackList || [])?.map((item, index) => (\n          <TrackNodeItem\n            nodeType={item?.data?._nodeType}\n            key={item?.id}\n            nodeStatus={item?.data?._status}\n            node={item}\n            logTrackList={item?.logTrackList || []}\n            onTrackCollapseChange={onTrackCollapseChange}\n          />\n        ))\n      ) : (\n        <Empty\n          image={Empty.PRESENTED_IMAGE_SIMPLE}\n          description=\"暂无有状态节点\"\n          style={{ fontSize: '12px' }}\n        />\n      )}\n    </div>\n  );\n});\n"
  },
  {
    "path": "packages/x-flow/src/components/NodeLogPanel/index.less",
    "content": ".node-log-container {\n  .log-header-tab {\n    .ant-tabs-nav,\n    .ant-tabs-content-holder {\n      padding-left: 16px;\n      padding-right: 16px;\n    }\n\n    .log-detail-panel-code-full {\n      height: 0;\n    }\n  }\n\n  .log-status-panel {\n    border: 1px solid var(--status-color);\n    // min-height: 100px;\n    border-radius: 6px;\n    padding: 8px 15px;\n    background-color: var(--status-bg-color);\n    box-shadow: inset 0px 0px var(--status-box-shadow),\n      inset 0 0px 24px 0 var(--status-box-shadow);\n\n    .log-status-panel-wrap {\n      display: flex;\n      width: 100%;\n      justify-content: space-around;\n      flex-wrap: wrap;\n    }\n\n    .log-status-panel-single {\n      justify-content: flex-start;\n    }\n\n    .log-status-panel-extra {\n      .log-status-panel-extra-text {\n        margin-bottom: 0;\n        font-size: 12px;\n      }\n    }\n  }\n\n  .log-status-item {\n    // border: 1px solid red;\n    width: 100px;\n    margin-bottom: 5px;\n\n    .log-status-item-title {\n      width: 100%;\n      font-size: 10px;\n      color: #676f83;\n    }\n\n    .log-status-item-content {\n      width: 100%;\n      font-size: 13px;\n    }\n\n    .log-status-item-badge {\n      .ant-badge-status-text {\n        font-size: 13px;\n      }\n    }\n  }\n\n  .log-code-panel {\n    margin-top: 10px;\n    border: 1px solid #d0d5dc;\n    border-radius: 6px;\n    padding: 1px;\n    background-color: #f2f4f7;\n\n    .log-code-title {\n      background-color: #f2f4f7;\n      padding: 6px 16px;\n      display: flex;\n      justify-content: space-between;\n\n      .log-code-title-text {\n        font-size: 12px;\n        font-weight: 500;\n      }\n\n      .log-code-copy {\n        cursor: pointer;\n        font-size: 12px;\n        margin-left: 8px;\n      }\n    }\n\n    .log-code-editor {\n      min-height: 172px;\n      border-radius: 6px;\n\n      .cm-focused {\n        outline: none;\n      }\n\n      .cm-content {\n        font-size: 12px;\n      }\n    }\n  }\n\n  .log-code-panel-full {\n    position: absolute;\n    z-index: 1000;\n    top: 38px;\n    margin-right: 16px;\n  }\n\n  .log-track-panel {\n    .log-track-node {\n      margin-bottom: 10px;\n\n      .log-track-collapse {\n        font-size: 12px;\n        border-radius: 8px;\n\n        .log-track-collapse-panel {\n          border-radius: 8px;\n\n          .ant-collapse-header {\n            padding: 6px;\n\n            .ant-collapse-expand-icon {\n              height: 20px;\n              padding-inline-end: 6px;\n            }\n\n            .ant-collapse-arrow {\n              margin-right: 4px;\n              vertical-align: -4px;\n            }\n          }\n\n          .track-collapse-header {\n            display: flex;\n            justify-content: flex-start;\n            align-items: center;\n            width: 70%;\n\n            .track-icon-box {\n              width: 24px;\n              height: 24px;\n              min-height: 24px;\n              min-width: 24px;\n              border-radius: 6px;\n              display: flex;\n              align-items: center;\n              justify-content: center;\n              margin-right: 8px;\n            }\n          }\n\n          .track-extra-badge {\n            .ant-badge-status-text {\n              font-size: 12px;\n            }\n          }\n\n          .ant-collapse-content {\n            padding: 0 5px 10px 5px;\n          }\n        }\n      }\n    }\n  }\n}\n\n.log-detail-panel-title {\n  display: flex;\n  align-items: center;\n  margin-bottom: 12px;\n  padding: 0 4px;\n\n  .log-detail-panel-title-text {\n    font-size: 12px;\n    color: #676f83;\n    white-space: nowrap;\n    margin-right: 8px;\n    max-width: 300px;\n    overflow: hidden;\n    text-overflow: ellipsis;\n    font-weight: 500;\n    transition: all 0.3s;\n\n    &:hover {\n      color: #333;\n    }\n  }\n\n  .log-detail-panel-title-line {\n    flex: 1;\n    height: 1px;\n    background: #e8e8e8;\n    opacity: 0.8;\n    transition: opacity 0.3s;\n\n    &:hover {\n      opacity: 1;\n    }\n  }\n}\n\n"
  },
  {
    "path": "packages/x-flow/src/components/NodeLogPanel/index.tsx",
    "content": "import { Empty, Space, Spin, Tabs } from 'antd';\nimport { isArray } from 'lodash';\nimport React, { FC, useContext } from 'react';\nimport { ConfigContext } from '../../models/context';\nimport DetailPanel from './components/DetailPanel';\nimport TrackPanel from './components/TrackPanel';\nimport './index.less';\n\ninterface INodeEditorProps {\n  data: any;\n  onChange?: (data: any) => void;\n  nodeType: string;\n  id: string;\n  node: any;\n  onTrackCollapseChange: (data: any) => void; // 追踪面板点击collapse方法\n}\n\nconst NodeLogPanel: FC<INodeEditorProps> = (props: any) => {\n  const { data, onChange, nodeType, id, node, onTrackCollapseChange } = props;\n  const { widgets, globalConfig, logPanel } = useContext(ConfigContext);\n  const {\n    nodeView: { status = [] },\n  } = globalConfig;\n  const CustomWidget = widgets[logPanel?.logWidget]; // 内置setting组件\n  const logData = isArray(logPanel?.logList)\n    ? (logPanel?.logList || [])?.filter(item => item?.nodeId === id)\n    : [];\n\n  if (logPanel?.logWidget && CustomWidget) {\n    return (\n      <CustomWidget\n        logList={logPanel?.logList}\n        node={node}\n        logPanel={logPanel}\n      />\n    );\n  } else {\n    return (\n      <div className=\"node-log-container\">\n        <Spin spinning={Boolean(logPanel?.loading)}>\n          <Tabs\n            size=\"small\"\n            className=\"log-header-tab\"\n            {...logPanel?.tabsProps}\n          >\n            <Tabs.TabPane tab=\"详情\" key=\"detail\">\n              {Boolean(logData?.length) ? (\n                <Space direction=\"vertical\" size={16} style={{width:'100%'}}>\n                  {(logData || [])?.map((item, index) => {\n                    return (\n                      <DetailPanel\n                        key={index}\n                        currentStatus={item?._status || node?._status}\n                        detailData={item}\n                      />\n                    );\n                  })}\n                </Space>\n              ) : (\n                <Empty\n                  image={Empty.PRESENTED_IMAGE_SIMPLE}\n                  description=\"暂无日志信息\"\n                  style={{ fontSize: '12px' }}\n                />\n              )}\n            </Tabs.TabPane>\n            <Tabs.TabPane tab=\"追踪\" key=\"track\">\n              <TrackPanel\n                logList={logPanel?.logList || []}\n                onTrackCollapseChange={onTrackCollapseChange}\n              />\n            </Tabs.TabPane>\n          </Tabs>\n        </Spin>\n      </div>\n    );\n  }\n};\n\nexport default NodeLogPanel;\n"
  },
  {
    "path": "packages/x-flow/src/components/NodesMenu/index.less",
    "content": ".xflow-node-menu{\n  min-height: 340px;\n  min-width: 150px;\n\n  .menu-group-title {\n    padding: 0 10px;\n    color: #667085;\n    font-size: 12px;\n    font-weight: 500;\n    line-height: 24px;\n  }\n\n  .menu-item {\n    height: 32px;\n    color: #101828;\n    padding: 0 10px;\n    display: flex;\n    align-items: center;\n    cursor: pointer;\n  }\n\n  .menu-item:hover {\n    background-color: #f9fafb;\n    border-radius: 8px;\n  }\n\n  .icon-box {\n    width: 20px;\n    height: 20px;\n    border-radius: 6px;\n    display: inline-flex;\n    align-items: center;\n    justify-content: center;\n  }\n\n    .xflow-node-menu-list{\n      max-height: 380px;\n      overflow-y: auto;\n      scrollbar-width: none; /* Firefox */\n      -ms-overflow-style: none; /* IE/Edge */\n      &::-webkit-scrollbar { /* Chrome/Safari/Webkit */\n        display: none;\n      }\n    }\n\n}\n\n.xflow-node-menu-tooltip {\n  width: 200px;\n\n  .title {\n    color: #101828;\n    margin-top: 3px;\n  }\n\n  .description {\n    font-size: 12px;\n    font-weight: normal;\n    color: #101828;\n  }\n\n  .icon-box-max {\n    width: 24px;\n    height: 24px;\n    border-radius: 6px;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n  }\n}"
  },
  {
    "path": "packages/x-flow/src/components/NodesMenu/index.tsx",
    "content": "import React, { forwardRef, Ref, useContext, useMemo } from 'react';\nimport { Popover, Input } from 'antd';\nimport { SearchOutlined } from '@ant-design/icons';\nimport { ConfigContext } from '../../models/context';\nimport { useSet } from '../../utils/hooks';\nimport createIconFont from '../../utils/createIconFont';\nimport { TNodeMenu } from '../../types';\nimport './index.less';\n\n// 检索节点\nconst searchNodeList = (query: string, list: any[]) => {\n  if (!query) {\n    return list;\n  }\n  const searchTerm = query.toLowerCase();\n\n  function searchList(nodes: any, preResult = []) {\n    if (nodes.length === 0) {\n      return preResult;\n    }\n\n    const [currentNode, ...restNodes] = nodes;\n    let result: any = [...preResult];\n\n    if (currentNode.title.toLowerCase().includes(searchTerm)) {\n      result.push(currentNode);\n    } else if (currentNode?.type === '_group' && currentNode.items) {\n      const matchingItems = searchList(currentNode.items);\n      if (matchingItems.length > 0) {\n        result.push({ ...currentNode, items: matchingItems });\n      }\n    }\n    return searchList(restNodes, result);\n  }\n  return searchList(list);\n};\n\n// 悬浮菜单项详细描述\nexport const MenuTooltip = ({ icon, title, description, iconFontUrl, iconSvg, SVGWidget, nodeSetting }: any) => {\n  const IconBox = useMemo(() => createIconFont(iconFontUrl), [iconFontUrl]);\n\n  return (\n    <div className='xflow-node-menu-tooltip'>\n      <div className='icon-box-max' style={{ background: icon?.bgColor || '#F79009', marginRight: '8px' }}>\n        {iconSvg ? <SVGWidget setting={nodeSetting} /> :<IconBox type={icon?.type} style={{ color: '#fff', fontSize: 13, ...icon?.style }} />}\n      </div>\n      <div className='title'>\n        {title}\n      </div>\n      <div className='description'>\n        {description}\n      </div>\n    </div>\n  )\n};\n\n// 节点菜单项\nconst MenuItem = (props: any) => {\n\n  const { title, type, icon, onClick, iconFontUrl, iconSvg } = props;\n  const IconBox = useMemo(() => createIconFont(iconFontUrl), [iconFontUrl]);\n  const { widgets, settingMap } = useContext(ConfigContext);\n  const nodeSetting = settingMap[type] || {};\n  const SVGWidget = widgets[nodeSetting?.iconSvg]\n\n  return (\n    <Popover\n      key={type}\n      content={<MenuTooltip {...props} SVGWidget={SVGWidget} nodeSetting={nodeSetting} />}\n      placement='right'\n      arrow={false}\n      getPopupContainer={() => document.getElementById('xflow-container') as HTMLElement}\n    >\n      <div\n        className='menu-item'\n        onClick={onClick(type)}\n      >\n        <span className='icon-box' style={{ background: icon?.bgColor || '#F79009', marginRight: '8px' }}>\n          {iconSvg ? <SVGWidget setting={nodeSetting} /> :\n            <IconBox\n            type={icon?.type}\n            style={{ color: '#fff', fontSize: 13 }}\n            />}\n        </span>\n        <span>{title}</span>\n      </div>\n    </Popover>\n  );\n};\n\n// 过滤 hidden 节点\nconst filterHiddenMenu = (list: any) => {\n  return (list || []).filter((item: any) => !item.hidden)\n}\n\n/**\n *\n * 节点菜单List\n *\n */\nconst NodesMenu = (props: TNodeMenu, ref: Ref<HTMLDivElement>) => {\n  const { items, showSearch, onClick } = props;\n  const { iconFontUrl } = useContext(ConfigContext);\n\n  const [state, setState] = useSet({\n    menuList: [...items]\n  });\n  const { menuList } = state;\n\n  const handleItemClick = (type: string) => (ev: React.MouseEvent<HTMLDivElement>) => {\n    ev.stopPropagation();\n    onClick({ type });\n  }\n\n  const handleSearch = (ev: any) => {\n    setState({ menuList: searchNodeList(ev.target.value, items) })\n  };\n\n  return (\n    <div className='xflow-node-menu' ref={ref}>\n      {!!showSearch && (\n        <div style={{ margin: '5px 9px 9px' }}>\n          <Input\n            placeholder='搜索节点'\n            onChange={handleSearch}\n            prefix={<SearchOutlined style={{ color: 'rgba(0,0,0,.45)' }} />}\n            style={{ width: '100%' }}\n          />\n        </div>\n      )}\n      <div className=\"xflow-node-menu-list\">\n        {filterHiddenMenu(menuList).map((item: any, index: number) => item.type === '_group' ? (\n          <div key={`${item.type}-${index}`}>\n            <div className='menu-group-title'>{item.title}</div>\n            {filterHiddenMenu(item.items).map((data: any, index: number) => (\n              <MenuItem\n                iconFontUrl={iconFontUrl}\n                {...data}\n                onClick={handleItemClick}\n                key={index}\n              />\n            ))}\n          </div>\n        ) : (\n          <div key={`${item.type}-${index}`}>\n            <MenuItem\n              iconFontUrl={iconFontUrl}\n              {...item}\n              onClick={handleItemClick}\n            />\n          </div>\n        ))}\n      </div>\n    </div>\n  );\n};\n\nexport default forwardRef(NodesMenu);\n"
  },
  {
    "path": "packages/x-flow/src/components/NodesPopover/index.less",
    "content": ".nodes-popover {\n  .ant-popover-inner-content {\n    padding: 0;\n  }\n}"
  },
  {
    "path": "packages/x-flow/src/components/NodesPopover/index.tsx",
    "content": "import { useClickAway } from 'ahooks';\nimport { Popover } from 'antd';\nimport { isFunction } from 'lodash';\nimport React, {\n  forwardRef,\n  useCallback,\n  useContext,\n  useImperativeHandle,\n  useMemo,\n  useRef,\n  useState,\n} from 'react';\nimport { useStore } from '../../hooks/useStore';\nimport { ConfigContext } from '../../models/context';\nimport { uuid } from '../../utils';\nimport NodesMenu from '../NodesMenu';\nimport './index.less';\n\nexport default forwardRef((props: any, popoverRef) => {\n  const { addNode, children, onNodeSelectPopoverChange,nodeType } = props;\n  const { setIsAddingNode } = useStore(s => ({\n    setIsAddingNode: s.setIsAddingNode,\n  }));\n  const ref = useRef<any>(null);\n  const closeRef: any = useRef<HTMLButtonElement>(null);\n  const [open, setOpen] = useState(false);\n\n  const { settings, nodeSelector, antdVersion ,readOnly ,clickAddNode,settingMap,filterSettings }: any = useContext(ConfigContext);\n  const items = useMemo(()=>{\n    if(typeof filterSettings === 'function'){\n      return filterSettings(settings,nodeType)\n    }\n    return settings\n  },[settings,nodeType])\n  const { showSearch, popoverProps = { placement: 'top' } } =\n    nodeSelector || {};\n\n  useImperativeHandle(popoverRef, () => ({\n    changeOpen: openChange,\n  }));\n\n  useClickAway(() => {\n    if (closeRef.current) {\n      setOpen(false);\n      onNodeSelectPopoverChange && onNodeSelectPopoverChange(false);\n      closeRef.current = false;\n    }\n  }, ref);\n\n  const handCreateNode = useCallback<any>(({ type }) => {\n    if (isFunction(clickAddNode)) {\n      clickAddNode(type,settingMap[type],( data = {} )=>{\n        addNode({ _nodeType: type, ...data })\n      });\n    }else{\n      if (type === 'Switch') {\n        addNode({ _nodeType: type, list: [{ '_id':`${uuid()}`}] });\n      } else if (type === 'Parallel') {\n        addNode({ _nodeType: type, list: [{ _id: `id_${uuid()}` }, { _id: `id_${uuid()}` }] });\n      } else {\n        addNode({ _nodeType: type });\n      }\n    }\n\n    setOpen(false);\n    onNodeSelectPopoverChange && onNodeSelectPopoverChange(false);\n  }, []);\n\n  const openChange = () => {\n    setTimeout(() => {\n      setIsAddingNode(true);\n      closeRef.current = true;\n      if (!readOnly) {\n        setOpen(true);\n      }\n    }, 50);\n  };\n\n  const popoverVersionProps = useMemo(() => {\n    if (antdVersion === 'V5') {\n      return {\n        open,\n        onOpenChange: openChange,\n      };\n    }\n    // V4\n    return {\n      visible: open,\n      onVisibleChange: openChange,\n    };\n  }, [open]);\n\n  return (\n    <Popover\n      overlayClassName=\"nodes-popover\"\n      getPopupContainer={() => document.getElementById('xflow-container')}\n      zIndex={2000}\n      arrow={false}\n      overlayInnerStyle={{ padding: '12px 6px' }}\n      {...popoverProps}\n      trigger=\"click\"\n      {...popoverVersionProps}\n      content={\n        <NodesMenu\n          ref={ref}\n          items={items}\n          showSearch={showSearch}\n          onClick={handCreateNode}\n        />\n      }\n    >\n      {children}\n    </Popover>\n  );\n});\n"
  },
  {
    "path": "packages/x-flow/src/components/PanelContainer/index.less",
    "content": ".custom-node-panel {\n  .ant-drawer-content-wrapper {\n    top: 54px;\n    bottom: 14px;\n    right: 12px;\n    border-radius: 20px;\n    height: auto; // 兼容antd4\n    box-shadow: 0px 4px 6px -2px rgba(16, 24, 40, 0.03),\n      0px 12px 16px -4px rgba(16, 24, 40, 0.08);\n\n    .ant-drawer-header-title,\n    .ant-drawer-title {\n      width: 100%;\n\n      .title-box>div:first-child {\n        max-width: 85%;\n      }\n    }\n  }\n\n  .ant-drawer-close {\n    display: none;\n  }\n\n  .ant-drawer-header {\n    padding: 16px 16px 0 16px;\n    border-bottom: none;\n  }\n\n  .ant-drawer-content {\n    border-radius: 20px;\n  }\n\n  .ant-drawer-body {\n    padding: 12px 16px;\n  }\n\n  .title-box {\n    display: flex;\n    align-items: center;\n    justify-content: space-between;\n\n    .ant-input {\n      border: 1px solid transparent;\n      font-weight: 600;\n      background: none;\n    }\n\n    .ant-input:focus{\n      border: 1px solid #40a9ff;\n    }\n\n    .ant-input-outlined:focus {\n       border: 1px solid #3b82f6;\n    }\n  }\n\n  .icon-box {\n    width: 24px;\n    height: 24px;\n    min-width: 24px;\n    min-height: 24px;\n    border-radius: 6px;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    margin-right: 12px;\n  }\n\n  .title-actions {\n    display: flex;\n    align-items: center;\n    margin-left: 24px;\n  }\n\n  .desc-box {\n    font-size: 12px;\n    line-height: 32px;\n    font-weight: normal;\n\n    .ant-input-outlined {\n      border-color: #fff;\n    }\n\n    textarea {\n      margin-top: 12px;\n    }\n\n    .readOnly-desc {\n      line-height: 20px;\n      color: #676f83;\n      font-size: 12px;\n      margin-top: 10px;\n    }\n  }\n\n  .ant-input-outlined:focus-within {\n    border-color: #3b82f6 !important;\n  }\n\n  .fr-table-cell-content {\n    .ant-col {\n      padding: 0 !important;\n    }\n  }\n\n  .ant-collapse-content-box {\n    padding: 0 !important;\n  }\n\n  .item-collapse {\n    border: none;\n    background-color: #fff;\n\n    .ant-collapse-header {\n      background: none;\n      padding: 6px 0;\n    }\n\n    .ant-collapse-item {\n      border: none !important;\n      padding: 0;\n    }\n\n    .ant-collapse-content {\n      border: none;\n    }\n  }\n\n  .ant-collapse-header-text {\n    color: #354052;\n    font-weight: 600;\n  }\n\n  .ant-table-thead>tr>th {\n    font-size: 12px;\n    font-weight: normal;\n  }\n\n  .ant-table-tbody>tr>td {\n    padding: 4px !important;\n  }\n\n  // input,\n  // select,\n  // textarea,\n  // .ant-select-selector {\n  //   font-size: 12px !important;\n  //   min-height: 30px;\n  // }\n\n  // input,\n  // select,\n  // .ant-select-selector {\n  //   height: 30px !important;\n  //   line-height: 30px !important;\n  // }\n\n  .fr-table-list {\n    .ant-btn {\n      font-size: 12px;\n    }\n  }\n}"
  },
  {
    "path": "packages/x-flow/src/components/PanelContainer/index.tsx",
    "content": "import { Divider, Drawer, Input, Space } from 'antd';\nimport { produce } from 'immer';\nimport { isNumber } from 'lodash';\nimport React, { FC, useContext, useEffect, useMemo, useState, useCallback, useRef } from 'react';\nimport { shallow } from 'zustand/shallow';\nimport { useStore } from '../../hooks/useStore';\nimport { ConfigContext } from '../../models/context';\nimport { isTruthy, getColorfulModeBackground } from '../../utils';\nimport createIconFont from '../../utils/createIconFont';\nimport IconView from '../IconView';\nimport TextEllipsis from '../TextEllipsis';\nimport './index.less';\n\ninterface IPanelProps {\n  disabled?: boolean; // 是否禁用  ---to do：确认一下取的地方\n  nodeType: string;\n  onClose: () => void;\n  node?: {\n    id: string;\n    _isCandidate: boolean;\n    _nodeType: string;\n    _status?: string | undefined;\n  };\n  children?: any;\n  id: string;\n  data: any; // data值\n  openLogPanel?: boolean; // 日志面板是否打开\n}\n\nconst Panel: FC<IPanelProps> = (props: IPanelProps) => {\n  const {\n    onClose,\n    children,\n    nodeType,\n    disabled,\n    node,\n    id,\n    data,\n    openLogPanel,\n  } = props;\n  // 1.获取节点配置信息\n  const {\n    settingMap,\n    iconFontUrl,\n    globalConfig,\n    antdVersion,\n    readOnly,\n    logPanel,\n    onTesting,\n    widgets,\n    openColorfulMode\n  }: any = useContext(ConfigContext);\n\n  const nodeSetting = settingMap[nodeType] || {};\n  const { nodes, setNodes } = useStore(\n    (state: any) => ({\n      nodes: state.nodes,\n      setNodes: state.setNodes,\n    }),\n    shallow\n  );\n\n  const activeNode = useMemo(()=>{\n    const node = nodes.find((r)=>r.id === id);\n    return node || {};\n  },[JSON.stringify(nodes), id]);\n\n  const titleText = activeNode.data?.title;\n  const descText = activeNode.data?.desc;\n\n  const { nodePanel, iconSvg, showTestingBtn } = nodeSetting;\n  const SVGWidget = widgets[nodeSetting?.iconSvg]\n  const hideDesc = nodePanel?.hideDesc ?? globalConfig?.nodePanel?.hideDesc ?? false;\n  const isEnableLogPanel = Boolean(logPanel?.enable ?? true);\n  const isShowStatusPanel = isEnableLogPanel && Boolean(isTruthy(node?._status) && openLogPanel);\n  const offsetRightStatus = isNumber(logPanel?.width) ? Number(logPanel?.width + 10) : 410;\n\n  const handleTitleChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n    handleNodeValueChange({ title: e.target.value });\n  };\n\n  const handleDescChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {\n    handleNodeValueChange({ desc: e.target.value });\n  };\n\n  const handleNodeValueChange = (data: any) => {\n    const newNodes = produce(nodes, draft => {\n      let node = null;\n      // 反向查询ID，因为有多个ID相同的元素\n      for (let i = draft?.length - 1; i >= 0; i--) {\n        if (draft[i].id === id) {\n          node = draft[i];\n          break;\n        }\n      }\n      if (node) {\n        // 更新节点的 data\n        node.data = { ...node.data, ...data };\n      }\n    });\n    setNodes(newNodes, false);\n  };\n\n  const Icon = useMemo(() => createIconFont(iconFontUrl), [iconFontUrl]);\n  const drawerVersionProps = useMemo(() => {\n    if (antdVersion === 'V5') {\n      return {\n        rootClassName: 'custom-node-panel',\n        open: true,\n      };\n    }\n    // V4\n    return {\n      className: 'custom-node-panel',\n      visible: true,\n    };\n  }, [antdVersion]);\n\n  return (\n    <Drawer\n      {...drawerVersionProps}\n      getContainer={false}\n      key={id}\n      width={nodePanel?.width || globalConfig?.nodePanel?.width || 400} // 改为配置的width 节点的width > 全局的width>  默认 400\n      mask={false}\n      onClose={onClose}\n      headerStyle={{ paddingBottom: '12px', ...getColorfulModeBackground(nodeSetting?.icon?.bgColor, openColorfulMode) }}\n      style={{\n        position: 'absolute',\n        right: isShowStatusPanel ? offsetRightStatus : 0,\n      }}\n      title={\n        <>\n          <div className=\"title-box\">\n            <div style={{ display: 'flex', alignItems: 'center', flex: 1 }}>\n              <span\n                className=\"icon-box\"\n                style={{ background: nodeSetting?.icon?.bgColor || '#F79009' }}\n              >\n                {iconSvg ? (\n                  <SVGWidget setting={nodeSetting} />\n                ) : (\n                  <Icon\n                    style={{ fontSize: 14, color: '#fff' }}\n                    type={nodeSetting?.icon?.type}\n                  />\n                )}\n              </span>\n              {disabled || readOnly ? (\n                <TextEllipsis text={titleText || nodeSetting?.title} style={{ marginLeft: '11px' }} />\n              ) : (\n                <Input\n                  size=\"small\"\n                  style={{ width: '100%' }}\n                  value={titleText}\n                  onChange={handleTitleChange}\n                  placeholder={nodeSetting?.title}\n                />\n              )}\n            </div>\n            <div className=\"title-actions\">\n              <Space size={[4, 4]}>\n                {!disabled && showTestingBtn && (\n                  <>\n                    <IconView\n                      type=\"icon-yunhang\"\n                      onClick={() => {\n                        const n =\n                          nodes?.find((item: any) => item?.id === node?.id) || {};\n                        onTesting && onTesting(n, nodes);\n                      }}\n                      style={{ fontSize: 16 }}\n                    />\n                    <Divider type=\"vertical\" />\n                  </>\n                )}\n                {/* <IconView type='icon-help'/> */}\n                <IconView\n                  type=\"icon-remove\"\n                  style={{ fontSize: 16 }}\n                  onClick={onClose}\n                />\n              </Space>\n            </div>\n          </div>\n          {!hideDesc && (\n            <div className=\"desc-box\">\n              {disabled || readOnly ? (\n                descText && (\n                  <TextEllipsis\n                    text={descText}\n                    type=\"paragraph\"\n                    rows={2}\n                    className=\"readOnly-desc\"\n                  />\n                )\n              ) : (\n                <Input.TextArea\n                  placeholder=\"添加描述...\"\n                  autoSize={{ minRows: 1, maxRows: 3 }}\n                  value={descText}\n                  onChange={handleDescChange}\n                  disabled={readOnly}\n                />\n              )}\n            </div>\n          )}\n        </>\n      }\n    >\n      {children}\n    </Drawer>\n  );\n};\n\nexport default React.memo(Panel);\n"
  },
  {
    "path": "packages/x-flow/src/components/PanelStatusLogContainer/index.less",
    "content": ".node-log-panel {\n  .ant-drawer-content-wrapper {\n    top: 54px;\n    bottom: 14px;\n    right: 12px;\n    border-radius: 20px;\n    height: auto;\n  }\n\n  .ant-drawer-close {\n    display: none;\n  }\n\n  .ant-drawer-header {\n    padding: 24px 16px 0 16px;\n  }\n\n  .ant-drawer-content {\n    border-radius: 20px;\n  }\n\n  .ant-drawer-body {\n    padding: 12px 16px;\n  }\n\n  .title-box {\n    display: flex;\n    align-items: center;\n    justify-content: space-between;\n    .ant-input {\n      border: none;\n      font-weight: 600;\n      background: none;\n    }\n\n    .ant-input:focus {\n      border-color: #40a9ff;\n    }\n\n    .ant-input-outlined:focus {\n      border-color: #3b82f6;\n    }\n  }\n\n  .icon-box {\n    width: 24px;\n    height: 24px;\n    border-radius: 6px;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    margin-right: 5px;\n    cursor: pointer;\n  }\n  .title-content {\n    margin-left: 11px;\n  }\n\n  .title-actions {\n    display: flex;\n    align-items: center;\n    margin-left: 24px;\n  }\n\n  .desc-box {\n    font-size: 12px;\n    line-height: 32px;\n    font-weight: normal;\n    .ant-input-outlined {\n      border-color: #fff;\n    }\n    textarea {\n      margin-top: 12px;\n    }\n  }\n\n  .ant-input-outlined:focus-within {\n    border-color: #3b82f6 !important;\n  }\n\n  .fr-table-cell-content {\n    .ant-col {\n      padding: 0 !important;\n    }\n  }\n\n  .ant-collapse-content-box {\n    padding: 0 !important;\n  }\n\n  .item-collapse {\n    border: none;\n    background-color: #fff;\n\n    .ant-collapse-header {\n      background: none;\n      padding: 6px 0;\n    }\n\n    .ant-collapse-item {\n      border: none !important;\n      padding: 0;\n    }\n\n    .ant-collapse-content {\n      border: none;\n    }\n  }\n\n  .ant-collapse-header-text {\n    color: #354052;\n    font-weight: 600;\n  }\n\n  .ant-table-thead > tr > th {\n    font-size: 12px;\n    font-weight: normal;\n  }\n\n  .ant-table-tbody > tr > td {\n    padding: 4px !important;\n  }\n\n  input,\n  select,\n  textarea,\n  .ant-select-selector {\n    font-size: 12px !important;\n    min-height: 30px;\n  }\n\n  input,\n  select,\n  .ant-select-selector {\n    height: 30px !important;\n    line-height: 30px !important;\n  }\n\n  .fr-table-list {\n    .ant-btn {\n      font-size: 12px;\n    }\n  }\n}\n\n.no-header-line {\n  .ant-drawer-header{\n    border-bottom: none;\n  }\n  .ant-drawer-body{\n    padding: 0px 0px 12px 0px;\n  }\n}\n"
  },
  {
    "path": "packages/x-flow/src/components/PanelStatusLogContainer/index.tsx",
    "content": "import { Drawer, Popover } from 'antd';\nimport classNames from 'classnames';\nimport { isNumber } from 'lodash';\nimport React, { FC, useContext, useMemo } from 'react';\nimport { ConfigContext } from '../../models/context';\nimport createIconFont from '../../utils/createIconFont';\nimport IconView from '../IconView';\nimport TitleMenuTooltip from '../NodeContainer/TitleMenuTooltip';\nimport './index.less';\n\ninterface IPanelProps {\n  nodeType: string;\n  onClose: () => void;\n  id: string;\n  data: any; // data值\n  children?: any;\n}\n\nconst PanelStatusLogContainer: FC<IPanelProps> = (props: IPanelProps) => {\n  const { onClose, children, nodeType } = props;\n  // 1.获取节点配置信息\n  const {\n    settingMap,\n    iconFontUrl,\n    globalConfig,\n    logPanel,\n    widgets,\n    antdVersion,\n  }: any = useContext(ConfigContext);\n  const nodeSetting = settingMap[nodeType] || {};\n  const { nodePanel, iconSvg } = nodeSetting;\n\n  const Icon = useMemo(() => createIconFont(iconFontUrl), [iconFontUrl]);\n  const CustomWidget = widgets[logPanel?.logWidget]; // 内置setting组件\n  const isCustomWidget = !Boolean(logPanel?.logWidget && CustomWidget);\n  const width = isNumber(logPanel?.width) ? logPanel?.width : 400;\n  const SVGWidget = widgets[nodeSetting?.iconSvg]\n\n\n\n  const drawerVersionProps = useMemo(() => {\n    if (antdVersion === 'V5') {\n      return {\n        rootClassName: classNames('node-log-panel', {\n          'no-header-line': isCustomWidget,\n        }),\n        open: true,\n      };\n    }\n    // V4\n    return {\n      className: classNames('node-log-panel', {\n        'no-header-line': isCustomWidget,\n      }),\n      visible: true,\n    };\n  }, []);\n\n  return (\n    <Drawer\n      {...drawerVersionProps}\n      getContainer={false}\n      width={width}\n      mask={false}\n      onClose={onClose}\n      headerStyle={{\n        paddingBottom: '12px',\n      }}\n      style={{\n        position: 'absolute',\n      }}\n      destroyOnClose={true}\n      title={\n        <>\n          <div className=\"title-box\">\n            <div style={{ display: 'flex', alignItems: 'center', flex: 1 }}>\n              <Popover\n                overlayClassName=\"nodes-popover\"\n                content={\n                  <TitleMenuTooltip\n                    {...nodeSetting}\n                    iconFontUrl={iconFontUrl}\n                    iconSvg={iconSvg ? <SVGWidget setting={nodeSetting} />:false}\n                    nodeSettingTitle={nodeSetting?.title}\n                  />\n                }\n                placement=\"bottom\"\n                trigger=\"hover\"\n                getPopupContainer={() =>\n                  document.getElementById('xflow-container') as HTMLElement\n                }\n                overlayInnerStyle={{ padding: '12px 16px' }}\n              >\n                <span\n                  className=\"icon-box\"\n                  style={{\n                    background: nodeSetting?.icon?.bgColor || '#F79009',\n                  }}\n                >\n                  {iconSvg ? (\n                    <SVGWidget setting={nodeSetting} />\n                  ) : (\n                    <Icon\n                      style={{ fontSize: 14, color: '#fff' }}\n                      type={nodeSetting?.icon?.type}\n                    />\n                  )}\n                </span>\n              </Popover>\n              <span className=\"title-content\">执行日志</span>\n            </div>\n            <div className=\"title-actions\">\n              <IconView\n                type=\"icon-remove\"\n                style={{ fontSize: 16 }}\n                onClick={onClose}\n              />\n            </div>\n          </div>\n        </>\n      }\n    >\n      {children}\n    </Drawer>\n  );\n};\n\nexport default React.memo(PanelStatusLogContainer);\n"
  },
  {
    "path": "packages/x-flow/src/components/TextEllipsis/index.less",
    "content": ".text-ellipsis {\n  display: inline-block;\n  max-width: 100%;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  white-space: nowrap;\n}\n\n\n.paragraph-ellipsis {\n  display: block;\n  width: 100%;\n  word-break: break-all;\n}\n"
  },
  {
    "path": "packages/x-flow/src/components/TextEllipsis/index.tsx",
    "content": "import { Tooltip, TooltipProps } from 'antd';\nimport React, { FC, memo, useEffect, useState } from 'react';\nimport './index.less';\n\ninterface ITextEllipsisProps {\n  text: string;\n  style?: object;\n  className?: string;\n  toolTipProps?: TooltipProps;\n  type?: 'text' | 'paragraph';\n  rows?: number;\n}\nconst TextEllipsis: FC<ITextEllipsisProps> = ({\n  text,\n  style,\n  toolTipProps,\n  type = 'text',\n  rows = 1,\n  className,\n}) => {\n  const typographyRef = React.useRef<HTMLElement>(null);\n  const [isEllipse, setIsEllipse] = useState(false);\n\n  const component =\n    type === 'paragraph' ? (\n      <span\n        ref={typographyRef}\n        className={`paragraph-ellipsis ${className}`}\n        style={{\n          ...style,\n          display: '-webkit-box',\n          overflow: 'hidden',\n          WebkitBoxOrient: 'vertical',\n          WebkitLineClamp: rows,\n        }}\n      >\n        {text}\n      </span>\n    ) : (\n      <span\n        ref={typographyRef}\n        className={`text-ellipsis ${className}`}\n        style={style}\n      >\n        {text}\n      </span>\n    );\n\n  useEffect(() => {\n    if (text) {\n      if (type === 'paragraph') {\n        const { offsetHeight, scrollHeight, clientHeight } =\n          typographyRef.current;\n        setIsEllipse(scrollHeight > clientHeight);\n      } else {\n        const isEllipse = isEleEllipsis(typographyRef?.current);\n        setIsEllipse(isEllipse);\n      }\n    }\n  }, [text]);\n\n  const isEleEllipsis = (ele: HTMLElement): boolean => {\n    const childDiv = document.createElement('em');\n    ele.appendChild(childDiv);\n\n    const rect = ele.getBoundingClientRect();\n    const childRect = childDiv.getBoundingClientRect();\n\n    ele.removeChild(childDiv);\n\n    return (\n      // Horizontal out of range\n      rect.left > childRect.left ||\n      childRect.right > rect.right ||\n      // Vertical out of range\n      rect.top > childRect.top ||\n      childRect.bottom > rect.bottom\n    );\n  };\n  if (isEllipse) {\n    return (\n      <Tooltip\n        title={text}\n        getPopupContainer={() =>\n          document.getElementById('xflow-container') as HTMLElement\n        }\n        color=\"#ffff\"\n        overlayInnerStyle={{\n          color: '#354052',\n          fontSize: '12px',\n          borderRadius: '8px',\n        }}\n        placement=\"bottomRight\"\n        {...toolTipProps}\n      >\n        {component}\n      </Tooltip>\n    );\n  }\n  return component;\n};\n\nexport default memo(TextEllipsis);\n"
  },
  {
    "path": "packages/x-flow/src/hooks/useEdges.ts",
    "content": "import type { Edge } from \"@xyflow/react\";\nimport { shallow } from 'zustand/shallow';\n\nimport { useStore } from './useStore';\nimport type { FlowState } from '../models/store';\n\nconst nodesSelector = (state: FlowState) => state.edges;\n\n/**\n * Hook for getting the current edges from the store.\n *\n * @public\n * @returns An array of edges\n */\nexport function useEdges<EdgeType extends Edge = Edge>(): Edge[] {\n  const nodes = useStore(nodesSelector, shallow) as EdgeType[];\n\n  return nodes;\n}\n"
  },
  {
    "path": "packages/x-flow/src/hooks/useFlow.ts",
    "content": "import { useMemo } from 'react';\nimport { useMemoizedFn } from 'ahooks';\nimport { Edge, useReactFlow } from '@xyflow/react';\n\nimport { FlowNode } from '../models/store';\nimport { useStoreApi } from './useStore';\nimport { useTemporalStore } from './useTemporalStore';\nimport autoLayoutNodes from '../utils/autoLayoutNodes';\nimport { generateCopyNodes, safeJSONParse, safeJsonStringify, uuid } from '../utils';\nimport { message } from 'antd';\n\n// useFlow 维护原则\n// 1. 尽量复用 reactflow 已有的方法，不要重复造轮子\n// 2. 非必要不暴露新的方法和状态\n\nexport const useFlow = () => {\n  const storeApi = useStoreApi();\n  const instance = storeApi.getState();\n\n  const {\n    zoomIn,\n    zoomOut,\n    zoomTo,\n    getZoom,\n    setViewport,\n    getViewport,\n    fitView,\n    setCenter,\n    fitBounds,\n    toObject: _toObject,\n    getNodes: _getNodes,\n    getEdges,\n    screenToFlowPosition,\n    flowToScreenPosition\n  } = useReactFlow();\n\n  const { record } = useTemporalStore();\n\n  const toObject = useMemoizedFn(() => {\n    const { nodes, ...rest } = _toObject();\n    return {\n      ...rest,\n      nodes: getNodes(nodes)\n    };\n  });\n\n  const getFlowData = () => {\n    const { nodes, edges } = _toObject();\n    return {\n      edges,\n      nodes: getNodes(nodes)\n    };\n  };\n\n  const setFlowData = ({ nodes, edges }) => {\n    if (!!nodes) {\n      setNodes(nodes);\n    }\n\n    if (!!edges) {\n      setEdges(edges);\n    }\n  };\n\n  const getNodes = useMemoizedFn((_nodes: any) => {\n    const nodes = _nodes || _getNodes();\n    const result = nodes.map((item: any) => {\n      const { data, ...rest } = item;\n      const { _nodeType, ...restData } = data;\n      return {\n        ...rest,\n        data: restData,\n        type: _nodeType\n      }\n    });\n    return result;\n  });\n\n  const setNodes = useMemoizedFn((nodes: FlowNode[]) => {\n    storeApi.getState().setNodes(nodes);\n  });\n\n  const addNodes = useMemoizedFn((nodes: FlowNode[]) => {\n    record(() => {\n      storeApi.getState().addNodes(nodes);\n    })\n  });\n\n  const setEdges = useMemoizedFn((edges: Edge[]) => {\n    storeApi.getState().setEdges(edges);\n  });\n\n  const addEdges = useMemoizedFn((edges: Edge[]) => {\n    storeApi.getState().addEdges(edges);\n  });\n\n  const copyNode = useMemoizedFn((nodeId) => {\n    const copyNodes = generateCopyNodes(\n      storeApi.getState().nodes.find((node) => node.id === nodeId),\n    );\n\n    // 清除之前的超时定时器\n    if (storeApi.getState().copyTimeoutId) {\n      clearTimeout(storeApi.getState().copyTimeoutId);\n    }\n\n    // 设置isAddingNode为true，开启mousemove监听\n    storeApi.getState().setIsAddingNode(true);\n\n    // 设置30秒超时，自动关闭mousemove监听\n    const timeoutId = setTimeout(() => {\n      storeApi.getState().setIsAddingNode(false);\n      storeApi.setState({ copyTimeoutId: null });\n    }, 30000);\n\n    storeApi.setState({\n      copyNodes,\n      copyTimeoutId: timeoutId,\n    });\n  });\n\n  const copyFLowNodes = useMemoizedFn((nodes: any[],onCopyCompleted:(nodes:any[])=>void) => {\n    const copyNodes = nodes.map(node => {\n      return {\n        id: uuid(),\n        type: node.type,\n        data: {\n          ...node.data,\n        },\n        position: { x: 0, y: 0 },\n        sourceId: node.id,\n        ...node\n      };\n    });\n\n    navigator.clipboard.writeText(safeJsonStringify(copyNodes)).catch(err => {\n      message.error('节点复制失败');\n    }).then(()=>{\n      message.success('节点复制成功');\n      onCopyCompleted && onCopyCompleted(copyNodes)\n    })\n  });\n\n  const pasteNode = useMemoizedFn((nodeId: string, data: any) => {\n    if (storeApi.getState().copyNodes.length > 0) {\n      // 清除超时定时器\n      if (storeApi.getState().copyTimeoutId) {\n        clearTimeout(storeApi.getState().copyTimeoutId);\n      }\n\n      // 关闭mousemove监听\n      storeApi.getState().setIsAddingNode(false);\n\n      const newEdges = {\n        id: uuid(),\n        source: nodeId,\n        target: storeApi.getState().copyNodes[0].id,\n        ...data\n      };\n      record(() => {\n        storeApi.getState().addNodes(storeApi.getState().copyNodes, false);\n      })\n      storeApi.getState().addEdges(newEdges);\n      storeApi.setState({\n        copyNodes: [],\n        copyTimeoutId: null,\n      });\n    }else{\n      message.warning('请先复制节点！')\n    }\n  });\n\n  const pasteFLowNodes = useMemoizedFn(async (onPasteCompleted:(nodes:any[])=>void) => {\n    const text = await navigator.clipboard.readText().catch(() => null);\n    const mousePosition = storeApi.getState().mousePosition;\n    if (text) {\n      const copyNodeData = safeJSONParse(text, []);\n      const flowPos = screenToFlowPosition({\n        x: mousePosition.elementX,\n        y: mousePosition.elementY,\n      });\n      const copyNodes = copyNodeData.map((node, index) => ({\n        ...node,\n        id: uuid(),\n        position: {\n          x: flowPos.x + index * 200,\n          y: flowPos.y + index * 200,\n        },\n      }));\n      record(() => {\n        storeApi.getState().addNodes(copyNodes, false);\n      });\n      onPasteCompleted && onPasteCompleted(copyNodes)\n    }\n  });\n\n  const pasteNodeSimple = useMemoizedFn(() => {\n    const mousePosition = storeApi.getState().mousePosition;\n    if (storeApi.getState().copyNodes.length > 0) {\n      // 清除超时定时器\n      if (storeApi.getState().copyTimeoutId) {\n        clearTimeout(storeApi.getState().copyTimeoutId);\n      }\n\n      // 关闭mousemove监听\n      storeApi.getState().setIsAddingNode(false);\n\n      const flowPos = screenToFlowPosition({\n        x: mousePosition.elementX,\n        y: mousePosition.elementY,\n      });\n      const copyNodes = storeApi.getState().copyNodes.map(node => ({\n        ...node,\n        position: {\n          x: flowPos.x,\n          y: flowPos.y,\n        },\n      }));\n      record(() => {\n        storeApi.getState().addNodes(copyNodes, false);\n        // 将清空剪贴板的操作也加入记录，使其可撤销\n        storeApi.setState({\n          copyNodes: [],\n          copyTimeoutId: null,\n        });\n      });\n    } else {\n      // message.warning('请先复制节点！');\n    }\n  });\n\n  const deleteNode = useMemoizedFn(nodeId => {\n    record(() => {\n      storeApi.setState({\n        edges: storeApi\n          .getState()\n          .edges.filter(\n            edge => edge.source !== nodeId && edge.target !== nodeId\n          ),\n      });\n    });\n    record(() => {\n      storeApi.setState({\n        nodes: storeApi.getState().nodes.filter(node => node.id !== nodeId),\n      });\n    });\n  });\n\n  const runAutoLayout = useMemoizedFn(() => {\n    const newNodes: any = autoLayoutNodes(\n      storeApi.getState().nodes,\n      storeApi.getState().edges,\n      storeApi.getState().layout\n    );\n    setNodes(newNodes);\n  });\n\n  return useMemo(\n    () => ({\n      setNodes,\n      addNodes,\n      setEdges,\n      addEdges,\n      getFlowData,\n      setFlowData,\n      getNodes,\n      getEdges,\n      toObject,\n      zoomIn,\n      zoomOut,\n      zoomTo,\n      getZoom,\n      setViewport,\n      getViewport,\n      fitView,\n      setCenter,\n      fitBounds,\n      screenToFlowPosition,\n      flowToScreenPosition,\n      runAutoLayout,\n      copyNode,\n      pasteNode,\n      pasteNodeSimple,\n      deleteNode,\n      copyFLowNodes,\n      pasteFLowNodes,\n    }),\n    [instance]\n  );\n}\n"
  },
  {
    "path": "packages/x-flow/src/hooks/useNodes.ts",
    "content": "import { shallow } from 'zustand/shallow';\n\nimport { useStore } from '../hooks/useStore';\nimport type { FlowNode, FlowState } from '../models/store';\n\nconst nodesSelector = (state: FlowState) => state.nodes;\n\n/**\n * Hook for getting the current nodes from the store.\n *\n * @public\n * @returns An array of nodes\n */\nexport function useNodes<NodeType extends FlowNode = FlowNode>(): NodeType[] {\n  const nodes = useStore(nodesSelector, shallow) as NodeType[];\n\n  return nodes;\n}\n"
  },
  {
    "path": "packages/x-flow/src/hooks/useStore.ts",
    "content": "import { useContext, useMemo } from 'react';\nimport StoreContext from '../models/context';\nimport { FlowNode, FlowState } from '../models/store';\n\nimport { Edge } from '@xyflow/react';\nimport { useStoreWithEqualityFn } from 'zustand/traditional';\n\nconst useStore = <T = unknown>(\n  selector: (state: FlowState) => T,\n  equalityFn?: (a: T, b: T) => boolean\n) => {\n  const store = useContext(StoreContext);\n\n  if (store === null) {\n    throw new Error(\n      '[XFlow]: Seems like you have not used zustand provider as an ancestor.'\n    );\n  }\n\n  return useStoreWithEqualityFn(store, selector, equalityFn);\n};\n\nconst useStoreApi = <\n  NodeType extends FlowNode = FlowNode,\n  EdgeType extends Edge = Edge\n>() => {\n  const store = useContext(StoreContext);\n\n  if (store === null) {\n    throw new Error(\n      '[XFlow]: Seems like you have not used zustand provider as an ancestor.'\n    );\n  }\n  return useMemo(\n    () => ({\n      getState: store.getState,\n      setState: store.setState,\n      subscribe: store.subscribe,\n      temporal: store.temporal,\n    }),\n    [store]\n  );\n};\n\nexport { useStore, useStoreApi };\n"
  },
  {
    "path": "packages/x-flow/src/hooks/useTemporalStore.ts",
    "content": "import StoreContext from '../models/context';\nimport { useContext } from 'react';\n\nexport const useTemporalStore = () => {\n  const store = useContext(StoreContext);\n\n  if (store === null) {\n    throw new Error(\n      '[XFlow]: Seems like you have not used zustand provider as an ancestor.'\n    );\n  }\n  const temporalStore = store.temporal.getState();\n  // 默认关闭时间机器\n  temporalStore.pause();\n\n  return {\n    ...store.temporal.getState(),\n    record: (callback: () => void) => {\n      temporalStore.resume();\n      callback();\n      temporalStore.pause();\n    },\n  };\n};\n"
  },
  {
    "path": "packages/x-flow/src/index.less",
    "content": "#xflow-container {\n  height: 100%;\n  width: 100%;\n  background: #F0F2F7;\n  position: relative;\n  .react-flow__attribution {\n    display: none;\n  }\n\n  // 移除框选时画布边框的蓝色\n  .react-flow__selection {\n    border: none !important;\n    outline: none !important;\n  }\n\n  // 移除框选矩形区域的边框\n  // .react-flow__nodesselection-rect {\n  //   border: none !important;\n  //   outline: none !important;\n  // }\n\n  // 移除焦点时的蓝色边框\n  &:focus {\n    outline: none !important;\n    border: none !important;\n  }\n}\n"
  },
  {
    "path": "packages/x-flow/src/index.ts",
    "content": "import XFlow from './XFlow';\nimport withProvider from './withProvider';\n\nimport * as nodes from './nodes';\nimport FlowProps from './types';\n\nexport type {\ndefault as FR,\n} from './types';\n\nexport { FlowProvider } from './components/FlowProvider';\nexport { useFlow } from './hooks/useFlow';\nexport { useNodes } from './hooks/useNodes';\nexport { useEdges } from './hooks/useEdges';\n\nexport default withProvider<FlowProps>(XFlow, nodes);\n"
  },
  {
    "path": "packages/x-flow/src/models/context.ts",
    "content": "import { createContext } from 'react';\nimport { FlowProps } from '../types';\nimport { FlowStore } from './store';\n\ntype Config = FlowProps & Record<string,any>\nexport const ConfigContext = createContext(null);\n\nconst StoreContext = createContext<FlowStore | null>(null);\nexport const Provider = StoreContext.Provider;\nexport default StoreContext;\n"
  },
  {
    "path": "packages/x-flow/src/models/event-emitter.tsx",
    "content": "import React from 'react';\nimport { useEventEmitter } from 'ahooks';\nimport type { EventEmitter } from 'ahooks/lib/useEventEmitter';\nimport { createContext, useContext } from 'use-context-selector';\n\nconst EventEmitterContext = createContext<{ eventEmitter: EventEmitter<string> | null }>({\n  eventEmitter: null,\n})\n\nexport const useEventEmitterContextContext = () => useContext(EventEmitterContext)\n\ntype EventEmitterContextProviderProps = {\n  children: React.ReactNode\n}\n\nexport const EventEmitterContextProvider = ({\n  children,\n}: EventEmitterContextProviderProps) => {\n  const eventEmitter = useEventEmitter<string>()\n\n  return (\n    <EventEmitterContext.Provider value={{ eventEmitter }}>\n      {children}\n    </EventEmitterContext.Provider>\n  )\n}\n\nexport default EventEmitterContext\n"
  },
  {
    "path": "packages/x-flow/src/models/store.ts",
    "content": "import { generateCopyNodes, transformNodes, uuid } from '../utils';\nimport {\n  addEdge,\n  applyEdgeChanges,\n  applyNodeChanges,\n  Edge,\n  Node,\n  OnConnect,\n  OnEdgesChange,\n  OnNodesChange,\n} from '@xyflow/react';\nimport isDeepEqual from 'fast-deep-equal';\nimport { temporal } from 'zundo';\nimport { createWithEqualityFn } from 'zustand/traditional';\n\nexport type FlowProps = {\n  nodes?: Node[];\n  edges?: Edge[];\n  panOnDrag?: boolean;\n  layout?: 'LR' | 'TB';\n};\n\nexport type FlowStore = ReturnType<typeof createStore>;\n\nexport type FlowNode = Node;\n\nexport type FlowState = {\n  layout?: 'LR' | 'TB';\n  nodes?: FlowNode[];\n  edges?: Edge[];\n  copyNodes: FlowNode[];\n  copyEdges: Edge[];\n  panOnDrag?: boolean;\n  isAddingNode?: boolean;\n  candidateNode: any;\n  mousePosition: any;\n  copyTimeoutId: NodeJS.Timeout | null; // 添加超时定时器ID\n  onNodesChange: OnNodesChange<FlowNode>;\n  onEdgesChange: OnEdgesChange;\n  onConnect: OnConnect;\n  setNodes: (nodes: FlowNode[], isTransform?: boolean) => void;\n  setEdges: (edges: Edge[]) => void;\n  addNodes: (nodes: FlowNode[]| FlowNode, isTransform?: boolean) => void;\n  addEdges: (edges: Edge[] | Edge) => void;\n  setLayout: (layout: 'LR' | 'TB') => void;\n  setIsAddingNode: (payload: boolean) => void;\n  setCandidateNode: (candidateNode: any) => void;\n  setMousePosition: (mousePosition: any) => void;\n  setCopyTimeoutId: (timeoutId: NodeJS.Timeout | null) => void; // 添加设置超时定时器的方法\n};\n\nconst createStore = (initProps?: Partial<FlowProps>) => {\n  const DEFAULT_PROPS: FlowProps = {\n    layout: 'LR',\n    panOnDrag: true,\n    nodes: [],\n    edges: []\n  };\n\n  return createWithEqualityFn<FlowState>()(\n    temporal(\n      (set, get) => ({\n        ...DEFAULT_PROPS,\n        ...initProps,\n        copyNodes: [],\n        copyEdges: [],\n        isAddingNode: false,\n        candidateNode: null,\n        copyTimeoutId: null, // 添加超时定时器ID初始值\n        // nodeMenus: [],\n        mousePosition: { pageX: 0, pageY: 0, elementX: 0, elementY: 0 },\n        onNodesChange: changes => {\n          set({\n            nodes: applyNodeChanges(changes, get().nodes),\n          });\n        },\n        onEdgesChange: changes => {\n          set({\n            edges: applyEdgeChanges(changes, get().edges),\n          });\n        },\n        onConnect: connection => {\n          set({\n            edges: addEdge(connection, get().edges),\n          });\n        },\n        setNodes: (nodes) => {\n          set({ nodes: transformNodes(nodes) });\n        },\n        setEdges: edges => {\n          set({ edges });\n        },\n        addNodes: (payload, isTransform = true) => {\n          const newNodes = get().nodes.concat(transformNodes(Array.isArray(payload) ? payload : [payload]));\n          set({ nodes: newNodes });\n        },\n        addEdges: payload => {\n          set({ edges: get().edges.concat(payload) });\n        },\n        // setNodeMenus: (nodeMenus: any) => {\n        //   set({ nodeMenus });\n        // },\n        setIsAddingNode: payload => {\n          set({ isAddingNode: payload });\n        },\n        setCandidateNode: candidateNode => {\n          set({ candidateNode });\n        },\n        setMousePosition: (mousePosition: any) => {\n          set({ mousePosition });\n        },\n        setCopyTimeoutId: (timeoutId: NodeJS.Timeout | null) => {\n          set({ copyTimeoutId: timeoutId });\n        },\n        setLayout: (layout: 'LR' | 'TB') => {\n          if (!layout) {\n            return;\n          }\n          set({ layout });\n        }\n      }),\n      {\n        // nodes 和 edges 是引用类型，所以使用深比较\n        equality: isDeepEqual,\n        // 偏函数\n        partialize: state => {\n          const { nodes, edges } = state;\n          return {\n            edges,\n            nodes,\n          };\n        },\n        onSave(pastState, currentState) {\n          // console.log('onSave', pastState, currentState);\n        },\n      }\n    ),\n    Object.is\n  );\n};\n\nexport { createStore };\n"
  },
  {
    "path": "packages/x-flow/src/nodes/index.less",
    "content": ".node-container {\n  border: 2px solid #fff;\n  border-radius: 14px;\n\n  .react-flow__edge-path,\n  .react-flow__connection-path {\n    stroke: #d0d5dc;\n    stroke-width: 2px;\n  }\n}\n\n.node-container-selected {\n  border: 2px solid #296dff;\n\n  .react-flow__handle::after {\n    display: none;\n  }\n}\n\n.react-flow__handle {\n  width: 32px;\n  height: 32px;\n  background: transparent;\n  border-radius: 0;\n  border: none;\n\n  :hover {\n    border: 2px solid #00a952;\n    transform: scale(1.25);\n  }\n}\n\n.react-flow__handle::after {\n  content: '';\n  --tw-bg-opacity: 1;\n  background-color: #2970ff;\n  width: 8px;\n  height: 2px;\n  display: block;\n  margin: 15px 0 0 12px;\n}"
  },
  {
    "path": "packages/x-flow/src/nodes/index.tsx",
    "content": "export { default as StartNode } from './node-start';\nexport { default as EndNode } from './node-end';\nexport { default as CommonNode } from './node-common';\nexport { default as SwitchNode } from './node-switch';\nexport { default as SwitchNodeSettingWidget } from './node-switch/setting';\nexport { default as ParallelNode } from './node-parallel';\nexport { default as ParallelNodeSettingWidget } from './node-parallel/setting';\nexport { default as NoteNode } from './node-note';\n"
  },
  {
    "path": "packages/x-flow/src/nodes/node-common/index.less",
    "content": ".custom-node-start {\n  width: 240px;\n  padding: 0 12px;\n  background: #fff;\n  border-radius: 12px;\n\n  .title {\n    display: flex;\n    height: 50px;\n    align-items: center;\n    span {\n      font-weight: bold;\n      margin-left: 8px;\n    }\n  }\n}"
  },
  {
    "path": "packages/x-flow/src/nodes/node-common/index.tsx",
    "content": "import React, { memo, useContext } from 'react';\nimport NodeContainer from '../../components/NodeContainer';\nimport { ConfigContext } from '../../models/context';\nimport { getColorfulModeBackground } from '../../utils';\n\nexport default memo((props: any) => {\n  const { type, onClick, data ,id} = props;\n  const { settingMap, widgets, iconFontUrl,globalConfig, openColorfulMode } = useContext(ConfigContext);\n  const nodeSetting = settingMap[type] || {};\n  const NodeWidget = widgets[nodeSetting?.nodeWidget] || undefined;\n  const nodeDescription = nodeSetting?.description || '';\n  const gradientHeight = nodeSetting?.gradientHeight;\n\n  const hideDesc = nodeSetting?.nodePanel?.hideDesc ?? globalConfig?.nodePanel?.hideDesc ?? false;\n  const hideTitleTips = globalConfig?.nodeView?.hideTitleTips ?? false;\n  const SVGWidget = widgets[nodeSetting?.iconSvg]; // 自定义面板配置组件\n\n  return (\n    <NodeContainer\n      className='custom-node-code'\n      title={data?.title || nodeSetting.title}//  || nodeSetting.title 去除默认值\n      icon={{\n        type: nodeSetting?.icon?.type,\n        style: { fontSize: 14, color: '#fff' },\n        bgColor: nodeSetting?.icon?.bgColor || '#F79009',\n      }}\n      onClick={onClick}\n      hideDesc={hideDesc}\n      desc={data?.desc}\n      NodeWidget={NodeWidget ? <NodeWidget data={data} id={id} nodeType={type}/> : undefined}\n      iconFontUrl={iconFontUrl}\n      description={nodeDescription} // 不允许用户更改的节点描述\n      iconSvg={SVGWidget ? <SVGWidget setting={nodeSetting} /> : false}\n      hideTitleTips={hideTitleTips}\n      nodeSettingTitle={nodeSetting.title}\n      gradientHeight={gradientHeight}\n      style={{...getColorfulModeBackground(nodeSetting?.icon?.bgColor, openColorfulMode)}}\n    />\n  );\n});\n"
  },
  {
    "path": "packages/x-flow/src/nodes/node-end/index.less",
    "content": ".custom-node-start {\n  width: 240px;\n  padding: 0 12px;\n  background: #fff;\n  border-radius: 12px;\n\n  .title {\n    display: flex;\n    height: 50px;\n    align-items: center;\n    span {\n      font-weight: bold;\n      margin-left: 8px;\n    }\n  }\n}"
  },
  {
    "path": "packages/x-flow/src/nodes/node-end/index.tsx",
    "content": "import React, { memo, useContext } from 'react';\nimport NodeContainer from '../../components/NodeContainer';\nimport { ConfigContext } from '../../models/context';\nimport { getColorfulModeBackground } from '../../utils';\n\nexport default memo((props: any) => {\n  const { onClick, type, data,id } = props;\n  const { settingMap, widgets, iconFontUrl, globalConfig, openColorfulMode } = useContext(ConfigContext);\n  const nodeSetting = settingMap[type] || {};\n  const NodeWidget = widgets[nodeSetting?.nodeWidget] || undefined;\n  const nodeDescription = nodeSetting?.description || '';\n  const hideDesc = nodeSetting?.nodePanel?.hideDesc ?? globalConfig?.nodePanel?.hideDesc ?? false;\n  const gradientHeight = nodeSetting?.gradientHeight;\n  const hideTitleTips = globalConfig?.nodeView?.hideTitleTips ?? false;\n  const SVGWidget = widgets[nodeSetting?.iconSvg]; // 自定义面板配置组件\n\n  return (\n    <NodeContainer\n      className='custom-node-code'\n      title={data?.title || nodeSetting?.title || '结束'}\n      icon={{\n        type: nodeSetting?.icon?.type || 'icon-end',\n        style: { fontSize: 14, color: '#fff' },\n        bgColor: nodeSetting?.icon?.bgColor || '#F79009',\n      }}\n      onClick={onClick}\n      hideDesc={hideDesc}\n      desc={data?.desc}\n      NodeWidget={NodeWidget ? <NodeWidget data={data}  id={id} nodeType={type} /> : undefined}\n      iconFontUrl={iconFontUrl}\n      description={nodeDescription} // 不允许用户更改的节点描述\n      iconSvg={SVGWidget ? <SVGWidget setting={nodeSetting} /> : false}\n      hideTitleTips={hideTitleTips}\n      nodeSettingTitle={nodeSetting.title||'结束'}\n      gradientHeight={gradientHeight}\n      style={{...getColorfulModeBackground(nodeSetting?.icon?.bgColor, openColorfulMode)}}\n    />\n  );\n});\n"
  },
  {
    "path": "packages/x-flow/src/nodes/node-note/index.less",
    "content": ".node-note-wrap {\n  .node-note-braft {\n    border: 1px solid rgb(132, 202, 255);\n    border-radius: 8px;\n    width: 240px;\n    height: 88px;\n    background: rgb(239, 248, 255);\n\n    .node-note-braft-content {\n      height: 100px !important;\n      overflow-y: auto;\n    }\n\n    .node-note-braft-control {\n      .control-item.button {\n        font-size: 12px;\n        min-width: 8px;\n        padding: 0px 6px;\n      }\n    }\n  }\n\n  .bf-dropdown .dropdown-content-inner {\n    background-color: rgb(239, 248, 255);\n    color: black;\n    border-radius: 8px;\n  }\n  .text-color-dropdown .bf-color-switch-buttons button {\n    color: black;\n    border-bottom: 1px solid #e4e9ec;\n  }\n  .text-color-dropdown .bf-color-switch-buttons button.active {\n    border-bottom-color: #3498db;\n    color: #3498db;\n  }\n  .bf-dropdown .dropdown-content .menu-item:not(.active) {\n    color: black;\n    border-bottom: 1px solid #e4e9ec;\n  }\n  .bf-font-sizes li {\n    color: black;\n  }\n  .bf-dropdown .dropdown-content .dropdown-arrow {\n    background-color: rgb(239, 248, 255);\n  }\n}\n"
  },
  {
    "path": "packages/x-flow/src/nodes/node-note/index.tsx",
    "content": "import BraftEditor from 'braft-editor';\nimport 'braft-editor/dist/index.css';\nimport { debounce } from 'lodash';\nimport React, { memo, useRef, useState } from 'react';\nimport { shallow } from 'zustand/shallow';\nimport { useStore } from '../../hooks/useStore';\nimport './index.less';\n\nexport default memo((props: any) => {\n  const { onClick, type, data, id } = props;\n  const { nodes, setNodes } = useStore(\n    s => ({\n      nodes: s.nodes,\n      setNodes: s.setNodes,\n    }),\n    shallow\n  );\n\n  const editorRef = useRef(null);\n  const [editorState, setEditorState] = useState(\n    BraftEditor.createEditorState(data?.value)\n  );\n\n  // 当编辑器内容发生变化时触发\n  const handleEditorChange = newEditorState => {\n    setEditorState(newEditorState);\n    handleNodeValueChange({ value: newEditorState.toHTML() });\n  };\n\n  const handleNodeValueChange = debounce((data: any) => {\n    for (let node of nodes) {\n      if (node.id === id) {\n        node.data = {\n          ...node?.data,\n          ...data,\n        };\n        break;\n      }\n    }\n    setNodes([...nodes], false);\n  }, 200);\n\n  return (\n    <div\n      className=\"node-note-wrap\"\n      onMouseDown={e => e.stopPropagation()}\n      onClick={e => e.stopPropagation()}\n    >\n      <BraftEditor\n        ref={editorRef}\n        value={editorState}\n        onChange={handleEditorChange}\n        placeholder=\"请输入内容...\"\n        className=\"nodrag nopan nowheel node-note-braft\"\n        language=\"zh\"\n        style={{\n          width: '240px',\n          height: '160px',\n          backgroundColor: 'rgb(239, 248, 255)',\n          border: '1px solid rgb(132, 202, 255)',\n          borderRadius: '8px',\n          cursor: 'text',\n          userSelect: 'text',\n        }}\n        controls={[\n          // 'headings',\n          // 'font-size',\n          'bold',\n          'italic',\n          'underline',\n          'text-color',\n          // 'link',\n          // 'media',\n          'strike-through',\n          'list-ol',\n        ]}\n        contentStyle={{ height: '100px', overflowY: 'auto' }}\n        contentClassName=\"node-note-braft-content\"\n        controlBarClassName=\"node-note-braft-control\"\n      />\n    </div>\n  );\n});\n"
  },
  {
    "path": "packages/x-flow/src/nodes/node-parallel/ParallelBuildInNodeWidget.tsx",
    "content": "import { Space } from 'antd';\nimport classNames from 'classnames';\nimport React, { memo } from 'react';\nimport { shallow } from 'zustand/shallow';\nimport SourceHandle from '../../components/CustomNode/sourceHandle';\nimport TextEllipsis from '../../components/TextEllipsis';\nimport { useStore } from '../../hooks/useStore';\nimport { uuid } from '../../utils';\nimport './index.less';\n\nexport default memo((props: any) => {\n  const {\n    data,\n    position,\n    isConnectable,\n    selected,\n    isHovered,\n    handleAddNode,\n    CustomNodeWidget,\n    isSwitchBottom,\n    nodeSetting,\n  } = props;\n\n  const { parallelExtra } = nodeSetting;\n\n  const { nodes, edges } = useStore(\n    (state: any) => ({\n      nodes: state.nodes,\n      edges: state.edges,\n      mousePosition: state.mousePosition,\n      addNodes: state.addNodes,\n      addEdges: state.addEdges,\n      onEdgesChange: state.onEdgesChange,\n    }),\n    shallow\n  );\n\n  const renderTitle = (item, index) => {\n    const defTitle = item?.title || `事件${index}`;\n    const title = parallelExtra?.titleKey\n      ? item[parallelExtra?.titleKey]\n      : defTitle;\n    return (\n      <div className=\"item-header\">\n        <div className=\"item-title\">\n          {title && (\n            <TextEllipsis\n              text={title}\n              // rows={1}\n              // type=\"paragraph\"\n              className=\"item-content-in\"\n            />\n          )}\n        </div>\n        <SourceHandle\n          position={position}\n          isConnectable={\n            (edges || [])?.filter(flow => flow?.sourceHandle === item?._id)\n              ?.length === 0\n          }\n          selected={selected}\n          isHovered={isHovered}\n          handleAddNode={data => {\n            handleAddNode(data, item?._id);\n          }}\n          id={item?._id}\n          className=\"item-handle\"\n          isConnected={\n            (edges || [])?.filter(flow => flow?.sourceHandle === item?._id)\n              ?.length > 0\n          }\n          nodeType={'Parallel'}\n        />\n      </div>\n    );\n  };\n\n  const renderContent = (item, index) => {\n    const value = parallelExtra?.valueKey\n      ? item[parallelExtra?.valueKey]\n      : item?.value;\n    return (\n      <div className=\"item-content\">\n        {CustomNodeWidget ? (\n          <CustomNodeWidget data={item} index={index} />\n        ) : (\n          <div>\n            {value && (\n              <div className=\"item-content-in\">\n                <TextEllipsis text={value} rows={5} type=\"paragraph\" />\n              </div>\n            )}\n          </div>\n        )}\n      </div>\n    );\n  };\n\n  return (\n    <Space\n      direction={isSwitchBottom ? 'horizontal' : 'vertical'}\n      className={classNames('node-parallel-widget', {\n        'node-parallel-widget-bottom': isSwitchBottom,\n      })}\n      size={5}\n    >\n      {(data?.list || [{ _id: `id_${uuid()}` }, { _id: `id_${uuid()}` }])?.map(\n        (item, index) => (\n          <div\n            className={classNames('node-parallel-widget-item', {\n              'node-parallel-bottom-item': isSwitchBottom,\n            })}\n            key={index}\n          >\n            {isSwitchBottom ? (\n              <>\n                {renderContent(item, index)}\n                {renderTitle(item, index)}\n              </>\n            ) : (\n              <>\n                {renderTitle(item, index)}\n                {renderContent(item, index)}\n              </>\n            )}\n          </div>\n        )\n      )}\n    </Space>\n  );\n});\n"
  },
  {
    "path": "packages/x-flow/src/nodes/node-parallel/index.less",
    "content": ".custom-node-start {\n  width: 240px;\n  padding: 0 12px;\n  background: #fff;\n  border-radius: 12px;\n\n  .title {\n    display: flex;\n    height: 50px;\n    align-items: center;\n\n    span {\n      font-weight: bold;\n      margin-left: 8px;\n    }\n  }\n}\n\n.custom-node-switch-setting {\n\n  .fr-list-simple,\n  .ant-form-item,\n  .fr-panel,\n  .fr-inline-container {\n    width: 100%;\n  }\n}\n\n.node-parallel-widget {\n  width: 100%;\n\n  .item-header {\n    position: relative;\n\n    .item-title {\n      text-align: right;\n      font-weight: 600;\n      min-height: 28px;\n\n      .ant-typography {\n        margin-bottom: 0;\n      }\n    }\n\n    .item-handle {\n      right: -12px;\n      top: 9px;\n\n      .xflow-node-add-box {\n        right: 4px;\n      }\n    }\n  }\n\n  .item-content {\n    margin: 5px 0;\n\n    .item-content-in {\n      background-color: #f2f4f7;\n      border-radius: 6px;\n      padding: 6px;\n      word-wrap: break-word;\n      text-align: justify;\n    }\n  }\n}\n\n// TB 模式样式：\n.node-parallel-widget-bottom {\n  width: 100%;\n  align-items: end;\n\n  .node-parallel-bottom-item {\n    width: 100px;\n\n    .item-header {\n      .item-title {\n        text-align: center;\n        font-weight: 600;\n        min-width: 50px;\n\n        .ant-typography {\n          margin-bottom: 0;\n        }\n      }\n\n      .item-handle {\n        top: 0px;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "packages/x-flow/src/nodes/node-parallel/index.tsx",
    "content": "import { Position } from '@xyflow/react';\nimport classNames from 'classnames';\nimport React, { memo, useContext } from 'react';\nimport NodeContainer from '../../components/NodeContainer';\nimport { ConfigContext } from '../../models/context';\nimport ParallelBuildInNodeWidget from './ParallelBuildInNodeWidget';\nimport { getColorfulModeBackground } from '../../utils';\n\n\nexport default memo((props: any) => {\n  const {\n    onClick,\n    type,\n    data,\n    position,\n    isConnectable,\n    selected,\n    isHovered,\n    handleAddNode,\n  } = props;\n  const { settingMap, widgets, iconFontUrl, globalConfig, openColorfulMode } = useContext(ConfigContext);\n  const nodeSetting = settingMap[type] || {};\n  const NodeWidget = widgets[nodeSetting?.nodeWidget] || undefined;\n  const nodeDescription = nodeSetting?.description || '';\n  const gradientHeight = nodeSetting?.gradientHeight;\n  const hideDesc =\n    nodeSetting?.nodePanel?.hideDesc ??\n    globalConfig?.nodePanel?.hideDesc ??\n    false;\n  const hideTitleTips = globalConfig?.nodeView?.hideTitleTips ?? false;\n  const isSwitchBottom = position === Position.Bottom;\n  const SVGWidget = widgets[nodeSetting?.iconSvg]; // 自定义面板配置组件\n\n  return (\n    <NodeContainer\n      className={classNames('custom-node-code', {\n        'switch-node-code-bottom': isSwitchBottom\n      })}\n      title={data?.title || nodeSetting?.title || 'parallel'}\n      icon={{\n        type: nodeSetting?.icon?.type || 'icon-parallel',\n        style: { fontSize: 14, color: '#fff' },\n        bgColor: nodeSetting?.icon?.bgColor || '#06AED4',\n      }}\n      onClick={onClick}\n      hideDesc={hideDesc}\n      desc={data?.desc}\n      iconFontUrl={iconFontUrl}\n      gradientHeight={gradientHeight}\n      NodeWidget={\n        <ParallelBuildInNodeWidget\n          data={data}\n          position={position}\n          isConnectable={isConnectable}\n          selected={selected}\n          isHovered={isHovered}\n          handleAddNode={handleAddNode}\n          CustomNodeWidget={NodeWidget}\n          isSwitchBottom={isSwitchBottom}\n          nodeSetting={nodeSetting}\n        />\n      }\n      description={nodeDescription} // 不允许用户更改的节点描述\n      iconSvg={SVGWidget ? <SVGWidget setting={nodeSetting} /> : false}\n      hideTitleTips={hideTitleTips}\n      isSwitchBottom={isSwitchBottom}\n      nodeSettingTitle={nodeSetting?.title || 'parallel'}\n      style={{...getColorfulModeBackground(nodeSetting?.icon?.bgColor, openColorfulMode)}}\n    />\n  );\n});\n"
  },
  {
    "path": "packages/x-flow/src/nodes/node-parallel/setting/index.tsx",
    "content": "import FormRender, { Schema, useForm } from 'form-render';\nimport React, { memo, useEffect } from 'react';\nimport '../index.less';\nimport { safeJsonStringify } from '../../../utils';\n\ninterface INodeSwitchSettingPorps {\n  onChange: (val: any) => void;\n  value: any;\n  readOnly:boolean\n}\n\nconst schema: Schema = {\n  type: 'object',\n  displayType:'row',\n  properties: {\n    list: {\n      type: 'array',\n      widget: 'simpleList',\n      props: {\n        hideCopy: true,\n        hideMove: true,\n      },\n      items: {\n        type: 'object',\n        properties: {\n          title: {\n            title: '标题',\n            type: 'string',\n            props: {\n              allowClear:true\n            },\n            span:6\n          },\n          value: {\n            title: '事件',\n            type: 'string',\n            props: {\n              allowClear: true\n            },\n            span: 6\n          },\n        },\n      },\n    },\n  },\n};\n\nexport default memo((props: INodeSwitchSettingPorps) => {\n  const form = useForm();\n  const { onChange, value } = props;\n\n  const watch = {\n    '#': (allValues: any) => {\n      onChange({ ...allValues });\n    },\n  };\n\n  useEffect(() => {\n    form.resetFields();\n    form.setValues(value || {});\n  }, [safeJsonStringify(value)]);\n\n  return (\n    <FormRender\n      schema={schema}\n      form={form}\n      watch={watch}\n      size={'small'}\n      className=\"custom-node-switch-setting\"\n      readOnly={props.readOnly}\n    />\n  );\n});\n"
  },
  {
    "path": "packages/x-flow/src/nodes/node-start/index.less",
    "content": ".custom-node-start {\n  width: 240px;\n  padding: 0 12px;\n  background: #fff;\n  border-radius: 12px;\n\n  .title {\n    display: flex;\n    height: 50px;\n    align-items: center;\n    span {\n      font-weight: bold;\n      margin-left: 8px;\n    }\n  }\n}"
  },
  {
    "path": "packages/x-flow/src/nodes/node-start/index.tsx",
    "content": "import React, { memo, useContext } from 'react';\nimport NodeContainer from '../../components/NodeContainer';\nimport { ConfigContext } from '../../models/context';\nimport { getColorfulModeBackground } from '../../utils';\n\nexport default memo((props: any) => {\n  const { onClick, type, data,id } = props;\n  const { settingMap, widgets, iconFontUrl, globalConfig, openColorfulMode } =\n    useContext(ConfigContext);\n  const nodeSetting = settingMap[type] || {};\n  const NodeWidget = widgets[nodeSetting?.nodeWidget] || undefined;\n  const gradientHeight = nodeSetting?.gradientHeight;\n  const nodeDescription = nodeSetting?.description || '';\n  const hideDesc =\n    nodeSetting?.nodePanel?.hideDesc ??\n    globalConfig?.nodePanel?.hideDesc ??\n    false;\n  const hideTitleTips = globalConfig?.nodeView?.hideTitleTips ?? false;\n  const SVGWidget = widgets[nodeSetting?.iconSvg]; // 自定义面板配置组件\n\n  return (\n    <NodeContainer\n      className='custom-node-code'\n      title={data?.title || nodeSetting?.title || '开始'}\n      icon={{\n        type: nodeSetting?.icon?.type || 'icon-start',\n        style: { fontSize: 14, color: '#fff' },\n        bgColor: nodeSetting?.icon?.bgColor || '#17B26A',\n      }}\n      onClick={onClick}\n      hideDesc={hideDesc}\n      desc={data?.desc}\n      NodeWidget={NodeWidget ? <NodeWidget data={data} id={id} nodeType={type} /> : undefined}\n      iconFontUrl={iconFontUrl}\n      description={nodeDescription} // 不允许用户更改的节点描述\n      iconSvg={SVGWidget ? <SVGWidget setting={nodeSetting} /> : false}\n      hideTitleTips={hideTitleTips}\n      nodeSettingTitle={nodeSetting?.title || '开始'}\n      gradientHeight={gradientHeight}\n      style={{...getColorfulModeBackground(nodeSetting?.icon?.bgColor, openColorfulMode)}}\n    />\n  );\n});\n"
  },
  {
    "path": "packages/x-flow/src/nodes/node-switch/SwitchBuildInNodeWidget.tsx",
    "content": "import { Space } from 'antd';\nimport classNames from 'classnames';\nimport React, { memo } from 'react';\nimport { shallow } from 'zustand/shallow';\nimport SourceHandle from '../../components/CustomNode/sourceHandle';\nimport TextEllipsis from '../../components/TextEllipsis';\nimport { useStore } from '../../hooks/useStore';\nimport { uuid } from '../../utils';\nimport './index.less';\n\nexport default memo((props: any) => {\n  const {\n    data,\n    position,\n    isConnectable,\n    selected,\n    isHovered,\n    handleAddNode,\n    CustomNodeWidget,\n    isSwitchBottom,\n    nodeSetting,\n    id,\n  } = props;\n  const { switchExtra } = nodeSetting;\n\n  const { nodes, edges } = useStore(\n    (state: any) => ({\n      nodes: state.nodes,\n      edges: state.edges,\n    }),\n    shallow\n  );\n\n  const renderTitle = (item, index) => {\n    const defTitle = item?.title || `条件${index}`;\n    const title = switchExtra?.titleKey\n      ? item[switchExtra?.titleKey]\n      : defTitle;\n    return (\n      <div className=\"item-header\">\n        <div className=\"item-title\">{title}</div>\n        <SourceHandle\n          position={position}\n          isConnectable={\n            (edges || [])?.filter(flow => flow?.sourceHandle === item?._id)\n              ?.length === 0\n          }\n          selected={selected}\n          isHovered={isHovered}\n          handleAddNode={data => {\n            handleAddNode(data, item?._id);\n          }}\n          id={item?._id}\n          className=\"item-handle\"\n          isConnected={\n            (edges || [])?.filter(flow => flow?.sourceHandle === item?._id)\n              ?.length > 0\n          }\n          nodeType={'switch'}\n        />\n      </div>\n    );\n  };\n\n  const renderContent = (item, index) => {\n    const value = switchExtra?.valueKey\n      ? item[switchExtra?.valueKey]\n      : item?.value;\n\n    return (\n      <div className=\"item-content\">\n        {CustomNodeWidget ? (\n          <CustomNodeWidget data={item} index={index} />\n        ) : (\n          <div>\n            {value && (\n              <div className=\"item-content-in\">\n                <TextEllipsis text={value} rows={5} type=\"paragraph\" />\n              </div>\n            )}\n          </div>\n        )}\n      </div>\n    );\n  };\n\n  return (\n    <Space\n      direction={isSwitchBottom ? 'horizontal' : 'vertical'}\n      className={classNames('node-switch-widget', {\n        'node-switch-widget-bottom': isSwitchBottom,\n      })}\n      size={5}\n    >\n      {(data?.list || [{ _id: `id_${uuid()}` }])?.map((item, index) => (\n        <div\n          className={classNames('node-switch-widget-item', {\n            'node-switch-bottom-item': isSwitchBottom,\n          })}\n          key={index}\n        >\n          {isSwitchBottom ? (\n            <>\n              {renderContent(item, index)}\n              {renderTitle(item, index)}\n            </>\n          ) : (\n            <>\n              {renderTitle(item, index)}\n              {renderContent(item, index)}\n            </>\n          )}\n        </div>\n      ))}\n      {!switchExtra?.hideElse && (\n        <div\n          className={classNames('node-switch-widget-item', {\n            'node-switch-bottom-item': isSwitchBottom,\n          })}\n        >\n          <div className=\"item-header\">\n            <div className=\"item-title\">默认</div>\n            <SourceHandle\n              position={position}\n              isConnectable={\n                (edges || [])?.filter(\n                  flow =>\n                    flow?.sourceHandle === 'id_else' && flow?.source === id\n                )?.length === 0\n              }\n              selected={selected}\n              isHovered={isHovered}\n              handleAddNode={data => {\n                handleAddNode(data, 'id_else');\n              }}\n              className=\"item-handle\"\n              id={'id_else'}\n              isConnected={\n                (edges || [])?.filter(\n                  flow =>\n                    flow?.sourceHandle === 'id_else' && flow?.source === id\n                )?.length > 0\n              }\n              nodeType={'Switch'}\n            />\n          </div>\n        </div>\n      )}\n    </Space>\n  );\n});\n"
  },
  {
    "path": "packages/x-flow/src/nodes/node-switch/index.less",
    "content": ".custom-node-start {\n  width: 240px;\n  padding: 0 12px;\n  background: #fff;\n  border-radius: 12px;\n\n  .title {\n    display: flex;\n    height: 50px;\n    align-items: center;\n\n    span {\n      font-weight: bold;\n      margin-left: 8px;\n    }\n  }\n}\n\n.custom-node-switch-setting {\n  .fr-list-simple,\n  .ant-form-item,\n  .fr-panel,\n  .fr-inline-container {\n    width: 100%;\n  }\n}\n\n.node-switch-widget {\n  width: 100%;\n\n  .item-header {\n    position: relative;\n\n    .item-title {\n      text-align: right;\n      font-weight: 600;\n      padding-right: 5px;\n    }\n\n    .item-handle {\n      right: -12px;\n    }\n  }\n\n  .item-content {\n    margin: 5px 0;\n\n    .item-content-in {\n      background-color: #f2f4f7;\n      border-radius: 6px;\n      padding: 6px;\n      word-wrap: break-word;\n      text-align: justify;\n    }\n  }\n}\n\n// TB 模式样式：\n.node-switch-widget-bottom {\n  width: 100%;\n  align-items: end;\n\n  .node-switch-bottom-item {\n    width: 100px;\n\n    .item-header {\n      .item-title {\n        text-align: center;\n        font-weight: 600;\n        padding-bottom: 8px;\n      }\n\n      .item-handle {\n        top: 1px;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "packages/x-flow/src/nodes/node-switch/index.tsx",
    "content": "import { Position } from '@xyflow/react';\nimport classNames from 'classnames';\nimport React, { memo, useContext } from 'react';\nimport NodeContainer from '../../components/NodeContainer';\nimport { ConfigContext } from '../../models/context';\nimport SwitchBuildInNodeWidget from './SwitchBuildInNodeWidget';\nimport { getColorfulModeBackground } from '../../utils';\n\nimport './index.less';\n\nexport default memo((props: any) => {\n  const {\n    onClick,\n    type,\n    data,\n    position,\n    isConnectable,\n    selected,\n    isHovered,\n    handleAddNode,\n    id,\n  } = props;\n  const { settingMap, widgets, iconFontUrl, globalConfig, openColorfulMode } =\n    useContext(ConfigContext);\n  const nodeSetting = settingMap[type] || {};\n  const NodeWidget = widgets[nodeSetting?.nodeWidget] || undefined;\n  const nodeDescription = nodeSetting?.description || '';\n  const gradientHeight = nodeSetting?.gradientHeight;\n  const hideDesc =\n    nodeSetting?.nodePanel?.hideDesc ??\n    globalConfig?.nodePanel?.hideDesc ??\n    false;\n  const hideTitleTips = globalConfig?.nodeView?.hideTitleTips ?? false;\n  const isSwitchBottom = position === Position.Bottom;\n  const SVGWidget = widgets[nodeSetting?.iconSvg]; // 自定义面板配置组件\n\n\n\n  return (\n    <NodeContainer\n      className={classNames('custom-node-code', {\n        'switch-node-code-bottom': isSwitchBottom,\n      })}\n      title={data?.title || nodeSetting?.title || 'Switch'}\n      icon={{\n        type: nodeSetting?.icon?.type || 'icon-switch',\n        style: { fontSize: 14, color: '#fff' },\n        bgColor: nodeSetting?.icon?.bgColor || '#06AED4',\n      }}\n      onClick={onClick}\n      hideDesc={hideDesc}\n      desc={data?.desc}\n      iconFontUrl={iconFontUrl}\n      NodeWidget={\n        <SwitchBuildInNodeWidget\n          data={data}\n          position={position}\n          isConnectable={isConnectable}\n          selected={selected}\n          isHovered={isHovered}\n          handleAddNode={handleAddNode}\n          CustomNodeWidget={NodeWidget}\n          isSwitchBottom={isSwitchBottom}\n          nodeSetting={nodeSetting}\n          id={id}\n        />\n      }\n      description={nodeDescription} // 不允许用户更改的节点描述\n      iconSvg={SVGWidget ? <SVGWidget setting={nodeSetting} /> : false}\n      hideTitleTips={hideTitleTips}\n      isSwitchBottom={isSwitchBottom}\n      nodeSettingTitle={nodeSetting?.title || 'Switch'}\n      gradientHeight={gradientHeight}\n      style={{...getColorfulModeBackground(nodeSetting?.icon?.bgColor, openColorfulMode)}}\n    />\n  );\n});\n"
  },
  {
    "path": "packages/x-flow/src/nodes/node-switch/setting/index.tsx",
    "content": "import FormRender, { Schema, useForm } from 'form-render';\nimport React, { memo, useEffect } from 'react';\nimport '../index.less';\nimport { safeJsonStringify } from '../../../utils';\n\ninterface INodeSwitchSettingPorps {\n  onChange: (val: any) => void;\n  value: any;\n  readOnly:boolean\n}\n\nconst schema: Schema = {\n  type: 'object',\n  span: 24,\n  displayType: 'row',\n  properties: {\n    list: {\n      type: 'array',\n      widget: 'simpleList',\n      display: 'block',\n      props: {\n        hideCopy: true,\n        hideMove: true,\n      },\n      items: {\n        type: 'object',\n        properties: {\n          value: {\n            title: '条件',\n            type: 'string',\n          },\n        },\n      },\n    },\n  },\n};\n\nexport default memo((props: INodeSwitchSettingPorps) => {\n  const form = useForm();\n  const { onChange, value } = props;\n\n  const watch = {\n    '#': (allValues: any) => {\n      onChange({ ...allValues });\n    },\n  };\n\n  useEffect(() => {\n    form.resetFields();\n    form.setValues(value || {});\n  }, [safeJsonStringify(value)]);\n\n  return (\n    <FormRender\n      schema={schema}\n      form={form}\n      watch={watch}\n      size={'small'}\n      className=\"custom-node-switch-setting\"\n      disabled={props.readOnly}\n    />\n  );\n});\n"
  },
  {
    "path": "packages/x-flow/src/operator/Control/index.less",
    "content": ".fai-reactflow-control {\n  display: flex;\n  align-items: center;\n  padding: 2px 1px;\n  border-radius: 8px;\n  border: 0.5px solid #f3f4f6;\n  background-color: #fff;\n  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);\n  color: #667085;\n\n  .ant-btn {\n    display: inline-flex;\n    align-items: center;\n    justify-content: center;\n  }\n}\n\n// .control-item {\n//   display: flex;\n//   align-items: center;\n//   justify-content: center;\n//   width: 2rem;\n//   height: 2rem;\n//   border-radius: 0.5rem;\n//   cursor: pointer;\n\n//   &.inactive:hover {\n//     background-color: rgba(0, 0, 0, 0.05);\n//     color: #374151;\n//   }\n\n//   &.active {\n//     background-color: #ecfdf5;\n//     color: #10b981;\n//   }\n\n//   &.disabled {\n//     cursor: not-allowed;\n//     opacity: 0.5;\n//   }\n// }\n\n.separator {\n  margin: 0 4px;\n  width: 1px;\n  height:18px;\n  background-color: #e5e7eb;\n}\n"
  },
  {
    "path": "packages/x-flow/src/operator/Control/index.tsx",
    "content": "import { Button, Tooltip } from 'antd';\nimport type { MouseEvent } from 'react';\nimport React, { memo, useContext } from 'react';\nimport IconView from '../../components/IconView';\nimport NodeSelectPopover from '../../components/NodesPopover';\nimport { useStore, useStoreApi } from '../../hooks/useStore';\nimport { ConfigContext } from '../../models/context';\nimport { useEventEmitterContextContext } from '../../models/event-emitter';\nimport { useFullscreen } from 'ahooks';\nimport './index.less';\n\nconst Control = (props: any) => {\n  const { addNode, xflowRef } = props;\n  const [isFullscreen, { toggleFullscreen }] = useFullscreen(xflowRef);\n  const { globalConfig, readOnly } = useContext(ConfigContext);\n\n  const hideAddNode = globalConfig?.controls?.hideAddNode ?? false;\n  const hideAnnotate = globalConfig?.controls?.hideAnnotate ?? false;\n  const hideAutoLayout = globalConfig?.controls?.hideAutoLayout ?? false;\n  const hideFullscreen = globalConfig?.controls?.hideFullscreen ?? false;\n  const hideInteractionMode =\n    globalConfig?.controls?.hideInteractionMode ?? false;\n\n  const { setIsAddingNode, panOnDrag } = useStore(s => ({\n    setIsAddingNode: s.setIsAddingNode,\n    panOnDrag: s.panOnDrag,\n  }));\n\n  const addNote = (e: MouseEvent<HTMLDivElement>) => {\n    e.stopPropagation();\n    setIsAddingNode(true);\n    addNode({ _nodeType: 'Note' });\n  };\n  const storeApi = useStoreApi();\n\n  const { eventEmitter } = useEventEmitterContextContext();\n\n  const handleInteractionModeChange = panOnDrag => {\n    storeApi.setState({ panOnDrag });\n  };\n\n  return (\n    <div className=\"fai-reactflow-control\">\n      {!hideAddNode && !readOnly && (\n        <NodeSelectPopover addNode={addNode}>\n          <Tooltip\n            title=\"添加节点\"\n            getPopupContainer={() =>\n              document.getElementById('xflow-container') as HTMLElement\n            }\n          >\n            <Button\n              type=\"text\"\n              icon={<IconView type=\"icon-add-circle\" className=\"icon\" />}\n            />\n          </Tooltip>\n        </NodeSelectPopover>\n      )}\n      {!hideAnnotate && !readOnly && (\n        <Tooltip\n          title=\"添加注释\"\n          getPopupContainer={() =>\n            document.getElementById('xflow-container') as HTMLElement\n          }\n        >\n          <Button\n            type=\"text\"\n            icon={\n              <IconView type=\"icon-sticky-note-add-line\" className=\"icon\" />\n            }\n            onClick={addNote}\n          />\n        </Tooltip>\n      )}\n      {(!(hideAddNode && hideAnnotate )&& !hideInteractionMode )&& !readOnly && (\n        <div className=\"separator\"></div>\n      )}\n      {!hideInteractionMode && (\n        <Tooltip\n          title=\"指针模式\"\n          getPopupContainer={() =>\n            document.getElementById('xflow-container') as HTMLElement\n          }\n        >\n          <Button\n            type=\"text\"\n            icon={\n              <IconView\n                type=\"icon-zhizhen\"\n                className=\"icon\"\n                style={{\n                  color: !panOnDrag ? 'rgb(21,94,239)' : '#666F83',\n                  fontSize: '14px',\n                }}\n              />\n            }\n            onClick={() => panOnDrag && handleInteractionModeChange(false)}\n            style={{ backgroundColor: !panOnDrag ? 'rgb(239,244,255)' : '' }}\n          />\n        </Tooltip>\n      )}\n      {!hideInteractionMode && (\n        <Tooltip\n          title=\"手模式\"\n          getPopupContainer={() =>\n            document.getElementById('xflow-container') as HTMLElement\n          }\n        >\n          <Button\n            type=\"text\"\n            icon={\n              <IconView\n                type=\"icon-xianxingshouzhangtubiao\"\n                className=\"icon\"\n                style={{\n                  color: panOnDrag ? 'rgb(21,94,239)' : '#666F83',\n                }}\n              />\n            }\n            onClick={() => !panOnDrag && handleInteractionModeChange(true)}\n            style={{\n              backgroundColor: panOnDrag ? 'rgb(239,244,255)' : '',\n              marginLeft: '1px',\n            }}\n          />\n        </Tooltip>\n      )}\n      {(!hideAutoLayout || !hideFullscreen) && ((!hideInteractionMode || !hideAddNode || !hideAnnotate) && !readOnly&&(\n        <div className=\"separator\"></div>\n      ))}\n      {!hideAutoLayout && (\n        <Tooltip\n          title=\"整理画布\"\n          getPopupContainer={() =>\n            document.getElementById('xflow-container') as HTMLElement\n          }\n        >\n          <Button\n            type=\"text\"\n            icon={<IconView type=\"icon-function-add-line1\" className=\"icon\" />}\n            onClick={() => {\n              eventEmitter?.emit({ type: 'auto-layout-nodes' } as any);\n            }}\n          />\n        </Tooltip>\n      )}\n      {!hideFullscreen && (\n        <Tooltip\n          title=\"画布全屏\"\n          getPopupContainer={() =>\n            document.getElementById('xflow-container') as HTMLElement\n          }\n        >\n          <Button\n            type=\"text\"\n            icon={\n              <IconView\n                type={isFullscreen ? 'icon-fullscreen-exit' : 'icon-fullscreen'}\n                className=\"icon\"\n                style={{ fontSize: '14px' }}\n              />\n            }\n            onClick={toggleFullscreen}\n          />\n        </Tooltip>\n      )}\n    </div>\n  );\n};\n\nexport default memo(Control);\n"
  },
  {
    "path": "packages/x-flow/src/operator/UndoRedo/index.less",
    "content": ".fai-reactflow-undoredo {\n  display: flex;\n  align-items: center;\n  padding: 2px 1px;\n  border-radius: 8px;\n  border: 0.5px solid #f3f4f6;\n  background-color: #ffffff;\n  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);\n  color: #667085;\n  margin-right: 10px;\n\n  .ant-btn {\n    display: inline-flex;\n    align-items: center;\n    justify-content: center;\n  }\n}"
  },
  {
    "path": "packages/x-flow/src/operator/UndoRedo/index.tsx",
    "content": "import React, { memo, useContext } from 'react';\nimport { Button, Tooltip } from 'antd';\nimport IconView from '../../components/IconView';\nimport './index.less';\nimport { ConfigContext } from '../../models/context';\n\nexport type UndoRedoProps = {\n  handleUndo: () => void;\n  handleRedo: () => void;\n  pastStates: any[];\n  futureStates: any[];\n};\n\nexport default memo(({ handleUndo, handleRedo, pastStates, futureStates }: UndoRedoProps) => {\n  const { readOnly } = useContext(ConfigContext);\n\n  if (readOnly) {\n    return null;\n  }\n\n  return (\n    <div className='fai-reactflow-undoredo'>\n      <Tooltip title='撤销' getPopupContainer={() => document.getElementById('xflow-container') as HTMLElement}>\n        <Button\n          type='text'\n          icon={<IconView type='icon-undo' className=\"icon\" style={{ color: !pastStates?.length ? 'rgba(0, 0, 0, 0.25)':  '#666F83'}} />}\n          onClick={handleUndo}\n          disabled={!pastStates?.length}\n        />\n      </Tooltip>\n      <Tooltip title='重做' getPopupContainer={() => document.getElementById('xflow-container') as HTMLElement}>\n        <Button\n          type='text'\n          icon={<IconView type='icon-redo' className=\"icon\" style={{ color: !futureStates?.length ? 'rgba(0, 0, 0, 0.25)' : '#666F83' }} />}\n          onClick={handleRedo}\n          disabled={!futureStates?.length}\n        />\n      </Tooltip>\n    </div>\n  );\n})\n"
  },
  {
    "path": "packages/x-flow/src/operator/ZoomInOut/index.less",
    "content": ".fai-reactflow-zoominout {\n  display: flex;\n  align-items: center;\n  padding: 2px 1px;\n  border-radius: 8px;\n  border: 0.5px solid #f3f4f6;\n  background-color: #ffffff;\n  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);\n  color: #667085;\n  margin-right: 10px;\n\n  .ant-btn {\n    display: inline-flex;\n    align-items: center;\n    justify-content: center;\n  }\n}\n\n.fai-reactflow-zoom-select {\n\n  .parting-line {\n    height: 1px;\n    background-color: #F3F4F6;\n  }\n\n  .zoom-item {\n    display: flex;\n    align-items: center;\n    justify-content: space-between;\n    padding-left: 10px;\n    padding-right: 10px;\n    height: 30px;\n    border-radius: 8px;\n    cursor: pointer;\n    font-size: 14px;\n    color: #374151;\n\n    &:hover {\n      background-color: #F9FAFB;\n    }\n  }\n}\n"
  },
  {
    "path": "packages/x-flow/src/operator/ZoomInOut/index.tsx",
    "content": "import { useReactFlow, useViewport } from '@xyflow/react';\nimport { Button, Popover, Tooltip } from 'antd';\nimport type { FC } from 'react';\nimport React, { Fragment, memo } from 'react';\nimport IconView from '../../components/IconView';\nimport { getKeyboardKeyNameBySystem } from '../../utils';\nimport './index.less';\nimport ShortcutsName from './shortcuts-name';\n\nenum ZoomType {\n  zoomIn = 'zoomIn',\n  zoomOut = 'zoomOut',\n  zoomToFit = 'zoomToFit',\n  zoomTo25 = 'zoomTo25',\n  zoomTo50 = 'zoomTo50',\n  zoomTo75 = 'zoomTo75',\n  zoomTo100 = 'zoomTo100',\n  zoomTo200 = 'zoomTo200',\n}\n\nconst ZOOM_IN_OUT_OPTIONS = [\n  [\n    {\n      key: ZoomType.zoomTo200,\n      text: '200%',\n    },\n    {\n      key: ZoomType.zoomTo100,\n      text: '100%',\n    },\n    {\n      key: ZoomType.zoomTo75,\n      text: '75%',\n    },\n    {\n      key: ZoomType.zoomTo50,\n      text: '50%',\n    },\n    {\n      key: ZoomType.zoomTo25,\n      text: '25%',\n    },\n  ],\n  [\n    {\n      key: ZoomType.zoomToFit,\n      text: '自适应视图',\n    },\n  ],\n];\n\nconst ZoomSelect = ({ handleZoom }: any) => {\n  return (\n    <div className='fai-reactflow-zoom-select'>\n      {ZOOM_IN_OUT_OPTIONS.map((options, i) => (\n        <Fragment key={i}>\n          {i !== 0 && <div className='parting-line' />}\n          <div className='p-1'>\n            {options.map(option => (\n              <div\n                className='zoom-item'\n                key={option.key}\n                onClick={() => handleZoom(option.key)}\n              >\n                {option.text}\n                {option.key === ZoomType.zoomToFit && (\n                  <ShortcutsName\n                    keys={[`${getKeyboardKeyNameBySystem('ctrl')}`, '1']}\n                  />\n                )}\n                {option.key === ZoomType.zoomTo50 && (\n                  <ShortcutsName keys={['shift', '5']} />\n                )}\n                {option.key === ZoomType.zoomTo100 && (\n                  <ShortcutsName keys={['shift', '1']} />\n                )}\n              </div>\n            ))}\n          </div>\n        </Fragment>\n      ))}\n    </div>\n  );\n};\n\nconst ZoomInOut: FC = () => {\n  const { zoomIn, zoomOut, zoomTo, fitView } = useReactFlow();\n\n  const { zoom } = useViewport();\n\n  const handleZoom = (type: string) => {\n    if (type === ZoomType.zoomToFit) fitView();\n\n    if (type === ZoomType.zoomTo25) zoomTo(0.25);\n\n    if (type === ZoomType.zoomTo50) zoomTo(0.5);\n\n    if (type === ZoomType.zoomTo75) zoomTo(0.75);\n\n    if (type === ZoomType.zoomTo100) zoomTo(1);\n\n    if (type === ZoomType.zoomTo200) zoomTo(2);\n  };\n\n  return (\n    <div className='fai-reactflow-zoominout'>\n      <Tooltip title='放大' getPopupContainer={() => document.getElementById('xflow-container') as HTMLElement}>\n        <Button\n          type='text'\n          icon={<IconView type='icon-zoom-out-line' className='icon' />}\n          onClick={e => {\n            e.stopPropagation();\n            zoomOut();\n          }}\n        />\n      </Tooltip>\n      <div\n        style={{\n          fontSize: '13px',\n          fontWeight: 500,\n          padding: '0 2px',\n          textAlign: 'center',\n          width: '32px',\n        }}\n      >\n        <Popover\n          content={<ZoomSelect handleZoom={handleZoom} />}\n          zIndex={1000}\n          trigger='click'\n        >\n          {parseFloat(`${zoom * 100}`).toFixed(0)}%\n        </Popover>\n      </div>\n      <Tooltip title='缩小' getPopupContainer={() => document.getElementById('xflow-container') as HTMLElement}>\n        <Button\n          type='text'\n          icon={<IconView type='icon-zoom-in-line' className='icon' />}\n          onClick={e => {\n            e.stopPropagation();\n            zoomIn();\n          }}\n        />\n      </Tooltip>\n    </div>\n  );\n};\n\nexport default memo(ZoomInOut);\n"
  },
  {
    "path": "packages/x-flow/src/operator/ZoomInOut/shortcuts-name.tsx",
    "content": "import React, { memo } from 'react'\nimport { getKeyboardKeyNameBySystem } from '../../utils'\nimport cn from 'classnames'\n\ntype ShortcutsNameProps = {\n  keys: string[]\n  className?: string\n}\nconst ShortcutsName = ({\n  keys,\n  className,\n}: ShortcutsNameProps) => {\n  return (\n    <div className={cn(\n      'flex items-center gap-0.5 h-4 text-xs text-gray-400',\n      className,\n    )}>\n      {\n        keys.map(key => (\n          <div\n            key={key}\n            className='capitalize'\n          >\n            {getKeyboardKeyNameBySystem(key)}\n          </div>\n        ))\n      }\n    </div>\n  )\n}\n\nexport default memo(ShortcutsName)\n"
  },
  {
    "path": "packages/x-flow/src/operator/index.less",
    "content": ".fai-reactflow-operator {\n  position: absolute;\n  left: 4px;\n  bottom: 14px;\n  height: 50px;\n\n  .mini-map {\n    position: absolute;\n    left: 12px;\n    bottom: 45px;\n    margin: 0;\n    border: 0.5px solid rgba(0, 0, 0, 0.08);\n    border-radius: 8px;\n    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);\n    z-index: 9;\n  }\n\n  .operator-section {\n    display: flex;\n    align-items: center;\n    position: absolute;\n    left: 10px;\n    bottom: 4px;\n    z-index: 9;\n  }\n}\n\n\n.icon {\n  width: 16px;\n  height: 16px;\n  color: #666F83;\n  font-size: 20;\n}\n"
  },
  {
    "path": "packages/x-flow/src/operator/index.tsx",
    "content": "import React, { memo, useContext } from 'react';\n// import UndoRedo from '../header/undo-redo'\nimport Control from './Control';\nimport UndoRedo from './UndoRedo';\nimport ZoomInOut from './ZoomInOut';\n\nimport { useTemporalStore } from '../hooks/useTemporalStore';\nimport { ConfigContext } from '../models/context';\nimport './index.less';\n\nexport type OperatorProps = {\n  addNode: any;\n  xflowRef: any;\n};\n\nconst Operator = ({ addNode, xflowRef }: OperatorProps) => {\n  const { undo, redo, pastStates, futureStates } = useTemporalStore();\n  const { globalConfig } = useContext(ConfigContext);\n  const hideUndoRedoBtns = globalConfig?.controls?.hideUndoRedoBtns ?? false;\n  const hideZoomInOutBtns = globalConfig?.controls?.hideZoomInOutBtns ?? false;\n  const hideControlBtns = globalConfig?.controls?.hideControlBtns ?? false;\n\n  return (\n    <div className=\"fai-reactflow-operator\">\n      <div className=\"operator-section\">\n        {!Boolean(hideZoomInOutBtns) && <ZoomInOut />}\n        {!Boolean(hideUndoRedoBtns) && (\n          <UndoRedo\n            handleUndo={() => undo()}\n            handleRedo={() => redo()}\n            pastStates={pastStates}\n            futureStates={futureStates}\n          />\n        )}\n        {!Boolean(hideControlBtns) && <Control addNode={addNode} xflowRef={xflowRef} />}\n      </div>\n    </div>\n  );\n};\n\nexport default memo(Operator);\n"
  },
  {
    "path": "packages/x-flow/src/types.ts",
    "content": "import { NodeMouseHandler,Handle, Edge } from '@xyflow/react';\nimport { TabPaneProps } from 'antd';\nimport { Schema,useForm } from 'form-render';\nimport { ReactFlow } from '@xyflow/react';\nimport React, { ReactNode ,ComponentProps} from 'react';\n\nimport SourceHandle, {\n  HandleProps,\n} from './components/CustomNode/sourceHandle';\n\ntype SourceHandleType = typeof SourceHandle;\ntype ReactFlowProps = ComponentProps<typeof ReactFlow>\nexport interface TNodeItem {\n  title: string; // 节点 title\n  type: string; // 节点类型 _group 比较te\n  description?: string; // 节点描述\n  hidden?: boolean; // 是否可见\n  iconSvg?: string;// svg图标\n  icon: {\n    type: string;\n    bgColor: string;\n  };\n  settingSchema?: Schema; // 节点的配置schema（弹窗） string为自定义组件\n  settingWidget?: string; // 自定义组件\n  settingWidgetProps?: object;// 自定义组件参数\n  hideDesc?: boolean; // 隐藏业务描述\n  nodePanel?: {\n    // 配置面板属性设置\n    width?: string | number; // 配置面板宽度\n    hideDesc?: boolean; // 配置面板描述\n  };\n  nodeWidget?:string// 自定义节点组件\n  renderHandle?: (\n    sourceHandle: SourceHandleType,\n    sourceHandleProps: ComponentProps<SourceHandleType>,\n    nodeProps: {\n      id: string;\n      type: string;\n      data: any;\n      layout: 'LR' | 'TB';\n      isConnectable: boolean;\n      readOnly: boolean;\n    }\n  ) => React.JSX.Element;\n  getSettingSchema?: (nodeId: string, nodeType: string, nodeItem:TNodeItem,nodeData:any,form: ReturnType<typeof useForm>) => Promise<Schema>;\n  switchExtra: {   // 条件节点额外属性配置\n    hideElse: boolean;\n    valueKey: string;\n    titleKey: string;\n  };\n  parallelExtra: {  // 并行节点额外配置\n    valueKey: string;\n    titleKey: string;\n  };\n  disabledCopy?: boolean;\n  disabledDelete?: boolean;\n  onTesting: (node, nodes) => void;// 单点调试方法\n  showTestingBtn?: boolean; // 是否显示单点调试按钮\n  className?: string;// 自定义节点class\n  disabledShortcutDelete?: boolean; // 是否禁用快捷键删除\n  disabledShortcutCopy?: boolean; // 是否禁用快捷键复制\n}\n\nexport interface TNodeGroup {\n  title: string; // 节点 title\n  type: '_group';\n  items: TNodeItem[];\n}\n\nexport interface TNodeMenu {\n  ref: React.RefObject<any>; // 可选的 ref 属性\n  showSearch: boolean; // 配置是否可搜索\n  items: (TNodeGroup | TNodeItem)[];\n  onClick: ({}: { type: string }) => void;\n}\n\nexport interface TNodePanel {\n  // 配置面板属性设置\n  width?: string | number; // 配置面板宽度\n  hideDesc?: boolean; // 配置面板描述\n  onClose?: (activeNodeId: string) => void;\n  showPanel?: boolean;\n}\n\nexport interface TNodeSelector {\n  showSearch: boolean; // 配置是否可搜索\n  items?: (TNodeGroup | TNodeItem)[];\n}\n\nexport interface TLogListItem {\n  // 日志数据格式：\n  statusPanel?: {\n    status?: Array<{ label: string; value?: string; isBadge?: boolean }>; // isBadge是否为badge形式显示状态\n    extra?: string | ReactNode;\n  };\n  codePanel?: Array<{ title: string; code: string }>;\n  nodeId: string;// 节点ID\n  groupTitle?: string;// 节点组标题\n  showDetailLogWidget?: boolean;// 是否展示详情日志面板\n  _status?: string | number;// 当前log的状态，如果没有，则以data._status为准\n}\n\nexport interface TLogPanel {\n  // 日志面板\n  // logData: any; // 日志面板接受的数据\n  logList: Array<TLogListItem>; // 日志面板的所有数据===》默认能拿到页面所有节点的日志数据\n  loading?: boolean; // 日志面板loading\n  logWidget?: string; // 自定义日志面板组件\n  width?: number;// 日志面板宽度\n  tabsProps?: TabPaneProps;// 内置日志面板,详情和追踪选项卡切换组件,antd的tabs配置透传\n  detailLogWidget?: string;// 自定义详情日志面板组件\n  enable?: boolean;// 是否启用日志面板，默认为true\n}\n\nexport interface TNodeView {\n  hideTitleTips?: boolean;\n  status?: Array<{\n    name: string; // 状态名称\n    color: string; // 状态颜色\n  }>;\n}\n\nexport interface TEdge {\n  // 边的配置\n  hideEdgeAddBtn?: boolean; // 是否隐藏两个节点之间，连线上的增加节点按钮\n  hideEdgeDelBtn?: boolean; // 是否隐藏两个节点之间，连线上的删除节点按钮\n  deletable?: boolean; // 是否允许删除线条 初始化的edges不受此项影响\n\n  strokeWidth?:number\n}\n\nexport interface TControl{\n  hideAddNode?:boolean\n  hideAnnotate?: boolean\n  hideUndoRedoBtns?: boolean\n  hideZoomInOutBtns?: boolean\n  hideControlBtns?: boolean\n  hideAutoLayout?: boolean\n  hideFullscreen?: boolean\n  hideInteractionMode?: boolean\n  onAutoLayoutCompleted?: (nodes: node[]) => void | Promise<void>; // 整理画布完成后的回调函数\n}\n\nexport interface THandle{\n  // isConnectableStart?:boolean\n  // isConnectableEnd?:boolean\n  isValidConnection?:(edges:Parameters<HandleProps['isValidConnection']>[0],edgeType:'source'|'target',nodeType:string)=>boolean\n}\n\ntype node = {\n  id: string;\n  type: string;\n  data: Record<string, any>;\n  position: {\n    x: number;\n    y: number;\n  };\n}\ntype edge = {\n  id: string;\n  source: string;\n  target: string;\n}\nexport interface FlowProps {\n  initialValues?: {\n    nodes: Array<node>;\n    edges: Array<edge>;\n  };\n  layout?: 'LR' | 'TB';\n  /**\n   * 自定义组件\n   */\n  widgets?: any;\n  /**\n   * 节点配置\n   */\n  settings?: (TNodeGroup | TNodeItem)[];\n  filterSettings?:(settings:(TNodeGroup | TNodeItem)[],nodeType:string)=>(TNodeGroup | TNodeItem)[]\n  nodeSelector?: TNodeSelector;\n  iconFontUrl?: string;\n  globalConfig?: {\n    nodePanel?: TNodePanel;\n    nodeView?: TNodeView;\n    edge?: TEdge;\n    controls?:TControl\n    handle?:THandle\n    deleteKeyCode?:string | string[] | null\n  };\n  logPanel?: TLogPanel; // 日志面板配置\n  readOnly?:boolean//只读模式\n  onNodeClick?: ReactFlowProps['onNodeClick']\n  onEdgeClick?:ReactFlowProps['onEdgeClick']\n  onPasteCompleted?:(addNodes:node[])=>void\n  onCopyCompleted?:(addNodes:node[])=>void\n  onMenuItemClick?: (itemInfo: ItemInfo, defaultAction: () => void) => void;\n  clickAddNode?: (type: string, nodeItem: TNodeItem, addNode: (initData?: Record<string, any>) => void) => void;\n  zoomOnScroll?: boolean;\n  panOnScroll?: boolean;\n  preventScrolling?: boolean;\n  openColorfulMode?: boolean; // 是否开启多彩模式\n  connectionLineComponent?: ReactFlowProps['connectionLineComponent']; // 透传给 ReactFlow，用于自定义连线渲染\n}\ninterface ItemInfo {\n  key: 'copy' | 'paste' | 'delete' | string;\n  nodeId: string;\n  sourceHandle?: string;\n}\n\nexport default FlowProps;\n"
  },
  {
    "path": "packages/x-flow/src/utils/autoLayoutNodes.ts",
    "content": "import { produce } from 'immer';\nimport dagre from '@dagrejs/dagre';\nimport { cloneDeep } from 'lodash-es'\n\nexport const CUSTOM_NODE = 'custom';\nexport const CUSTOM_EDGE = 'custom';\n\nexport const getLayoutByDagre = (originNodes: any[], originEdges: any[], rankdir: 'TB' | 'BT' | 'LR' | 'RL') => {\n  const dagreGraph = new dagre.graphlib.Graph()\n  dagreGraph.setDefaultEdgeLabel(() => ({}))\n\n  const nodes = cloneDeep(originNodes).filter(node => !node.parentId && node.type === CUSTOM_NODE)\n  const edges = cloneDeep(originEdges).filter(edge => !edge.data?.isInIteration)\n  dagreGraph.setGraph({\n    ranker: 'network-simplex', // 节点分层算法，可选：'tight-tree' 'longest-path' 'network-simplex'\n    rankdir, // 图的延展方向，可选： 'TB' | 'BT' | 'LR' | 'RL'\n    nodesep: 150,  // 同层各个节点之间的间距\n    ranksep: 150, // 图的各个层次之间的间距\n    // align: '', // 节点对齐方式，可选：'UL' | 'UR' | 'DL' | 'DR' | undefined\n  });\n\n  nodes.forEach((node) => {\n    dagreGraph.setNode(node.id, {\n      width: node.width || 204,\n      height: node.height || 45,\n    });\n  });\n\n  edges.forEach((edge) => {\n    dagreGraph.setEdge(edge.source, edge.target)\n  });\n\n  dagre.layout(dagreGraph);\n  return dagreGraph;\n}\n\nexport default (nodes: any, edges: any, rankdir: 'LR' | 'TB') => {\n  const layout = getLayoutByDagre(nodes, edges, rankdir);\n  const rankMap: any = {} as Record<string, Node>;\n  nodes.forEach((node: any) => {\n    if (!node.parentId && node.type === CUSTOM_NODE) {\n      const rank: any = layout.node(node.id).rank!\n\n      if (!rankMap[rank]) {\n        rankMap[rank] = node\n      }\n      else {\n        if (rankMap[rank].position.y > node.position.y)\n          rankMap[rank] = node\n      }\n    }\n  });\n\n  const newNodes = produce(nodes, (draft: any) => {\n    draft.forEach((node: any) => {\n      if (!node.parentId && node.type === CUSTOM_NODE) {\n        const nodeWithPosition = layout.node(node.id)\n        node.position = {\n          x: nodeWithPosition.x - (node.width || 204) / 2,\n          y: nodeWithPosition.y - (node.height || 45) / 2 + (rankMap[nodeWithPosition.rank!].height || 45) / 2,\n        }\n      }\n    })\n  });\n\n  return newNodes;\n}\n"
  },
  {
    "path": "packages/x-flow/src/utils/createIconFont.ts",
    "content": "import { createFromIconfontCN } from '@ant-design/icons';\n\nexport default (url?: string) => {\n  return createFromIconfontCN({\n    // scriptUrl: url || '//at.alicdn.com/t/a/font_4201076_frx3c9x07if.js',\n    scriptUrl: url || '//at.alicdn.com/t/a/font_4069358_dd524fgnynb.js',\n  });\n};\n"
  },
  {
    "path": "packages/x-flow/src/utils/flow.ts",
    "content": "import { uuid } from './';\n\n/**\n * 获取所有子节点\n */\nexport const generateCopyNodes = (parentNode: any) => {\n  // 1、定义 childNodeIds 数组，用于存储找到的所有节点的 id，默认把 rootNode 添加到数组中\n  const childNodes: any[] = [];\n  const rootNode = {\n    id: uuid(),\n    type: parentNode.type,\n    data: {\n      ...parentNode.data,\n    },\n    position: { x: 0, y: 0 },\n    sourceId: parentNode.id,\n  };\n  childNodes.push(rootNode);\n\n  return childNodes;\n};\n"
  },
  {
    "path": "packages/x-flow/src/utils/hooks.ts",
    "content": "import { useReducer, useRef, useEffect, useState } from 'react';\nimport { message } from 'antd';\n\nexport function usePrevious(value: any) {\n  const ref = useRef(null);\n  useEffect(() => {\n    ref.current = value;\n  }, [value]);\n  return ref.current;\n}\n\n// 类似于class component的setState\nexport const useSet = (initState: any) => {\n  return useReducer((state: any, newState: any) => {\n    return { ...state, ...newState };\n  }, initState);\n};\n\n/**\n * 业务中为了防止提交按钮会被连续点击 我们会手动的设置setLoading true 和setLoading false 非常麻烦\n * 所以写了这个hook 省事\n * @returns\n */\ntype TCallback = () => Promise<any>;\ntype TRequestLoading = () => [boolean, (callback: TCallback) => void];\n\nexport const useLoadingRequest: TRequestLoading = () => {\n  const [loading, setLoading] = useState(false);\n\n  const handleRequest = async (callback: TCallback) => {\n    try {\n      setLoading(true);\n      await callback();\n    } catch (error: any) {\n      message.error(error);\n      throw error;\n    } finally {\n      setLoading(false);\n    }\n  };\n\n  return [loading, handleRequest];\n};\n"
  },
  {
    "path": "packages/x-flow/src/utils/index.ts",
    "content": "import { customAlphabet } from 'nanoid';\nimport tinycolor from 'tinycolor2';\nexport const uuid = customAlphabet('0123456789abcdefghijklmnopqrstuvwxyz', 16);\nexport const uuid4 = customAlphabet('0123456789abcdefghijklmnopqrstuvwxyz', 4);\n\nimport {\n  has as _has,\n  cloneDeep,\n  get,\n  isMatch,\n  isNil,\n  isUndefined,\n  merge,\n  mergeWith,\n  omitBy,\n  set,\n  some,\n} from 'lodash-es';\n\nexport const _set = set;\nexport const _get = get;\nexport const _cloneDeep = cloneDeep;\n// export const _has = has;\nexport { _has };\nexport const _merge = merge;\nexport const _mergeWith = mergeWith;\nexport const _isUndefined = isUndefined;\nexport const _omitBy = omitBy;\nexport const _some = some;\nexport const _isMatch = isMatch;\n\nexport const isObject = (data: any) => {\n  const str = Object.prototype.toString.call(data);\n  return str.indexOf('Object') > -1;\n};\n\nexport const isArray = (data: any) => {\n  const str = Object.prototype.toString.call(data);\n  return str.indexOf('Array') > -1;\n};\n\nexport const isFunction = (data: any) => typeof data === 'function';\n\nexport function isUrl(string: string) {\n  const protocolRE = /^(?:\\w+:)?\\/\\/(\\S+)$/;\n  // const domainRE = /^[^\\s\\.]+\\.\\S{2,}$/;\n  if (typeof string !== 'string') return false;\n  return protocolRE.test(string);\n}\n\nexport const isNumber = (str: string | number) => !isNaN(Number(str));\n\nexport const getArray = (arr, defaultValue = []) => {\n  if (Array.isArray(arr)) return arr;\n  return defaultValue;\n};\n\nexport function getFormat(format) {\n  let dateFormat;\n  switch (format) {\n    case 'date':\n      dateFormat = 'YYYY-MM-DD';\n      break;\n    case 'time':\n      dateFormat = 'HH:mm:ss';\n      break;\n    case 'dateTime':\n      dateFormat = 'YYYY-MM-DD HH:mm:ss';\n      break;\n    case 'week':\n      dateFormat = 'YYYY-w';\n      break;\n    case 'year':\n      dateFormat = 'YYYY';\n      break;\n    case 'quarter':\n      dateFormat = 'YYYY-Q';\n      break;\n    case 'month':\n      dateFormat = 'YYYY-MM';\n      break;\n    default:\n      // dateTime\n      if (typeof format === 'string') {\n        dateFormat = format;\n      } else {\n        dateFormat = 'YYYY-MM-DD';\n      }\n  }\n  return dateFormat;\n}\n\n// TODO: to support case that item is not an object\nexport function isObjType(schema: any) {\n  //return schema?.type === 'object' && schema.properties && !schema.widget;\n  return (\n    schema?.type === 'object' &&\n    schema?.properties &&\n    schema?.widgetType !== 'field'\n  );\n}\n\nexport function isListType(schema: any) {\n  return (\n    schema?.type === 'array' &&\n    isObjType(schema?.items) &&\n    schema?.enum === undefined\n  );\n}\n\nexport function isCheckBoxType(schema: any, readOnly: boolean) {\n  if (readOnly) return false;\n  if (schema.widget === 'checkbox') return true;\n  if (schema && schema.type === 'boolean') {\n    if (schema.enum) return false;\n    if (schema.widget === undefined) return true;\n    return false;\n  }\n}\n\nexport const translation = (configCtx: any) => (key: string) => {\n  const locale = configCtx?.locale.FormRender;\n  return locale[key];\n};\n\nexport const hasFuncProperty = (obj: any) => {\n  return _some(obj, value => {\n    if (isFunction(value)) {\n      return true;\n    }\n    if (isObject(value)) {\n      return hasFuncProperty(value);\n    }\n    return false;\n  });\n};\n\n/**\n * 安全地获取对象的值，如果值为 null 或 undefined，则返回 defaultValue。\n *\n * @param {Object} object - 要获取值的对象。\n * @param {string|Array} path - 要获取的路径，可以是字符串或数组。\n * @param {*} [defaultValue] - 如果值为 null 或 undefined，则返回 defaultValue。\n * @returns {*} - 返回获取的值，或者默认值。\n */\nexport const safeGet = (object: any, path: string, defaultValue: any) => {\n  return get(object, path, defaultValue) ?? defaultValue;\n};\n\nexport const isMac = () => {\n  return navigator.userAgent.toUpperCase().includes('MAC');\n};\n\nconst specialKeysNameMap: Record<string, string | undefined> = {\n  ctrl: '⌘',\n  alt: '⌥',\n};\n\nexport const getKeyboardKeyNameBySystem = (key: string) => {\n  if (isMac()) return specialKeysNameMap[key] || key;\n\n  return key;\n};\n\nexport const capitalize = (string: string) => {\n  if (typeof string !== 'string' || string.length === 0) {\n    return string;\n  }\n  return `${string.charAt(0).toUpperCase()}${string.slice(1)}`;\n};\n\nexport const transformNodes = (nodes: any[]) => {\n  return nodes?.map(item => {\n    const { type, data, ...rest } = item;\n    if (type === 'custom') {\n      return item;\n    }\n\n    if (type === 'Switch' || type === 'Parallel') {\n      return {\n        type: 'custom',\n        data: {\n          ...data,\n          _nodeType: type,\n          ...(data?.list?.length && {\n            list: (data?.list || [])?.map(n => {\n              if (n?._id) {\n                return n;\n              } else {\n                return { ...n, _id: `id_${uuid()}` };\n              }\n            }),\n          }),\n        },\n        ...rest,\n      };\n    } else {\n      return {\n        type: 'custom',\n        data: {\n          ...data,\n          _nodeType: type,\n        },\n        ...rest,\n      };\n    }\n  });\n};\n\nexport const transformSwitchNodes = (nodes: any[]) => {\n  return (nodes || [])?.map(item => {\n    if (item?.type === 'Switch' || item?.type === 'Parallel') {\n      const { list, ...rest } = item?.data;\n      return {\n        ...item,\n        data: {\n          ...rest,\n          list: (list || [])?.map(item => {\n            if (item?._id) {\n              return item;\n            } else {\n              return { ...item, _id: `id_${uuid()}` };\n            }\n          }),\n        },\n      };\n    } else {\n      return item;\n    }\n  });\n};\n\n// 废弃：\n// export const getAntdVersion = () => {\n//   const majorVersion = parseInt(antdVersion?.split('.')?.[0], 10);\n//   if (majorVersion >= 5) {\n//     return 'V5';\n//   } else if (majorVersion === 4) {\n//     return 'V4';\n//   } else {\n//     return 'V4';\n//   }\n// };\n\n// 安全的JSON.stringify\nexport function safeJsonStringify(obj: Object) {\n  const seen = new WeakSet();\n\n  function replacer(key, value) {\n    // 循环引用\n    if (typeof value === 'object' && value !== null) {\n      if (seen.has(value)) {\n        return '[Circular]';\n      }\n      seen.add(value);\n    }\n\n    // React\n    if (value && typeof value === 'object' && value._owner) {\n      return '[React Element]';\n    }\n\n    // BigInt\n    if (typeof value === 'bigint') {\n      return value.toString();\n    }\n\n    // 其他无法序列化的类型\n    if (typeof value === 'function') {\n      return `[Function: ${value.name || 'anonymous'}]`;\n    }\n\n    return value;\n  }\n\n  try {\n    return JSON.stringify(obj, replacer, 2);\n  } catch (error) {\n    console.error('json.stringify error', error);\n    return null;\n  }\n}\n\nexport function safeJSONParse<T = any>(\n  jsonString: string | null,\n  defaultVal: T = {} as any,\n): T {\n  if (typeof jsonString === 'string') {\n    try {\n      return JSON.parse(jsonString);\n    } catch (e) {\n      return defaultVal; // 或返回默认值 {}\n    }\n  } else {\n    return defaultVal;\n  }\n}\n\nexport * from './flow';\n\n// 内置节点状态\nexport const NODE_STATUS = {\n  success: {\n    color: '#52c41a',\n    name: '成功',\n  },\n  error: {\n    color: '#ff4d4f',\n    name: '失败',\n  },\n  warning: {\n    color: '#faad14',\n    name: '告警',\n  },\n};\n\nexport const transformNodeStatus = (statusList = []) => {\n  const obj: Record<string, any> = {};\n  statusList?.forEach(\n    (status: { name: string; color: string; value: string }) => {\n      if (isTruthy(status?.value) && status?.color)\n        obj[status.value] = {\n          color: status.color,\n          name: status?.name,\n        };\n    }\n  );\n\n  return {\n    ...NODE_STATUS,\n    ...obj,\n  };\n};\n\n// 处理颜色值\nexport function getTransparentColor(colorInput: string, alpha: number) {\n  const color = tinycolor(colorInput);\n  const alphaNum = Number(alpha);\n  if (!color.isValid()) {\n    return;\n  }\n  color.setAlpha(alphaNum);\n  // 返回 RGBA 格式的颜色字符串\n  return color.toRgbString();\n}\n\nexport function isTruthy(value: any) {\n  if (isNil(value)) {\n    return false;\n  }\n\n  if (isNumber(value) && value === 0) {\n    return true;\n  }\n  return Boolean(value);\n}\n\nexport function hexToRgba(hex: string, alpha = 0.25) {\n  // 处理缺少 # 前缀的情况\n  if (hex.charAt(0) !== '#') {\n    hex = '#' + hex;\n  }\n\n  // 处理3位十六进制颜色值 (#RGB)\n  if (/^#([0-9A-Fa-f]{3})$/.test(hex)) {\n    const r = hex.charAt(1);\n    const g = hex.charAt(2);\n    const b = hex.charAt(3);\n    hex = `#${r}${r}${g}${g}${b}${b}`;\n  }\n\n  // 如果不是合法的六位十六进制颜色，返回默认颜色\n  if (!/^#([0-9A-Fa-f]{6})$/.test(hex)) {\n    hex = '#F79009'; // 使用默认颜色\n  }\n\n  // 提取 R、G 和 B 值\n  const r = parseInt(hex.slice(1, 3), 16);\n  const g = parseInt(hex.slice(3, 5), 16);\n  const b = parseInt(hex.slice(5, 7), 16);\n\n  // 返回 `rgba` 格式字符串\n  return `rgba(${r}, ${g}, ${b}, ${alpha})`;\n}\n\n\nexport function getColorfulModeBackground(color: string, openColorfulMode: boolean) {\n\n  if(!openColorfulMode) {\n    return {};\n  }\n  return {\n    background: `linear-gradient(to bottom, ${hexToRgba(color || '#F79009')}, #fff)`,\n  }\n}"
  },
  {
    "path": "packages/x-flow/src/withProvider.tsx",
    "content": "import { EventEmitterContextProvider } from './models/event-emitter';\nimport { ConfigProvider } from 'antd';\nimport React, { useMemo } from 'react';\nimport { FlowProviderWrapper } from './components/FlowProvider';\nimport { ConfigContext } from './models/context';\nimport { TNodeGroup, TNodeItem } from './types';\ninterface ProviderProps<T> {\n  configProvider?: any;\n  widgets?: any;\n  methods?: any;\n  nodeSelector?: any;\n  settings?: (TNodeGroup | TNodeItem)[];\n  [key: string]: any;\n}\n\nexport default function withProvider<T>(\n  Element: any,\n  defaultWidgets?: any\n): React.ComponentType<T> {\n  return (props: ProviderProps<T>) => {\n    const {\n      configProvider,\n      widgets,\n      methods,\n      nodeSelector,\n      settings,\n      initialValues,\n      layout,\n      iconFontUrl,\n      globalConfig,\n      logPanel,\n      onMenuItemClick,\n      antdVersion ='V5',\n      readOnly,\n      clickAddNode,\n      onTesting,\n      openColorfulMode,\n      filterSettings,\n      ...restProps\n    } = props;\n    const settingMap = useMemo(() => {\n      const obj: Record<string, any> = {};\n      settings?.forEach((node: any) => {\n        if (node.type !== '_group') {\n          obj[node.type] = node;\n        } else {\n          node.items.forEach((item: any) => {\n            obj[item.type] = item;\n          });\n        }\n      });\n      return obj;\n    }, [settings]);\n\n    const configContext = {\n      methods,\n      nodeSelector,\n      settings,\n      settingMap,\n      iconFontUrl,\n      globalConfig,\n      logPanel,\n      antdVersion,\n      onMenuItemClick,\n      readOnly,\n      clickAddNode,\n      onTesting,\n      openColorfulMode,\n      filterSettings,\n      widgets: {\n        ...defaultWidgets,\n        ...widgets,\n      },\n    };\n\n    return (\n      <ConfigProvider {...configProvider}>\n        <ConfigContext.Provider value={configContext}>\n          <EventEmitterContextProvider>\n            <FlowProviderWrapper\n              nodes={initialValues?.nodes}\n              edges={initialValues?.edges}\n              layout={layout}\n            >\n              <Element\n                {...restProps}\n                initialValues={initialValues}\n                settings={settings}\n              />\n            </FlowProviderWrapper>\n          </EventEmitterContextProvider>\n        </ConfigContext.Provider>\n      </ConfigProvider>\n    );\n  };\n}\n"
  },
  {
    "path": "packages/x-flow/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"ES2015\",\n    \"module\": \"ES2015\",\n    \"moduleResolution\": \"node\",\n    \"importHelpers\": true,\n    \"jsx\": \"react\",\n    \"esModuleInterop\": true,\n    \"sourceMap\": true,\n    \"baseUrl\": \"./\",\n    \"skipLibCheck\": true,\n    // \"strict\": true,\n    \"declaration\": true,\n    \"paths\": {\n      \"@/*\": [\n        \"src/*\"\n      ],\n      \"@@/*\": [\n        \"src/.umi/*\"\n      ],\n      \"form-render\": [\n        \"packages/form-render/src/*\"\n      ],\n    },\n    \"allowJs\": true,\n    \"allowSyntheticDefaultImports\": true,\n    \"noImplicitAny\": false,\n    \"resolveJsonModule\": true\n  },\n  \"exclude\": [\n    \"node_modules\",\n    \"lib\",\n    \"es\",\n    \"dist\",\n    \"typings\",\n    \"**/__test__\",\n    \"test\",\n    \"tests\",\n    \"docs\",\n    \"**/*.js\"\n  ]\n}\n"
  },
  {
    "path": "scripts/dumi-plugin/publicPath.ts",
    "content": "import { IApi } from 'dumi';\n\nexport default (api: IApi) => {\n  api.modifyPublicPathStr(() => {\n    return '/x-render/';\n  });\n};\n"
  },
  {
    "path": "scripts/dumi-plugin/redirect.ts",
    "content": "import { IApi } from 'dumi'\n\nexport default (api: IApi) => {\n  api.addEntryCodeAhead(() => {\n    const code = `\n    if(location.origin.includes('gitee')) location.href = 'https://xrender.fun'\n    `\n    return code\n  })\n};"
  },
  {
    "path": "scripts/prettier-plugin/index.js",
    "content": "const { parsers } = require('prettier-plugin-organize-imports');\n\nfunction createParser(original, transform) {\n  return {\n    ...original,\n    parse: (text, parsers, options) => {\n      const ast = original.parse(text, parsers, options);\n      transform(ast, { ...options, text });\n      return ast;\n    }\n  }\n}\n\n// https://lihautan.com/manipulating-ast-with-javascript/\nfunction visit(ast, callbackMap) {\n  function _visit(node, parent, key, index) {\n    if (typeof callbackMap === 'function') {\n      if (callbackMap(node, parent, key, index) === false) {\n        return\n      }\n    } else if (node.type in callbackMap) {\n      if (callbackMap[node.type](node, parent, key, index) === false) {\n        return\n      }\n    }\n\n    const keys = Object.keys(node)\n    for (let i = 0; i < keys.length; i++) {\n      const child = node[keys[i]]\n      if (Array.isArray(child)) {\n        for (let j = 0; j < child.length; j++) {\n          if (child[j] !== null) {\n            _visit(child[j], node, keys[i], j)\n          }\n        }\n      } else if (typeof child?.type === 'string') {\n        _visit(child, node, keys[i], i)\n      }\n    }\n  }\n  _visit(ast)\n}\n\nfunction transformJavaScript(ast, options) {\n  if (!options.text.includes('// sort-object-keys')) return;\n  visit(ast, {\n    ObjectExpression(node) {\n      const { properties } = node;\n      properties.sort((a, b) => {\n        const { key: aKey } = a;\n        const { key: bKey } = b;\n        if (aKey.type === 'Identifier' && bKey.type === 'Identifier') {\n          return aKey.name.localeCompare(bKey.name);\n        }\n        return 0;\n      });\n    },\n    TSTypeLiteral(node) {\n      const { members } = node;\n      members.sort((a, b) => {\n        const { key: aKey } = a;\n        const { key: bKey } = b;\n        if (aKey.type === 'Identifier' && bKey.type === 'Identifier') {\n          return aKey.name.localeCompare(bKey.name);\n        }\n        return 0;\n      });\n    }\n  })\n}\n\nexports.parsers = {\n  ...parsers,\n  typescript: createParser(parsers.typescript, transformJavaScript),\n};\n"
  },
  {
    "path": "tools/schema-builder/.fatherrc.js",
    "content": "import copy from 'rollup-plugin-copy';\n\nexport default {\n  cjs: 'babel',\n  esm: {\n    type: 'babel',\n    importLibToEs: true,\n  },\n  lessInBabelMode: true,\n  extraRollupPlugins: [\n    copy({\n      targets: [{ src: 'src/index.d.ts', dest: 'dist/' }],\n    }),\n  ],\n  extraBabelPlugins: [\n    [\n      'import',\n      {\n        libraryName: 'antd',\n        libraryDirectory: 'es',\n        style: true,\n      },\n      'antd',\n    ],\n    [\n      'import',\n      {\n        libraryName: '@ant-design/icons',\n        libraryDirectory: 'lib/icons',\n        camel2DashComponentName: false,\n      },\n      '@ant-design/icons'\n    ]\n  ]\n};\n"
  },
  {
    "path": "tools/schema-builder/CHANGELOG.md",
    "content": "# Change Log"
  },
  {
    "path": "tools/schema-builder/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2019-present XRender Team\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "tools/schema-builder/README.md",
    "content": "## 如何使用\n\n### 安装\n\n```bash\nnpm i @xrenders/schema-builder\n```\n\n### 代码演示\n\n```jsx\n/**\n * transform: true\n * defaultShowCode: true\n */\nimport React from 'react';\nimport SchemaBuilder from '@xrenders/schema-builder';\n\nconst defaultValue = {\n  type: 'object',\n  properties: {\n    inputName: {\n      title: '简单输入框',\n      type: 'string',\n    },\n  },\n};\n\nconst Demo = () => {\n  return (\n    <div style={{ height: '80vh' }}>\n      <SchemaBuilder defaultValue={defaultValue} />\n    </div>\n  );\n};\n\nexport default Demo;\n```\n\n"
  },
  {
    "path": "tools/schema-builder/package.json",
    "content": "{\n  \"name\": \"@xrenders/schema-builder\",\n  \"version\": \"1.0.0-alpha.20\",\n  \"description\": \"通过 JSON Schema 生成标准 Form，常用于自定义搭建配置界面生成\",\n  \"keywords\": [\n    \"Form\",\n    \"FormRender\",\n    \"Render\",\n    \"XRender\",\n    \"React\",\n    \"Json Schema\",\n    \"Ant Design\"\n  ],\n  \"homepage\": \"https://xrender.fun/form-render\",\n  \"bugs\": {\n    \"url\": \"https://github.com/alibaba/x-render/issues\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git@github.com:alibaba/x-render.git\"\n  },\n  \"license\": \"MIT\",\n  \"main\": \"lib/index.js\",\n  \"module\": \"es/index.js\",\n  \"files\": [\n    \"es\",\n    \"lib\",\n    \"package.json\"\n  ],\n  \"scripts\": {\n    \"beta\": \"npm publish --tag beta\",\n    \"alpha\": \"npm publish --tag alpha --access public\",\n    \"build\": \"father-build\",\n    \"prepare\": \"yarn build\",\n    \"prettier\": \"prettier --write \\\"**/*.{js,jsx,tsx,ts,less,md,json}\\\"\",\n    \"postpublish\": \"git push --tags\",\n    \"test\": \"umi-test\",\n    \"test:coverage\": \"umi-test --coverage\"\n  },\n  \"lint-staged\": {\n    \"*.{js,jsx,less,md,json}\": [\n      \"prettier --write\"\n    ],\n    \"*.ts?(x)\": [\n      \"prettier --parser=typescript --write\"\n    ]\n  },\n  \"dependencies\": {\n    \"clone\": \"^2.1.2\",\n    \"i18next\": \"^21.8.11\",\n    \"nanoid\": \"^3.1.22\",\n    \"randombytes\": \"2.1.0\",\n    \"rc-color-picker\": \"^1.2.6\",\n    \"react-dnd\": \"^14.0.2\",\n    \"react-dnd-html5-backend\": \"^14.0.0\",\n    \"react-i18next\": \"^11.17.2\",\n    \"react-sortable-hoc\": \"^2.0.0\"\n  },\n  \"devDependencies\": {\n    \"father-build\": \"^1.17.2\",\n    \"less\": \"^3.0.0\",\n    \"@rollup/plugin-babel\": \"^6.0.3\",\n    \"rollup-plugin-commonjs\": \"^10.1.0\",\n    \"rollup-plugin-copy\": \"^3.4.0\",\n    \"@alib/build-scripts\": \"^0.1.18\",\n    \"@alilc/lowcode-engine\": \"^1.0.0\",\n    \"@alilc/lowcode-engine-ext\": \"^1.0.0\",\n    \"@alilc/lowcode-types\": \"^1.0.0\",\n    \"@types/events\": \"^3.0.0\",\n    \"@types/react\": \"^16.8.3\",\n    \"@types/react-dom\": \"^16.8.2\",\n    \"@types/streamsaver\": \"^2.0.0\",\n    \"@types/uuid\": \"^8.3.4\",\n    \"build-plugin-fusion\": \"^0.1.0\",\n    \"build-plugin-moment-locales\": \"^0.1.0\",\n    \"build-plugin-react-app\": \"^1.1.2\",\n    \"fs-extra\": \"^10.0.1\",\n    \"tsconfig-paths-webpack-plugin\": \"^3.2.0\"\n  },\n  \"peerDependencies\": {\n    \"antd\": \"4.x\",\n    \"react\": \">=16.14.0\",\n    \"react-dom\": \">=16.14.0\"\n  },\n  \"gitHooks\": {\n    \"pre-commit\": \"lint-staged\"\n  },\n  \"types\": \"./lib/index.d.ts\",\n  \"publishConfig\": {\n    \"registry\": \"https://registry.npmjs.org/\",\n    \"access\": \"public\"\n  }\n}\n"
  },
  {
    "path": "tools/schema-builder/src/createIframe.ts",
    "content": "const createIframeContent = () => {\n  const html = `\n    <html>\n      <head>\n        <meta charset=\"UTF-8\" />\n        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n        <meta http-equiv=\"X-UA-Compatible\" content=\"ie=edge\" />\n        <title>XRender</title>\n        <link rel=\"icon\" href=\"https://img.alicdn.com/tfs/TB17UtINiLaK1RjSZFxXXamPFXa-606-643.png\">\n        <link href=\"https://alifd.alicdn.com/npm/@alifd/theme-lowcode-light@0.2.1/variables.css\" rel=\"stylesheet\" />\n        <link href=\"https://alifd.alicdn.com/npm/@alifd/theme-lowcode-light@0.2.1/dist/next.var.min.css\" rel=\"stylesheet\" />\n        <link rel=\"stylesheet\" href=\"https://uipaas-assets.com/prod/npm/@alilc/lowcode-engine/1.2.3/dist/css/engine-core.css\" />\n        <link rel=\"stylesheet\" href=\"https://uipaas-assets.com/prod/npm/@alilc/lowcode-engine-ext/1.0.6/dist/css/engine-ext.css\" />\n        <link rel=\"stylesheet\" href=\"https://g.alicdn.com/fone-lowcode/fr-generator/1.1.0/css/index.css\" />\n\n        <script>\n          window.React = window.parent.React;\n          window.ReactDOM = window.parent.ReactDOM;\n        </script>\n        \n        <script src=\"https://g.alicdn.com/code/lib/prop-types/15.7.2/prop-types.js\"></script>\n        <script src=\"https://g.alicdn.com/platform/c/react15-polyfill/0.0.1/dist/index.js\"></script>\n        <script src=\"https://g.alicdn.com/platform/c/lodash/4.6.1/lodash.min.js\"></script>\n        <script src=\"https://g.alicdn.com/mylib/moment/2.24.0/min/moment.min.js\"></script>\n        <script src=\"https://g.alicdn.com/code/lib/alifd__next/1.23.24/next.min.js\"></script>\n        <script crossorigin=\"anonymous\" src=\"https://uipaas-assets.com/prod/npm/@alilc/lowcode-engine/1.2.3/dist/js/engine-core.js\"></script>\n        <script crossorigin=\"anonymous\" src=\"https://uipaas-assets.com/prod/npm/@alilc/lowcode-engine-ext/1.0.6/dist/js/engine-ext.js\"></script>\n      </head>\n\n      <body>\n        <div id=\"lce-container\"></div>\n        <script type=\"text/javascript\" src=\"https://g.alicdn.com/fone-lowcode/fr-generator/1.1.0/js/index.js\"></script>\n      </body>\n    </html>\n  `;\n  return html;\n};\n\nexport default () => {\n  const iframe = document.createElement('iframe');\n  iframe.width = '100%';\n  iframe.height = '100%';\n  iframe.frameBorder = '0';\n  iframe.srcdoc = createIframeContent();\n  \n  return iframe;\n};"
  },
  {
    "path": "tools/schema-builder/src/index.ts",
    "content": "import Main from './main';\n\nexport default Main"
  },
  {
    "path": "tools/schema-builder/src/main.tsx",
    "content": "import React, { useEffect, useRef, useImperativeHandle, forwardRef } from 'react';\nimport createIframe from './createIframe';\nimport * as defaultSetting from './settings';\n\nimport { TSchemaBuilder } from './type';\n\nlet iframe: any;\n\nconst Design = (props: TSchemaBuilder, ref: any) => {\n  const { widgets, settings, ...restProps } = props;\n  const containerRef: any = useRef();\n\n  useImperativeHandle(ref, () => ({\n    getValue: () => {\n      return iframe?.contentWindow?.__FR_ENGINE__?.exportSchema?.();\n    },\n    setValue: (schema: any) => {\n      return iframe?.contentWindow?.__FR_ENGINE__?.importSchema?.(schema);\n    }\n  }))\n\n  useEffect(() => {\n    initIframe();\n    window.addEventListener('message', engineOnLoad);\n    return () => {\n      window.removeEventListener('message', engineOnLoad);\n    }\n  }, []);\n\n  const initIframe = () => {\n    iframe = createIframe();\n    containerRef.current.appendChild(iframe);\n  }\n\n  const engineOnLoad = (event: any) => {\n    if (event.data.type !== 'engine-load') {\n      return;\n    }\n    \n    iframe?.contentWindow?.__FR_ENGINE__?.init({\n      settings: {\n        ...defaultSetting,\n        ...settings\n      },\n      widgets,\n      // recordEnable: true,\n      logo: {\n        title: 'XRender'\n      },\n      ...restProps\n    });\n  };\n\n  return (\n    <div ref={containerRef} style={{ width: '100%', height: '100%'}} />\n  );\n}\n\nexport default forwardRef(Design);"
  },
  {
    "path": "tools/schema-builder/src/settings/index.ts",
    "content": "// 常用组件配置\nexport { default as Input } from './meta/input';\nexport { default as Number } from './meta/number';\nexport { default as Select } from './meta/select';\nexport { default as Radio } from './meta/radio';\nexport { default as Checkbox } from './meta/checkbox';\nexport { default as Checkboxes } from './meta/checkboxes';\nexport { default as Textarea } from './meta/textarea';\nexport { default as DatePicker } from './meta/date';\nexport { default as DateRange } from './meta/dateRange';\nexport { default as TimePicker } from './meta/time';\nexport { default as TimeRange } from './meta/timeRange';\nexport { default as Rate } from './meta/rate';\nexport { default as Switch } from './meta/switch';\nexport { default as Slider } from './meta/slider';\n\n// 其他组件配置\nexport { default as Color } from './meta/color';\nexport { default as ImageInput } from './meta/imageInput';\nexport { default as UrlInput } from './meta/urlInput';\nexport { default as TreeSelect } from './meta/treeSelect';\n\n// 容器组件配置\nexport { default as Form } from './meta/form';\nexport { default as card } from './meta/card';\nexport { default as CardList } from './meta/cardList';"
  },
  {
    "path": "tools/schema-builder/src/settings/meta/card.ts",
    "content": "import { createMeta } from '../utils';\n\nconst props: any = [\n  {\n    name: 'title',\n    title: { label: '标题', tip: 'title | 卡片主题' },\n    setter: 'StringSetter'\n  },\n  {\n    name: 'code',\n    title: { label: '字段名', tip: 'key | 字段名' },\n    setter: 'StringSetter',\n  },\n  {\n    name: 'description',\n    title: { label: '描述', tip: 'description ｜ 卡片的描述' },\n    setter: 'StringSetter'\n  },\n  {\n    name: 'column',\n    title: {\n      label: '一行多列',\n      tip: 'column ｜ 表单内容分成几列展示',\n    },\n    defaultValue: 1,\n    setter: {\n      componentName: 'RadioGroupSetter',\n      props: {\n        options: [\n          {\n            title: '一列',\n            value: 1,\n          },\n          {\n            title: '两列',\n            value: 2,\n          },\n          {\n            title: '三列',\n            value: 3,\n          },\n          {\n            title: '四列',\n            value: 4\n          }\n        ]\n      }\n    }\n  },\n  {\n    name: 'widget',\n    title: {\n      label: '类型',\n      tip: 'widget ｜ 类型',\n    },\n    defaultValue: 1,\n    setter: {\n      componentName: 'RadioGroupSetter',\n      props: {\n        options: [\n          {\n            title: '卡片',\n            value: 'card',\n          },\n          {\n            title: '折叠面板',\n            value: 'collapse',\n          },\n          {\n            title: '标题线',\n            value: 'lineTitle',\n          },\n          {\n            title: '内联',\n            value: 'subInline'\n          }\n        ]\n      }\n    }\n  },\n];\n\nconst snippets = [\n  {\n    title: '对象',\n    screenshot: 'icon-object',\n    schema: {\n      componentName: 'Card',\n      props: {\n        title: '卡片主题',\n        description: '这是一个对象类型',\n        column: 3,\n        type: 'object',\n        widget: 'collapse'\n      }\n    }\n  }\n]\n\nexport default createMeta('Card', {\n  title: '对象',\n  category: '布局',\n  group: '基础组件',\n  priority: 1,\n  props,\n  snippets,\n  configure: {\n    supports: {\n      loop: false,\n      condition: false\n    },\n    component: {\n      isContainer: true,\n      isModal: false,\n      nestingRule: {\n        parentWhitelist: ['FormRender', 'Card', 'CardList']\n      }\n    }\n  }\n});\n"
  },
  {
    "path": "tools/schema-builder/src/settings/meta/cardList.ts",
    "content": "import { createMeta } from '../utils';\n\nconst props: any = [\n  {\n    name: 'title',\n    title: { label: '标题', tip: 'title | 卡片主题' },\n    setter: 'StringSetter'\n  },\n  {\n    name: 'code',\n    title: { label: '字段名', tip: 'key | 字段名' },\n    setter: 'StringSetter',\n  },\n  {\n    name: 'description',\n    title: { label: '描述', tip: 'description ｜ 卡片的描述' },\n    setter: 'StringSetter'\n  },\n  {\n    title: '模版配置',\n    display: 'block',\n    type: 'group',\n    items: [\n      {\n        name: 'widget',\n        title: {\n          label: '类型',\n          tip: 'widget ｜ 类型',\n        },\n        setter: {\n          componentName: 'SelectSetter',\n          props: {\n            options: [\n              {\n                title: 'SimpleList',\n                value: 'simpleList',\n              },\n              {\n                title: 'CardList',\n                value: 'cardList',\n              },\n              {\n                title: 'DrawerList',\n                value: 'drawerList',\n              },\n              {\n                title: 'TableList',\n                value: 'tableList'\n              },\n              {\n                title: 'VirtualList',\n                value: 'virtualList'\n              },\n              {\n                title: 'TabList',\n                value: 'tabList'\n              }\n            ]\n          }\n        },\n        extraProps: {\n          setValue(target: any, value: string) {\n            const node = target.getNode();\n            if (value !== 'cardList') {\n              node.setPropValue('items.widget', undefined);\n              node.setPropValue('items.title', undefined);\n              node.setPropValue('items.description', undefined);\n              node.setPropValue('items.column', undefined);\n            } \n          }\n        }\n      },\n      {\n        name: 'items.widget',\n        title: {\n          label: '样式类型',\n          tip: '样式类型',\n        },\n        setter: {\n          componentName: 'RadioGroupSetter',\n          props: {\n            options: [\n              {\n                title: '卡片',\n                value: 'card',\n              },\n              {\n                title: '折叠面板',\n                value: 'collapse',\n              },\n              {\n                title: '标题线',\n                value: 'lineTitle',\n              },\n              {\n                title: '内联',\n                value: 'subInline'\n              }\n            ]\n          }\n        },\n        condition: (target: any) => target.getProps().getPropValue('widget') === 'cardList'\n      },\n      {\n        name: 'items.title',\n        title: { label: '标题', tip: 'title | 卡片主题' },\n        setter: 'StringSetter',\n        condition: (target: any) => target.getProps().getPropValue('widget') === 'cardList'\n      },\n      {\n        name: 'items.description',\n        title: { label: '描述', tip: 'description ｜ 卡片的描述' },\n        setter: 'StringSetter',\n        condition: (target: any) => target.getProps().getPropValue('widget') === 'cardList'\n      },\n      {\n        name: 'items.column',\n        title: {\n          label: '一行多列',\n          tip: 'column ｜ 表单内容分成几列展示',\n        },\n        defaultValue: 1,\n        setter: {\n          componentName: 'RadioGroupSetter',\n          props: {\n            options: [\n              {\n                title: '一列',\n                value: 1,\n              },\n              {\n                title: '两列',\n                value: 2,\n              },\n              {\n                title: '三列',\n                value: 3,\n              },\n              {\n                title: '四列',\n                value: 4\n              }\n            ]\n          }\n        },\n        condition: (target: any) => target.getProps().getPropValue('widget') === 'cardList',\n        extraProps: {\n          setValue(target: any, value: number) {\n            const node = target.getNode();\n            let labelSpan = 8;\n            let wrapperColSpan = 16;\n            if (value === 1) {\n              labelSpan = 4;\n              wrapperColSpan = 6;\n            } else if (value === 2) {\n              wrapperColSpan = 10;\n            }\n            node.setPropValue('labelCol.span', labelSpan);\n            node.setPropValue('wrapperCol.span', wrapperColSpan);\n\n            node.mergeChildren(\n              (child: any) => {\n                let span = 24 / value;\n                child.setPropValue('span', span);\n                return false;\n              },\n              () => {},\n              () => {},\n            );\n          }\n        }\n      }\n    ]\n  }\n];\n\nconst snippets = [\n  {\n    title: '列表',\n    screenshot: 'icon-list',\n    schema: {\n      componentName: 'CardList',\n      props: {\n        title: '列表',\n        description: '这是一个列表',\n        type: 'array',\n        items: {\n          title: '卡片主题',\n          description: '这是一个对象类型',\n          column: 3,\n          type: 'object',\n        }\n      }\n    }\n  }\n]\n\nexport default createMeta('CardList', {\n  title: '列表',\n  category: '布局',\n  group: '基础组件',\n  priority: 1,\n  props,\n  snippets,\n  configure: {\n    supports: {\n      loop: false,\n      condition: false\n    },\n    component: {\n      isContainer: true,\n      isModal: false,\n      nestingRule: {\n        parentWhitelist: ['FormRender', 'Card']\n      }\n    },\n  }\n});\n"
  },
  {
    "path": "tools/schema-builder/src/settings/meta/checkbox.ts",
    "content": "import { createMeta, getNotInputPropsBasic } from '../utils';\n\nexport default createMeta('Checkbox', {\n  title: '是否选择',\n  priority: 994,\n  props: [\n    {\n      title: '基础配置',\n      type: 'group',\n      display: 'accordion',\n      items: getNotInputPropsBasic({\n        name: 'defaultValue',\n        title: { label: '默认值', tip: 'defaultValue | 默认值'},\n        setter: 'BoolSetter'\n      })\n    },\n  ],\n  snippets: [\n    {\n      title: '是否选择',\n      screenshot:'icon-isNot',\n      schema: {\n        componentName: 'Checkbox',\n        props: {\n          title: '是否选择',\n          type: 'boolean'\n        },\n      }\n    }\n  ],\n  \n});\n"
  },
  {
    "path": "tools/schema-builder/src/settings/meta/checkboxes.ts",
    "content": "import { createMeta, optionsProp, getNotInputPropsBasic } from '../utils';\n\nexport default createMeta('Checkboxes', {\n  title: '点击多选框',\n  // priority: 996,\n  props: [\n    {\n      title: '基础配置',\n      type: 'group',\n      display: 'accordion',\n      items: getNotInputPropsBasic({\n        name: 'defaultValue',\n        title: { label: '默认值', tip: 'defaultValue | 默认值'},\n        setter: 'JsonSetter'\n      })\n    },\n    optionsProp,\n    {\n      title: '其他配置',\n      display: 'accordion',\n      type: 'group',\n      items: [\n        {\n          name: 'props.direction',\n          title: { label: '排列方向', tip: '选项的排列方向'},\n          defaultValue: 'row',\n          setter: {\n            componentName: 'RadioGroupSetter',\n            props: {\n              options: [\n                {\n                  title: '水平',\n                  value: 'row',\n                },\n                {\n                  title: '垂直',\n                  value: 'column',\n                },\n              ]\n            }\n          }\n        }\n      ]\n    }\n  ],\n  snippets: [{\n    title: '点击多选',\n    screenshot: 'icon-checkbox',\n    schema: {\n      componentName: 'Checkboxes',\n      props: {\n        title: '点击多选',\n        type: 'array',\n        props: {\n          options: [\n            {\n              label: 'A',\n              value: 'A'\n            },\n            {\n              label: 'B',\n              value: 'B'\n            },\n            {\n              label: 'C',\n              value: 'C'\n            }\n          ],\n          direction: 'row'\n        }\n      }\n    }\n  }]\n});"
  },
  {
    "path": "tools/schema-builder/src/settings/meta/color.ts",
    "content": "import { createMeta, notInputPropsBasic } from '../utils';\n\nexport default createMeta('Color', {\n  title: '颜色选择',\n  category: '其他',\n  props: [\n    {\n      title: '基础配置',\n      type: 'group',\n      display: 'accordion',\n      items: notInputPropsBasic\n    }\n  ],\n  snippets: [\n    {\n      title: '颜色选择',\n      screenshot:'icon-color',\n      schema: {\n        componentName: 'Color',\n        props: {\n          title: '颜色选择',\n        }\n      }\n    }\n  ]\n});\n"
  },
  {
    "path": "tools/schema-builder/src/settings/meta/date.ts",
    "content": "import { createMeta, getInputPropsBasic } from '../utils';\n\nexport default createMeta('DatePicker', {\n  title: '日期选择',\n  props: [\n    {\n      title: '基础配置',\n      type: 'group',\n      display: 'accordion',\n      items: getInputPropsBasic({\n        name: 'defaultValue',\n        title: { label: '默认值', tip: 'defaultValue | 默认值'},\n        setter: 'CustomDateSetter'\n      })\n    },\n  ],\n  snippets: [\n    {\n      title: '日期选择',\n      screenshot: 'icon-date',\n      schema: {\n        componentName: 'DatePicker',\n        props: {\n          title: '日期选择',\n          type: 'string'\n        }\n      }\n    }\n  ]\n});"
  },
  {
    "path": "tools/schema-builder/src/settings/meta/dateRange.ts",
    "content": "import { createMeta, getInputPropsBasic } from '../utils';\n\nexport default createMeta('DateRange', {\n  title: '日期选择区间',\n  props: [\n    {\n      title: '基础配置',\n      type: 'group',\n      display: 'accordion',\n      items: getInputPropsBasic({\n        name: 'defaultValue',\n        title: { label: '默认值', tip: 'defaultValue | 默认值'},\n        setter: 'CustomDateRangeSetter'\n      },\n      {\n        name: 'props.placeholder',\n        title: {\n          label: '提示文字',\n          tip: 'placeholder | 输入框提示文字',\n        },\n        setter: 'JsonSetter',\n        defaultValue: ['开始时间', '结束时间']\n      })\n    },\n  ],\n  snippets: [\n    {\n      title: '日期区间选择',\n      screenshot: 'icon-date',\n      schema: {\n        componentName: 'DateRange',\n        props: {\n          title: '日期区间选择',\n          type: 'range',\n          format: 'date',\n        }\n      }\n    }\n  ]\n});"
  },
  {
    "path": "tools/schema-builder/src/settings/meta/form.ts",
    "content": "\nimport { createMeta } from '../utils';\n\nexport default createMeta('FormRender', {\n  title: '表单',\n  group: '基础组件',\n  category: '表单',\n  props: [\n    {\n      title: '表单布局',\n      display: 'accordion',\n      type: 'group',\n      items: [\n        {\n          name: 'displayType',\n          title: {\n            label: '标签位置',\n            tip: 'displayType ｜ 标签的展示位置',\n          },\n          defaultValue: 'row',\n          setter: {\n            componentName: 'RadioGroupSetter',\n            props: {\n              options: [\n                {\n                  title: '水平居左',\n                  value: 'row'\n                },\n                {\n                  title: '垂直居上',\n                  value: 'column'\n                },\n                {\n                  title: '紧凑',\n                  value: 'inline'\n                }\n              ]\n            }\n          }\n        },\n        {\n          name: 'column',\n          title: {\n            label: '一行多列',\n            tip: 'column ｜ 一行多列',\n          },\n          defaultValue: 1,\n          setter: {\n            componentName: 'RadioGroupSetter',\n            props: {\n              options: [\n                {\n                  title: '一列',\n                  value: 1\n                },\n                {\n                  title: '两列',\n                  value: 2\n                },\n                {\n                  title: '三列',\n                  value: 3\n                },\n                {\n                  title: '四列',\n                  value: 4\n                }\n              ]\n            }\n          }\n        },\n        {\n          name: 'labelWidth',\n          title: {\n            label: '标签宽度',\n            tip: 'labelWidth ｜ 标签宽度',\n          },\n          setter: 'NumberSetter'\n        }\n      ]\n    },\n    {\n      title: '标签和控件栅格总数不能超过24',\n      display: 'block',\n      type: 'group',\n      items: [\n        {\n          name: 'labelCol',\n          title: {\n            label: '标签栅格数',\n            tip: 'labelCol | 栅格占位格数',\n          },\n          setter: {\n            componentName: 'NumberSetter',\n            props: {\n              min: 0,\n              max: 24\n            }\n          }\n        },\n        {\n          name: 'fieldCol',\n          title: {\n            label: '控件栅格数',\n            tip: 'fieldCol | 栅格占位格数'\n          },\n          setter: {\n            componentName: 'NumberSetter',\n            props: {\n              min: 0,\n              max: 24\n            }\n          }\n        },\n        {\n          name: 'maxWidth',\n          title: {\n            label: '最大宽度',\n            tip: 'maxWidth ｜ 最大宽度'\n          },\n          defaultValue: '340px',\n          setter: 'StringSetter'\n        }\n      ]\n    }\n  ],\n  configure: {\n    supports: {\n      loop: false,\n      condition: false\n    },\n    component: {\n      isContainer: true,\n      isModal: false\n    }\n  }\n});\n\n"
  },
  {
    "path": "tools/schema-builder/src/settings/meta/imageInput.ts",
    "content": "import { createMeta, inputPropsBasic } from '../utils';\n\nexport default createMeta('ImageInput', {\n  title: '图片 URL',\n  category: '其他',\n  props: [\n    {\n      title: '基础配置',\n      type: 'group',\n      display: 'accordion',\n      items: inputPropsBasic\n    },\n  ],\n  snippets: [\n    {\n      title: '图片 URL',\n      screenshot: 'icon-image',\n      schema: {\n        componentName: 'ImageInput',\n        props: {\n          title: '图片 URL',\n        },\n      }\n    }\n  ]\n});\n"
  },
  {
    "path": "tools/schema-builder/src/settings/meta/input.ts",
    "content": "import { createMeta, inputPropsBasic } from '../utils';\n\nexport default createMeta('Input', {\n  title: '单行文本',\n  priority: 1000,\n  props: [\n    {\n      title: '基础配置',\n      type: 'group',\n      display: 'accordion',\n      items: inputPropsBasic\n    },\n    {\n      title: '其他配置',\n      type: 'group',\n      display: 'accordion',\n      items: [\n        {\n          name: 'props.prefix',\n          title: { label: '前缀', tip: 'prefix | 前缀' },\n          setter: 'StringSetter'\n        },\n        {\n          name: 'props.suffix',\n          title: { label: '后缀', tip: 'suffix | 后缀' },\n          setter: 'StringSetter'\n        },\n        {\n          name: 'props.addonBefore',\n          title: { label: '前置标签', tip: 'addonBefore | 前置标签' },\n          setter: 'StringSetter'\n        },\n        {\n          name: 'props.addonAfter',\n          title: { label: '后置标签', tip: 'addonAfter | 后置标签' },\n          setter: 'StringSetter'\n        },\n        {\n          name: 'props.allowClear',\n          title: { label: '支持清除', tip: 'allowClear | 支持清除' },\n          setter: 'BoolSetter'\n        }\n      ]\n    }\n  ],\n  snippets: [{\n    label: '单行文本',\n    screenshot: 'icon-input',\n    schema: {\n      componentName: 'Input',\n      props: {\n        title: '单行文本',\n        type: 'string'\n      }\n    }\n  }]\n});\n"
  },
  {
    "path": "tools/schema-builder/src/settings/meta/number.ts",
    "content": "import { createMeta, getInputPropsBasic } from '../utils';\n\nexport default createMeta('InputNumber', {\n  title: '数字输入框',\n  priority: 999,\n  props: [\n    {\n      title: '基础配置',\n      type: 'group',\n      display: 'accordion',\n      items: getInputPropsBasic({\n        name: 'defaultValue',\n        title: { label: '默认值', tip: 'defaultValue | 默认值'},\n        setter: 'NumberSetter'\n      })\n    },\n    {\n      title: '其他配置',\n      display: 'accordion',\n      type: 'group',\n      items: [\n        {\n          name: 'props.prefix',\n          title: { label: '前缀', tip: 'prefix ｜ 前缀' },\n          setter: 'StringSetter',\n        },\n        {\n          name: 'props.addonBefore',\n          title: { label: '前置标签', tip: 'addonBefore ｜ 前置标签' },\n          setter: 'StringSetter',\n        },\n        {\n          name: 'props.addonAfter',\n          title: { label: '后置标签', tip: 'addonAfter ｜ 后置标签' },\n          setter: 'StringSetter',\n        },\n        {\n          name: 'props.precision',\n          title: { label: '数值精度', tip: 'precision ｜ 数值精度' },\n          setter: 'NumberSetter',\n        },\n        {\n          name: 'props.step',\n          title: { label: '单步步长', tip: 'step ｜ 每次改变步数，可以为小数' },\n          setter: ['NumberSetter', 'StringSetter'],\n        }\n      ]\n    }\n  ],\n  snippets: [\n    {\n      label: '数字输入框',\n      screenshot: 'icon-inputNumber',\n      schema: {\n        componentName: 'InputNumber',\n        props: {\n          title: '数字输入框',\n          type: 'number'\n        }\n      }\n    }\n  ]\n});\n\n\n"
  },
  {
    "path": "tools/schema-builder/src/settings/meta/radio.ts",
    "content": "import { getNotInputPropsBasic, createMeta, optionsProp } from '../utils';\n\nexport default createMeta('Radio', {\n  title: '点击单选',\n  priority: 997,\n  props: [\n    {\n      title: '基础配置',\n      type: 'group',\n      display: 'accordion',\n      items: getNotInputPropsBasic({\n        name: 'defaultValue',\n        title: { label: '默认值', tip: 'defaultValue | 默认值'},\n        setter: 'JsonSetter'\n      })\n    },\n    optionsProp,\n    {\n      title: '其他配置',\n      display: 'accordion',\n      type: 'group',\n      items: [\n        {\n          name: 'props.direction',\n          title: { label: '排列方向', tip: '选项的排列方向'},\n          defaultValue: 'row',\n          setter: {\n            componentName: 'RadioGroupSetter',\n            props: {\n              options: [\n                {\n                  title: '水平',\n                  value: 'row',\n                },\n                {\n                  title: '垂直',\n                  value: 'column'\n                },\n              ]\n            }\n          }\n        }\n      ]\n    }\n  ],\n  snippets: [\n    {\n      title: '点击单选',\n      screenshot: 'icon-radio',\n      schema: {\n        componentName: 'Radio',\n        props: {\n          title: '点击单选',\n          type: 'string',\n          props: {\n            options: [\n              {\n                label: 'A',\n                value: 'A'\n              },\n              {\n                label: 'B',\n                value: 'B'\n              },\n              {\n                label: 'C',\n                value: 'C'\n              }\n            ]\n          }\n        }\n      }\n    }\n  ]\n});"
  },
  {
    "path": "tools/schema-builder/src/settings/meta/rate.ts",
    "content": "import { createMeta, getNotInputPropsBasic } from '../utils';\n\nexport default createMeta('Rate', {\n  title: '评分',\n  priority: 992,\n  props: [\n    {\n      title: '基础配置',\n      type: 'group',\n      display: 'accordion',\n      items: getNotInputPropsBasic({\n        name: 'defaultValue',\n        title: { label: '默认值', tip: 'defaultValue | 默认值'},\n        setter: 'NumberSetter'\n      })\n    },\n    {\n      title: '其他配置',\n      display: 'accordion',\n      type: 'group',\n      items: [\n        {\n          name: 'props.allowClear',\n          title: { label: '支持清除', tip: '是否允许清除' },\n          setter: 'BoolSetter',\n          defaultValue: true,\n        },\n        {\n          name: 'props.allowHalf',\n          title: { label: '支持半选', tip: '支持半选' },\n          setter: 'BoolSetter',\n        },\n        {\n          name: 'props.count',\n          title: { label: '总数', tip: 'star 总数' },\n          setter: 'NumberSetter',\n          defaultValue: 5,\n        },\n      ]\n    }\n  ],\n  snippets: [\n    {\n      label: '评分',\n      screenshot: 'icon-rate',\n      schema: {\n        componentName: 'Rate',\n        props: {\n          title: '评分'\n        }\n      }\n    }\n  ]\n});\n"
  },
  {
    "path": "tools/schema-builder/src/settings/meta/select.ts",
    "content": "import { createMeta, getInputPropsBasic, optionsProp } from '../utils';\n\nexport default createMeta('Select', {\n  title: '下拉选择',\n  priority: 998,\n  props: [\n    {\n      title: '基础配置',\n      type: 'group',\n      display: 'accordion',\n      items: getInputPropsBasic({\n        name: 'defaultValue',\n        title: { label: '默认值', tip: 'defaultValue | 默认值'},\n        setter: 'JsonSetter'\n      })\n    },\n    optionsProp\n  ],\n  snippets: [\n    {\n      title: '下拉单选',\n      screenshot: 'icon-select',\n      schema: {\n        componentName: 'Select',\n        props: {\n          title: '下拉单选',\n          type: 'string',\n          props: {\n            options: [\n              {\n                label: 'A',\n                value: 'A'\n              },\n              {\n                label: 'B',\n                value: 'B'\n              }\n            ]\n          }\n        }\n      }\n    },\n    {\n      title: '下拉多选',\n      screenshot: 'icon-multiSelect',\n      schema: {\n        componentName: 'Select',\n        props: {\n          title: '下拉多选',\n          type: 'array',\n          widget: 'multiSelect',\n          props: {\n            options: [\n              {\n                label: 'A',\n                value: 'A'\n              },\n              {\n                label: 'B',\n                value: 'B'\n              }\n            ]\n          }\n        }\n      }\n    }\n  ]\n});\n\n\n\n\n\n"
  },
  {
    "path": "tools/schema-builder/src/settings/meta/slider.ts",
    "content": "import { createMeta, getNotInputPropsBasic } from '../utils';\n\nexport default createMeta('Slider', {\n  title: '滑动条',\n  priority: 991,\n  props: [\n    {\n      title: '基础配置',\n      type: 'group',\n      display: 'accordion',\n      items: getNotInputPropsBasic({\n        name: 'defaultValue',\n        title: { label: '默认值', tip: 'defaultValue | 默认值'},\n        setter: 'NumberSetter'\n      })\n    },\n    {\n      title: '其他配置',\n      display: 'accordion',\n      type: 'group',\n      items: [\n        {\n          name: 'props.hideInput',\n          title: {label: '隐藏输入框', tip: '隐藏输入框'},\n          setter: 'BoolSetter',\n        }\n      ]\n    }\n  ],\n  snippets: [\n    {\n      label: '滑动条',\n      screenshot: 'icon-slider',\n      schema: {\n        componentName: 'Slider',\n        props: {\n          title: '滑动条'\n        }\n      }\n    }\n  ]\n});\n"
  },
  {
    "path": "tools/schema-builder/src/settings/meta/switch.ts",
    "content": "import { createMeta, getNotInputPropsBasic } from '../utils';\n\nexport default createMeta('Switch', {\n  title: '开关',\n  priority: 993,\n  props: [\n    {\n      title: '基础配置',\n      type: 'group',\n      display: 'accordion',\n      items: getNotInputPropsBasic({\n        name: 'defaultValue',\n        title: { label: '默认值', tip: 'defaultValue | 默认值'},\n        setter: 'BoolSetter'\n      })\n    },\n    {\n      title: '其他配置',\n      display: 'accordion',\n      type: 'group',\n      items: [\n        {\n          name: 'props.checkedChildren',\n          title: { label: '选中时内容', tip: 'checkedChildren | 选中时内容' },\n          setter: 'StringSetter',\n        },\n        {\n          name: 'props.unCheckedChildren',\n          title: { label: '非选中时内容', tip: 'unCheckedChildren | 非选中时内容' },\n          setter: 'StringSetter',\n        }\n      ]\n    }\n  ],\n  snippets: [\n    {\n      label: '开关',\n      screenshot: 'icon-switch',\n      schema: {\n        componentName: 'Switch',\n        props: {\n          title: '开关',\n          type: 'boolean'\n        }\n      }\n    }\n  ]\n});"
  },
  {
    "path": "tools/schema-builder/src/settings/meta/textarea.ts",
    "content": "import { createMeta, inputPropsBasic } from '../utils';\n\nexport default createMeta('TextArea', {\n  title: '多行文本',\n  priority: 995,\n  props: [\n    {\n      title: '基础配置',\n      type: 'group',\n      display: 'accordion',\n      items: inputPropsBasic\n    },\n    {\n      title: '其他配置',\n      display: 'accordion',\n      type: 'group',\n      items: [\n        {\n          name: 'props.allowClear',\n          title: { label: '支持清除', tip: 'allowClear | 支持清除' },\n          setter: 'BoolSetter'\n        },\n        {\n          name: 'props.showCount',\n          title: { label: '展示字数', tip: 'showCount ｜ 是否展示字数' },\n          setter: 'BoolSetter',\n        },\n        {\n          name: 'props.autoSize',\n          title: { label: '高度自动', tip: 'autoSize ｜ 文本域高度自适应内容' },\n          setter: 'BoolSetter',\n        },\n        {\n          name: 'props.rows',\n          title: { label: '指定行数', tip: 'minRows | 指定显示的行数' },\n          setter: 'NumberSetter',\n        },\n        {\n          name: 'props.minLength',\n          title: { label: '最小长度', tip: 'minLength | 内容最小长度' },\n          setter: 'NumberSetter'\n        },\n        {\n          name: 'props.maxLength',\n          title: { label: '最大长度', tip: 'maxLength | 内容最大长度' },\n          setter: 'NumberSetter'\n        },\n      ]\n    }\n  ],\n  snippets: [\n    {\n      label: '多行文本',\n      screenshot: 'icon-textarea',\n      schema: {\n        componentName: 'TextArea',\n        props: {\n          title: '多行文本',\n          type: 'string'\n        }\n      }\n    }\n  ]\n});\n"
  },
  {
    "path": "tools/schema-builder/src/settings/meta/time.ts",
    "content": "import { createMeta, getInputPropsBasic } from '../utils';\n\nexport default createMeta('TimePicker', {\n  title: '时间选择',\n  props: [\n    {\n      title: '基础配置',\n      type: 'group',\n      display: 'accordion',\n      items: getInputPropsBasic({\n        name: 'defaultValue',\n        title: { label: '默认值', tip: 'default | 默认值'},\n        setter: 'CustomTimeSetter'\n      })\n    }\n  ],\n  snippets: [\n    {\n      title: '时间选择',\n      screenshot: 'icon-time',\n      schema: {\n        componentName: 'TimePicker',\n        props: {\n          title: '时间选择',\n          type: 'string',\n          format: 'time'\n        }\n      }\n    }\n  ]\n});"
  },
  {
    "path": "tools/schema-builder/src/settings/meta/timeRange.ts",
    "content": "import { createMeta, getInputPropsBasic } from '../utils';\n\nexport default createMeta('TimeRange', {\n  title: '时间区间选择',\n  props: [\n    {\n      title: '基础配置',\n      type: 'group',\n      display: 'accordion',\n      items: getInputPropsBasic({\n        name: 'defaultValue',\n        title: { label: '默认值', tip: 'default | 默认值'},\n        setter: 'CustomTimeRangeSetter'\n      },\n      {\n        name: 'props.placeholder',\n        title: {\n          label: '提示文字',\n          tip: 'placeholder | 输入框提示文字',\n        },\n        defaultValue: ['开始时间', '结束时间'],\n        setter: 'JsonSetter',\n      })\n    }\n  ],\n  snippets: [\n    {\n      title: '时间区间选择',\n      screenshot: 'icon-time',\n      schema: {\n        componentName: 'TimeRange',\n        props: {\n          title: '时间区间',\n          type: 'range',\n          format: 'time'\n        }\n      }\n    }\n  ]\n});"
  },
  {
    "path": "tools/schema-builder/src/settings/meta/treeSelect.ts",
    "content": "import { createMeta, getInputPropsBasic } from '../utils';\n\nexport default createMeta('TreeSelect', {\n  title: '树选择',\n  category: '其他',\n  props: [\n    {\n      title: '基础配置',\n      type: 'group',\n      display: 'accordion',\n      items: getInputPropsBasic({\n        name: 'defaultValue',\n        title: { label: '默认值', tip: 'defaultValue | 默认值'},\n        setter: 'JsonSetter'\n      })\n    },\n    {\n      title: '其他配置',\n      display: 'accordion',\n      type: 'group',\n      items: [\n        {\n          name: 'props.treeData',\n          title: { label: '数据源', tip: '数据源' },\n          setter: 'JsonSetter',\n        },\n        {\n          name: 'props.multiple',\n          title: {\n            label: '支持多选',\n            tip: '支持多选（当设置 treeCheckable 时自动变为 true）',\n          },\n          setter: 'BoolSetter',\n        },\n        {\n          name: 'props.allowClear',\n          title: { label: '支持清除', tip: '是否允许清除' },\n          setter: 'BoolSetter',\n        },\n        {\n          name: 'props.treeCheckable',\n          title: { label: '显示勾选框', tip: '显示勾选框' },\n          setter: 'BoolSetter',\n        },\n        {\n          name: 'props.treeDefaultExpandAll',\n          title: { label: '默认展开所有树节点', tip: '默认展开所有树节点' },\n          setter: 'BoolSetter',\n        }\n      ],\n    }\n  ],\n  snippets: [\n    {\n      label: '树选择',\n      screenshot: 'icon-tree',\n      schema: {\n        componentName: 'TreeSelect',\n        props: {\n          title: '树选择',\n          props: {\n            treeData: [\n              {\n                value: 'parent 1',\n                title: 'parent 1',\n                children: [\n                  {\n                    value: 'parent 1-0',\n                    title: 'parent 1-0',\n                    children: [\n                      {\n                        value: 'leaf1',\n                        title: 'leaf1',\n                      },\n                      {\n                        value: 'leaf2',\n                        title: 'leaf2',\n                      },\n                    ],\n                  },\n                ]\n              }\n            ]\n          }\n        }\n      }\n    }\n  ]\n});\n"
  },
  {
    "path": "tools/schema-builder/src/settings/meta/urlInput.ts",
    "content": "import { createMeta, inputPropsBasic } from '../utils';\n\nexport default createMeta('UrlInput', {\n  title: '链接输入框',\n  category: '其他',\n  props: [\n    {\n      title: '基础配置',\n      type: 'group',\n      display: 'accordion',\n      items: inputPropsBasic\n    },\n    {\n      title: '其他配置',\n      display: 'accordion',\n      type: 'group',\n      items: [\n        {\n          name: 'props.addonText',\n          title: { label: '按钮文案', tip: '跳转按钮的文案配置' },\n          defaultValue: '测试链接',\n          setter: 'StringSetter'\n        }\n      ]\n    }\n  ],\n  snippets: [\n    {\n      title: '链接输入框',\n      screenshot: 'icon-link',\n      schema: {\n        componentName: 'UrlInput',\n        props: {\n          title: '链接输入框',\n        },\n      }\n    }\n  ]\n});\n"
  },
  {
    "path": "tools/schema-builder/src/settings/utils.ts",
    "content": "import cloneDeep from 'lodash/cloneDeep';\n\nexport const inputPropsBasic = [\n  {\n    name: 'title',\n    title: { label: '标题', tip: 'title | 标题' },\n    setter: 'StringSetter',\n  },\n  {\n    name: 'code',\n    title: { label: '字段名', tip: 'key | 字段名' },\n    setter: 'StringSetter',\n  },\n  {\n    name: 'defaultValue',\n    title: { label: '默认值', tip: 'defaultValue | 默认值'},\n    setter: 'StringSetter'\n  },\n  {\n    name: 'props.placeholder',\n    title: { label: '提示文案', tip: 'placeholder | 提示文案' },\n    setter: 'StringSetter'\n  },\n  {\n    name: 'description',\n    title: { label: '补充说明', tip: 'description | 补充说明' },\n    setter: 'StringSetter',\n  },\n  {\n    name: 'tooltip.title',\n    title: { label: '气泡提示', tip: 'tooltip.title | 气泡提示文案' },\n    setter: 'StringSetter',\n  },\n  {\n    name: 'extra',\n    title: { label: '额外提示', tip: 'extra | 额外的提示信息'},\n    setter: 'StringSetter'\n  },\n  {\n    name: 'disabled',\n    title: { label: '禁用', tip: 'disabled | 禁用' },\n    setter: 'FrExpressionSetter'\n  },\n  {\n    name: 'hidden',\n    title: { label: '隐藏', tip: 'hidden | 隐藏' },\n    setter: 'FrExpressionSetter'\n  },\n  {\n    name: 'readOnly',\n    title: { label: '只读', tip: 'readOnly | 只读' },\n    setter: 'FrExpressionSetter'\n  },\n  {\n    name: 'readOnlyWidget',\n    title: { label: '只读组件', tip: 'readOnlyWidget | 只读组件' },\n    setter: 'StringSetter',\n    condition: (target: any) => !!target.getProps().getPropValue('readOnly')\n  }\n];\n\nexport const notInputPropsBasic = [\n  {\n    name: 'title',\n    title: { label: '标题', tip: 'title | 标题' },\n    setter: 'StringSetter',\n  },\n  {\n    name: 'code',\n    title: { label: '字段名', tip: 'key | 字段名' },\n    setter: 'StringSetter',\n  },\n  {\n    name: 'defaultValue',\n    title: { label: '默认值', tip: 'defaultValue | 默认值'},\n    setter: 'StringSetter'\n  },\n  {\n    name: 'description',\n    title: { label: '补充说明', tip: 'description | 补充说明' },\n    setter: 'StringSetter',\n  },\n  {\n    name: 'tooltip',\n    title: { label: '气泡提示', tip: 'tooltip | 气泡提示文案' },\n    setter: 'StringSetter',\n  },\n  {\n    name: 'extra',\n    title: { label: '额外提示', tip: 'extra | 额外的提示信息'},\n    setter: 'StringSetter'\n  },\n  {\n    name: 'disabled',\n    title: { label: '禁用', tip: 'disabled | 禁用' },\n    setter: 'FrExpressionSetter'\n  },\n  {\n    name: 'hidden',\n    title: { label: '隐藏', tip: 'hidden | 隐藏' },\n    setter: 'FrExpressionSetter'\n  },\n  {\n    name: 'readOnly',\n    title: { label: '只读', tip: 'readOnly | 只读' },\n    setter: 'FrExpressionSetter'\n  },\n  {\n    name: 'readOnlyWidget',\n    title: { label: '只读组件', tip: 'readOnlyWidget | 只读组件' },\n    setter: 'StringSetter',\n    condition: (target: any) => !!target.getProps().getPropValue('readOnly')\n  }\n];\n\nexport const optionsProp = {\n  display: 'accordion',\n  name: 'props.options',\n  title: { label: '选项配置', tip: 'options ｜ 选项配置' },\n  setter: {\n    componentName: 'ArraySetter',\n    props: {\n      itemSetter: {\n        componentName: 'ObjectSetter',\n        initialValue: () => ({\n          label: '选项名',\n          value: uuid()\n        }),\n        props: {\n          config: {\n            items: [\n              {\n                name: 'label',\n                title: '选项名',\n                important: true,\n                setter: 'StringSetter',\n              },\n              {\n                name: 'value',\n                title: '选项值',\n                setter: ['StringSetter', 'NumberSetter'],\n                important: true,\n              },\n              {\n                name: 'disabled',\n                title: '禁用',\n                setter: 'JsonSetter',\n              }\n            ]\n          }\n        }\n      }\n    }\n  }\n};\n\nexport const getInputPropsBasic = (defaultValueProp: any, placeholder?: any) => {\n  const result = cloneDeep(inputPropsBasic);\n  result.splice(2, 0, defaultValueProp);\n  if (placeholder) {\n    result.splice(3, 0, defaultValueProp);\n  }\n  return result;\n}\n\nexport const getNotInputPropsBasic = (defaultValueProp: any) => {\n  const result = cloneDeep(notInputPropsBasic);\n  result.splice(2, 0, defaultValueProp);\n  return result;\n}\n\nexport const uuid = () => {\n  return ((Math.random() * 1e6) >> 0).toString(36);\n};\n\nexport const createMeta = (componentName: string, params: any) => {\n  return {\n    componentName,\n    docUrl: '',\n    screenshot: '',\n    devMode: 'proCode',\n    npm: {\n      package: '@ali/form-render-material',\n      version: '1.0.0',\n      exportName: componentName,\n      main: 'src/index.tsx',\n      destructuring: true,\n      subName: '',\n    },\n    configure: {\n      supports: {\n        loop: false,\n        condition: false\n      },\n      component: {\n        isContainer: false,\n        isModal: false,\n        nestingRule: {\n          parentWhitelist: ['FormRender', 'Card', 'CardList', 'TableList']\n        }\n      },\n    },\n    group: '基础组件',\n    category: '常用',\n    icon: 'https://img.alicdn.com/imgextra/i4/O1CN01gxzRdT1hm9KXRbZkU_!!6000000004319-2-tps-200-200.png',\n    ...params\n  };\n}\n\n"
  },
  {
    "path": "tools/schema-builder/src/type.ts",
    "content": "interface TProperties {\n  [key: string]: any;\n}\n\ninterface TLogo {\n  title?: string;\n  image?: string;\n  href?: string;\n}\n\ninterface TSchema {\n  \"type\": \"object\",\n  \"properties\": TProperties\n}\n\ninterface TToolBtn {\n  text: string;\n  order: number;\n  onClick: (schema: TSchema) => void;\n}\n\nexport interface TSchemaBuilder {\n  logo?: TLogo;\n  importBtn?: boolean;\n  exportBtn?: boolean;\n  clearBtn?: boolean | TToolBtn;\n  saveBtn?: boolean | TToolBtn;\n  pubBtn?: boolean | TToolBtn;\n  extraBtns?: TToolBtn[];\n  defaultValue?: TSchema;\n  widgets?: any;\n  settings?: any;\n  editorWidgets?: any;\n  onMount?: () => void;\n  [key: string]: any\n}"
  },
  {
    "path": "tools/schema-builder/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"baseUrl\": \".\",\n    \"declaration\": true,\n    \"lib\": [\"es2015\", \"dom\"],\n    // Target latest version of ECMAScript.\n    \"target\": \"esnext\",\n    // Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'.\n    \"module\": \"esnext\",\n    // Search under node_modules for non-relative imports.\n    \"moduleResolution\": \"node\",\n    // Process & infer types from .js files.\n    \"allowJs\": true,\n    // Report errors in .js files.\n    \"checkJs\": false,\n    // Don't emit; allow Babel to transform files.\n    // \"noEmit\": true,\n    // Enable strictest settings like strictNullChecks & noImplicitAny.\n    \"strict\": true,\n    // Allow default imports from modules with no default export. This does not affect code emit, just typechecking.\n    \"allowSyntheticDefaultImports\": true,\n    // Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'.\n    \"esModuleInterop\": true,\n    // Specify JSX code generation: 'preserve', 'react-native', or 'react'.\n    \"jsx\": \"preserve\",\n    // Import emit helpers (e.g. __extends, __rest, etc..) from tslib\n    \"importHelpers\": true,\n    // Enables experimental support for ES7 decorators.\n    \"experimentalDecorators\": true,\n    // Generates corresponding .map file.\n    \"sourceMap\": true,\n    // Disallow inconsistently-cased references to the same file.\n    \"forceConsistentCasingInFileNames\": true,\n    // Allow json import\n    \"resolveJsonModule\": true,\n    // skip type checking of declaration files\n    \"skipLibCheck\": true,\n    \"outDir\": \"lib\"\n  },\n  \"include\": [\n    \"./src/\"\n  ],\n  \"exclude\": [\"**/test\", \"**/lib\", \"**/es\", \"node_modules\",\"/docs\"]\n}\n"
  },
  {
    "path": "tsconfig.jest.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"ES5\",\n    \"types\": [\"jest\", \"jest-enzyme\"],\n    \"allowJs\": true,\n    \"skipLibCheck\": false,\n    \"esModuleInterop\": true,\n    \"allowSyntheticDefaultImports\": true,\n    \"jsx\": \"react\",\n    \"paths\": {\n      \"@@/*\": [\".dumi/tmp/*\"]\n    }\n  },\n  \"exclude\": [\"./packages/*/esm\", \"./packages/*/lib\"]\n}\n"
  },
  {
    "path": "turbo.json",
    "content": "{\n  \"$schema\": \"https://turborepo.org/schema.json\",\n  \"pipeline\": {\n    \"build\": {\n      \"dependsOn\": [\"^build\"],\n      \"outputs\": [\"dist/**\"]\n    }\n  }\n}\n"
  },
  {
    "path": "typing.d.ts",
    "content": "interface Window {\n  publicPath: string;\n}\n"
  },
  {
    "path": "vitest.config.ts",
    "content": "/// <reference types=\"vitest\" />\n\nimport { defineConfig } from 'vitest/config';\nimport react from '@vitejs/plugin-react';\n\nexport default defineConfig({\n  plugins: [\n    react({\n      babel: {\n        plugins: ['@babel/plugin-transform-react-jsx'],\n      },\n    }) as any,\n  ],\n  test: {\n    globals: true,\n    environment: 'jsdom',\n  },\n});\n"
  },
  {
    "path": "widgets/AsyncOptions/.fatherrc.js",
    "content": "// 通用的配置，可以在每个package里写 fatherrc.js 来覆盖\nexport default {\n  esm: 'rollup',\n  cjs: 'rollup',\n  disableTypeCheck: false, // 如果出了问题，这个可以改成true\n  extraBabelPlugins: [\n    [\n      'babel-plugin-import',\n      {\n        libraryName: 'antd',\n        libraryDirectory: 'lib',\n        style: true,\n      },\n    ],\n  ],\n};\n"
  },
  {
    "path": "widgets/AsyncOptions/README.md",
    "content": ""
  },
  {
    "path": "widgets/AsyncOptions/package.json",
    "content": "{\n  \"name\": \"@form-render/async-options\",\n  \"version\": \"1.0.0\",\n  \"keywords\": [\n    \"FormRender\",\n    \"Render\",\n    \"React\",\n    \"Json Schema\",\n    \"Ant Design\"\n  ],\n  \"main\": \"dist/index.js\",\n  \"module\": \"dist/index.esm.js\",\n  \"scripts\": {\n    \"beta\": \"npm publish --tag beta\",\n    \"build\": \"father-build\",\n    \"prepare\": \"npm run build\",\n    \"prettier\": \"prettier --write \\\"**/*.{js,jsx,tsx,ts,less,md,json}\\\"\",\n    \"postpublish\": \"git push --tags\",\n    \"release\": \"npm publish --access public\",\n    \"test\": \"umi-test\",\n    \"test:coverage\": \"umi-test --coverage\"\n  },\n  \"lint-staged\": {\n    \"*.{js,jsx,less,md,json}\": [\n      \"prettier --write\"\n    ],\n    \"*.ts?(x)\": [\n      \"prettier --parser=typescript --write\"\n    ]\n  },\n  \"dependencies\": {\n    \"axios\": \"^0.21.1\"\n  },\n  \"peerDependencies\": {\n    \"antd\": \"^4.x\",\n    \"react\": \">=16.8.0\"\n  },\n  \"gitHooks\": {\n    \"pre-commit\": \"lint-staged\"\n  }\n}\n"
  },
  {
    "path": "widgets/AsyncOptions/src/index.js",
    "content": "import React from 'react';\n\nexport default function index() {\n  return <div>haha</div>;\n}\n"
  },
  {
    "path": "widgets/RichText/.fatherrc.js",
    "content": "// 通用的配置，可以在每个package里写 fatherrc.js 来覆盖\nexport default {\n  esm: 'rollup',\n  cjs: 'rollup',\n  disableTypeCheck: false, // 如果出了问题，这个可以改成true\n  extraBabelPlugins: [\n    [\n      'babel-plugin-import',\n      {\n        libraryName: 'antd',\n        libraryDirectory: 'lib',\n        style: true,\n      },\n    ],\n  ],\n};\n"
  },
  {
    "path": "widgets/RichText/README.md",
    "content": ""
  },
  {
    "path": "widgets/RichText/package.json",
    "content": "{\n  \"name\": \"@form-render/rich-text\",\n  \"version\": \"1.0.2\",\n  \"keywords\": [\n    \"FormRender\",\n    \"Render\",\n    \"React\",\n    \"Json Schema\",\n    \"Ant Design\"\n  ],\n  \"main\": \"dist/index.js\",\n  \"module\": \"dist/index.esm.js\",\n  \"scripts\": {\n    \"beta\": \"npm publish --tag beta\",\n    \"build\": \"father-build\",\n    \"prepare\": \"npm run build\",\n    \"prettier\": \"prettier --write \\\"**/*.{js,jsx,tsx,ts,less,md,json}\\\"\",\n    \"postpublish\": \"git push --tags\",\n    \"release\": \"npm publish --access public\",\n    \"test\": \"umi-test\",\n    \"test:coverage\": \"umi-test --coverage\"\n  },\n  \"lint-staged\": {\n    \"*.{js,jsx,less,md,json}\": [\n      \"prettier --write\"\n    ],\n    \"*.ts?(x)\": [\n      \"prettier --parser=typescript --write\"\n    ]\n  },\n  \"dependencies\": {\n    \"braft-editor\": \"^2.3.9\"\n  },\n  \"peerDependencies\": {\n    \"antd\": \"^4.x\",\n    \"react\": \">=16.8.0\"\n  },\n  \"gitHooks\": {\n    \"pre-commit\": \"lint-staged\"\n  }\n}\n"
  },
  {
    "path": "widgets/RichText/src/index.js",
    "content": "import BraftEditor from 'braft-editor';\nimport 'braft-editor/dist/index.css';\nimport React, { useEffect, useState } from 'react';\n\nconst { createEditorState } = BraftEditor;\n\nconst RichTextEditor = ({ name, onChange, value, ...rest }) => {\n  const [editor, set] = useState(null);\n\n  useEffect(() => {\n    if (value !== undefined) {\n      set(createEditorState(value));\n    }\n  }, [value]);\n\n  const handleChange = editor => {\n    set(editor);\n    // const htmlContent = editor.toHTML();\n    // onChange(name, htmlContent);\n  };\n\n  const onSave = () => {\n    // Pressing ctrl + s when the editor has focus will execute this method\n    // Before the editor content is submitted to the server, you can directly call editorState.toHTML () to get the HTML content\n    const htmlContent = editor.toHTML();\n    if (name) {\n      onChange(name, htmlContent);\n    } else {\n      onChange(htmlContent);\n    }\n  };\n\n  return (\n    <div style={{ border: '1px solid rgba(0,0,0,0.2)' }}>\n      <BraftEditor\n        contentStyle={{ minHeight: 100, height: 'auto', maxHeight: 500 }}\n        value={editor}\n        onChange={handleChange}\n        onSave={onSave}\n        onBlur={onSave}\n      />\n    </div>\n  );\n};\n\nexport default RichTextEditor;\n"
  },
  {
    "path": "widgets/template/.fatherrc.js",
    "content": "// 通用的配置，可以在每个package里写 fatherrc.js 来覆盖\nexport default {\n  esm: 'rollup',\n  cjs: 'rollup',\n  disableTypeCheck: false, // 如果出了问题，这个可以改成true\n  extraBabelPlugins: [\n    [\n      'babel-plugin-import',\n      {\n        libraryName: 'antd',\n        libraryDirectory: 'lib',\n        style: true,\n      },\n    ],\n  ],\n};\n"
  },
  {
    "path": "widgets/template/README.md",
    "content": ""
  },
  {
    "path": "widgets/template/package.json",
    "content": "{\n  \"name\": \"@form-render/template\",\n  \"version\": \"1.0.0\",\n  \"keywords\": [\n    \"FormRender\",\n    \"Render\",\n    \"React\",\n    \"Json Schema\",\n    \"Ant Design\"\n  ],\n  \"main\": \"dist/index.js\",\n  \"module\": \"dist/index.esm.js\",\n  \"scripts\": {\n    \"beta\": \"npm publish --tag beta\",\n    \"build\": \"father-build\",\n    \"prepare\": \"npm run build\",\n    \"prettier\": \"prettier --write \\\"**/*.{js,jsx,tsx,ts,less,md,json}\\\"\",\n    \"postpublish\": \"git push --tags\",\n    \"release\": \"npm publish --access public\",\n    \"test\": \"umi-test\",\n    \"test:coverage\": \"umi-test --coverage\"\n  },\n  \"lint-staged\": {\n    \"*.{js,jsx,less,md,json}\": [\n      \"prettier --write\"\n    ],\n    \"*.ts?(x)\": [\n      \"prettier --parser=typescript --write\"\n    ]\n  },\n  \"peerDependencies\": {\n    \"antd\": \"^4.x\",\n    \"react\": \">=16.8.0\"\n  },\n  \"gitHooks\": {\n    \"pre-commit\": \"lint-staged\"\n  }\n}\n"
  },
  {
    "path": "widgets/template/src/index.js",
    "content": "import React from 'react';\n\nexport default function Template() {\n  return <div>hello world</div>;\n}\n"
  }
]