@nocobase/client-v2 2.1.0-alpha.40 → 2.1.0-alpha.46
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/es/Application.d.ts +7 -0
- package/es/BaseApplication.d.ts +13 -0
- package/es/RouterManager.d.ts +1 -0
- package/es/authRedirect.d.ts +12 -0
- package/es/collection-field-interface/CollectionFieldInterface.d.ts +51 -15
- package/es/collection-field-interface/CollectionFieldInterfaceManager.d.ts +82 -3
- package/es/collection-manager/field-configure.d.ts +80 -0
- package/es/collection-manager/field-validation.d.ts +43 -0
- package/es/collection-manager/filter-operators/index.d.ts +46 -0
- package/es/collection-manager/filter-operators/operators.d.ts +30 -0
- package/es/collection-manager/interfaces/checkbox.d.ts +1 -41
- package/es/collection-manager/interfaces/checkboxGroup.d.ts +12 -44
- package/es/collection-manager/interfaces/collection.d.ts +12 -51
- package/es/collection-manager/interfaces/color.d.ts +1 -16
- package/es/collection-manager/interfaces/createdAt.d.ts +1 -44
- package/es/collection-manager/interfaces/createdBy.d.ts +0 -4
- package/es/collection-manager/interfaces/dateOnly.d.ts +7 -44
- package/es/collection-manager/interfaces/datetime.d.ts +1 -44
- package/es/collection-manager/interfaces/datetimeNoTz.d.ts +1 -44
- package/es/collection-manager/interfaces/email.d.ts +1 -29
- package/es/collection-manager/interfaces/id.d.ts +1 -16
- package/es/collection-manager/interfaces/index.d.ts +2 -3
- package/es/collection-manager/interfaces/input.d.ts +1 -102
- package/es/collection-manager/interfaces/integer.d.ts +1 -95
- package/es/collection-manager/interfaces/json.d.ts +16 -7
- package/es/collection-manager/interfaces/m2m.d.ts +11 -19
- package/es/collection-manager/interfaces/m2o.d.ts +11 -19
- package/es/collection-manager/interfaces/markdown.d.ts +1 -63
- package/es/collection-manager/interfaces/multipleSelect.d.ts +12 -44
- package/es/collection-manager/interfaces/nanoid.d.ts +1 -34
- package/es/collection-manager/interfaces/number.d.ts +1 -87
- package/es/collection-manager/interfaces/o2m.d.ts +12 -24
- package/es/collection-manager/interfaces/obo.d.ts +207 -0
- package/es/collection-manager/interfaces/oho.d.ts +207 -0
- package/es/collection-manager/interfaces/password.d.ts +1 -56
- package/es/collection-manager/interfaces/percent.d.ts +1 -84
- package/es/collection-manager/interfaces/phone.d.ts +1 -25
- package/es/collection-manager/interfaces/properties/index.d.ts +0 -28
- package/es/collection-manager/interfaces/radioGroup.d.ts +1 -29
- package/es/collection-manager/interfaces/richText.d.ts +1 -63
- package/es/collection-manager/interfaces/select.d.ts +12 -44
- package/es/collection-manager/interfaces/snowflake-id.d.ts +1 -34
- package/es/collection-manager/interfaces/tableoid.d.ts +1 -10
- package/es/collection-manager/interfaces/textarea.d.ts +1 -51
- package/es/collection-manager/interfaces/time.d.ts +1 -16
- package/es/collection-manager/interfaces/types.d.ts +3 -12
- package/es/collection-manager/interfaces/unixTimestamp.d.ts +1 -44
- package/es/collection-manager/interfaces/updatedAt.d.ts +1 -44
- package/es/collection-manager/interfaces/updatedBy.d.ts +0 -4
- package/es/collection-manager/interfaces/url.d.ts +1 -20
- package/es/collection-manager/interfaces/uuid.d.ts +1 -34
- package/es/collection-manager/template-fields.d.ts +53 -0
- package/es/components/KeepAlive.d.ts +22 -0
- package/es/components/RouterBridge.d.ts +9 -0
- package/es/components/form/DialogFormLayout.d.ts +5 -29
- package/es/components/form/TypedVariableInput.d.ts +75 -0
- package/es/components/form/VariableInput.d.ts +53 -2
- package/es/components/form/filter/CollectionFilter.d.ts +49 -0
- package/es/components/form/filter/CollectionFilterItem.d.ts +49 -0
- package/es/components/form/filter/DateFilterDynamicComponent.d.ts +57 -0
- package/es/components/form/filter/FilterValueInput.d.ts +29 -0
- package/es/components/form/filter/index.d.ts +11 -0
- package/es/components/form/filter/useFilterActionProps.d.ts +96 -0
- package/es/components/form/index.d.ts +2 -0
- package/es/data-source/ExtendCollectionsProvider.d.ts +50 -0
- package/es/data-source/index.d.ts +9 -0
- package/es/flow/FlowPage.d.ts +2 -1
- package/es/flow/admin-shell/AdminLayoutRouteCoordinator.d.ts +8 -40
- package/es/flow/admin-shell/BaseLayoutModel.d.ts +89 -0
- package/es/flow/admin-shell/BaseLayoutRouteCoordinator.d.ts +74 -0
- package/es/flow/admin-shell/admin-layout/AdminLayoutEntryGuard.d.ts +12 -0
- package/es/flow/admin-shell/admin-layout/AdminLayoutMenuModels.d.ts +1 -0
- package/es/flow/admin-shell/admin-layout/AdminLayoutModel.d.ts +7 -92
- package/es/flow/admin-shell/admin-layout/AppListRender.d.ts +11 -0
- package/es/flow/admin-shell/admin-layout/index.d.ts +3 -0
- package/es/flow/admin-shell/admin-layout/useApplications.d.ts +3 -2
- package/es/flow/admin-shell/useAdminLayoutRoutePage.d.ts +2 -2
- package/es/flow/admin-shell/useLayoutRoutePage.d.ts +23 -0
- package/es/flow/components/ConditionBuilder.d.ts +1 -0
- package/es/flow/components/FieldAssignRulesEditor.d.ts +3 -1
- package/es/flow/components/FieldAssignValueInput.d.ts +1 -0
- package/es/flow/components/FlowRoute.d.ts +10 -1
- package/es/flow/components/fieldAssignOptions.d.ts +10 -0
- package/es/flow/components/filter/LinkageFilterItem.d.ts +1 -0
- package/es/flow/components/filter/VariableFilterItem.d.ts +1 -0
- package/es/flow/components/filter/index.d.ts +2 -0
- package/es/flow/components/filter/metaTreeAssociationDepth.d.ts +13 -0
- package/es/flow/components/filter/useFilterOptions.d.ts +66 -0
- package/es/flow/index.d.ts +4 -0
- package/es/flow/models/base/AssociationFieldGroupModel.d.ts +2 -33
- package/es/flow/models/base/PageModel/PageModel.d.ts +3 -1
- package/es/flow/models/blocks/assign-form/assignFieldValuesFlow.d.ts +92 -0
- package/es/flow/models/blocks/assign-form/index.d.ts +1 -0
- package/es/flow/models/blocks/form/FormActionGroupModel.d.ts +1 -0
- package/es/flow/models/blocks/form/FormActionModel.d.ts +9 -2
- package/es/flow/models/blocks/table/TableBlockModel.d.ts +10 -0
- package/es/flow/models/blocks/table/TableColumnModel.d.ts +2 -0
- package/es/flow/models/fields/AssociationFieldModel/SubTableFieldModel/index.d.ts +1 -1
- package/es/flow-compat/passwordUtils.d.ts +1 -1
- package/es/index.d.ts +6 -0
- package/es/index.mjs +552 -459
- package/es/layout-manager/LayoutContentRoute.d.ts +14 -0
- package/es/layout-manager/LayoutManager.d.ts +22 -0
- package/es/layout-manager/LayoutRoute.d.ts +14 -0
- package/es/layout-manager/index.d.ts +13 -0
- package/es/layout-manager/types.d.ts +20 -0
- package/es/layout-manager/utils.d.ts +14 -0
- package/es/nocobase-buildin-plugin/index.d.ts +3 -10
- package/es/settings-center/index.d.ts +1 -1
- package/es/settings-center/plugin-manager/BulkEnableButton.d.ts +15 -0
- package/es/settings-center/plugin-manager/PluginCard.d.ts +15 -0
- package/es/settings-center/plugin-manager/PluginDetail.d.ts +16 -0
- package/es/settings-center/{PluginManagerPage.d.ts → plugin-manager/index.d.ts} +1 -7
- package/es/settings-center/plugin-manager/types.d.ts +34 -0
- package/lib/index.js +552 -459
- package/package.json +8 -7
- package/src/Application.tsx +51 -12
- package/src/BaseApplication.tsx +32 -0
- package/src/PluginSettingsManager.ts +1 -1
- package/src/RouterManager.tsx +17 -1
- package/src/__tests__/PluginSettingsManager.test.ts +41 -2
- package/src/__tests__/app.test.tsx +17 -1
- package/src/__tests__/globalDeps.test.ts +1 -0
- package/src/__tests__/nocobase-buildin-plugin-auth.test.tsx +50 -2
- package/src/__tests__/plugin-manager.test.tsx +177 -0
- package/src/__tests__/settings-center.test.tsx +24 -2
- package/src/authRedirect.ts +43 -0
- package/src/collection-field-interface/CollectionFieldInterface.ts +71 -77
- package/src/collection-field-interface/CollectionFieldInterfaceManager.ts +201 -4
- package/src/collection-manager/field-configure.ts +548 -0
- package/src/collection-manager/field-validation.ts +195 -0
- package/src/collection-manager/filter-operators/index.ts +176 -0
- package/src/collection-manager/{interfaces/properties → filter-operators}/operators.ts +24 -13
- package/src/collection-manager/interfaces/checkbox.ts +2 -9
- package/src/collection-manager/interfaces/checkboxGroup.ts +2 -10
- package/src/collection-manager/interfaces/collection.ts +2 -15
- package/src/collection-manager/interfaces/color.ts +2 -2
- package/src/collection-manager/interfaces/createdAt.ts +2 -2
- package/src/collection-manager/interfaces/createdBy.ts +1 -12
- package/src/collection-manager/interfaces/dateOnly.ts +8 -2
- package/src/collection-manager/interfaces/datetime.ts +2 -2
- package/src/collection-manager/interfaces/datetimeNoTz.ts +2 -2
- package/src/collection-manager/interfaces/email.ts +2 -9
- package/src/collection-manager/interfaces/id.ts +1 -2
- package/src/collection-manager/interfaces/index.ts +2 -3
- package/src/collection-manager/interfaces/input.ts +2 -133
- package/src/collection-manager/interfaces/integer.ts +2 -71
- package/src/collection-manager/interfaces/json.tsx +17 -11
- package/src/collection-manager/interfaces/m2m.tsx +0 -21
- package/src/collection-manager/interfaces/m2o.tsx +0 -22
- package/src/collection-manager/interfaces/markdown.ts +2 -51
- package/src/collection-manager/interfaces/multipleSelect.ts +2 -14
- package/src/collection-manager/interfaces/nanoid.ts +2 -2
- package/src/collection-manager/interfaces/number.ts +2 -85
- package/src/collection-manager/interfaces/o2m.tsx +1 -22
- package/src/collection-manager/interfaces/obo.tsx +145 -0
- package/src/collection-manager/interfaces/oho.tsx +145 -0
- package/src/collection-manager/interfaces/password.ts +2 -44
- package/src/collection-manager/interfaces/percent.ts +2 -74
- package/src/collection-manager/interfaces/phone.ts +2 -2
- package/src/collection-manager/interfaces/properties/index.ts +0 -133
- package/src/collection-manager/interfaces/radioGroup.ts +2 -2
- package/src/collection-manager/interfaces/richText.ts +2 -51
- package/src/collection-manager/interfaces/select.ts +2 -14
- package/src/collection-manager/interfaces/snowflake-id.ts +2 -2
- package/src/collection-manager/interfaces/tableoid.ts +1 -2
- package/src/collection-manager/interfaces/textarea.ts +2 -51
- package/src/collection-manager/interfaces/time.ts +2 -2
- package/src/collection-manager/interfaces/types.ts +4 -12
- package/src/collection-manager/interfaces/unixTimestamp.tsx +2 -2
- package/src/collection-manager/interfaces/updatedAt.ts +2 -2
- package/src/collection-manager/interfaces/updatedBy.ts +1 -12
- package/src/collection-manager/interfaces/url.ts +2 -4
- package/src/collection-manager/interfaces/uuid.ts +2 -2
- package/src/collection-manager/template-fields.ts +109 -0
- package/src/components/KeepAlive.tsx +131 -0
- package/src/components/README.md +138 -6
- package/src/components/README.zh-CN.md +138 -7
- package/src/components/RouterBridge.tsx +28 -4
- package/src/components/__tests__/KeepAlive.test.tsx +63 -0
- package/src/components/__tests__/RouterBridge.test.tsx +27 -0
- package/src/components/form/DialogFormLayout.tsx +5 -29
- package/src/components/form/TypedVariableInput.tsx +441 -0
- package/src/components/form/VariableInput.tsx +101 -28
- package/src/components/form/__tests__/TypedVariableInput.test.tsx +152 -0
- package/src/components/form/__tests__/VariableInput.test.ts +85 -0
- package/src/components/form/filter/CollectionFilter.tsx +111 -0
- package/src/components/form/filter/CollectionFilterItem.tsx +184 -0
- package/src/components/form/filter/DateFilterDynamicComponent.tsx +283 -0
- package/src/components/form/filter/FilterValueInput.tsx +198 -0
- package/src/components/form/filter/__tests__/CollectionFilterItem.test.tsx +247 -0
- package/src/components/form/filter/__tests__/DateFilterDynamicComponent.test.tsx +148 -0
- package/src/components/form/filter/__tests__/FilterValueInput.test.tsx +243 -0
- package/src/components/form/filter/__tests__/compileFilterGroup.test.ts +146 -0
- package/src/components/form/filter/index.ts +13 -0
- package/src/components/form/filter/useFilterActionProps.ts +203 -0
- package/src/components/form/index.tsx +2 -0
- package/src/data-source/ExtendCollectionsProvider.tsx +144 -0
- package/src/data-source/__tests__/ExtendCollectionsProvider.test.tsx +264 -0
- package/src/data-source/index.ts +10 -0
- package/src/flow/FlowPage.tsx +35 -7
- package/src/flow/__tests__/FlowPage.test.tsx +79 -0
- package/src/flow/__tests__/FlowRoute.test.tsx +537 -3
- package/src/flow/actions/__tests__/linkageRules.hiddenSync.restore.test.ts +100 -0
- package/src/flow/actions/__tests__/linkageRules.subFormSetFieldProps.test.ts +191 -0
- package/src/flow/actions/__tests__/linkageRulesRefresh.test.ts +23 -1
- package/src/flow/actions/__tests__/openView.subModelKey.test.tsx +33 -0
- package/src/flow/actions/aclCheck.tsx +4 -0
- package/src/flow/actions/aclCheckRefresh.tsx +4 -0
- package/src/flow/actions/dataScope.tsx +8 -1
- package/src/flow/actions/dateTimeFormat.tsx +12 -8
- package/src/flow/actions/linkageRules.tsx +127 -2
- package/src/flow/actions/linkageRulesRefresh.tsx +2 -3
- package/src/flow/actions/openView.tsx +28 -4
- package/src/flow/actions/setTargetDataScope.tsx +1 -1
- package/src/flow/admin-shell/AdminLayoutRouteCoordinator.ts +11 -329
- package/src/flow/admin-shell/BaseLayoutModel.tsx +455 -0
- package/src/flow/admin-shell/BaseLayoutRouteCoordinator.ts +502 -0
- package/src/flow/admin-shell/__tests__/AdminLayoutRouteCoordinator.test.ts +547 -3
- package/src/flow/admin-shell/admin-layout/AdminLayoutComponent.tsx +35 -7
- package/src/flow/admin-shell/admin-layout/AdminLayoutEntryGuard.tsx +160 -0
- package/src/flow/admin-shell/admin-layout/AdminLayoutMenuModels.tsx +15 -13
- package/src/flow/admin-shell/admin-layout/AdminLayoutModel.tsx +28 -201
- package/src/flow/admin-shell/admin-layout/AdminLayoutSlotModels.tsx +11 -2
- package/src/flow/admin-shell/admin-layout/AppListRender.tsx +139 -0
- package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutMenuModels.test.ts +76 -27
- package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutModel.test.tsx +149 -27
- package/src/flow/admin-shell/admin-layout/index.ts +3 -0
- package/src/flow/admin-shell/admin-layout/resolveAdminRouteRuntimeTarget.test.ts +11 -0
- package/src/flow/admin-shell/admin-layout/resolveAdminRouteRuntimeTarget.ts +2 -4
- package/src/flow/admin-shell/admin-layout/useApplications.tsx +34 -1
- package/src/flow/admin-shell/useAdminLayoutRoutePage.ts +10 -26
- package/src/flow/admin-shell/useLayoutRoutePage.ts +61 -0
- package/src/flow/common/Markdown/Edit.tsx +32 -4
- package/src/flow/components/AdminLayout.tsx +4 -154
- package/src/flow/components/ConditionBuilder.tsx +12 -2
- package/src/flow/components/FieldAssignRulesEditor.tsx +37 -46
- package/src/flow/components/FieldAssignValueInput.tsx +8 -4
- package/src/flow/components/FlowRoute.tsx +105 -15
- package/src/flow/components/__tests__/FieldAssignRulesEditor.test.tsx +112 -1
- package/src/flow/components/__tests__/fieldAssignOptions.test.ts +71 -1
- package/src/flow/components/code-editor/__tests__/runjsCompletions.test.ts +272 -27
- package/src/flow/components/code-editor/runjsCompletions.ts +490 -9
- package/src/flow/components/fieldAssignOptions.ts +23 -6
- package/src/flow/components/filter/LinkageFilterItem.tsx +22 -9
- package/src/flow/components/filter/VariableFilterItem.tsx +27 -9
- package/src/flow/components/filter/__tests__/fieldsToOptions.test.ts +48 -0
- package/src/flow/components/filter/__tests__/metaTreeAssociationDepth.test.ts +60 -0
- package/src/flow/components/filter/fieldsToOptions.ts +9 -4
- package/src/flow/components/filter/index.ts +3 -0
- package/src/flow/components/filter/metaTreeAssociationDepth.ts +69 -0
- package/src/flow/components/filter/useFilterOptions.ts +102 -0
- package/src/flow/index.ts +4 -0
- package/src/flow/models/actions/FilterActionModel.tsx +15 -3
- package/src/flow/models/actions/UpdateRecordActionModel.tsx +14 -95
- package/src/flow/models/actions/UpdateRecordActionUtils.ts +4 -7
- package/src/flow/models/actions/__tests__/AssignFormRefill.test.ts +116 -1
- package/src/flow/models/base/ActionModel.tsx +8 -1
- package/src/flow/models/base/AssociationFieldGroupModel.tsx +20 -3
- package/src/flow/models/base/GridModel.tsx +1 -1
- package/src/flow/models/base/PageModel/ChildPageModel.tsx +5 -1
- package/src/flow/models/base/PageModel/PageModel.tsx +51 -18
- package/src/flow/models/base/PageModel/RootPageModel.tsx +6 -13
- package/src/flow/models/base/PageModel/__tests__/ChildPageModel.test.ts +12 -0
- package/src/flow/models/base/PageModel/__tests__/PageModel.test.ts +102 -1
- package/src/flow/models/base/RouteModel.tsx +1 -1
- package/src/flow/models/base/__tests__/AssociationFieldGroupModel.test.ts +86 -0
- package/src/flow/models/base/__tests__/GridModel.render.test.tsx +53 -0
- package/src/flow/models/blocks/assign-form/AssignFormGridModel.tsx +12 -3
- package/src/flow/models/blocks/assign-form/AssignFormItemModel.tsx +77 -6
- package/src/flow/models/blocks/assign-form/__tests__/assignFieldValuesFlow.editor.test.tsx +216 -0
- package/src/flow/models/blocks/assign-form/assignFieldValuesFlow.tsx +308 -0
- package/src/flow/models/blocks/assign-form/index.ts +1 -0
- package/src/flow/models/blocks/filter-form/FilterFormItemModel.tsx +1 -1
- package/src/flow/models/blocks/filter-form/__tests__/FilterFormItemModel.defineChildren.test.ts +6 -1
- package/src/flow/models/blocks/filter-manager/flow-actions/connectFields.tsx +48 -6
- package/src/flow/models/blocks/form/FormActionGroupModel.tsx +14 -0
- package/src/flow/models/blocks/form/FormActionModel.tsx +30 -3
- package/src/flow/models/blocks/form/FormAssociationItemModel.tsx +9 -0
- package/src/flow/models/blocks/form/FormItemModel.tsx +17 -1
- package/src/flow/models/blocks/form/__tests__/FormActionGroupModel.test.ts +46 -0
- package/src/flow/models/blocks/form/__tests__/FormBlockModel.test.tsx +17 -0
- package/src/flow/models/blocks/form/__tests__/FormItemModel.defineChildren.test.ts +27 -2
- package/src/flow/models/blocks/form/__tests__/submitHandler.test.ts +71 -0
- package/src/flow/models/blocks/form/submitHandler.ts +8 -1
- package/src/flow/models/blocks/form/submitValues.ts +4 -1
- package/src/flow/models/blocks/form/value-runtime/__tests__/runtime.test.ts +29 -0
- package/src/flow/models/blocks/form/value-runtime/runtime.ts +5 -1
- package/src/flow/models/blocks/table/TableBlockModel.tsx +118 -16
- package/src/flow/models/blocks/table/TableColumnModel.tsx +15 -5
- package/src/flow/models/blocks/table/TableCustomColumnModel.tsx +2 -2
- package/src/flow/models/blocks/table/__tests__/TableBlockModel.rowSelection.test.tsx +114 -0
- package/src/flow/models/blocks/table/__tests__/TableColumnModel.test.tsx +18 -0
- package/src/flow/models/fields/AssociationFieldModel/SubFormFieldModel.tsx +7 -1
- package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/SubTableColumnModel.tsx +6 -7
- package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/SubTableField.tsx +1 -1
- package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/index.tsx +6 -5
- package/src/flow/models/fields/ClickableFieldModel.tsx +9 -1
- package/src/flow/models/fields/CollectionSelectorFieldModel.tsx +8 -2
- package/src/flow/models/fields/DisplayEnumFieldModel.tsx +8 -2
- package/src/flow/models/fields/DisplayTimeFieldModel.tsx +1 -1
- package/src/flow/models/fields/TimeFieldModel.tsx +1 -1
- package/src/flow/models/fields/__tests__/TimeFieldModel.test.tsx +61 -0
- package/src/flow/models/fields/mobile-components/MobileDatePicker.tsx +19 -3
- package/src/flow/models/fields/mobile-components/MobileLazySelect.tsx +5 -3
- package/src/flow/models/fields/mobile-components/__tests__/MobileDatePicker.test.tsx +94 -0
- package/src/flow/models/fields/mobile-components/__tests__/MobileSelect.test.tsx +70 -0
- package/src/flow/models/topbar/TopbarActionModel.tsx +1 -1
- package/src/flow/utils/__tests__/dateTimeFormat.test.ts +91 -0
- package/src/index.ts +6 -0
- package/src/layout-manager/LayoutContentRoute.tsx +90 -0
- package/src/layout-manager/LayoutManager.tsx +185 -0
- package/src/layout-manager/LayoutRoute.tsx +138 -0
- package/src/layout-manager/__tests__/LayoutManager.test.tsx +335 -0
- package/src/layout-manager/__tests__/LayoutRoute.test.tsx +473 -0
- package/src/layout-manager/index.ts +14 -0
- package/src/layout-manager/types.ts +22 -0
- package/src/layout-manager/utils.ts +37 -0
- package/src/nocobase-buildin-plugin/index.tsx +69 -67
- package/src/nocobase-buildin-plugin/plugins/LocalePlugin.ts +1 -0
- package/src/settings-center/index.ts +1 -1
- package/src/settings-center/plugin-manager/BulkEnableButton.tsx +111 -0
- package/src/settings-center/plugin-manager/PluginCard.tsx +270 -0
- package/src/settings-center/plugin-manager/PluginDetail.tsx +195 -0
- package/src/settings-center/plugin-manager/index.tsx +254 -0
- package/src/settings-center/plugin-manager/types.ts +35 -0
- package/src/settings-center/utils.tsx +8 -1
- package/src/theme/__tests__/globalStyles.test.ts +24 -0
- package/src/theme/globalStyles.ts +10 -0
- package/src/utils/globalDeps.ts +2 -0
- package/es/collection-manager/interfaces/linkTo.d.ts +0 -90
- package/es/collection-manager/interfaces/o2o.d.ts +0 -621
- package/es/collection-manager/interfaces/properties/operators.d.ts +0 -294
- package/es/collection-manager/interfaces/subTable.d.ts +0 -172
- package/src/collection-manager/interfaces/linkTo.ts +0 -120
- package/src/collection-manager/interfaces/o2o.tsx +0 -561
- package/src/collection-manager/interfaces/subTable.ts +0 -218
- package/src/settings-center/PluginManagerPage.tsx +0 -162
|
@@ -25,28 +25,75 @@ import { TextAreaWithContextSelector } from '../../flow/components/TextAreaWithC
|
|
|
25
25
|
* stored values round-trip to a labelled pill instead of falling back to a
|
|
26
26
|
* raw `{{…}}` literal.
|
|
27
27
|
*/
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Variable delimiters: opening + closing tokens. Default `['{{', '}}']`
|
|
31
|
+
* matches NocoBase server template convention (Handlebars HTML-escaped
|
|
32
|
+
* output). Pass `['{{{', '}}}']` to switch to Handlebars' raw/unescaped
|
|
33
|
+
* form — required for fields whose content is rendered as HTML (e.g.
|
|
34
|
+
* in-app message body) so the variable expansion bypasses HTML escaping.
|
|
35
|
+
*
|
|
36
|
+
* Restrict to literal-token pairs, since the regex builder escapes them
|
|
37
|
+
* verbatim.
|
|
38
|
+
*/
|
|
39
|
+
export type VariableDelimiters = readonly [string, string];
|
|
40
|
+
|
|
41
|
+
const DEFAULT_DELIMITERS: VariableDelimiters = ['{{', '}}'];
|
|
42
|
+
|
|
43
|
+
function escapeForRegExp(s: string): string {
|
|
44
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
40
45
|
}
|
|
41
46
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
47
|
+
/**
|
|
48
|
+
* Factory: returns a `parseValueToPath` bound to the given delimiters.
|
|
49
|
+
* Anchored (`^…$`) — only treats the whole input as a single variable
|
|
50
|
+
* reference, matching the v1 single-line picker behaviour.
|
|
51
|
+
*/
|
|
52
|
+
export function makeParseVariablePath(delimiters: VariableDelimiters = DEFAULT_DELIMITERS) {
|
|
53
|
+
const [open, close] = delimiters;
|
|
54
|
+
const re = new RegExp(`^${escapeForRegExp(open)}\\s*(.+?)\\s*${escapeForRegExp(close)}$`);
|
|
55
|
+
return (value?: string): string[] | undefined => {
|
|
56
|
+
if (typeof value !== 'string') return undefined;
|
|
57
|
+
const match = value.trim().match(re);
|
|
58
|
+
if (!match) return undefined;
|
|
59
|
+
let pathString = match[1];
|
|
60
|
+
// Backwards-compat: accept the legacy `ctx.` prefix so values produced by
|
|
61
|
+
// pre-fix versions of the picker still resolve to a labelled pill.
|
|
62
|
+
if (pathString === 'ctx') return [];
|
|
63
|
+
if (pathString.startsWith('ctx.')) pathString = pathString.slice(4);
|
|
64
|
+
return pathString.split('.');
|
|
65
|
+
};
|
|
48
66
|
}
|
|
49
67
|
|
|
68
|
+
/**
|
|
69
|
+
* Factory: returns a `formatPathToValue` bound to the given delimiters.
|
|
70
|
+
* No inner spaces — matches the v1 storage shape exactly so round-trips
|
|
71
|
+
* through the API stay byte-stable.
|
|
72
|
+
*/
|
|
73
|
+
export function makeFormatVariablePath(delimiters: VariableDelimiters = DEFAULT_DELIMITERS) {
|
|
74
|
+
const [open, close] = delimiters;
|
|
75
|
+
return (meta?: MetaTreeNode): string | undefined => {
|
|
76
|
+
const paths = meta?.paths || [];
|
|
77
|
+
if (paths.length === 0) return undefined;
|
|
78
|
+
return `${open}${paths.join('.')}${close}`;
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Factory: returns a global regex matching every occurrence of the
|
|
84
|
+
* variable token within a longer string. Used by `VariableHybridInput`
|
|
85
|
+
* to render embedded variables as pills.
|
|
86
|
+
*/
|
|
87
|
+
export function makeVariableRegExp(delimiters: VariableDelimiters = DEFAULT_DELIMITERS): RegExp {
|
|
88
|
+
const [open, close] = delimiters;
|
|
89
|
+
return new RegExp(`${escapeForRegExp(open)}\\s*([^{}]+?)\\s*${escapeForRegExp(close)}`, 'g');
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Default exports — `{{ ... }}` for the most common case. Kept named so
|
|
93
|
+
// existing callers don't need to switch to the factory.
|
|
94
|
+
export const parseVariablePath = makeParseVariablePath();
|
|
95
|
+
export const formatVariablePath = makeFormatVariablePath();
|
|
96
|
+
|
|
50
97
|
const META_TREE_CACHE_PREFIX = '@nocobase/client-v2:VariableInput:metaTree';
|
|
51
98
|
|
|
52
99
|
/**
|
|
@@ -123,8 +170,22 @@ export interface VariableInputProps {
|
|
|
123
170
|
* Override the converters used by the underlying `VariableHybridInput`.
|
|
124
171
|
* Mostly useful when the caller wants to constrain `formatPathToValue` to a
|
|
125
172
|
* specific namespace (see `EnvVariableInput` for that pattern).
|
|
173
|
+
*
|
|
174
|
+
* Takes precedence over `delimiters` when both are set on the same field
|
|
175
|
+
* (an explicit converter wins over the delimiter-derived one).
|
|
126
176
|
*/
|
|
127
177
|
converters?: VariableHybridInputConverters;
|
|
178
|
+
/**
|
|
179
|
+
* Token pair wrapping variable references in the stored string. Defaults
|
|
180
|
+
* to `['{{', '}}']` — the standard NocoBase server-template form,
|
|
181
|
+
* HTML-escaped by Handlebars. Pass `['{{{', '}}}']` for fields rendered
|
|
182
|
+
* as HTML where escaping would corrupt the variable value (e.g. the
|
|
183
|
+
* in-app message body).
|
|
184
|
+
*
|
|
185
|
+
* Ignored when `converters` is also supplied — caller-provided converters
|
|
186
|
+
* win.
|
|
187
|
+
*/
|
|
188
|
+
delimiters?: VariableDelimiters;
|
|
128
189
|
className?: string;
|
|
129
190
|
style?: React.CSSProperties;
|
|
130
191
|
}
|
|
@@ -136,16 +197,24 @@ export interface VariableInputProps {
|
|
|
136
197
|
* line of mixed literal+variable content is appropriate.
|
|
137
198
|
*/
|
|
138
199
|
export function VariableInput(props: VariableInputProps) {
|
|
139
|
-
const { namespaces, extraNodes, converters, ...rest } = props;
|
|
200
|
+
const { namespaces, extraNodes, converters, delimiters, ...rest } = props;
|
|
140
201
|
const metaTree = useFilteredMetaTree({ namespaces, extraNodes });
|
|
141
|
-
const mergedConverters = useMemo<VariableHybridInputConverters>(
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
202
|
+
const mergedConverters = useMemo<VariableHybridInputConverters>(() => {
|
|
203
|
+
// Default delimiters → reuse the pre-built singletons.
|
|
204
|
+
if (!delimiters) {
|
|
205
|
+
return {
|
|
206
|
+
formatPathToValue: formatVariablePath,
|
|
207
|
+
parseValueToPath: parseVariablePath,
|
|
208
|
+
...converters,
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
return {
|
|
212
|
+
formatPathToValue: makeFormatVariablePath(delimiters),
|
|
213
|
+
parseValueToPath: makeParseVariablePath(delimiters),
|
|
214
|
+
variableRegExp: makeVariableRegExp(delimiters),
|
|
145
215
|
...converters,
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
);
|
|
216
|
+
};
|
|
217
|
+
}, [converters, delimiters]);
|
|
149
218
|
return <VariableHybridInput {...rest} converters={mergedConverters} metaTree={metaTree} />;
|
|
150
219
|
}
|
|
151
220
|
|
|
@@ -161,9 +230,13 @@ export interface VariableTextAreaProps extends Omit<VariableInputProps, 'convert
|
|
|
161
230
|
* is desirable (the server expands them at render time).
|
|
162
231
|
*/
|
|
163
232
|
export function VariableTextArea(props: VariableTextAreaProps) {
|
|
164
|
-
const { namespaces, extraNodes, rows, maxRows, style, ...rest } = props;
|
|
233
|
+
const { namespaces, extraNodes, rows, maxRows, style, delimiters, ...rest } = props;
|
|
165
234
|
const metaTree = useFilteredMetaTree({ namespaces, extraNodes });
|
|
166
235
|
const metaTreeGetter = useMemo(() => () => metaTree, [metaTree]);
|
|
236
|
+
const formatPathToValue = useMemo(
|
|
237
|
+
() => (delimiters ? makeFormatVariablePath(delimiters) : formatVariablePath),
|
|
238
|
+
[delimiters],
|
|
239
|
+
);
|
|
167
240
|
return (
|
|
168
241
|
<TextAreaWithContextSelector
|
|
169
242
|
{...rest}
|
|
@@ -171,7 +244,7 @@ export function VariableTextArea(props: VariableTextAreaProps) {
|
|
|
171
244
|
maxRows={maxRows}
|
|
172
245
|
style={style}
|
|
173
246
|
metaTree={metaTreeGetter}
|
|
174
|
-
formatPathToValue={(meta) =>
|
|
247
|
+
formatPathToValue={(meta) => formatPathToValue(meta) ?? ''}
|
|
175
248
|
/>
|
|
176
249
|
);
|
|
177
250
|
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { FlowContext, FlowContextProvider } from '@nocobase/flow-engine';
|
|
11
|
+
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
|
|
12
|
+
import React from 'react';
|
|
13
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
14
|
+
import { TypedVariableInput } from '../TypedVariableInput';
|
|
15
|
+
|
|
16
|
+
function createContextWithEnv() {
|
|
17
|
+
const ctx = new FlowContext();
|
|
18
|
+
(ctx as any).t = (key: string) => key;
|
|
19
|
+
|
|
20
|
+
ctx.defineProperty('$env', {
|
|
21
|
+
value: { SMTP_PORT: 465, SECURE_FLAG: true },
|
|
22
|
+
meta: {
|
|
23
|
+
title: 'Env',
|
|
24
|
+
type: 'object',
|
|
25
|
+
properties: {
|
|
26
|
+
SMTP_PORT: { title: 'SMTP Port', type: 'number' },
|
|
27
|
+
SECURE_FLAG: { title: 'Secure flag', type: 'boolean' },
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
return ctx;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function renderWithCtx(ctx: FlowContext, node: React.ReactNode) {
|
|
36
|
+
return render(<FlowContextProvider context={ctx}>{node}</FlowContextProvider>);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
describe('TypedVariableInput - constant rendering', () => {
|
|
40
|
+
it('renders an InputNumber for a numeric value when types=[number]', async () => {
|
|
41
|
+
const ctx = createContextWithEnv();
|
|
42
|
+
renderWithCtx(
|
|
43
|
+
ctx,
|
|
44
|
+
<TypedVariableInput
|
|
45
|
+
value={465}
|
|
46
|
+
types={[['number', { min: 1, max: 65535 }]]}
|
|
47
|
+
namespaces={['$env']}
|
|
48
|
+
onChange={() => undefined}
|
|
49
|
+
/>,
|
|
50
|
+
);
|
|
51
|
+
const numberInput = await screen.findByDisplayValue('465');
|
|
52
|
+
expect(numberInput).toBeInTheDocument();
|
|
53
|
+
expect(numberInput.getAttribute('role')).toBe('spinbutton');
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('renders a boolean Select when value is true and types=[boolean]', async () => {
|
|
57
|
+
const ctx = createContextWithEnv();
|
|
58
|
+
renderWithCtx(
|
|
59
|
+
ctx,
|
|
60
|
+
<TypedVariableInput value={true} types={['boolean']} namespaces={['$env']} onChange={() => undefined} />,
|
|
61
|
+
);
|
|
62
|
+
await waitFor(() => {
|
|
63
|
+
// antd Select renders the chosen item label via a selection-item span
|
|
64
|
+
const item = screen.getByText('True');
|
|
65
|
+
expect(item).toBeInTheDocument();
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('renders the Null placeholder when value=null and nullable=true', async () => {
|
|
70
|
+
const ctx = createContextWithEnv();
|
|
71
|
+
renderWithCtx(
|
|
72
|
+
ctx,
|
|
73
|
+
<TypedVariableInput value={null} types={['number']} namespaces={['$env']} nullable onChange={() => undefined} />,
|
|
74
|
+
);
|
|
75
|
+
// Uses placeholder slot (not value) so antd's grey placeholder colour
|
|
76
|
+
// applies — see comment in TypedVariableInput.tsx.
|
|
77
|
+
const nullInput = await screen.findByPlaceholderText('<Null>');
|
|
78
|
+
expect(nullInput).toBeInTheDocument();
|
|
79
|
+
expect(nullInput.getAttribute('readonly')).not.toBeNull();
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
describe('TypedVariableInput - variable rendering', () => {
|
|
84
|
+
it('renders a labelled pill when value is a {{ $env.X }} expression', async () => {
|
|
85
|
+
const ctx = createContextWithEnv();
|
|
86
|
+
renderWithCtx(
|
|
87
|
+
ctx,
|
|
88
|
+
<TypedVariableInput
|
|
89
|
+
value="{{$env.SMTP_PORT}}"
|
|
90
|
+
types={['number']}
|
|
91
|
+
namespaces={['$env']}
|
|
92
|
+
onChange={() => undefined}
|
|
93
|
+
/>,
|
|
94
|
+
);
|
|
95
|
+
await waitFor(() => {
|
|
96
|
+
const tag = screen.getByRole('button', { name: 'variable-tag' });
|
|
97
|
+
expect(tag.textContent).toContain('Env');
|
|
98
|
+
expect(tag.textContent).toContain('SMTP Port');
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('clears back to null when the close button is clicked (nullable=true)', async () => {
|
|
103
|
+
const ctx = createContextWithEnv();
|
|
104
|
+
const handleChange = vi.fn();
|
|
105
|
+
renderWithCtx(
|
|
106
|
+
ctx,
|
|
107
|
+
<TypedVariableInput
|
|
108
|
+
value="{{$env.SMTP_PORT}}"
|
|
109
|
+
types={['number']}
|
|
110
|
+
namespaces={['$env']}
|
|
111
|
+
nullable
|
|
112
|
+
onChange={handleChange}
|
|
113
|
+
/>,
|
|
114
|
+
);
|
|
115
|
+
const clear = await screen.findByRole('button', { name: 'icon-close' });
|
|
116
|
+
fireEvent.click(clear);
|
|
117
|
+
expect(handleChange).toHaveBeenCalledWith(null);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('clears back to default-of-first-type when nullable=false', async () => {
|
|
121
|
+
const ctx = createContextWithEnv();
|
|
122
|
+
const handleChange = vi.fn();
|
|
123
|
+
renderWithCtx(
|
|
124
|
+
ctx,
|
|
125
|
+
<TypedVariableInput
|
|
126
|
+
value="{{$env.SMTP_PORT}}"
|
|
127
|
+
types={['number']}
|
|
128
|
+
namespaces={['$env']}
|
|
129
|
+
nullable={false}
|
|
130
|
+
onChange={handleChange}
|
|
131
|
+
/>,
|
|
132
|
+
);
|
|
133
|
+
const clear = await screen.findByRole('button', { name: 'icon-close' });
|
|
134
|
+
fireEvent.click(clear);
|
|
135
|
+
expect(handleChange).toHaveBeenCalledWith(0);
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
describe('TypedVariableInput - editor onChange propagation', () => {
|
|
140
|
+
it('forwards numeric edits via onChange', async () => {
|
|
141
|
+
const ctx = createContextWithEnv();
|
|
142
|
+
const handleChange = vi.fn();
|
|
143
|
+
renderWithCtx(ctx, <TypedVariableInput value={465} types={['number']} onChange={handleChange} />);
|
|
144
|
+
const numberInput = await screen.findByDisplayValue('465');
|
|
145
|
+
fireEvent.change(numberInput, { target: { value: '587' } });
|
|
146
|
+
// antd InputNumber may emit on blur; force blur to flush
|
|
147
|
+
fireEvent.blur(numberInput);
|
|
148
|
+
expect(handleChange).toHaveBeenCalled();
|
|
149
|
+
const lastCall = handleChange.mock.calls[handleChange.mock.calls.length - 1][0];
|
|
150
|
+
expect(lastCall).toBe(587);
|
|
151
|
+
});
|
|
152
|
+
});
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { describe, expect, it } from 'vitest';
|
|
11
|
+
import {
|
|
12
|
+
formatVariablePath,
|
|
13
|
+
makeFormatVariablePath,
|
|
14
|
+
makeParseVariablePath,
|
|
15
|
+
makeVariableRegExp,
|
|
16
|
+
parseVariablePath,
|
|
17
|
+
} from '../VariableInput';
|
|
18
|
+
|
|
19
|
+
describe('VariableInput delimiter helpers', () => {
|
|
20
|
+
describe('default delimiters {{ }}', () => {
|
|
21
|
+
it('formats meta nodes into `{{x.y}}` with no inner spaces', () => {
|
|
22
|
+
expect(formatVariablePath({ paths: ['$user', 'id'] } as any)).toBe('{{$user.id}}');
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('parses `{{x.y}}` into a path array', () => {
|
|
26
|
+
expect(parseVariablePath('{{ $user.id }}')).toEqual(['$user', 'id']);
|
|
27
|
+
expect(parseVariablePath('{{$user.id}}')).toEqual(['$user', 'id']);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('strips a legacy `ctx.` prefix for backwards-compat', () => {
|
|
31
|
+
expect(parseVariablePath('{{ctx.$user.id}}')).toEqual(['$user', 'id']);
|
|
32
|
+
expect(parseVariablePath('{{ctx}}')).toEqual([]);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('returns undefined for plain text', () => {
|
|
36
|
+
expect(parseVariablePath('hello world')).toBeUndefined();
|
|
37
|
+
expect(parseVariablePath('')).toBeUndefined();
|
|
38
|
+
expect(parseVariablePath(undefined)).toBeUndefined();
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
describe('triple-brace delimiters {{{ }}}', () => {
|
|
43
|
+
const format = makeFormatVariablePath(['{{{', '}}}']);
|
|
44
|
+
const parse = makeParseVariablePath(['{{{', '}}}']);
|
|
45
|
+
|
|
46
|
+
it('formats meta nodes into `{{{x.y}}}`', () => {
|
|
47
|
+
expect(format({ paths: ['$user', 'id'] } as any)).toBe('{{{$user.id}}}');
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('parses `{{{x.y}}}` into a path array', () => {
|
|
51
|
+
expect(parse('{{{$user.id}}}')).toEqual(['$user', 'id']);
|
|
52
|
+
expect(parse('{{{ $user.id }}}')).toEqual(['$user', 'id']);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('does not match the double-brace form', () => {
|
|
56
|
+
expect(parse('{{$user.id}}')).toBeUndefined();
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
describe('makeVariableRegExp', () => {
|
|
61
|
+
it('matches every occurrence in a longer string under default delimiters', () => {
|
|
62
|
+
const re = makeVariableRegExp();
|
|
63
|
+
const matches = Array.from('hello {{ $user.id }} world {{ $env.X }}'.matchAll(re)).map((m) => m[1]);
|
|
64
|
+
expect(matches).toEqual(['$user.id', '$env.X']);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('matches triple-brace occurrences when configured', () => {
|
|
68
|
+
const re = makeVariableRegExp(['{{{', '}}}']);
|
|
69
|
+
const matches = Array.from('hi {{{$user.id}}} :)'.matchAll(re)).map((m) => m[1]);
|
|
70
|
+
expect(matches).toEqual(['$user.id']);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('does not match double-brace occurrences under triple-brace config', () => {
|
|
74
|
+
const re = makeVariableRegExp(['{{{', '}}}']);
|
|
75
|
+
const matches = Array.from('plain {{ x }} text'.matchAll(re));
|
|
76
|
+
expect(matches).toHaveLength(0);
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('handles empty paths consistently', () => {
|
|
81
|
+
expect(formatVariablePath({ paths: [] } as any)).toBeUndefined();
|
|
82
|
+
expect(formatVariablePath(undefined)).toBeUndefined();
|
|
83
|
+
expect(makeFormatVariablePath(['{{{', '}}}'])({ paths: [] } as any)).toBeUndefined();
|
|
84
|
+
});
|
|
85
|
+
});
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { FilterOutlined } from '@ant-design/icons';
|
|
11
|
+
import type { Collection } from '@nocobase/flow-engine';
|
|
12
|
+
import { Button, type ButtonProps, Popover, type PopoverProps } from 'antd';
|
|
13
|
+
import React, { FC, useState } from 'react';
|
|
14
|
+
import { FilterContent } from '../../../flow/components/filter';
|
|
15
|
+
import { CompiledFilter, FilterApplyAction, useFilterActionProps } from './useFilterActionProps';
|
|
16
|
+
|
|
17
|
+
const identity = (s: string) => s;
|
|
18
|
+
|
|
19
|
+
export interface CollectionFilterProps {
|
|
20
|
+
/** Collection whose fields drive the filter row's field picker. */
|
|
21
|
+
collection: Collection | undefined;
|
|
22
|
+
/** Called on Submit or Reset with the compiled NocoBase filter param (`undefined` when cleared). */
|
|
23
|
+
onChange: (filter: CompiledFilter) => void;
|
|
24
|
+
/** Translator. Defaults to identity. */
|
|
25
|
+
t?: (key: string, options?: Record<string, any>) => string;
|
|
26
|
+
/** Whitelist of root-level field names to expose. */
|
|
27
|
+
filterableFieldNames?: string[];
|
|
28
|
+
/**
|
|
29
|
+
* Blacklist of root-level field names to drop. Mirrors v1's `nonfilterable: [...]` on `Filter.Action`. When both `filterableFieldNames` and this prop are supplied, both apply (final = whitelist ∩ ¬blacklist).
|
|
30
|
+
*/
|
|
31
|
+
nonfilterableFieldNames?: string[];
|
|
32
|
+
/**
|
|
33
|
+
* Bypass the `filterableFieldNames` whitelist.
|
|
34
|
+
*
|
|
35
|
+
* Legacy escape hatch — prefer adjusting `filterableFieldNames` / `nonfilterableFieldNames` instead.
|
|
36
|
+
*/
|
|
37
|
+
noIgnore?: boolean;
|
|
38
|
+
/** Override the trigger button's label. Defaults to `t('Filter')`, or the v1-style `t('{{count}} filter items', { count })` when conditions are present. */
|
|
39
|
+
buttonText?: React.ReactNode;
|
|
40
|
+
/** Swap the default `t('Filter')` label for v1's `t('{{count}} filter items', { count })` when conditions are present. Defaults to `true`. */
|
|
41
|
+
showCount?: boolean;
|
|
42
|
+
/** Pass-through props for the antd `<Popover>`. */
|
|
43
|
+
popoverProps?: Omit<PopoverProps, 'open' | 'onOpenChange' | 'content' | 'children'>;
|
|
44
|
+
/** Pass-through props for the trigger `<Button>`. */
|
|
45
|
+
buttonProps?: Omit<ButtonProps, 'icon' | 'type' | 'children' | 'onClick'>;
|
|
46
|
+
/** Min-width applied to the popover body. Defaults to `520`. */
|
|
47
|
+
popoverMinWidth?: number;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Filter button bound to a collection. Renders an antd `<Popover>` over a `<Button>`; the popover hosts a multi-condition filter form (field picker, operator, value). Submit dismisses the popover and emits the compiled filter via `onChange`; Reset keeps the popover open and emits `undefined`.
|
|
52
|
+
*
|
|
53
|
+
* Pair with `<ExtendCollectionsProvider>` when the target collection is client-only (e.g. a `schema-only` server collection that isn't auto-published to the v2 data source).
|
|
54
|
+
*/
|
|
55
|
+
export const CollectionFilter: FC<CollectionFilterProps> = (props) => {
|
|
56
|
+
const {
|
|
57
|
+
collection,
|
|
58
|
+
onChange,
|
|
59
|
+
t = identity,
|
|
60
|
+
filterableFieldNames,
|
|
61
|
+
nonfilterableFieldNames,
|
|
62
|
+
noIgnore,
|
|
63
|
+
buttonText,
|
|
64
|
+
showCount = true,
|
|
65
|
+
popoverProps,
|
|
66
|
+
buttonProps,
|
|
67
|
+
popoverMinWidth = 520,
|
|
68
|
+
} = props;
|
|
69
|
+
|
|
70
|
+
const [open, setOpen] = useState(false);
|
|
71
|
+
|
|
72
|
+
const filterAction = useFilterActionProps({
|
|
73
|
+
collection,
|
|
74
|
+
filterableFieldNames,
|
|
75
|
+
nonfilterableFieldNames,
|
|
76
|
+
noIgnore,
|
|
77
|
+
t,
|
|
78
|
+
onApply: (filter: CompiledFilter, action: FilterApplyAction) => {
|
|
79
|
+
onChange(filter);
|
|
80
|
+
if (action === 'submit') setOpen(false);
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// Matches v1's `Filter.Action`: when at least one condition is set, the button label switches to the count-aware string (`"N 个筛选项"` in zh-CN). The button itself stays in the default (white) style — v1 never flipped it to `type='primary'`.
|
|
85
|
+
const label =
|
|
86
|
+
buttonText ??
|
|
87
|
+
(showCount && filterAction.conditionCount > 0
|
|
88
|
+
? t('{{count}} filter items', { count: filterAction.conditionCount })
|
|
89
|
+
: t('Filter'));
|
|
90
|
+
|
|
91
|
+
return (
|
|
92
|
+
<Popover
|
|
93
|
+
trigger="click"
|
|
94
|
+
placement="bottomLeft"
|
|
95
|
+
{...popoverProps}
|
|
96
|
+
open={open}
|
|
97
|
+
onOpenChange={setOpen}
|
|
98
|
+
content={
|
|
99
|
+
<div style={{ minWidth: popoverMinWidth }}>
|
|
100
|
+
<FilterContent value={filterAction.value} ctx={filterAction.ctx} FilterItem={filterAction.FilterItem} />
|
|
101
|
+
</div>
|
|
102
|
+
}
|
|
103
|
+
>
|
|
104
|
+
<Button icon={<FilterOutlined />} disabled={!collection} {...buttonProps}>
|
|
105
|
+
{label}
|
|
106
|
+
</Button>
|
|
107
|
+
</Popover>
|
|
108
|
+
);
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
export default CollectionFilter;
|