update docs

This commit is contained in:
chenos 2021-07-14 08:43:18 +08:00
parent e50267d64c
commit 03da153052
47 changed files with 583 additions and 978 deletions

View File

@ -1,27 +0,0 @@
---
title: API - 接口
nav:
title: 组件
path: /client
order: 99
group:
order: 3
title: 其他
path: /client/others
---
# API - 接口
## setGlobalState
## setGlobalActionState
## useGlobalState
## useGlobalAction
## refreshGlobalAction
## useAction
## doAction

View File

@ -1,110 +0,0 @@
---
title: Drawer - 抽屉
nav:
title: 组件
path: /client
group:
order: 3
title: 其他
path: /client/others
---
# Drawer - 抽屉
通过 `Drawer.open(props)` 方法打开抽屉,无需预渲染,可在触发事件中使用。
### 基础抽屉
```tsx
/**
* title: 基础抽屉
*/
import React from 'react';
import { Button } from 'antd';
import Drawer from './index.tsx';
export default () => (
<Button onClick={() => {
Drawer.open({
title: 'Basic Drawer',
content: () => {
return (
<div>
<p>Some contents...</p>
<p>Some contents...</p>
<p>Some contents...</p>
</div>
)
},
});
}}>Open Drawer</Button>
);
```
### 多层抽屉
```tsx
/**
* title: 多层抽屉
* desc: 有多少层就执行多少个 `Drawer.open(props)`
*/
import React from 'react';
import { Button } from 'antd';
import Drawer from './index.tsx';
export default () => (
<Button onClick={() => {
Drawer.open({
title: 'Multi-level drawer',
content: () => {
return (
<div>
<Button onClick={() => {
Drawer.open({
title: 'Two-level Drawer',
content: () => {
return (
<div>This is two-level drawer</div>
)
},
});
}}>Two-level Drawer</Button>
</div>
)
},
});
}}>Open drawer</Button>
);
```
### 关闭时提示
```tsx
/**
* title: 关闭时提示
* desc: 通过 `closeWithConfirm(props)` 方法触发,更多参数请参考 `Modal.confirm`
*/
import React from 'react';
import { Button } from 'antd';
import Drawer from './index.tsx';
export default () => (
<Button onClick={() => {
Drawer.open({
title: 'Basic Drawer',
content: ({ closeWithConfirm }) => {
closeWithConfirm({
title: '您确定关闭抽屉吗?'
});
return (
<div>
<p>Some contents...</p>
<p>Some contents...</p>
<p>Some contents...</p>
</div>
)
},
});
}}>Open Drawer</Button>
);
```

View File

@ -1,206 +0,0 @@
import React, { Fragment, useLayoutEffect, useRef, useState } from 'react';
import ReactDOM, { createPortal } from 'react-dom';
import { Modal, Drawer as AntdDrawer } from 'antd';
import { DrawerProps } from 'antd/lib/drawer';
import { useContext } from 'react';
import { ConfigProvider } from 'antd';
import zhCN from 'antd/lib/locale/zh_CN';
import { ModalFuncProps } from 'antd/lib/modal/Modal'
import isNum from 'lodash/isNumber';
import isBool from 'lodash/isBoolean';
import isStr from 'lodash/isString';
import './style.less';
export const usePrefixCls = (
tag?: string,
props?: {
prefixCls?: string;
},
) => {
const { getPrefixCls } = useContext(ConfigProvider.ConfigContext);
return getPrefixCls(tag, props?.prefixCls);
};
type DrawerTitle = string | number | React.ReactElement;
const isDrawerTitle = (props: any): props is DrawerTitle => {
return (
isNum(props) || isStr(props) || isBool(props) || React.isValidElement(props)
);
};
const getDrawerProps = (props: any): DrawerProps => {
if (isDrawerTitle(props)) {
return {
title: props,
};
} else {
return props;
}
};
const createElement = (content, props?: any) => {
if (!content) {
return null;
}
if (typeof content === 'string') {
return content;
}
if (React.isValidElement(content)) {
return content;
}
return React.createElement(content, props);
};
export interface IDrawer {
open(props?: any): void;
close(): void;
}
export function Drawer(title: DrawerProps, content: any): IDrawer;
export function Drawer(title: DrawerTitle, content: any): IDrawer;
export function Drawer(title: any, content: any): IDrawer {
document.querySelectorAll('.env-root').forEach((el) => {
el.className = 'env-root env-root-push';
});
const env = {
root: document.createElement('div'),
promise: null,
};
env.root.className = 'env-root';
const props = getDrawerProps(title);
const drawerProps = {
width: '75%',
...props,
onClose: (e: any) => {
props?.onClose?.(e);
drawer.close();
},
afterVisibleChange: (visible: boolean) => {
props?.afterVisibleChange?.(visible);
if (visible) return;
ReactDOM.unmountComponentAtNode(env.root);
env.root?.parentNode?.removeChild(env.root);
env.root = undefined;
},
};
const drawer: any = {
open: (props: any) => {
render(
false,
() => {
drawer.closeWithConfirm = null;
drawer.close();
},
() => {
drawer.close();
},
);
setTimeout(() => {
render(
true,
() => {
drawer.closeWithConfirm = null;
drawer.close();
},
() => {
drawer.close();
},
);
});
},
close: () => {
if (!env.root) return;
if (drawer.closeWithConfirm) {
Modal.confirm({
okText: '确定',
cancelText: '取消',
...drawer.closeWithConfirm,
onOk() {
drawer.closeWithConfirm = null;
const els = document.querySelectorAll('.env-root-push');
if (els.length) {
const last = els[els.length - 1];
last.className = 'env-root';
}
render(false);
},
});
} else {
const els = document.querySelectorAll('.env-root-push');
if (els.length) {
const last = els[els.length - 1];
last.className = 'env-root';
}
render(false);
}
},
closeWithConfirm: null,
};
const closeWithConfirm = (props) => {
drawer.closeWithConfirm = props;
};
const render = (visible = true, resolve?: () => any, reject?: () => any) => {
ReactDOM.render(
<ConfigProvider locale={zhCN}>
<AntdDrawer {...drawerProps} className={'nb-drawer'} visible={visible}>
{createElement(content, {
resolve,
reject,
closeWithConfirm,
})}
</AntdDrawer>
</ConfigProvider>,
env.root,
);
};
document.body.appendChild(env.root);
return drawer;
}
const DrawerFooter: React.FC = (props) => {
const ref = useRef<HTMLDivElement>();
const [footer, setFooter] = useState<HTMLDivElement>();
const footerRef = useRef<HTMLDivElement>();
const prefixCls = usePrefixCls('drawer');
useLayoutEffect(() => {
const content = ref.current?.closest(`.${prefixCls}-wrapper-body`);
if (content) {
if (!footerRef.current) {
footerRef.current = content.querySelector(`.${prefixCls}-footer`);
if (!footerRef.current) {
footerRef.current = document.createElement('div');
footerRef.current.classList.add(`${prefixCls}-footer`);
content.appendChild(footerRef.current);
}
}
setFooter(footerRef.current);
}
});
footerRef.current = footer;
return (
<div ref={ref} style={{ display: 'none' }}>
{footer && createPortal(props.children, footer)}
</div>
);
};
interface ContentPorps {
resolve?: () => any;
closeWithConfirm?: (props: ModalFuncProps) => any;
}
Drawer.open = (props: DrawerProps & { content: (contentPorps?: ContentPorps) => any }) => {
const { content, visible, ...others } = props;
return Drawer(others, content).open({ visible });
};
Drawer.Footer = DrawerFooter;
export default Drawer;

