@nocobase/client-v2 2.2.0-alpha.1 → 2.2.0-alpha.10
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/APIClient.d.ts +1 -3
- package/es/BaseApplication.d.ts +3 -0
- package/es/PluginSettingsManager.d.ts +33 -0
- package/es/RouteRepository.d.ts +29 -6
- package/es/RouterManager.d.ts +15 -0
- package/es/authRedirect.d.ts +1 -0
- package/es/collection-field-interface/CollectionFieldInterface.d.ts +1 -0
- package/es/collection-field-interface/CollectionFieldInterfaceManager.d.ts +1 -0
- package/es/components/category-tabs/SortableCategoryTabs.d.ts +55 -0
- package/es/components/category-tabs/index.d.ts +9 -0
- package/es/components/form/JsonTextArea.d.ts +2 -1
- package/es/components/form/ScanInput/useCodeScanner.d.ts +3 -2
- package/es/components/form/TypedVariableInput.d.ts +31 -5
- package/es/components/form/VariableJsonTextArea.d.ts +19 -0
- package/es/components/form/filter/CollectionFilter.d.ts +2 -0
- package/es/components/form/filter/CollectionFilterItem.d.ts +11 -1
- package/es/components/form/filter/CollectionFilterPanel.d.ts +2 -0
- package/es/components/form/filter/index.d.ts +2 -0
- package/es/components/form/filter/useFilterActionProps.d.ts +2 -0
- package/es/components/form/index.d.ts +1 -0
- package/es/components/index.d.ts +2 -0
- package/es/entry-actions/EntryActionManager.d.ts +24 -0
- package/es/entry-actions/index.d.ts +9 -0
- package/es/flow/actions/afterSuccess.d.ts +8 -1
- package/es/flow/actions/index.d.ts +2 -2
- package/es/flow/actions/linkageRules.d.ts +2 -0
- package/es/flow/admin-shell/BaseLayoutModel.d.ts +6 -0
- package/es/flow/admin-shell/BaseLayoutRouteCoordinator.d.ts +7 -0
- package/es/flow/admin-shell/admin-layout/AdminLayoutEntryGuard.d.ts +2 -0
- package/es/flow/admin-shell/admin-layout/AdminLayoutMenuFlowUtils.d.ts +2 -1
- package/es/flow/admin-shell/admin-layout/AdminLayoutMenuModels.d.ts +3 -0
- package/es/flow/admin-shell/admin-layout/AdminLayoutMenuUtils.d.ts +3 -2
- package/es/flow/admin-shell/admin-layout/AdminLayoutSlotModels.d.ts +5 -0
- package/es/flow/admin-shell/admin-layout/AppListRender.d.ts +2 -1
- package/es/flow/admin-shell/admin-layout/AppSwitcherActionPanelModel.d.ts +24 -0
- package/es/flow/admin-shell/admin-layout/index.d.ts +1 -0
- package/es/flow/admin-shell/admin-layout/resolveAdminRouteRuntimeTarget.d.ts +6 -0
- package/es/flow/admin-shell/admin-layout/useApplications.d.ts +7 -2
- package/es/flow/components/FieldAssignExactDatePicker.d.ts +1 -0
- package/es/flow/components/FieldAssignRulesEditor.d.ts +3 -1
- package/es/flow/components/FieldAssignValueInput.d.ts +9 -0
- package/es/flow/components/FlowRoute.d.ts +2 -2
- package/es/flow/components/RunJSValueEditor.d.ts +1 -0
- package/es/flow/components/filter/FilterGroup.d.ts +1 -0
- package/es/flow/components/filter/VariableFilterItem.d.ts +6 -1
- package/es/flow/index.d.ts +1 -0
- package/es/flow/internal/registerDeviceTypeContext.d.ts +10 -0
- package/es/flow/models/base/ActionModelCore.d.ts +2 -0
- package/es/flow/models/base/BlockGridModel.d.ts +3 -0
- package/es/flow/models/base/GridModel.d.ts +1 -1
- package/es/flow/models/base/PageModel/PageModel.d.ts +12 -0
- package/es/flow/models/base/PageModel/PageModelTabBar.d.ts +22 -0
- package/es/flow/models/base/PageModel/PageTabModel.d.ts +12 -0
- package/es/flow/models/blocks/filter-form/FilterFormBlockModel.d.ts +5 -0
- package/es/flow/models/blocks/form/FormBlockModel.d.ts +4 -0
- package/es/flow/models/blocks/form/QuickEditFormModel.d.ts +17 -2
- package/es/flow/models/blocks/form/submitHandler.d.ts +1 -1
- package/es/flow/models/blocks/form/value-runtime/rules.d.ts +21 -0
- package/es/flow/models/blocks/form/value-runtime/runtime.d.ts +30 -0
- package/es/flow/models/blocks/form/value-runtime/types.d.ts +2 -2
- package/es/flow/models/blocks/js-block/JSBlock.d.ts +1 -0
- package/es/flow/models/blocks/table/TableActionsColumnModel.d.ts +1 -0
- package/es/flow/models/blocks/table/TableBlockModel.d.ts +1 -0
- package/es/flow/models/blocks/table/TableColumnModel.d.ts +1 -0
- package/es/flow/models/blocks/table/dragSort/dragSortHooks.d.ts +1 -1
- package/es/flow/models/blocks/table/dragSort/dragSortSettings.d.ts +2 -1
- package/es/flow/models/blocks/table/dragSort/dragSortUtils.d.ts +6 -4
- package/es/flow/models/fields/AssociationFieldModel/RecordSelectFieldModel.d.ts +2 -0
- package/es/flow/models/fields/AssociationFieldModel/SubTableFieldModel/SubTableColumnModel.d.ts +6 -0
- package/es/flow/models/fields/DateTimeFieldModel/dateLimit.d.ts +15 -7
- package/es/flow/resolveViewParamsToViewList.d.ts +1 -1
- package/es/flow/routeTransientInputArgs.d.ts +14 -0
- package/es/flow/utils/dataScopeRowSnapshot.d.ts +59 -0
- package/es/flow/utils/dateTimeDisplayProps.d.ts +49 -0
- package/es/flow/utils/formValueDeps.d.ts +32 -0
- package/es/flow-compat/FieldValidation.d.ts +2 -1
- package/es/flow-compat/fieldValidationConstants.d.ts +1 -1
- package/es/flow-compat/routeTypes.d.ts +1 -0
- package/es/index.d.ts +8 -1
- package/es/index.mjs +367 -181
- package/es/utils/getRouteRuntimeVersion.d.ts +23 -0
- package/es/utils/index.d.ts +1 -0
- package/es/utils/markdownSanitize.d.ts +11 -0
- package/lib/index.js +367 -181
- package/lib/locale/languageCodes.js +3 -1
- package/package.json +9 -7
- package/src/APIClient.ts +1 -10
- package/src/Application.tsx +4 -0
- package/src/BaseApplication.tsx +13 -6
- package/src/PluginSettingsManager.ts +53 -0
- package/src/RouteRepository.ts +151 -24
- package/src/RouterManager.tsx +142 -1
- package/src/__tests__/PluginSettingsManager.test.ts +13 -0
- package/src/__tests__/RouteRepository.test.ts +239 -0
- package/src/__tests__/RouterManager.test.ts +125 -0
- package/src/__tests__/app.test.tsx +73 -0
- package/src/__tests__/authRedirect.test.ts +17 -0
- package/src/__tests__/browserChecker.test.ts +61 -0
- package/src/__tests__/exports.test.ts +16 -0
- package/src/__tests__/getRouteRuntimeVersion.test.ts +68 -0
- package/src/__tests__/nocobase-buildin-plugin-auth.test.tsx +234 -2
- package/src/__tests__/plugin-manager.test.tsx +44 -2
- package/src/__tests__/settings-center.test.tsx +40 -1
- package/src/authRedirect.ts +38 -0
- package/src/collection-field-interface/CollectionFieldInterface.ts +1 -0
- package/src/collection-field-interface/CollectionFieldInterfaceManager.ts +1 -0
- package/src/collection-manager/field-configure.ts +1 -1
- package/src/collection-manager/field-validation.ts +1 -1
- package/src/collection-manager/interfaces/id.ts +1 -1
- package/src/collection-manager/interfaces/m2m.tsx +2 -2
- package/src/collection-manager/interfaces/m2o.tsx +2 -2
- package/src/collection-manager/interfaces/nanoid.ts +1 -1
- package/src/collection-manager/interfaces/o2m.tsx +2 -2
- package/src/collection-manager/interfaces/obo.tsx +2 -2
- package/src/collection-manager/interfaces/oho.tsx +2 -2
- package/src/collection-manager/interfaces/properties/index.ts +2 -2
- package/src/collection-manager/interfaces/uuid.ts +1 -1
- package/src/components/AppComponents.tsx +19 -3
- package/src/components/README.md +7 -1
- package/src/components/README.zh-CN.md +6 -1
- package/src/components/category-tabs/SortableCategoryTabs.tsx +210 -0
- package/src/components/category-tabs/index.ts +10 -0
- package/src/components/form/JsonTextArea.tsx +25 -11
- package/src/components/form/ScanInput/CodeScanner.tsx +79 -35
- package/src/components/form/ScanInput/__tests__/CodeScanner.test.tsx +101 -0
- package/src/components/form/ScanInput/__tests__/useCodeScanner.test.tsx +284 -11
- package/src/components/form/ScanInput/useCodeScanner.ts +332 -24
- package/src/components/form/TypedVariableInput.tsx +452 -101
- package/src/components/form/VariableJsonTextArea.tsx +175 -0
- package/src/components/form/__tests__/JsonTextArea.test.tsx +43 -0
- package/src/components/form/__tests__/TypedVariableInput.test.tsx +364 -12
- package/src/components/form/__tests__/VariableJsonTextArea.test.tsx +86 -0
- package/src/components/form/filter/CollectionFilter.tsx +4 -0
- package/src/components/form/filter/CollectionFilterItem.tsx +32 -7
- package/src/components/form/filter/CollectionFilterPanel.tsx +4 -0
- package/src/components/form/filter/__tests__/CollectionFilterItem.test.tsx +38 -0
- package/src/components/form/filter/__tests__/useFilterActionProps.test.tsx +95 -0
- package/src/components/form/filter/index.ts +2 -0
- package/src/components/form/filter/useFilterActionProps.ts +37 -6
- package/src/components/form/index.tsx +1 -0
- package/src/components/index.ts +2 -0
- package/src/entry-actions/EntryActionManager.ts +76 -0
- package/src/entry-actions/index.ts +10 -0
- package/src/flow/FlowPage.tsx +38 -3
- package/src/flow/__tests__/FlowPage.test.tsx +201 -27
- package/src/flow/__tests__/FlowRoute.test.tsx +1181 -80
- package/src/flow/__tests__/PluginFlowEngine.test.ts +58 -0
- package/src/flow/__tests__/getKey.test.ts +7 -0
- package/src/flow/__tests__/resolveViewParamsToViewList.test.ts +34 -0
- package/src/flow/actions/__tests__/actionLinkageRules.forkProps.test.ts +115 -0
- package/src/flow/actions/__tests__/afterSuccess.test.ts +73 -0
- package/src/flow/actions/__tests__/dataScopeFilter.test.ts +120 -0
- package/src/flow/actions/__tests__/dataScopeFormValueClear.test.ts +1124 -0
- package/src/flow/actions/__tests__/fieldLinkageRules.scopeDepth.test.ts +150 -1
- package/src/flow/actions/__tests__/linkageAssignField.legacy.test.ts +135 -0
- package/src/flow/actions/__tests__/linkageRules.hiddenSync.restore.test.ts +19 -5
- package/src/flow/actions/__tests__/linkageRules.subFormSetFieldProps.test.ts +92 -0
- package/src/flow/actions/__tests__/linkageRules.tab.test.ts +171 -0
- package/src/flow/actions/__tests__/linkageRulesRefresh.test.ts +7 -3
- package/src/flow/actions/__tests__/openView.defineProps.route.test.tsx +164 -0
- package/src/flow/actions/__tests__/subFormFieldLinkageRules.inputArgs.test.ts +2 -2
- package/src/flow/actions/afterSuccess.tsx +142 -3
- package/src/flow/actions/customVariable.tsx +1 -2
- package/src/flow/actions/dataScopeFilter.ts +20 -1
- package/src/flow/actions/dateTimeFormat.tsx +42 -28
- package/src/flow/actions/index.ts +3 -1
- package/src/flow/actions/linkageRules.tsx +209 -47
- package/src/flow/actions/linkageRulesFormValueRefresh.ts +22 -130
- package/src/flow/actions/linkageRulesRefresh.tsx +2 -6
- package/src/flow/actions/openView.tsx +59 -5
- package/src/flow/actions/runjs.tsx +2 -14
- package/src/flow/actions/validation.tsx +62 -30
- package/src/flow/admin-shell/BaseLayoutModel.tsx +149 -3
- package/src/flow/admin-shell/BaseLayoutRouteCoordinator.ts +187 -42
- package/src/flow/admin-shell/__tests__/AdminLayoutRouteCoordinator.test.ts +1020 -110
- package/src/flow/admin-shell/admin-layout/AdminLayoutComponent.tsx +74 -14
- package/src/flow/admin-shell/admin-layout/AdminLayoutEntryGuard.test.tsx +486 -0
- package/src/flow/admin-shell/admin-layout/AdminLayoutEntryGuard.tsx +64 -8
- package/src/flow/admin-shell/admin-layout/AdminLayoutMenuFlowUtils.ts +5 -3
- package/src/flow/admin-shell/admin-layout/AdminLayoutMenuModels.tsx +61 -9
- package/src/flow/admin-shell/admin-layout/AdminLayoutMenuUtils.tsx +71 -8
- package/src/flow/admin-shell/admin-layout/AdminLayoutModel.tsx +1 -1
- package/src/flow/admin-shell/admin-layout/AdminLayoutSlotModels.tsx +23 -9
- package/src/flow/admin-shell/admin-layout/AppListRender.tsx +13 -2
- package/src/flow/admin-shell/admin-layout/AppSwitcherActionPanelModel.tsx +289 -0
- package/src/flow/admin-shell/admin-layout/HelpLite.tsx +1 -3
- package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutComponent.test.tsx +435 -0
- package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutMenuModels.test.ts +99 -0
- package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutModel.test.tsx +518 -2
- package/src/flow/admin-shell/admin-layout/__tests__/TopbarActionsBar.test.tsx +214 -2
- package/src/flow/admin-shell/admin-layout/index.ts +1 -0
- package/src/flow/admin-shell/admin-layout/resolveAdminRouteRuntimeTarget.test.ts +20 -0
- package/src/flow/admin-shell/admin-layout/resolveAdminRouteRuntimeTarget.ts +33 -5
- package/src/flow/admin-shell/admin-layout/useApplications.tsx +81 -12
- package/src/flow/common/Markdown/Display.tsx +14 -3
- package/src/flow/common/Markdown/Edit.tsx +19 -7
- package/src/flow/common/Markdown/style.ts +4 -0
- package/src/flow/components/BlockItemCard.tsx +2 -2
- package/src/flow/components/DefaultValue.tsx +1 -2
- package/src/flow/components/DynamicFlowsIcon.tsx +447 -126
- package/src/flow/components/FieldAssignExactDatePicker.tsx +25 -11
- package/src/flow/components/FieldAssignRulesEditor.tsx +128 -21
- package/src/flow/components/FieldAssignValueInput.tsx +91 -20
- package/src/flow/components/FlowRoute.tsx +256 -53
- package/src/flow/components/RunJSValueEditor.tsx +9 -1
- package/src/flow/components/__tests__/DynamicFlowsIcon.test.tsx +148 -2
- package/src/flow/components/__tests__/FieldAssignRulesEditor.test.tsx +508 -4
- package/src/flow/components/__tests__/FieldAssignValueInput.context.test.tsx +216 -0
- package/src/flow/components/__tests__/fieldAssignOptions.test.ts +151 -3
- package/src/flow/components/code-editor/__tests__/useCodeRunner.test.tsx +2 -17
- package/src/flow/components/code-editor/hooks/useCodeRunner.ts +2 -14
- package/src/flow/components/code-editor/runjsDiagnostics.ts +8 -45
- package/src/flow/components/fieldAssignOptions.ts +155 -27
- package/src/flow/components/filter/FilterGroup.tsx +33 -5
- package/src/flow/components/filter/VariableFilterItem.tsx +158 -10
- package/src/flow/components/filter/__tests__/FilterGroup.test.tsx +45 -0
- package/src/flow/components/filter/__tests__/VariableFilterItem.leftMetaTree.test.tsx +9 -0
- package/src/flow/components/filter/__tests__/VariableFilterItem.test.tsx +70 -1
- package/src/flow/flows/editMarkdownFlow.tsx +1 -1
- package/src/flow/getViewDiffAndUpdateHidden.tsx +1 -0
- package/src/flow/index.ts +4 -1
- package/src/flow/internal/components/Markdown/util.ts +2 -1
- package/src/flow/internal/registerDeviceTypeContext.ts +39 -0
- package/src/flow/internal/utils/operatorSchemaHelper.ts +15 -1
- package/src/flow/models/actions/JSActionModel.tsx +2 -7
- package/src/flow/models/actions/JSCollectionActionModel.tsx +2 -7
- package/src/flow/models/actions/JSItemActionModel.tsx +2 -7
- package/src/flow/models/actions/JSRecordActionModel.tsx +2 -7
- package/src/flow/models/base/ActionModel.tsx +21 -8
- package/src/flow/models/base/ActionModelCore.tsx +11 -4
- package/src/flow/models/base/BlockGridModel.tsx +26 -0
- package/src/flow/models/base/CollectionBlockModel.tsx +6 -2
- package/src/flow/models/base/GridModel.tsx +38 -7
- package/src/flow/models/base/PageModel/PageModel.tsx +387 -50
- package/src/flow/models/base/PageModel/PageModelTabBar.tsx +114 -0
- package/src/flow/models/base/PageModel/PageTabModel.tsx +249 -18
- package/src/flow/models/base/PageModel/__tests__/PageModel.test.ts +817 -13
- package/src/flow/models/base/PageModel/__tests__/PageModelTabBar.module-isolation.test.tsx +130 -0
- package/src/flow/models/base/PageModel/__tests__/PageModelTabBar.test.tsx +118 -0
- package/src/flow/models/base/PageModel/__tests__/PageTabModel.test.ts +1075 -7
- package/src/flow/models/base/__tests__/ActionModel.test.ts +83 -0
- package/src/flow/models/base/__tests__/ActionModelCore.render.test.tsx +37 -0
- package/src/flow/models/base/__tests__/BlockGridModel.selectSceneActivation.test.ts +124 -0
- package/src/flow/models/base/__tests__/CollectionBlockModel.addAppends.test.ts +41 -0
- package/src/flow/models/base/__tests__/GridModel.dragSnapshotContainer.test.ts +239 -1
- package/src/flow/models/base/__tests__/transformRowsToSingleColumn.test.ts +48 -0
- package/src/flow/models/blocks/filter-form/FilterFormBlockModel.tsx +59 -6
- package/src/flow/models/blocks/filter-form/FilterFormItemModel.tsx +27 -12
- package/src/flow/models/blocks/filter-form/FilterFormJSActionModel.tsx +2 -7
- package/src/flow/models/blocks/filter-form/__tests__/FilterFormItemModel.defineChildren.test.ts +36 -0
- package/src/flow/models/blocks/filter-form/__tests__/defaultValues.wiring.test.ts +143 -4
- package/src/flow/models/blocks/filter-form/fields/FilterFormCustomFieldModel.tsx +1 -1
- package/src/flow/models/blocks/filter-manager/flow-actions/__tests__/customizeFilterRender.test.tsx +56 -1
- package/src/flow/models/blocks/form/EditFormModel.tsx +1 -0
- package/src/flow/models/blocks/form/FormActionModel.tsx +1 -1
- package/src/flow/models/blocks/form/FormBlockModel.tsx +7 -0
- package/src/flow/models/blocks/form/JSFormActionModel.tsx +2 -7
- package/src/flow/models/blocks/form/QuickEditFormModel.tsx +189 -36
- package/src/flow/models/blocks/form/__tests__/FormBlockModel.test.tsx +17 -0
- package/src/flow/models/blocks/form/__tests__/QuickEditFormModel.quickEdit.test.ts +350 -2
- package/src/flow/models/blocks/form/__tests__/submitHandler.test.ts +54 -1
- package/src/flow/models/blocks/form/submitHandler.ts +17 -3
- package/src/flow/models/blocks/form/value-runtime/__tests__/runtime.test.ts +940 -75
- package/src/flow/models/blocks/form/value-runtime/rules.ts +445 -13
- package/src/flow/models/blocks/form/value-runtime/runtime.ts +348 -13
- package/src/flow/models/blocks/form/value-runtime/types.ts +2 -2
- package/src/flow/models/blocks/js-block/JSBlock.tsx +225 -9
- package/src/flow/models/blocks/js-block/__tests__/JSBlockModel.test.tsx +150 -0
- package/src/flow/models/blocks/table/JSColumnModel.tsx +1 -9
- package/src/flow/models/blocks/table/TableActionsColumnModel.tsx +38 -14
- package/src/flow/models/blocks/table/TableBlockModel.tsx +42 -19
- package/src/flow/models/blocks/table/TableColumnModel.tsx +72 -6
- package/src/flow/models/blocks/table/__tests__/TableActionsColumnModel.test.tsx +111 -1
- package/src/flow/models/blocks/table/__tests__/TableBlockModel.dragSort.test.ts +127 -0
- package/src/flow/models/blocks/table/__tests__/TableBlockModel.mobileSettingsButtons.test.tsx +78 -0
- package/src/flow/models/blocks/table/__tests__/TableBlockModel.quickEditRefresh.test.ts +12 -0
- package/src/flow/models/blocks/table/__tests__/TableBlockModel.rowSelection.test.tsx +1 -0
- package/src/flow/models/blocks/table/__tests__/TableColumnModel.test.tsx +243 -1
- package/src/flow/models/blocks/table/dragSort/dragSortHooks.tsx +28 -17
- package/src/flow/models/blocks/table/dragSort/dragSortSettings.ts +13 -6
- package/src/flow/models/blocks/table/dragSort/dragSortUtils.ts +15 -2
- package/src/flow/models/fields/AssociationFieldModel/AssociationFieldModel.tsx +1 -1
- package/src/flow/models/fields/AssociationFieldModel/PopupSubTableFieldModel/PopupSubTableFieldModel.tsx +31 -7
- package/src/flow/models/fields/AssociationFieldModel/RecordSelectFieldModel.tsx +61 -17
- package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/SubTableColumnModel.tsx +141 -19
- package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/SubTableField.tsx +13 -1
- package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/__tests__/SubTableColumnModel.rowRecord.test.ts +197 -0
- package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/__tests__/SubTableFieldModel.reset.test.ts +180 -0
- package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/index.tsx +6 -4
- package/src/flow/models/fields/AssociationFieldModel/__tests__/AssociationFieldModel.updateAssociation.test.ts +72 -0
- package/src/flow/models/fields/AssociationFieldModel/__tests__/RecordPickerFieldModel.itemContext.test.ts +23 -0
- package/src/flow/models/fields/ClickableFieldModel.tsx +5 -0
- package/src/flow/models/fields/DateTimeFieldModel/__tests__/DateTimeNoTzFieldModel.dateLimit.test.tsx +149 -0
- package/src/flow/models/fields/DateTimeFieldModel/dateLimit.ts +149 -113
- package/src/flow/models/fields/DisplayDateTimeFieldModel.tsx +29 -1
- package/src/flow/models/fields/InputFieldModel.tsx +40 -2
- package/src/flow/models/fields/JSEditableFieldModel.tsx +2 -11
- package/src/flow/models/fields/JSFieldModel.tsx +2 -7
- package/src/flow/models/fields/JSItemModel.tsx +2 -14
- package/src/flow/models/fields/SelectFieldModel.tsx +6 -4
- package/src/flow/models/fields/TextareaFieldModel.tsx +72 -3
- package/src/flow/models/fields/VariableFieldFormModel.tsx +3 -3
- package/src/flow/models/fields/__tests__/DisplayDateTimeFieldModel.test.tsx +96 -0
- package/src/flow/models/fields/__tests__/InputFieldModel.test.tsx +117 -1
- package/src/flow/models/fields/__tests__/SelectFieldModel.test.tsx +43 -0
- package/src/flow/models/fields/__tests__/TextareaFieldModel.test.tsx +131 -0
- package/src/flow/models/fields/__tests__/VariableFieldFormModel.test.tsx +51 -0
- package/src/flow/models/fields/mobile-components/MobileLazySelect.tsx +20 -10
- package/src/flow/models/fields/mobile-components/MobileSelect.tsx +24 -12
- package/src/flow/models/topbar/TopbarActionModel.tsx +168 -10
- package/src/flow/resolveViewParamsToViewList.tsx +11 -3
- package/src/flow/routeTransientInputArgs.ts +27 -0
- package/src/flow/utils/__tests__/dateTimeFormat.test.ts +300 -0
- package/src/flow/utils/dataScopeFormValueClear.ts +219 -81
- package/src/flow/utils/dataScopeRowSnapshot.ts +616 -0
- package/src/flow/utils/dateTimeDisplayProps.ts +135 -0
- package/src/flow/utils/formValueDeps.ts +170 -0
- package/src/flow-compat/FieldValidation.tsx +122 -60
- package/src/flow-compat/Popover.tsx +43 -4
- package/src/flow-compat/__tests__/Popover.test.tsx +34 -0
- package/src/flow-compat/fieldValidationConstants.ts +1 -1
- package/src/flow-compat/routeTypes.ts +1 -0
- package/src/index.ts +14 -1
- package/src/layout-manager/__tests__/LayoutRoute.test.tsx +41 -1
- package/src/locale/languageCodes.ts +3 -1
- package/src/nocobase-buildin-plugin/index.tsx +63 -15
- package/src/settings-center/AdminSettingsLayout.tsx +10 -2
- package/src/settings-center/SystemSettingsPage.tsx +1 -2
- package/src/settings-center/plugin-manager/PluginCard.tsx +2 -2
- package/src/settings-center/plugin-manager/index.tsx +3 -0
- package/src/settings-center/utils.tsx +0 -6
- package/src/utils/getRouteRuntimeVersion.ts +185 -0
- package/src/utils/index.tsx +1 -0
- package/src/utils/markdownSanitize.ts +88 -0
|
@@ -15,7 +15,12 @@ import type { Application } from '../Application';
|
|
|
15
15
|
import { getCurrentV2RedirectPath, getDefaultV2AdminRedirectPath } from '../authRedirect';
|
|
16
16
|
import { AppNotFound } from '../components';
|
|
17
17
|
import { PluginFlowEngine } from '../flow';
|
|
18
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
ADMIN_LAYOUT_MODEL_UID,
|
|
20
|
+
AdminLayoutMenuItemModel,
|
|
21
|
+
AdminLayoutModel,
|
|
22
|
+
AppSwitcherActionPanelModel,
|
|
23
|
+
} from '../flow/admin-shell/admin-layout';
|
|
19
24
|
import { useApp } from '../hooks/useApp';
|
|
20
25
|
import { Plugin } from '../Plugin';
|
|
21
26
|
import { AdminSettingsLayoutModel } from '../settings-center';
|
|
@@ -28,6 +33,13 @@ export type CurrentUserState = {
|
|
|
28
33
|
loading: boolean;
|
|
29
34
|
};
|
|
30
35
|
|
|
36
|
+
type CurrentUserAuthStatus = 'unknown' | 'authenticated' | 'unauthenticated' | 'redirecting';
|
|
37
|
+
|
|
38
|
+
type CurrentUserInternalState = CurrentUserState & {
|
|
39
|
+
authStatus: CurrentUserAuthStatus;
|
|
40
|
+
error?: Error | null;
|
|
41
|
+
};
|
|
42
|
+
|
|
31
43
|
export type CurrentRoleOption = {
|
|
32
44
|
name: string;
|
|
33
45
|
title: string;
|
|
@@ -151,7 +163,7 @@ const DataSourceBootstrapProvider: FC = ({ children }) => {
|
|
|
151
163
|
}
|
|
152
164
|
};
|
|
153
165
|
|
|
154
|
-
|
|
166
|
+
run();
|
|
155
167
|
|
|
156
168
|
return () => {
|
|
157
169
|
mounted = false;
|
|
@@ -173,21 +185,33 @@ const CurrentUserProvider: FC = ({ children }) => {
|
|
|
173
185
|
const app = useApp<Application>();
|
|
174
186
|
const location = useLocation();
|
|
175
187
|
const navigate = useNavigate();
|
|
176
|
-
const [state, setState] = useState<
|
|
188
|
+
const [state, setState] = useState<CurrentUserInternalState>({ loading: true, authStatus: 'unknown' });
|
|
177
189
|
const locationRef = useRef(location);
|
|
178
190
|
locationRef.current = location;
|
|
179
191
|
const authCheckRouteState = getCurrentUserAuthCheckRouteState(app, location.pathname);
|
|
192
|
+
const shouldBlockAuthRequiredRoute = authCheckRouteState === 'required' && state.authStatus !== 'authenticated';
|
|
193
|
+
const contextValue = useMemo<CurrentUserState>(
|
|
194
|
+
() => ({
|
|
195
|
+
data: state.data,
|
|
196
|
+
loading: state.loading,
|
|
197
|
+
}),
|
|
198
|
+
[state.data, state.loading],
|
|
199
|
+
);
|
|
180
200
|
|
|
181
201
|
useEffect(() => {
|
|
182
202
|
let mounted = true;
|
|
183
203
|
|
|
184
204
|
if (authCheckRouteState !== 'required') {
|
|
185
205
|
// 认证页等免鉴权路由不应再执行 `/auth:check`,否则未登录时会重复鉴权并触发重定向抖动。
|
|
186
|
-
setState({ loading: false });
|
|
206
|
+
setState({ loading: false, authStatus: 'unauthenticated', error: null });
|
|
187
207
|
return;
|
|
188
208
|
}
|
|
189
209
|
|
|
190
|
-
setState((previous) =>
|
|
210
|
+
setState((previous) =>
|
|
211
|
+
previous.authStatus === 'authenticated'
|
|
212
|
+
? previous
|
|
213
|
+
: { data: previous.data, loading: true, authStatus: 'unknown', error: null },
|
|
214
|
+
);
|
|
191
215
|
|
|
192
216
|
const run = async () => {
|
|
193
217
|
try {
|
|
@@ -204,17 +228,28 @@ const CurrentUserProvider: FC = ({ children }) => {
|
|
|
204
228
|
const user = res?.data?.data;
|
|
205
229
|
// 服务端通过 `{ code: 302, redirect }` 通知客户端先去某个中间页(例如 2FA 验证页)。这类响应没有 user.id,但也不能视为未登录——否则会和处理 302 的全局响应拦截器 (例如 plugin-two-factor-authentication 注册的那一个)竞态,而 `window.location.replace` 会覆盖更早发出的 `window.location.href`,把用户错误地弹回登录页。让响应拦截器接管跳转。
|
|
206
230
|
if (user?.code === 302) {
|
|
207
|
-
setState({ loading: false });
|
|
231
|
+
setState({ loading: false, authStatus: 'redirecting', error: null });
|
|
208
232
|
return;
|
|
209
233
|
}
|
|
210
234
|
if (user?.id == null) {
|
|
211
235
|
// 用 react-router navigate (虚拟跳转)而不是 location.replace, 这样如果有其他响应拦截器已经发起了 window.location.href 整页跳转(例如 2FA 插件接收到服务端 302 重定向), 真实跳转可以胜出 navigate, 不会被这里的 signin 重定向覆盖。
|
|
236
|
+
setState({ loading: true, authStatus: 'unauthenticated', error: null });
|
|
212
237
|
navigate(`/signin?redirect=${encodeURIComponent(getCurrentV2RedirectPath(app, locationRef.current))}`, {
|
|
213
238
|
replace: true,
|
|
214
239
|
});
|
|
215
240
|
return;
|
|
216
241
|
}
|
|
217
242
|
|
|
243
|
+
try {
|
|
244
|
+
await app.apiClient.auth.syncCookies();
|
|
245
|
+
} catch {
|
|
246
|
+
// Cookie bootstrap is best-effort; auth:check remains the source of truth for the current page load.
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (!mounted) {
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
|
|
218
253
|
const userMeta = createCollectionContextMeta(
|
|
219
254
|
() => app.flowEngine.context.dataSourceManager?.getDataSource('main')?.getCollection('users') || null,
|
|
220
255
|
app.flowEngine.translate('Current user'),
|
|
@@ -230,21 +265,28 @@ const CurrentUserProvider: FC = ({ children }) => {
|
|
|
230
265
|
setState({
|
|
231
266
|
data: res?.data,
|
|
232
267
|
loading: false,
|
|
268
|
+
authStatus: 'authenticated',
|
|
269
|
+
error: null,
|
|
233
270
|
});
|
|
234
|
-
} catch (error:
|
|
271
|
+
} catch (error: unknown) {
|
|
235
272
|
if (!mounted) {
|
|
236
273
|
return;
|
|
237
274
|
}
|
|
238
275
|
|
|
239
|
-
const
|
|
276
|
+
const errorLike = error as { response?: { status?: number }; status?: number };
|
|
277
|
+
const isAuthError = errorLike?.response?.status === 401 || errorLike?.status === 401;
|
|
240
278
|
if (isAuthError) {
|
|
279
|
+
setState({ loading: true, authStatus: 'unauthenticated', error: null });
|
|
241
280
|
navigate(`/signin?redirect=${encodeURIComponent(getCurrentV2RedirectPath(app, locationRef.current))}`, {
|
|
242
281
|
replace: true,
|
|
243
282
|
});
|
|
244
283
|
return;
|
|
245
284
|
}
|
|
246
|
-
setState({
|
|
247
|
-
|
|
285
|
+
setState({
|
|
286
|
+
loading: false,
|
|
287
|
+
authStatus: 'unknown',
|
|
288
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
289
|
+
});
|
|
248
290
|
}
|
|
249
291
|
};
|
|
250
292
|
|
|
@@ -255,13 +297,19 @@ const CurrentUserProvider: FC = ({ children }) => {
|
|
|
255
297
|
};
|
|
256
298
|
}, [app, authCheckRouteState, navigate]);
|
|
257
299
|
|
|
258
|
-
if (state.
|
|
300
|
+
if (state.error) {
|
|
301
|
+
throw state.error;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
if (state.loading || shouldBlockAuthRequiredRoute) {
|
|
259
305
|
return app.renderComponent('AppSpin');
|
|
260
306
|
}
|
|
261
307
|
|
|
262
|
-
return <CurrentUserContext.Provider value={
|
|
308
|
+
return <CurrentUserContext.Provider value={contextValue}>{children}</CurrentUserContext.Provider>;
|
|
263
309
|
};
|
|
264
310
|
|
|
311
|
+
CurrentUserProvider.displayName = 'CurrentUserProvider';
|
|
312
|
+
|
|
265
313
|
const RootRedirect: FC = () => {
|
|
266
314
|
const app = useApp<Application>();
|
|
267
315
|
const hasToken = !!app?.apiClient?.auth?.token;
|
|
@@ -289,11 +337,13 @@ export class NocoBaseBuildInPlugin extends Plugin<any, Application> {
|
|
|
289
337
|
this.addComponents();
|
|
290
338
|
this.addRoutes();
|
|
291
339
|
|
|
292
|
-
|
|
340
|
+
// Auth-required routes must finish auth check or redirect before data source metadata requests can start.
|
|
293
341
|
this.app.use(CurrentUserProvider);
|
|
342
|
+
this.app.use(DataSourceBootstrapProvider);
|
|
294
343
|
this.app.flowEngine.registerModels({
|
|
295
344
|
AdminLayoutModel,
|
|
296
345
|
AdminLayoutMenuItemModel,
|
|
346
|
+
AppSwitcherActionPanelModel,
|
|
297
347
|
AdminSettingsLayoutModel,
|
|
298
348
|
});
|
|
299
349
|
this.app.layoutManager.registerLayout({
|
|
@@ -323,7 +373,6 @@ export class NocoBaseBuildInPlugin extends Plugin<any, Application> {
|
|
|
323
373
|
title: this.app.i18n.t('System settings'),
|
|
324
374
|
icon: 'SettingOutlined',
|
|
325
375
|
aclSnippet: 'pm.system-settings.system-settings',
|
|
326
|
-
sort: -100,
|
|
327
376
|
});
|
|
328
377
|
this.app.pluginSettingsManager.addPageTabItem({
|
|
329
378
|
menuKey: 'system-settings',
|
|
@@ -331,7 +380,6 @@ export class NocoBaseBuildInPlugin extends Plugin<any, Application> {
|
|
|
331
380
|
title: this.app.i18n.t('System settings'),
|
|
332
381
|
componentLoader: () => import('../settings-center/SystemSettingsPage'),
|
|
333
382
|
aclSnippet: 'pm.system-settings.system-settings',
|
|
334
|
-
sort: -100,
|
|
335
383
|
});
|
|
336
384
|
// Parent menu for security-related plugin settings (password policy, locked users, etc.). Registered here in the buildin plugin so any pro plugin can attach page tabs to `menuKey: 'security'` without each one re-registering the same parent.
|
|
337
385
|
this.app.pluginSettingsManager.addMenuItem({
|
|
@@ -209,6 +209,9 @@ export const InternalAdminSettingsLayout = () => {
|
|
|
209
209
|
style={{
|
|
210
210
|
background: token.colorBgContainer,
|
|
211
211
|
borderInlineEnd: `${token.lineWidth}px solid ${token.colorBorderSecondary}`,
|
|
212
|
+
minHeight: 0,
|
|
213
|
+
overflowY: 'auto',
|
|
214
|
+
overflowX: 'hidden',
|
|
212
215
|
}}
|
|
213
216
|
>
|
|
214
217
|
<Menu
|
|
@@ -239,11 +242,12 @@ export const InternalAdminSettingsLayout = () => {
|
|
|
239
242
|
<Layout.Content
|
|
240
243
|
style={{
|
|
241
244
|
background: token.colorBgLayout,
|
|
245
|
+
flex: 1,
|
|
242
246
|
display: 'flex',
|
|
243
247
|
flexDirection: 'column',
|
|
244
248
|
minWidth: 0,
|
|
245
|
-
|
|
246
|
-
|
|
249
|
+
minHeight: 0,
|
|
250
|
+
overflow: 'hidden',
|
|
247
251
|
}}
|
|
248
252
|
>
|
|
249
253
|
<PageHeader
|
|
@@ -273,6 +277,10 @@ export const InternalAdminSettingsLayout = () => {
|
|
|
273
277
|
/>
|
|
274
278
|
<div
|
|
275
279
|
style={{
|
|
280
|
+
flex: 1,
|
|
281
|
+
minHeight: 0,
|
|
282
|
+
boxSizing: 'border-box',
|
|
283
|
+
overflow: 'auto',
|
|
276
284
|
padding: token.paddingLG,
|
|
277
285
|
}}
|
|
278
286
|
>
|
|
@@ -221,7 +221,6 @@ export const SystemSettingsPage = () => {
|
|
|
221
221
|
return (
|
|
222
222
|
<div
|
|
223
223
|
style={{
|
|
224
|
-
minHeight: '100%',
|
|
225
224
|
background: token.colorBgContainer,
|
|
226
225
|
borderRadius: token.borderRadiusLG,
|
|
227
226
|
padding: token.paddingLG,
|
|
@@ -240,7 +239,7 @@ export const SystemSettingsPage = () => {
|
|
|
240
239
|
<Form.Item
|
|
241
240
|
name="raw_title"
|
|
242
241
|
label={t('System title')}
|
|
243
|
-
rules={[{ required: true, message: t('Please enter') + t('System title') }]}
|
|
242
|
+
rules={[{ required: true, message: t('Please enter') + ' ' + t('System title') }]}
|
|
244
243
|
>
|
|
245
244
|
<Input.TextArea autoSize={{ minRows: 2, maxRows: 4 }} />
|
|
246
245
|
</Form.Item>
|
|
@@ -116,7 +116,7 @@ export const PluginCard: FC<PluginCardProps> = ({ data }) => {
|
|
|
116
116
|
});
|
|
117
117
|
|
|
118
118
|
const openSettings = useMemoizedFn(() => {
|
|
119
|
-
navigate(app.pluginSettingsManager.
|
|
119
|
+
navigate(app.pluginSettingsManager.getPluginSettingsRoutePath(name));
|
|
120
120
|
});
|
|
121
121
|
|
|
122
122
|
const cardClassName = useMemo(
|
|
@@ -180,7 +180,7 @@ export const PluginCard: FC<PluginCardProps> = ({ data }) => {
|
|
|
180
180
|
<ReadOutlined /> {t('Docs')}
|
|
181
181
|
</a>
|
|
182
182
|
)}
|
|
183
|
-
{enabled && app.pluginSettingsManager.
|
|
183
|
+
{enabled && app.pluginSettingsManager.hasPluginSettings(name) && (
|
|
184
184
|
<a
|
|
185
185
|
onClick={(e) => {
|
|
186
186
|
e.stopPropagation();
|
|
@@ -86,6 +86,9 @@ export const PluginManagerPage: React.FC = () => {
|
|
|
86
86
|
async () => {
|
|
87
87
|
const response = await app.apiClient.request({
|
|
88
88
|
url: 'pm:list',
|
|
89
|
+
params: {
|
|
90
|
+
v2: true,
|
|
91
|
+
},
|
|
89
92
|
skipNotify: true,
|
|
90
93
|
});
|
|
91
94
|
return Array.isArray(response?.data?.data) ? response.data.data : [];
|
|
@@ -85,12 +85,6 @@ export function filterVisibleSettings(settings: readonly PluginSettingsPageType[
|
|
|
85
85
|
*/
|
|
86
86
|
export function sortTopLevelSettings(settings: PluginSettingsPageType[] = []) {
|
|
87
87
|
return [...settings].sort((a, b) => {
|
|
88
|
-
if (a.name === SYSTEM_SETTINGS_SETTING_NAME && b.name !== SYSTEM_SETTINGS_SETTING_NAME) {
|
|
89
|
-
return -1;
|
|
90
|
-
}
|
|
91
|
-
if (b.name === SYSTEM_SETTINGS_SETTING_NAME && a.name !== SYSTEM_SETTINGS_SETTING_NAME) {
|
|
92
|
-
return 1;
|
|
93
|
-
}
|
|
94
88
|
if ((a.sort || 0) !== (b.sort || 0)) {
|
|
95
89
|
return (a.sort || 0) - (b.sort || 0);
|
|
96
90
|
}
|
|
@@ -0,0 +1,185 @@
|
|
|
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
|
+
export type RouteRuntimeVersion = 'legacy' | 'modern';
|
|
11
|
+
|
|
12
|
+
declare global {
|
|
13
|
+
interface Window {
|
|
14
|
+
__nocobase_modern_client_prefix__?: string;
|
|
15
|
+
__nocobase_public_path__?: string;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function hasModernClientPrefix() {
|
|
20
|
+
return (
|
|
21
|
+
typeof window !== 'undefined' &&
|
|
22
|
+
typeof window.__nocobase_modern_client_prefix__ === 'string' &&
|
|
23
|
+
window.__nocobase_modern_client_prefix__.trim().length > 0
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function hasConfiguredPublicPath() {
|
|
28
|
+
return typeof window !== 'undefined' && typeof window.__nocobase_public_path__ === 'string';
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Normalize any app/public-path-like input into a root-relative pathname:
|
|
32
|
+
*
|
|
33
|
+
* - `nocobase//v/` -> `/nocobase/v`
|
|
34
|
+
* - `/` -> `/`
|
|
35
|
+
*/
|
|
36
|
+
function normalizeRootRelativePath(value = '/') {
|
|
37
|
+
const normalized = `/${String(value || '/').trim() || '/'}`.replace(/\/{2,}/g, '/');
|
|
38
|
+
if (normalized !== '/' && normalized.endsWith('/')) {
|
|
39
|
+
return normalized.replace(/\/+$/g, '');
|
|
40
|
+
}
|
|
41
|
+
return normalized;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Public-path form always ends with `/`, so it can be compared as a base path:
|
|
45
|
+
*
|
|
46
|
+
* - `/nocobase/v` -> `/nocobase/v/`
|
|
47
|
+
* - `/` -> `/`
|
|
48
|
+
*/
|
|
49
|
+
function normalizePublicPath(value = '/') {
|
|
50
|
+
const normalized = normalizeRootRelativePath(value);
|
|
51
|
+
return normalized.endsWith('/') ? normalized : `${normalized}/`;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Check whether a concrete pathname is rendered under a runtime base path:
|
|
55
|
+
*
|
|
56
|
+
* - pathname `/nocobase/v/admin/workflow/workflows/1`
|
|
57
|
+
* basePath `/nocobase/v/`
|
|
58
|
+
* => true
|
|
59
|
+
*
|
|
60
|
+
* - pathname `/nocobase/admin/settings/workflow/workflows/1`
|
|
61
|
+
* basePath `/nocobase/v/`
|
|
62
|
+
* => false
|
|
63
|
+
*/
|
|
64
|
+
function isPathnameInsideBasePath(pathname: string, basePath: string) {
|
|
65
|
+
const normalizedPathname = normalizeRootRelativePath(pathname);
|
|
66
|
+
const normalizedBasePath = normalizePublicPath(basePath);
|
|
67
|
+
const trimmedBasePath = normalizedBasePath.replace(/\/+$/g, '') || '/';
|
|
68
|
+
|
|
69
|
+
if (trimmedBasePath === '/') {
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return normalizedPathname === trimmedBasePath || normalizedPathname.startsWith(`${trimmedBasePath}/`);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function getNormalizedModernClientPrefix() {
|
|
77
|
+
if (typeof window === 'undefined') {
|
|
78
|
+
return '';
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const prefix = window.__nocobase_modern_client_prefix__?.trim();
|
|
82
|
+
return prefix ? prefix.replace(/^\/+|\/+$/g, '') : '';
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Derive the actual modern-client base path from injected globals.
|
|
86
|
+
*
|
|
87
|
+
* Only returns a value when `__nocobase_public_path__` itself already points to
|
|
88
|
+
* the modern client shell:
|
|
89
|
+
*
|
|
90
|
+
* - legacy shell:
|
|
91
|
+
* `__nocobase_public_path__ = "/nocobase/"`
|
|
92
|
+
* `__nocobase_modern_client_prefix__ = "v"`
|
|
93
|
+
* => `""`
|
|
94
|
+
*
|
|
95
|
+
* - modern shell:
|
|
96
|
+
* `__nocobase_public_path__ = "/nocobase/v/"`
|
|
97
|
+
* `__nocobase_modern_client_prefix__ = "v"`
|
|
98
|
+
* => `"/nocobase/v/"`
|
|
99
|
+
*/
|
|
100
|
+
function getModernClientBasePath() {
|
|
101
|
+
const normalizedPrefix = getNormalizedModernClientPrefix();
|
|
102
|
+
if (!normalizedPrefix) {
|
|
103
|
+
return '';
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (typeof window === 'undefined') {
|
|
107
|
+
return '';
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const publicPath = window.__nocobase_public_path__?.trim();
|
|
111
|
+
if (publicPath && normalizePublicPath(publicPath).endsWith(`/${normalizedPrefix}/`)) {
|
|
112
|
+
return normalizePublicPath(publicPath);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return '';
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** True only when the current page is actually running under the resolved
|
|
119
|
+
* modern-client base path.
|
|
120
|
+
*
|
|
121
|
+
* Example:
|
|
122
|
+
* - pathname `/v/admin/workflow/workflows/1`, modern base `/v/` => true
|
|
123
|
+
* - pathname `/admin/settings/workflow/workflows/1`, modern base `/v/` => false
|
|
124
|
+
*/
|
|
125
|
+
function isPathnameInModernClientRuntime() {
|
|
126
|
+
if (typeof window === 'undefined') {
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const pathname = window.location?.pathname ?? '';
|
|
131
|
+
const modernClientBasePath = getModernClientBasePath();
|
|
132
|
+
if (!pathname || !modernClientBasePath) {
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return isPathnameInsideBasePath(pathname, modernClientBasePath);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** Fallback for incomplete/mocked environments where public path was not
|
|
140
|
+
* injected but the modern prefix still exists.
|
|
141
|
+
*
|
|
142
|
+
* Example:
|
|
143
|
+
* - pathname `/v/admin/workflow/workflows/1`
|
|
144
|
+
* `__nocobase_modern_client_prefix__ = "v"`
|
|
145
|
+
* `__nocobase_public_path__` missing
|
|
146
|
+
* => true
|
|
147
|
+
*/
|
|
148
|
+
function isPathnameInModernClientRuntimeByPrefix() {
|
|
149
|
+
if (typeof window === 'undefined') {
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const normalizedPrefix = getNormalizedModernClientPrefix();
|
|
154
|
+
const pathname = window.location?.pathname ?? '';
|
|
155
|
+
if (!normalizedPrefix || !pathname) {
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return isPathnameInsideBasePath(pathname, normalizePublicPath(normalizedPrefix));
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/** Resolve which client shell currently owns the route.
|
|
163
|
+
*
|
|
164
|
+
* - returns `modern` when the current page is actually mounted under the modern
|
|
165
|
+
* client base path (`/v/`, `/nocobase/v/`, ...)
|
|
166
|
+
* - returns `legacy` when the route is still under the legacy shell, even if the
|
|
167
|
+
* modern prefix global has been injected into the page
|
|
168
|
+
*/
|
|
169
|
+
export function getRouteRuntimeVersion(): RouteRuntimeVersion {
|
|
170
|
+
if (typeof window === 'undefined') {
|
|
171
|
+
return hasModernClientPrefix() ? 'modern' : 'legacy';
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (hasConfiguredPublicPath()) {
|
|
175
|
+
return isPathnameInModernClientRuntime() ? 'modern' : 'legacy';
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Defensive fallback for incomplete test/mocked environments where public
|
|
179
|
+
// path was not injected but the modern prefix still exists.
|
|
180
|
+
if (isPathnameInModernClientRuntimeByPrefix()) {
|
|
181
|
+
return 'modern';
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return 'legacy';
|
|
185
|
+
}
|
package/src/utils/index.tsx
CHANGED
|
@@ -11,6 +11,7 @@ import React, { ComponentType, FC } from 'react';
|
|
|
11
11
|
import { BlankComponent } from '../components';
|
|
12
12
|
|
|
13
13
|
export * from './appVersionHTML';
|
|
14
|
+
export * from './getRouteRuntimeVersion';
|
|
14
15
|
|
|
15
16
|
export function normalizeContainer(container: Element | ShadowRoot | string): Element | null {
|
|
16
17
|
if (!container) {
|
|
@@ -0,0 +1,88 @@
|
|
|
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
|
+
export function stripMarkdownIframeTags(markdown: string) {
|
|
11
|
+
if (!markdown) {
|
|
12
|
+
return markdown;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let result = '';
|
|
16
|
+
let cursor = 0;
|
|
17
|
+
|
|
18
|
+
while (cursor < markdown.length) {
|
|
19
|
+
const iframeStart = markdown.slice(cursor).search(/<iframe\b/i);
|
|
20
|
+
if (iframeStart === -1) {
|
|
21
|
+
result += markdown.slice(cursor);
|
|
22
|
+
break;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const start = cursor + iframeStart;
|
|
26
|
+
result += markdown.slice(cursor, start);
|
|
27
|
+
|
|
28
|
+
const openingEnd = findTagEnd(markdown, start);
|
|
29
|
+
if (openingEnd === -1) {
|
|
30
|
+
break;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const openingTag = markdown.slice(start, openingEnd + 1);
|
|
34
|
+
if (/\/\s*>$/.test(openingTag)) {
|
|
35
|
+
cursor = openingEnd + 1;
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const closingStart = markdown.slice(openingEnd + 1).search(/<\/iframe\s*>/i);
|
|
40
|
+
if (closingStart === -1) {
|
|
41
|
+
break;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
cursor =
|
|
45
|
+
openingEnd + 1 + closingStart + markdown.slice(openingEnd + 1 + closingStart).match(/^<\/iframe\s*>/i)[0].length;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return result;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function stripMarkdownIframes(html: string) {
|
|
52
|
+
if (!html || typeof DOMParser === 'undefined') {
|
|
53
|
+
return html;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const doc = new DOMParser().parseFromString(html, 'text/html');
|
|
57
|
+
doc.querySelectorAll('iframe').forEach((iframe) => iframe.remove());
|
|
58
|
+
return doc.body.innerHTML;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function removeMarkdownIframes(container?: ParentNode | null) {
|
|
62
|
+
container?.querySelectorAll('iframe').forEach((iframe) => iframe.remove());
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function findTagEnd(html: string, start: number) {
|
|
66
|
+
let quote: string | undefined;
|
|
67
|
+
|
|
68
|
+
for (let index = start; index < html.length; index += 1) {
|
|
69
|
+
const char = html[index];
|
|
70
|
+
if (quote) {
|
|
71
|
+
if (char === quote) {
|
|
72
|
+
quote = undefined;
|
|
73
|
+
}
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (char === '"' || char === "'") {
|
|
78
|
+
quote = char;
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (char === '>') {
|
|
83
|
+
return index;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return -1;
|
|
88
|
+
}
|