From b2f8a11cd020ece948a4d503e4560745c50debed Mon Sep 17 00:00:00 2001 From: charlzyx Date: Mon, 22 Jan 2024 22:46:20 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=F0=9F=92=A1=20pro-array-table-shad?= =?UTF-8?q?ow-modal,=20docs,=20but=20no=20demos=20up?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/components/pro/pro-array-table.mdx | 87 ++++++++++++++++++++++++- docs/components/pro/pro-editable.mdx | 2 +- docs/components/pro/shadow-form.mdx | 8 ++- src/__builtins__/index.ts | 1 + src/__builtins__/pickomit.ts | 33 ++++++++++ src/pro-array-table/mixin.tsx | 75 ++++++++++++--------- src/shadow-form/index.tsx | 5 +- src/shadow-form/shadow-form.tsx | 44 ++++++++++--- tsconfig.json | 2 +- 9 files changed, 213 insertions(+), 44 deletions(-) create mode 100644 src/__builtins__/pickomit.ts diff --git a/docs/components/pro/pro-array-table.mdx b/docs/components/pro/pro-array-table.mdx index 772f967..a26a44a 100644 --- a/docs/components/pro/pro-array-table.mdx +++ b/docs/components/pro/pro-array-table.mdx @@ -1,9 +1,10 @@ -# 👊 `Pro ArrayTable +# 👊 Pro ArrayTable > Pro ArrayTable 是一个增强版本的 ArrayTable, 增加了以下属性与功能 - ✅ schema 拓展: RowExpand / Toolbar / Footer - ✅ 列配置: 排序与拖动调整列宽 +- ✅ 可编辑弹窗, 使用事件委托优化 - ✅ 常用功能配置简化与增强 (pagination/rowSelection/expandable) - ✅ `expandable.defaultExpandedAllRows` 现在支持惰性加载啦! - ✅ `expandable.expandedRowKeys` / `rowSelection.selectedRowKeys` 现在会根据页码调整进行智能重置! @@ -60,3 +61,87 @@ export interface ITableSelectionContext { setSelectedRowKeys: React.Dispatch>; } ``` + +### ProArrayTable.DelegateAction 代理动作触发器 + +```tsx | pure +{ + /** 动作标志, 与 ShadowModal 对应 */ + act: string; + /** 当前列所属 index, 默认使用 ArrayBase.useIndex() 获取当前数据对应位置 */ + index?: number; + /** 数据初始化加载器, 默认使用 field.record 当前模型所对应 record */ + initLoader?: object | Promise; + /** + * 具体元素, 但必须有标签包装, 因为会被追加 data-属性字段标志给事件委托来识别 + * 不填写的情况下, 使用 Button 标签包装 field.title + */ + children?: React.ReactElement; +} +``` + +### ShadowModal/ShadowModal 通用属性 +```tsx | pure +{ + /** 动作标志, 与 ShadowModal 对应 */ + act: string; + /** 子表单Schema */ + schema: ISchema, + /** createForm 创建的表单实例, 有自定义 effects 等可以通过自定义一个 form 这个传入 */ + form?: ReturnType, + /** createSchemaField 函数配置 */ + schemaFieldOptions?: Parameters[0], + /** 取消事件 */ + onCancel?: ( + ctx: ReturnType, + ) => void | Promise; + /** 子表单提交事件, 可以做点什么 */ + onOk?: ( + data: any, + ctx: ReturnType, + ) => void | Promise; +} +``` +### ProArrayTable.ShadowModal 表格共享弹窗, 结合 ShadowForm/DelegateAction 使用 + +除了 `onOk,onCancel` antd/Modal 其他属性 + +### ProArrayTable.ShadowDrawer 表格共享抽屉, 结合 ShaodwForm/DelegateAction 使用 + +除了 `onOk,onCancel` antd/Drawer 其他属性 + +### ProArrayTable.ProAddition 弹窗/抽屉形式的可编辑内容新增组件 + +高级自增组件, 提供了弹窗/抽屉形式的空调编辑内容, 这个本质上是一个内置了一个 ShadowForm的 Modal/Drawer + +```tsx | pure +{ + /** 子表单Schema */ + schema: ISchema, + /** createForm 创建的表单实例, 有自定义 effects 等可以通过自定义一个 form 这个传入 */ + form?: ReturnType, + /** createSchemaField 函数配置 */ + schemaFieldOptions?: Parameters[0], + /** 弹窗形式: Modal 或 Drawer, 默认 modal */ + popType: "modal" | "drawer" + /** 其他 Modal / Drawer 原始属性 */ + ...others: React.ComponentProps +} +``` + +### ProArrayTable.Column 列配置组件 + +追加了可拖拽列宽配置开关, 优先级 > Table 上的 resizeable 属性 + +```tsx | pure +{ + // 是否可拖拽调整列宽 + resizeable?: boolean + // ... 其他参考 @formily/antd ArrayBase.Column 配置 +} +``` + +### ProArrayTable.Addition 自增组件 + +与 `@formily/antd` 中的 `ArrayTable.Addition` 表现一致 + diff --git a/docs/components/pro/pro-editable.mdx b/docs/components/pro/pro-editable.mdx index 6880612..cefdb6e 100644 --- a/docs/components/pro/pro-editable.mdx +++ b/docs/components/pro/pro-editable.mdx @@ -1,4 +1,4 @@ -# 💡Pro Editable +# 💡Pro Editable (废弃, 请使用 ShadowForm) > ProEditable 是为了解决常见的 `Popconfirm/Modal/Drawer` 弹窗编辑子表单模板代码的问题 diff --git a/docs/components/pro/shadow-form.mdx b/docs/components/pro/shadow-form.mdx index 120fd26..ab866b9 100644 --- a/docs/components/pro/shadow-form.mdx +++ b/docs/components/pro/shadow-form.mdx @@ -1,6 +1,8 @@ # 👻 ShaowForm 影子表单 -> 子表单 +> 脱离于当前 Form 响应式上下文的子表单 + +这很有用, 解决表格中的弹窗编辑与增加, 以及对象弹窗编辑的 Pro!Editable ### 代码案例: Confirm @@ -8,3 +10,7 @@ ### 代码案例: 在列表中使用 + +### useShadowForm + +### ShadowFormProvider diff --git a/src/__builtins__/index.ts b/src/__builtins__/index.ts index 178cd64..6d8f1b2 100644 --- a/src/__builtins__/index.ts +++ b/src/__builtins__/index.ts @@ -1 +1,2 @@ export * from "./utils"; +export * from "./pickomit"; diff --git a/src/__builtins__/pickomit.ts b/src/__builtins__/pickomit.ts new file mode 100644 index 0000000..8a7df07 --- /dev/null +++ b/src/__builtins__/pickomit.ts @@ -0,0 +1,33 @@ +export const omit = ( + o: T, + ...keys: K[] +) => { + if (typeof o !== "object") return o; + return Object.keys(o).reduce((p, k: string) => { + if (!keys.includes(k as any)) { + (p as any)[k] = (o as any)[k]; + } + return p; + }, {} as T); +}; + +export const pick = ( + o: T, + ...keys: K[] +) => { + if (typeof o !== "object") return o; + return Object.keys(o).reduce( + (p, k: string) => { + if (keys.includes(k as any)) { + (p as any)[k] = (o as any)[k]; + } + return p; + }, + {} as T, + // { + // // 不太好用, 晚点练习一下类型体操吧 + // // https://github.com/ascoders/weekly/blob/master/TS%20%E7%B1%BB%E5%9E%8B%E4%BD%93%E6%93%8D/243.%E7%B2%BE%E8%AF%BB%E3%80%8APick%2C%20Awaited%2C%20If...%E3%80%8B.md + // [Key in K]: T[K]; + // }, + ); +}; diff --git a/src/pro-array-table/mixin.tsx b/src/pro-array-table/mixin.tsx index 5ca8c2e..4786320 100644 --- a/src/pro-array-table/mixin.tsx +++ b/src/pro-array-table/mixin.tsx @@ -1,10 +1,24 @@ import { FormProvider, ReactFC, observer, useField } from "@formily/react"; import { toJS } from "@formily/reactive"; import React, { Fragment, useContext, useEffect, useRef } from "react"; -import { useRecord } from ".."; -import { Alert, BUTTON_TYPE, Button, Divider, Modal, Space } from "../adaptor"; +import { omit, pick } from "../__builtins__"; +import { + Alert, + BUTTON_TYPE, + Button, + Divider, + Drawer, + Modal, + Space, +} from "../adaptor"; import { ArrayBase, ArrayBaseMixins } from "../adaptor/adaptor"; -import { ShadowForm, ShadowFormContext } from "../shadow-form/shadow-form"; +import { + IShadowFormOptions, + ShadowFormContext, + ShadowFormProvider, + useShadowForm, +} from "../shadow-form/shadow-form"; +import { useRecord } from "../shared"; import { ArrayTableDelegateContext, DATE_DELEGATE_ACT_KEY, @@ -92,9 +106,7 @@ export const RowSelectionPro = (props: { rowKey: (record: any) => string | number; }) => { const { ds, rowKey } = props; - const array = ArrayBase.useArray(); const $row = useContext(TableRowSelectionContext); - // const [, $row] = useArrayCompPropsOf(array?.field, "rowSelection"); return ds.length > 0 ? ( { return { array, pagination, rowSelection, expanedable }; }; +export interface CommonShadowPopup extends IShadowFormOptions { + onCancel?: ( + ctx: ReturnType, + ) => void | Promise; + onOk?: ( + data: any, + ctx: ReturnType, + ) => void | Promise; +} export const ShadowModal: React.FC< - Omit, "children" | "onCancel" | "onOk"> & { - onCancel?: ( - ctx: ReturnType, - ) => void | Promise; - onOk?: ( - data: any, - ctx: ReturnType, - ) => void | Promise; - } + Omit, "children" | "onCancel" | "onOk"> & + CommonShadowPopup > = (props) => { - const { SchemaField, form, act, schema } = useContext(ShadowFormContext); + const { SchemaField, form, act, schema } = useShadowForm( + pick(props, "act", "schema", "schemaFieldOptions", "form"), + ); const delegate = useContext(ArrayTableDelegateContext); const visible = delegate.act === act && delegate.index > -1; const pending = useRef(false); @@ -213,7 +229,7 @@ export const ShadowModal: React.FC< return ( { if (pending.current) return; @@ -238,10 +254,18 @@ export const ShadowModal: React.FC< ); }; + export const DelegateAction: React.FC<{ + /** 动作标志, 与 ShadowModal 对应 */ act: string; + /** 当前列所属 index, 默认使用 ArrayBase.useIndex() 获取当前数据对应位置 */ index?: number; + /** 数据初始化加载器, 默认使用 field.record 当前模型所对应 record */ initLoader?: Required["initLoader"]["current"]; + /** + * 具体元素, 但必须有标签包装, 因为会被追加 data-属性字段标志给事件委托来识别 + * 不填写的情况下, 使用 Button 标签包装 field.title + */ children?: React.ReactElement; }> = (props) => { const index = "index" in (props ?? {}) ? props.index : ArrayBase.useIndex(); @@ -276,24 +300,17 @@ export const DelegateAction: React.FC<{ }; export const ProAddition = ({ - schema, - form, - schemaFieldOptions, popType = "modal", ...props -}: Omit, "act"> & { +}: Omit & { popType: "modal" | "drawer"; -}) => { +} & Omit< + React.ComponentProps, + "onOk" | "onCancel" | "children" + >) => { return ( - - - + ); diff --git a/src/shadow-form/index.tsx b/src/shadow-form/index.tsx index b2408d2..bdb36da 100644 --- a/src/shadow-form/index.tsx +++ b/src/shadow-form/index.tsx @@ -1,2 +1,5 @@ -export { ShadowForm, useShadowSchemaField } from "./shadow-form"; +export { + ShadowFormProvider as ShadowForm, + useShadowSchemaField, +} from "./shadow-form"; export { ShadowPopconfirm } from "./shadow-popconfirm"; diff --git a/src/shadow-form/shadow-form.tsx b/src/shadow-form/shadow-form.tsx index eec8676..fa0db10 100644 --- a/src/shadow-form/shadow-form.tsx +++ b/src/shadow-form/shadow-form.tsx @@ -23,10 +23,18 @@ export const useShadowSchemaField = ( return SchemaField; }; -export interface IShadowFormContext { - schema: ISchema; +export interface IShadowFormOptions { + /** 子表单对应动作名称 */ act: string; - form: ReturnType; + /** 子表单Schema */ + schema: ISchema; + /** createSchemaField 函数配置, components 和 scope */ + schemaFieldOptions?: Parameters[0]; + /** createForm 创建的表单实例, 有自定义 effects 等可以通过自定义一个 form 这个传入 */ + form?: ReturnType; +} +export interface IShadowFormContext + extends Required> { SchemaField: ReturnType; } export const ShadowFormContext = createContext({ @@ -36,23 +44,39 @@ export const ShadowFormContext = createContext({ SchemaField: createSchemaField(), }); -export const ShadowForm: React.FC< +export const useShadowForm = (options: { + schema: ISchema; + schemaFieldOptions?: Parameters[0]; + act?: string; + form?: ReturnType; +}) => { + const { schema, schemaFieldOptions, act, form } = options; + const SchemaField = useShadowSchemaField(schemaFieldOptions); + const realForm = form ?? createForm({}); + const myField = useFieldSchema(); + const name = myField?.name as string; + return { + form: realForm, + schema, + SchemaField, + act: act ?? name, + }; +}; + +export const ShadowFormProvider: React.FC< React.PropsWithChildren<{ schema: ISchema; schemaFieldOptions?: Parameters[0]; act?: string; form?: ReturnType; }> -> = ({ children, act, schemaFieldOptions, form, schema }) => { - const SchemaField = useShadowSchemaField(schemaFieldOptions); - const realForm = form ?? createForm({}); - const myField = useFieldSchema(); - const name = myField?.name as string; +> = ({ children, ...options }) => { + const { SchemaField, act, form, schema } = useShadowForm(options); return (