View File

@ -1,18 +0,0 @@
.env-root-push + style + div > div {
transform: translateX(-10%);
}
.env-root-push + div > div {
transform: translateX(-10%);
}
.ant-drawer.nb-drawer .ant-drawer-content-wrapper {
width: 75%;
max-width: 1000px;
}
@media only screen and (max-width: 800px) {
.ant-drawer.nb-drawer .ant-drawer-content-wrapper {
width: 100% !important;
}
.ant-drawer.noco-drawer .ant-drawer-content-wrapper {
width: 100% !important;
}
}

View File

@ -1,14 +0,0 @@
---
title: AdminLayout - 后台布局
nav:
title: 组件
path: /client
group:
order: 2
title: Templates
path: /client/templates
---
# AdminLayout - 后台布局
内置的后台布局模板,提供了基础的菜单和路由切换。

View File

@ -1,14 +0,0 @@
---
title: AuthLayout - 登录布局
nav:
title: 组件
path: /client
group:
order: 2
title: Templates
path: /client/templates
---
# AuthLayout - 登录布局
内置的登录、注册页布局

View File

@ -0,0 +1,14 @@
---
title: 全站演示
nav:
title: 组件
path: /client
toc: menu
group:
order: 0
title: 概念
path: /client
---
<code src="./demos/demo1.tsx"/>

View File

@ -1,14 +1,14 @@
---
title: 全站演示
title: 介绍
nav:
title: 组件
path: /client
toc: menu
group:
order: 0
title: 全站演示
title: 概念
path: /client
---
<code src="./demos/demo1.tsx"/>
# 介绍

View File

@ -23,7 +23,7 @@ import { uid, clone } from '@formily/shared';
import { ArrayCollapse, ArrayTable, FormLayout } from '@formily/antd';
import { Space, Card, Modal, Spin } from 'antd';
import { Action, useLogin, useRegister, useSubmit } from '../action';
import { Action, useLogin, useRegister, useSubmit, useDesignableValues } from '../action';
import { AddNew } from '../add-new';
import { Cascader } from '../cascader';
import { Checkbox } from '../checkbox';
@ -73,6 +73,7 @@ export const scope = {
useTableFilterAction,
useTableRow,
useTableUpdateAction,
useDesignableValues,
};
export const components = {
@ -363,8 +364,8 @@ export const createDesignableSchemaField = (options) => {
}}
>
<SchemaField scope={props.scope} components={props.components} schema={schema} />
<CodePreview schema={schema} />
<FormValues />
{props.debug && <CodePreview schema={schema} />}
{props.debug && <FormValues />}
</DesignableContext.Provider>
);
};
@ -435,6 +436,7 @@ export interface SchemaRendererProps {
onlyRenderProperties?: boolean;
scope?: any;
components?: any;
debug?: boolean;
}
export const SchemaRenderer = (props: SchemaRendererProps) => {
@ -463,6 +465,7 @@ export const SchemaRenderer = (props: SchemaRendererProps) => {
return (
<FormProvider form={form}>
<DesignableSchemaField
debug={props.debug}
scope={props.scope}
components={props.components}
onRefresh={props.onRefresh}

View File

@ -0,0 +1,61 @@
import { ISchema } from '@formily/react';
import {
useDesignableValues,
useDesignableUpdate,
useDesignableSchemaRemove,
} from '.';
export const DesignableBar: { [key: string]: ISchema } = {};
DesignableBar.Action = {
name: 'bar',
type: 'void',
// title: '操作栏2',
'x-component': 'Action.Dropdown',
'x-component-props': {
icon: 'MenuOutlined',
},
properties: {
edit: {
type: 'void',
title: '编辑按钮',
'x-component': 'Action',
'x-component-props': {
// icon,
},
properties: {
modal: {
type: 'void',
title: '修改按钮配置',
'x-decorator': 'Form',
'x-decorator-props': {
useValues: useDesignableValues,
},
'x-component': 'Action.Modal',
'x-component-props': {
width: '500px',
useAction: useDesignableUpdate,
},
properties: {
title: {
type: 'string',
title: '按钮标题',
required: true,
'x-component': 'Input',
'x-decorator': 'FormItem',
},
},
},
},
},
remove: {
type: 'void',
title: '删除按钮',
'x-component': 'Action',
'x-component-props': {
useAction: useDesignableSchemaRemove,
// icon,
},
},
},
};

View File

@ -11,8 +11,7 @@ group:
# Action - 操作
## 组件节点树
## Node Tree
<pre lang="tsx">
////////// 单节点 //////////
@ -67,17 +66,16 @@ group:
</Action.Dropdown>
</pre>
操作有三类:
## Designable Bar
- 常规操作Action
- 弹出层操作,弹出层可以是 Action.Drawer、Action.Modal、Action.Popover
- 指定容器内打开: Action.Container
- 下拉菜单Action.Dropdown用于收纳多种操作
- 跳转操作Action.Link、Action.URL
- Action.DesignableBar
- Action.Modal.DesignableBar
- Action.Drawer.DesignableBar
- Action.Popover.DesignableBar
Action.Drawer、Action.Modal、Action.Popover 和 Action.Container 需要和 Action 搭配使用
## Examples
## Action - 常规操作
### Action - 常规操作
```tsx
/**
@ -100,17 +98,18 @@ const schema = {
name: 'action1',
title: '按钮',
'x-component': 'Action',
'x-designable-bar': 'Action.DesignableBar',
'x-component-props': {
useAction,
useAction: '{{ useAction }}',
},
};
export default () => {
return <SchemaRenderer schema={schema} />
return <SchemaRenderer debug={true} scope={{ useAction }} schema={schema} />
}
```
## Action.Link - 内链跳转
### Action.Link - 内链跳转
```tsx
import React from 'react';
@ -131,7 +130,7 @@ export default () => {
}
```
## Action.URL - 外链跳转
### Action.URL - 外链跳转
```tsx
import React from 'react';
@ -152,7 +151,7 @@ export default () => {
}
```
## Action.Dropdown - 下拉操作
### Action.Dropdown - 下拉操作
```tsx
import React from 'react';
@ -253,7 +252,7 @@ export default () => {
}
```
## Action.Popover - 打开气泡
### Action.Popover - 打开气泡
```tsx
import React from 'react';
@ -285,7 +284,7 @@ export default () => {
}
```
## Action.Drawer - 打开抽屉
### Action.Drawer - 打开抽屉
```tsx
import React from 'react';
@ -338,10 +337,13 @@ export default () => {
}
```
## Action.Modal - 打开对话框
### Action.Modal - 打开对话框
`x-decorator='Form'`Model 为 Form并自带按钮
```tsx
import React from 'react';
import { Space } from 'antd';
import { SchemaRenderer } from '../';
import { useVisibleContext } from './';
@ -406,12 +408,40 @@ const schema = {
},
};
const schema2 = {
type: 'void',
name: 'action1',
title: 'x-decorator=Form',
'x-component': 'Action',
properties: {
drawer1: {
type: 'void',
title: '弹窗标题',
'x-component': 'Action.Modal',
'x-component-props': {},
'x-decorator': 'Form',
properties: {
input: {
type: 'string',
'x-decorator': 'FormItem',
'x-component': 'Input',
},
},
},
},
};
export default () => {
return <SchemaRenderer schema={schema} />
return (
<Space>
<SchemaRenderer schema={schema} />
<SchemaRenderer schema={schema2} />
</Space>
)
}
```
## Action.Container - 指定容器内打开
### Action.Container - 指定容器内打开
```tsx
import React, { useRef } from 'react';

View File

@ -9,6 +9,7 @@ import {
SchemaOptionsContext,
Schema,
useField,
SchemaExpressionScopeContext,
} from '@formily/react';
import { Button, Dropdown, Menu, Popover, Space, Drawer, Modal } from 'antd';
import { Link, useHistory, LinkProps } from 'react-router-dom';
@ -22,6 +23,9 @@ import { uid } from '@formily/shared';
import './style.less';
import constate from 'constate';
import { Icon } from '../icon-picker';
import { useMemo } from 'react';
import { createForm } from '@formily/core';
export function useDefaultAction() {
return {
@ -109,6 +113,7 @@ const BaseAction = observer((props: any) => {
const {
ButtonComponent = Button,
className,
icon,
useAction = useDefaultAction,
...others
} = props;
@ -122,11 +127,10 @@ const BaseAction = observer((props: any) => {
field.componentProps.setVisible = setVisible;
}, []);
console.log('BaseAction', { field, schema, fieldSchema }, field.title);
const renderButton = () => (
<ButtonComponent
{...others}
icon={<Icon type={icon} />}
className={classNames(className, `name-${schema.name}`)}
onClick={async (e) => {
e.stopPropagation && e.stopPropagation();
@ -227,7 +231,24 @@ Action.Popover = observer((props) => {
Action.Drawer = observer((props) => {
const field = useField();
// const { visible, setVisible } = useVisibleContext();
const { useAction = () => ({ async run() {} }), ...others } = props;
const { schema } = useDesignable();
const { visible, setVisible } = useVisibleContext();
const { run } = useAction();
const form = useForm();
console.log(`schema['x-decorator']`, schema['x-decorator'])
if (schema['x-decorator'] === 'Form.Decorator') {
Object.assign(others, {
footer: (
<Space>
<Button type={'primary'}>Ok</Button>
<Button>Cancel</Button>
</Space>
),
});
}
return (
<Drawer
onClick={(e) => {
@ -235,7 +256,7 @@ Action.Drawer = observer((props) => {
}}
title={field.title}
width={'50%'}
{...props}
{...others}
visible={visible}
onClose={(e) => {
e.stopPropagation();
@ -247,61 +268,132 @@ Action.Drawer = observer((props) => {
);
});
const [DesignableContextProvider, useDesignableContext] = constate(() => {
return useDesignable();
});
import { DesignableBar } from './designableBar';
import { cloneDeep } from 'lodash';
import { clone } from '@formily/shared';
export function useDesignableValues() {
const { schema } = useDesignableContext();
return schema
? {
title: schema.title,
}
: {};
}
export function useDesignableUpdate() {
const { schema, refresh } = useDesignableContext();
const form = useForm();
return {
async run() {
schema.title = form.values.title;
refresh();
console.log('useDesignableUpdate', schema, form.values);
},
};
}
export function useDesignableSchemaRemove() {
const { schema, refresh, remove } = useDesignableContext();
return {
async run() {
remove();
},
};
}
Action.Modal = observer((props) => {
const field = useField();
const { useAction = () => ({ async run() {} }), ...others } = props;
const { schema } = useDesignable();
const { setVisible: setDropdownVisible } = useDropdownVisibleContext();
const { visible, setVisible } = useVisibleContext();
const { run } = useAction();
const form = useForm();
if (schema['x-decorator'] !== 'Form.Decorator') {
Object.assign(others, { footer: null });
}
return (
<Modal
// onClick={e => {
// e.stopPropagation();
// }}
title={field.title}
title={schema.title}
width={'50%'}
{...props}
{...others}
visible={visible}
closable
maskClosable
onCancel={(e) => {
destroyOnClose
onCancel={async (e) => {
e.stopPropagation();
setVisible(false);
setDropdownVisible && setDropdownVisible(false);
}}
onOk={async (e) => {
console.log('onOk', form.values);
// await form.submit();
await run();
// e.stopPropagation();
setVisible(false);
setDropdownVisible && setDropdownVisible(false);
}}
footer={null}
>
<div onClick={(e) => e.stopPropagation()}>{props.children}</div>
<div
//onClick={(e) => e.stopPropagation()}
>
{props.children}
</div>
</Modal>
);
});
Action.Dropdown = observer((props) => {
// const { visible, setVisible } = useVisibleContext();
const [DropdownVisibleProvider, useDropdownVisibleContext] = constate((props: any = {}) => {
const { initialVisible = false } = props;
const [visible, setVisible] = useState(initialVisible);
return { visible, setVisible };
});
const ActionDropdown = observer((props: any) => {
const { icon, ...others } = props;
const schema = useFieldSchema();
const { visible, setVisible } = useDropdownVisibleContext();
return (
<>
<Popover
// visible={visible}
// onVisibleChange={(visible) => {
// setVisible(visible);
// }}
{...props}
visible={visible}
onVisibleChange={(visible) => {
setVisible(visible);
}}
// {...props}
overlayClassName={'nb-action-group'}
// trigger={'click'}
content={props.children}
placement={'bottomLeft'}
>
<Button>{schema.title}</Button>
<Button icon={<Icon type={icon} />} {...others}>
{schema.title}
</Button>
</Popover>
{/* popover 的按钮初始化时并未渲染,暂时先这么处理 */}
);
});
Action.Dropdown = observer((props) => {
return (
<DropdownVisibleProvider>
<ActionDropdown {...props} />
<div style={{ display: 'none' }}>{props.children}</div>
</>
</DropdownVisibleProvider>
);
});
Action.DesignableBar = () => {
const field = useField();
// const schema = useFieldSchema();
const { schema, insertAfter } = useDesignable();
const { schema, insertAfter, refresh } = useDesignable();
const [visible, setVisible] = useState(false);
return (
<DesignableContextProvider>
<div className={classNames('designable-bar', { active: visible })}>
<span
onClick={(e) => {
@ -309,41 +401,9 @@ Action.DesignableBar = () => {
}}
className={classNames('designable-bar-actions', { active: visible })}
>
<Dropdown
trigger={['click']}
visible={visible}
onVisibleChange={(visible) => {
setVisible(visible);
}}
overlay={
<Menu>
<Menu.Item
onClick={(e) => {
console.log({ field, schema });
schema.title = '按钮文案被修改了';
field.setTitle('按钮文案被修改了');
schema.properties.drawer1.title = '抽屉标题文案被修改了';
setVisible(false);
}}
>
</Menu.Item>
<Menu.Item
onClick={() => {
insertAfter({
name: uid(),
'x-component': 'Input',
});
}}
>
insertAfter
</Menu.Item>
</Menu>
}
>
<MenuOutlined />
</Dropdown>
<SchemaRenderer schema={DesignableBar.Action} />
</span>
</div>
</DesignableContextProvider>
);
};

View File

@ -80,3 +80,16 @@
}
}
}
.designable-bar-actions {
.ant-btn {
border: 0;
padding: 0;
background: none;
height: auto;
line-height: 1em;
width: auto;
color: #fff;
}
}

View File

@ -11,7 +11,7 @@ group:
# AddNew - 新增
## 组件节点树
## Node Tree
<pre lang="tsx">
// 简单场景,只支持上下拖拽布局
@ -24,7 +24,7 @@ group:
// 栅格布局,支持行列等复杂的拖拽操作
<Grid>
<Grid.Row>
<Grid.Row locked>
<Grid.Col>
// 常规区块
<AddNew.BlockItem/>
@ -35,7 +35,11 @@ group:
</Grid>
</pre>
## 代码演示
## Designable Bar
暂无
## Examples
### AddNew.BlockItem

View File

@ -0,0 +1,26 @@
---
title: BlockItem - 区块项
nav:
title: 组件
path: /client
group:
order: 1
title: Schemas
path: /client/schemas
---
# BlockItem - 区块项
## Node Tree
通常 BlockItem 并不单独占用一个节点,而是节点的 x-decorator
<pre lang="tsx">
<Table x-decorator={'BlockItem'}/>
</pre>
BlockItem 的作用主要用于处理 DesignableBar
<pre lang="tsx">
<Table x-decorator={'BlockItem'} x-designable-bar={'Table.DesignableBar'}/>
</pre>

View File

@ -11,13 +11,17 @@ group:
# Cascader - 级联选择
## 组件节点树
## Node Tree
<pre lang="tsx">
<Cascader/>
</pre>
## 代码演示
## Designable Bar
- Cascader.DesignableBar
## Examples
### 省市区级联

View File

@ -11,7 +11,7 @@ group:
# Checkbox - 多选框
## 组件节点树
## Node Tree
<pre lang="tsx">
// 勾选
@ -20,7 +20,11 @@ group:
<Checkbox.Group/>
</pre>
## 代码演示
## Designable Bar
- Checkbox.DesignableBar
## Examples
### 勾选

View File

@ -11,13 +11,17 @@ group:
# ColorSelect - 颜色选择器
## 组件节点树
## Node Tree
<pre lang="tsx">
<ColorSelect/>
</pre>
## 代码演示
## Designable Bar
- ColorSelect.DesignableBar
## Examples
### 颜色选择器

View File

@ -11,20 +11,23 @@ group:
# DatabaseField - 数据表字段
## 组件节点树
## Node Tree
<pre lang="tsx">
<DatabaseField/>
</pre>
## 代码演示
## Designable Bar
暂无
## Examples
```tsx
import React from 'react';
import { createForm } from '@formily/core';
import { SchemaRenderer } from '../';
import { observer, connect, useField } from '@formily/react';
import Editor from '@monaco-editor/react';
const schema = {
type: 'object',
@ -66,11 +69,6 @@ export default observer(() => {
return (
<div>
<SchemaRenderer form={form} schema={schema} />
<Editor
height="200px"
defaultLanguage="json"
value={JSON.stringify(form.values, null, 2)}
/>
</div>
)
})

View File

@ -11,13 +11,17 @@ group:
# DatePicker - 日期选择器
## 组件节点树
## Node Tree
<pre lang="tsx">
<DatePicker/>
</pre>
## 代码演示
## Designable Bar
- DatePicker.DesignableBar
## Examples
### 日期选择器

View File

@ -0,0 +1,25 @@
---
title: DesignableBar - 配置操作栏
nav:
title: 组件
path: /client
group:
order: 1
title: Schemas
path: /client/schemas
---
# DesignableBar - 配置操作栏
用于修改节点的参数配置DesignableBar 是所有配置操作栏的统称,不是固定的,可以根据不同节点定义不同的 `**.DesignableBar`
## Node Tree
通常 DesignableBar 并不单独占用一个节点,而是节点的 x-designable-bar
<pre lang="tsx">
<Table x-decorator={'BlockItem'} x-designable-bar={'Table.DesignableBar'}/>
<Action x-designable-bar={'Action.DesignableBar'}/>
</pre>
数据组件的 DesignableBar 通常与 FormItem 搭配使用,非数据组件的 DesignableBar 通常与 BlockItem 搭配使用,也有少量如 Action、Menu 无需 BlockItem

View File

@ -11,7 +11,7 @@ group:
# Filter - 筛选器
## 组件节点树
## Node Tree
<pre lang="tsx">
<Filter>
@ -24,16 +24,11 @@ group:
</Filter>
</pre>
## 代码演示
## Designable Bar
<!-- <Filter>
<Filter.Column title={'字段1'} operations={[]}>
<Input/>
</Filter.Column>
<Filter.Column title={'字段2'}>
<Input/>
</Filter.Column>
</Filter> -->
- Filter.DesignableBar
## Examples
```tsx
import React from 'react';

View File

@ -0,0 +1,26 @@
---
title: FormItem - 表单项
nav:
title: 组件
path: /client
group:
order: 1
title: Schemas
path: /client/schemas
---
# FormItem - 表单项
## Node Tree
通常 FormItem 并不单独占用一个节点,而是节点的 x-decorator
<pre lang="tsx">
<Input x-decorator={'FormItem'}/>
</pre>
FormItem 的作用主要用于处理表单项(控件)的校验、布局等
<pre lang="tsx">
<Input x-decorator={'FormItem'} x-designable-bar={'Input.DesignableBar'}/>
</pre>

View File

@ -11,7 +11,7 @@ group:
# Form - 表单
## 组件节点树
## Node Tree
<pre lang="tsx">
<Form>
@ -19,9 +19,15 @@ group:
<Select/>
// 添加其他节点
</Form>
<Table x-decorator={'Form'}/>
</pre>
## 代码演示
## Designable Bar
- Form.DesignableBar
## Examples
### 常规表单
@ -161,7 +167,7 @@ import { uid } from '@formily/shared';
const schema = {
type: 'void',
name: uid(),
'x-decorator': 'Card',
'x-decorator': 'BlockItem',
'x-component': 'Form',
'x-designable-bar': 'Form.DesignableBar',
properties: {

View File

@ -6,6 +6,8 @@ import {
useFieldSchema,
observer,
SchemaExpressionScopeContext,
FormProvider,
ISchema,
} from '@formily/react';
import { SchemaRenderer, useDesignable } from '../DesignableSchemaField';
import get from 'lodash/get';
@ -42,7 +44,6 @@ function useDefaultValues() {
export const Form: any = observer((props: any) => {
const scope = useContext(SchemaExpressionScopeContext);
const { useValues = useDefaultValues } = props;
const values = useValues();
const form = useMemo(() => {
@ -54,21 +55,21 @@ export const Form: any = observer((props: any) => {
}, [values]);
const schema = useFieldSchema();
const { schema: designableSchema, refresh } = useDesignable();
const { DesignableBar } = useDesignableBar();
const ref = useRef();
const [active, setActive] = useState(false);
const { onMouseEnter, onMouseLeave, onMouseMove } = useMouseEvents(ref);
onMouseEnter((e: React.MouseEvent) => {
setActive(true);
});
onMouseLeave((e: React.MouseEvent) => {
setActive(false);
});
onMouseMove((e: React.MouseEvent) => {});
const formSchema: ISchema = schema['x-decorator'] === 'Form' ? {
type: 'void',
"x-component": 'Blank',
properties: {
[schema.name]: {
...schema.toJSON(),
"x-decorator": 'Form.Decorator',
}
}
} : {
...schema.toJSON(),
'x-component': 'Form.Blank',
'x-component-props': {},
};
return (
<div ref={ref} className={'nb-form'}>
<SchemaRenderer
scope={scope}
// components={options.components}
@ -77,14 +78,14 @@ export const Form: any = observer((props: any) => {
refresh();
}}
form={form}
schema={schema.toJSON()}
schema={formSchema}
onlyRenderProperties
/>
<DesignableBar active={active} />
</div>
);
});
Form.Decorator = ({children}) => children;
Form.DesignableBar = (props) => {
const { active } = props;
return (

View File

@ -13,21 +13,6 @@ group:
基于行Row和列Col来定义区块Block的外部框架。
## 组件节点树
<pre lang="tsx">
<Grid>
<Grid.Row>
<Grid.Col>
// 添加其他节点
</Grid.Col>
<Grid.Col>
// 添加其他节点
</Grid.Col>
</Grid.Row>
</Grid>
</pre>
## 列宽说明
最大支持四列,如果要支持越多列,需要处理的比例也越多。
@ -88,4 +73,26 @@ group:
- 100%
## Node Tree
<pre lang="tsx">
<Grid>
<Grid.Row>
<Grid.Col>
// 添加其他节点
</Grid.Col>
<Grid.Col>
// 添加其他节点
</Grid.Col>
</Grid.Row>
</Grid>
</pre>
## Designable Bar
暂无
## Examples
<code src="./demos/demo1.tsx"/>

View File

@ -11,13 +11,17 @@ group:
# IconPicker - 图标选择器
## 节点树
## Node Tree
<pre lang="tsx">
<IconPicker/>
</pre>
## 代码演示
## Designable Bar
- IconPicker.DesignableBar
## Examples
```tsx
/**

View File

@ -11,13 +11,17 @@ group:
# InputNumber - 数字框
## 节点树
## Node Tree
<pre lang="tsx">
<InputNumber/>
</pre>
## 代码演示
## Designable Bar
- InputNumber.DesignableBar
## Examples
### 数字框

View File

@ -13,14 +13,19 @@ group:
输入框是非常常用的控件,参数可以组合成许多字段,如单行文本、多行文本、手机号、邮箱、网址等。
## 节点树
## Node Tree
<pre lang="tsx">
<Input/>
<Input.TextArea/>
</pre>
## 代码演示
## Designable Bar
- Input.DesignableBar
- Input.TextArea.DesignableBar
## Examples
### 单行文本

View File

@ -11,13 +11,17 @@ group:
# Markdown 编辑器
## 节点树
## Node Tree
<pre lang="tsx">
<Markdown/>
</pre>
## 代码演示
## Designable Bar
- Markdown.DesignableBar
## Examples
### Markdown 编辑器

View File

@ -13,7 +13,7 @@ group:
需要 antd v4.16+ 支持,在此之前的 Menu.Item 不支持 Fragment 包裹。
## 组件树
## Node Tree
<pre lang="tsx">
<Menu>
@ -43,7 +43,11 @@ group:
</Menu>
</pre>
## 代码演示
## Designable Bar
- Menu.DesignableBar
## Examples
### 横向菜单

View File

@ -11,14 +11,18 @@ group:
# Password - 密码
## 节点树
## Node Tree
<pre lang="tsx">
// 单选框
<Password/>
</pre>
## 代码演示
## Designable Bar
- Password.DesignableBar
## Examples
### 密码框

View File

@ -11,7 +11,7 @@ group:
# Radio - 单选框
## 节点树
## Node Tree
<pre lang="tsx">
// 单选框
@ -20,7 +20,11 @@ group:
<Radio.Group/>
</pre>
## 代码演示
## Designable Bar
- Radio.DesignableBar
## Examples
### 单选框

View File

@ -11,7 +11,7 @@ group:
# Select - 选择器
## 节点树
## Node Tree
<pre lang="tsx">
// 常规选择器
@ -37,7 +37,14 @@ group:
</Select.Drawer>
</pre>
## Select
## Designable Bar
- Select.DesignableBar
- Select.Drawer.DesignableBar
- Select.Options.DesignableBar
- Select.OptionTag.DesignableBar
## Examples
### 单选
@ -62,13 +69,11 @@ const schema = {
type: 'object',
properties: {
input: {
interface: 'string',
type: 'string',
type: 'number',
title: `编辑模式`,
name: 'name1',
enum: options,
'x-decorator': 'FormItem',
'x-component': 'Select',
'x-component': 'Select.Object',
'x-reactions': {
target: 'read',
fulfill: {
@ -79,14 +84,12 @@ const schema = {
},
},
read: {
interface: 'string',
type: 'string',
type: 'number',
title: `阅读模式`,
enum: options,
name: 'name2',
'x-read-pretty': true,
'x-decorator': 'FormItem',
'x-component': 'Select',
'x-component': 'Select.Object',
'x-read-pretty': true,
},
}
};
@ -257,8 +260,7 @@ const schema = {
type: 'object',
properties: {
input: {
interface: 'select',
type: 'string',
type: 'object',
title: `编辑模式`,
enum: dataSource,
'x-decorator': 'FormItem',
@ -281,8 +283,7 @@ const schema = {
},
},
read: {
interface: 'select',
type: 'string',
type: 'object',
title: `阅读模式`,
enum: dataSource,
'x-read-pretty': true,
@ -332,8 +333,7 @@ const schema = {
type: 'object',
properties: {
input: {
interface: 'select',
type: 'string',
type: 'array',
title: `编辑模式`,
enum: dataSource,
'x-decorator': 'FormItem',
@ -356,8 +356,7 @@ const schema = {
},
},
read: {
interface: 'select',
type: 'string',
type: 'array',
title: `阅读模式`,
enum: dataSource,
'x-read-pretty': true,
@ -571,22 +570,44 @@ export default () => {
};
```
<!--
<Select />
<Select.Object />
<Select.Drawer>
<Select.Options>
<Table />
<Action />
<Select.Options>
<Select.ItemDetails>
<Form/>
</Select.ItemDetails>
<Select.Drawer>
## Schema API
<Action />
### Select
<Action>
<Drawer />
</Action>
-->
type 支持 string、number、object 和 arrayarray 元素可以是 string、number 或 object。如
```ts
{
properties: {
select: {
type: 'string', // 也可以是 number、object、array
'x-component': 'Select',
}
}
}
```
可以配置 dataSourceenum的 fieldNames
```ts
{
properties: {
select: {
type: 'string', // 也可以是 number、object、array
'x-component': 'Select',
'x-component-props': {
fieldNames: {
label: 'label', // 标签文案
value: 'value',
color: 'color', // 标签颜色
children: 'children', // 选项分组时,选项字段对应的 key
},
},
},
}
}
```
### Select.Drawer
type 支持 object 和 arraySelect.Drawer 的可选项是以抽屉方式展开,提供了 Select.Options 和 Select.OptionTag 用于配置可选项界面和选中项详情界面。

View File

@ -18,6 +18,8 @@ import { useDesignable } from '../DesignableSchemaField';
import { createContext } from 'react';
import { useContext } from 'react';
import { SelectedRowKeysContext, useTableContext } from '../table';
import { isEmpty } from 'lodash';
import { isField } from '@formily/core';
export const Select: any = connect(
(props) => {
@ -189,10 +191,13 @@ Select.Object = connect(
),
mapReadPretty(
observer((props: any) => {
const { value, fieldNames = { label: 'label' }, ...others } = props;
const { value, fieldNames = { label: 'label', color: 'color' }, ...others } = props;
if (!value) {
return null;
}
if (isEmpty(value)) {
return null;
}
const values = toArr(value);
return (
<div>

View File

@ -11,7 +11,7 @@ group:
# Table - 表格
## 组件节点树
## Node Tree
<pre lang="tsx">
<Table>
@ -53,6 +53,11 @@ group:
</Table>
</pre>
## 代码演示
## Designable Bar
- Table.DesignableBar
- Table.Column.DesignableBar
## Examples
<code src="./demos/demo1.tsx"/>

View File

@ -11,7 +11,7 @@ group:
# Tabs - 标签页
## 组件节点树
## Node
<pre lang="tsx">
<Tabs>
@ -24,7 +24,11 @@ group:
</Tabs>
</pre>
## 代码演示
## Designable Bar
- Tabs.DesignableBar
## Examples
### 基本使用

View File

@ -11,13 +11,17 @@ group:
# TimePicker - 时间选择器
## 节点树
## Node Tree
<pre lang="tsx">
<TimePicker/>
</pre>
## 代码演示
## Designable Bar
- TimePicker.DesignableBar
## Examples
### 日期选择器

View File

@ -11,13 +11,17 @@ group:
# Upload - 上传
## 节点树
## Node Tree
<pre lang="tsx">
<Upload/>
</pre>
## 代码演示
## Designable Bar
- Upload.DesignableBar
## Examples
### 上传

View File

@ -1,14 +0,0 @@
---
title: AdminLayout - 后台布局
nav:
title: 组件
path: /client
group:
order: 2
title: Templates
path: /client/templates
---
# AdminLayout - 后台布局
内置的后台布局模板,提供了基础的菜单和路由切换。

View File

@ -1,229 +0,0 @@
import React, { useContext, useEffect, useRef, useState } from 'react';
import {
Button,
Spin,
Layout,
PageHeader,
Modal,
Menu,
Collapse,
Dropdown,
} from 'antd';
import isEmpty from 'lodash/isEmpty';
import {
Link,
useLocation,
useRouteMatch,
useHistory,
Redirect,
} from 'react-router-dom';
import {
useGlobalAction,
refreshGlobalAction,
RouteComponentContext,
} from '../../';
import { SchemaRenderer } from '../../schemas';
import { useRequest } from 'ahooks';
import {
DatabaseOutlined,
PlusOutlined,
DownOutlined,
} from '@ant-design/icons';
import { Tabs } from 'antd';
import '@formily/antd/esm/array-collapse/style';
import './style.less';
import { MenuContainerContext } from '../../schemas/menu';
function LogoutButton() {
const history = useHistory();
return (
<Button
onClick={async () => {
history.push('/login');
await refreshGlobalAction('routes:getAccessible');
}}
>
</Button>
);
}
function Database() {
const [visible, setVisible] = useState(false);
const schema = {
type: 'object',
properties: {
layout: {
type: 'void',
'x-component': 'FormLayout',
'x-component-props': {
layout: 'vertical',
},
properties: {
input: {
type: 'string',
title: '数据表名称',
required: true,
'x-decorator': 'FormItem',
'x-component': 'Input',
},
array: {
type: 'array',
title: '数据表字段',
'x-component': 'ArrayCollapse',
'x-component-props': {
accordion: true,
},
// maxItems: 3,
'x-decorator': 'FormItem',
items: {
type: 'object',
'x-component': 'ArrayCollapse.CollapsePanel',
'x-component-props': {
header: '字段',
},
properties: {
index: {
type: 'void',
'x-component': 'ArrayCollapse.Index',
},
input: {
type: 'string',
'x-decorator': 'FormItem',
title: 'Input',
required: true,
'x-component': 'Input',
},
remove: {
type: 'void',
'x-component': 'ArrayCollapse.Remove',
},
moveUp: {
type: 'void',
'x-component': 'ArrayCollapse.MoveUp',
},
moveDown: {
type: 'void',
'x-component': 'ArrayCollapse.MoveDown',
},
},
},
properties: {
addition: {
type: 'void',
title: '添加字段',
'x-component': 'ArrayCollapse.Addition',
},
},
},
},
},
},
};
return (
<>
<Modal
title={
<Dropdown
overlay={
<Menu>
<Menu.Item key={'1'}>1</Menu.Item>
<Menu.Item key={'2'}>2</Menu.Item>
<Menu.Item key={'3'}>3</Menu.Item>
<Menu.Item key={'4'}>4</Menu.Item>
<Menu.Divider></Menu.Divider>
<Menu.Item key={'5'}></Menu.Item>
</Menu>
}
>
<span>
1 <DownOutlined />
</span>
</Dropdown>
}
// width={'800px'}
visible={visible}
onCancel={() => setVisible(false)}
onOk={() => setVisible(false)}
// bodyStyle={{ padding: 0 }}
// footer={null}
>
<SchemaRenderer schema={schema} />
</Modal>
<DatabaseOutlined
onClick={() => setVisible(true)}
style={{ color: '#fff', lineHeight: '48px', width: 48 }}
/>
</>
);
}
function LayoutWithMenu({ schema }) {
const match = useRouteMatch<any>();
const location = useLocation();
const sideMenuRef = useRef();
const [activeKey, setActiveKey] = useState(match.params.name);
const onSelect = (info) => {
console.log('LayoutWithMenu', schema);
setActiveKey(info.key);
};
console.log({ match });
return (
<Layout>
<Layout.Header>
<SchemaRenderer
schema={schema}
scope={{ sideMenuRef, onSelect, selectedKeys: [activeKey].filter(Boolean) }}
/>
</Layout.Header>
<Layout>
<Layout.Sider
ref={sideMenuRef}
theme={'light'}
width={200}
></Layout.Sider>
<Layout.Content>
{location.pathname}
<Content activeKey={activeKey} />
</Layout.Content>
</Layout>
</Layout>
);
}
function Content({ activeKey }) {
const { data = {}, loading } = useRequest(
`/api/ui-schemas:getTree/${activeKey}?filter[parentId]=${activeKey}`,
{
refreshDeps: [activeKey],
formatResult: (result) => result?.data,
},
);
if (loading) {
return <Spin />;
}
return <SchemaRenderer schema={data} />;
}
export function AdminLayout({ route, children }: any) {
const { data = {}, loading } = useRequest(
`/api/ui-schemas:getTree/${route.schemaName}`,
{
refreshDeps: [route],
formatResult: (result) => result?.data,
},
);
if (loading) {
return <Spin />;
}
return <LayoutWithMenu schema={data} />;
}
export default AdminLayout;

View File

@ -1,49 +0,0 @@
// .fields-collapse {
// border: 1px solid #f0f0f0;
// border-bottom: 0;
// > .ant-collapse-item {
// border-bottom: 1px solid #f0f0f0;
// }
// .ant-collapse-content {
// &.ant-collapse-content-active {
// border-top: 1px solid #f0f0f0;
// }
// }
// > .ant-collapse-item > .ant-collapse-content {
// background: #fff;
// }
// > .ant-collapse-item > .ant-collapse-content > .ant-collapse-content-box {
// padding: 16px;
// padding-bottom: 1px;
// }
// }
.ant-collapse {
border: 1px solid #d9d9d9 !important;
border-bottom: 0 !important;
> .ant-collapse-item {
border-bottom: 1px solid #d9d9d9 !important;
}
.ant-collapse-content {
border-top: 1px solid #d9d9d9 !important;
}
.ant-collapse-content > .ant-collapse-content-box {
padding: 24px !important;
padding-bottom: 0 !important;
}
}
.database-sider {
background: #fafafa;
padding-top: 16px;
.ant-menu {
background: #fafafa;
border-right: 0;
}
.ant-menu:not(.ant-menu-horizontal) .ant-menu-item-selected {
background: #fff;
&::after {
display: none;
}
}
}

View File

@ -1,14 +0,0 @@
---
title: AuthLayout - 登录布局
nav:
title: 组件
path: /client
group:
order: 2
title: Templates
path: /client/templates
---
# AuthLayout - 登录布局
内置的登录、注册页布局

View File

@ -1,15 +0,0 @@
import React from 'react';
import { useLocation, useHistory } from 'react-router-dom';
export function AuthLayout({ children, route }: any) {
const location = useLocation();
const history = useHistory();
return (
<div style={{ maxWidth: 320, margin: '0 auto' }}>
<h1>NocoBase</h1>
{children}
</div>
);
}
export default AuthLayout;

View File

@ -1,14 +0,0 @@
---
title: PageTemplate - 页面模板
nav:
title: 组件
path: /client
group:
order: 2
title: Templates
path: /client/templates
---
# PageTemplate - 页面模板
常规的页面模板,支持区块配置。

View File

@ -1,25 +0,0 @@
import React, { useContext, useEffect, useState } from 'react';
import { Spin } from 'antd';
import { Helmet } from 'react-helmet';
import { useRequest } from 'ahooks';
import { SchemaRenderer } from '../../schemas';
export function DefaultPage({ route }) {
const { data = {}, loading } = useRequest(
`/api/ui-schemas:getTree/${route.schemaName}`,
{
refreshDeps: [route],
formatResult: (result) => result?.data,
},
);
if (loading) {
return <Spin />;
}
return (
<div>
<SchemaRenderer schema={data} />
</div>
);
}
export default DefaultPage;

View File

@ -1,3 +0,0 @@
export * from './auth-layout';
export * from './admin-layout';
export * from './default-page';