@nocobase/client-v2 2.1.31 → 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 +2 -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/components/category-tabs/SortableCategoryTabs.d.ts +55 -0
- package/es/components/category-tabs/index.d.ts +9 -0
- package/es/components/form/TypedVariableInput.d.ts +31 -5
- 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/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/index.d.ts +1 -1
- package/es/flow/actions/linkageRules.d.ts +2 -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/FieldAssignValueInput.d.ts +9 -0
- 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/models/base/ActionModelCore.d.ts +2 -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/form/value-runtime/runtime.d.ts +9 -0
- package/es/flow-compat/routeTypes.d.ts +1 -0
- package/es/index.d.ts +7 -1
- package/es/index.mjs +223 -135
- package/es/utils/getRouteRuntimeVersion.d.ts +23 -0
- package/es/utils/index.d.ts +1 -0
- package/lib/index.js +220 -132
- package/package.json +7 -7
- package/src/APIClient.ts +1 -10
- package/src/Application.tsx +4 -0
- package/src/BaseApplication.tsx +2 -0
- 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 +18 -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 +28 -0
- 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/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 +4 -0
- package/src/components/form/TypedVariableInput.tsx +452 -101
- package/src/components/form/__tests__/TypedVariableInput.test.tsx +364 -12
- 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/index.ts +2 -0
- package/src/entry-actions/EntryActionManager.ts +76 -0
- package/src/entry-actions/index.ts +10 -0
- package/src/flow/__tests__/FlowRoute.test.tsx +1084 -145
- package/src/flow/actions/__tests__/linkageRules.tab.test.ts +171 -0
- package/src/flow/actions/customVariable.tsx +1 -2
- package/src/flow/actions/index.ts +2 -0
- package/src/flow/actions/linkageRules.tsx +65 -8
- package/src/flow/actions/runjs.tsx +2 -14
- package/src/flow/admin-shell/BaseLayoutModel.tsx +13 -0
- package/src/flow/admin-shell/admin-layout/AdminLayoutComponent.tsx +46 -13
- 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/__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 +77 -0
- 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/components/DefaultValue.tsx +1 -2
- package/src/flow/components/FieldAssignExactDatePicker.tsx +25 -11
- package/src/flow/components/FieldAssignValueInput.tsx +76 -9
- package/src/flow/components/FlowRoute.tsx +185 -28
- package/src/flow/components/RunJSValueEditor.tsx +9 -1
- package/src/flow/components/__tests__/FieldAssignValueInput.context.test.tsx +84 -2
- 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/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/index.ts +1 -0
- 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 +5 -0
- package/src/flow/models/base/GridModel.tsx +38 -7
- package/src/flow/models/base/PageModel/PageModel.tsx +387 -53
- package/src/flow/models/base/PageModel/PageModelTabBar.tsx +114 -0
- package/src/flow/models/base/PageModel/PageTabModel.tsx +186 -3
- package/src/flow/models/base/PageModel/__tests__/PageModel.test.ts +790 -14
- 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 +586 -1
- package/src/flow/models/base/__tests__/ActionModel.test.ts +83 -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 +1 -2
- package/src/flow/models/blocks/filter-form/FilterFormJSActionModel.tsx +2 -7
- package/src/flow/models/blocks/form/JSFormActionModel.tsx +2 -7
- package/src/flow/models/blocks/form/value-runtime/__tests__/runtime.test.ts +87 -0
- package/src/flow/models/blocks/form/value-runtime/rules.ts +2 -2
- package/src/flow/models/blocks/form/value-runtime/runtime.ts +91 -0
- package/src/flow/models/blocks/js-block/JSBlock.tsx +2 -7
- package/src/flow/models/blocks/table/JSColumnModel.tsx +1 -9
- package/src/flow/models/blocks/table/TableActionsColumnModel.tsx +1 -1
- package/src/flow/models/blocks/table/TableBlockModel.tsx +13 -3
- package/src/flow/models/blocks/table/__tests__/TableActionsColumnModel.test.tsx +54 -0
- package/src/flow/models/blocks/table/__tests__/TableBlockModel.mobileSettingsButtons.test.tsx +78 -0
- 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/VariableFieldFormModel.tsx +3 -3
- package/src/flow/models/fields/__tests__/VariableFieldFormModel.test.tsx +51 -0
- package/src/flow/models/topbar/TopbarActionModel.tsx +168 -10
- package/src/flow-compat/Popover.tsx +43 -4
- package/src/flow-compat/__tests__/Popover.test.tsx +34 -0
- package/src/flow-compat/routeTypes.ts +1 -0
- package/src/index.ts +13 -1
- package/src/layout-manager/__tests__/LayoutRoute.test.tsx +41 -1
- package/src/nocobase-buildin-plugin/index.tsx +19 -3
- package/src/settings-center/AdminSettingsLayout.tsx +10 -2
- package/src/settings-center/SystemSettingsPage.tsx +0 -1
- package/src/settings-center/plugin-manager/PluginCard.tsx +2 -2
- package/src/settings-center/utils.tsx +0 -6
- package/src/utils/getRouteRuntimeVersion.ts +185 -0
- package/src/utils/index.tsx +1 -0
package/src/index.ts
CHANGED
|
@@ -30,6 +30,8 @@ export * from './layout-manager';
|
|
|
30
30
|
export * from './hooks';
|
|
31
31
|
export { default as languageCodes } from './locale/languageCodes';
|
|
32
32
|
export * from './nocobase-buildin-plugin';
|
|
33
|
+
export { getRouteRuntimeVersion } from './utils/getRouteRuntimeVersion';
|
|
34
|
+
export type { RouteRuntimeVersion } from './utils/getRouteRuntimeVersion';
|
|
33
35
|
export * from './collection-field-interface/CollectionFieldInterface';
|
|
34
36
|
export * from './collection-field-interface/CollectionFieldInterfaceManager';
|
|
35
37
|
export * from './collection-manager/field-configure';
|
|
@@ -38,7 +40,17 @@ export * from './collection-manager/filter-operators';
|
|
|
38
40
|
export * from './collection-manager/interfaces';
|
|
39
41
|
export * from './collection-manager/template-fields';
|
|
40
42
|
export * from './data-source';
|
|
43
|
+
export * from './entry-actions';
|
|
41
44
|
export * from './flow';
|
|
45
|
+
export { CodeEditorExtension } from './flow/components/code-editor/extension';
|
|
46
|
+
export type { CodeEditorExtra, CodeEditorExtraRegistry, EditorRef } from './flow/components/code-editor/types';
|
|
47
|
+
export {
|
|
48
|
+
DEFAULT_DATA_SOURCE_KEY,
|
|
49
|
+
IconPicker,
|
|
50
|
+
isTitleField,
|
|
51
|
+
isTitleFieldInterface,
|
|
52
|
+
NocoBaseDesktopRouteType,
|
|
53
|
+
} from './flow-compat';
|
|
54
|
+
export type { NocoBaseDesktopRoute } from './flow-compat';
|
|
42
55
|
export * from './utils/markdownSanitize';
|
|
43
|
-
export { DEFAULT_DATA_SOURCE_KEY, isTitleField, isTitleFieldInterface } from './flow-compat';
|
|
44
56
|
export { default as AntdAppProvider } from './theme/AntdAppProvider';
|
|
@@ -11,7 +11,7 @@ import { FlowEngine, FlowEngineProvider, observer } from '@nocobase/flow-engine'
|
|
|
11
11
|
import { render, screen, waitFor } from '@testing-library/react';
|
|
12
12
|
import React from 'react';
|
|
13
13
|
import { createMemoryRouter, Outlet, RouterProvider, useOutlet } from 'react-router-dom';
|
|
14
|
-
import { describe, expect, it } from 'vitest';
|
|
14
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
15
15
|
import { BaseLayoutModel } from '../../flow/admin-shell/BaseLayoutModel';
|
|
16
16
|
import { LayoutContentRoute } from '../LayoutContentRoute';
|
|
17
17
|
import { LayoutRoute } from '../LayoutRoute';
|
|
@@ -106,6 +106,46 @@ describe('LayoutRoute', () => {
|
|
|
106
106
|
});
|
|
107
107
|
});
|
|
108
108
|
|
|
109
|
+
it('does not activate desktop route loading for generic layouts', async () => {
|
|
110
|
+
const activateLayout = vi.fn(() => vi.fn());
|
|
111
|
+
const engine = new FlowEngine();
|
|
112
|
+
engine.registerModels({ TestLayoutModel });
|
|
113
|
+
engine.context.defineProperty('routeRepository', {
|
|
114
|
+
value: {
|
|
115
|
+
activateLayout,
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
engine.context.defineProperty('app', {
|
|
119
|
+
value: {
|
|
120
|
+
layoutManager: {
|
|
121
|
+
getLayout: () => layout,
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
const router = createMemoryRouter(
|
|
127
|
+
[
|
|
128
|
+
{
|
|
129
|
+
id: layout.routeName,
|
|
130
|
+
path: layout.routePath,
|
|
131
|
+
element: <LayoutRoute layoutRouteName="test" />,
|
|
132
|
+
},
|
|
133
|
+
],
|
|
134
|
+
{
|
|
135
|
+
initialEntries: ['/test'],
|
|
136
|
+
},
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
render(
|
|
140
|
+
<FlowEngineProvider engine={engine}>
|
|
141
|
+
<RouterProvider router={router} />
|
|
142
|
+
</FlowEngineProvider>,
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
expect(await screen.findByTestId('layout-route')).toHaveTextContent('test');
|
|
146
|
+
expect(activateLayout).not.toHaveBeenCalled();
|
|
147
|
+
});
|
|
148
|
+
|
|
109
149
|
it('syncs nested page route before the layout renders its outlet', async () => {
|
|
110
150
|
const nestedLayout: LayoutDefinition = {
|
|
111
151
|
...layout,
|
|
@@ -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';
|
|
@@ -235,6 +240,16 @@ const CurrentUserProvider: FC = ({ children }) => {
|
|
|
235
240
|
return;
|
|
236
241
|
}
|
|
237
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
|
+
|
|
238
253
|
const userMeta = createCollectionContextMeta(
|
|
239
254
|
() => app.flowEngine.context.dataSourceManager?.getDataSource('main')?.getCollection('users') || null,
|
|
240
255
|
app.flowEngine.translate('Current user'),
|
|
@@ -293,6 +308,8 @@ const CurrentUserProvider: FC = ({ children }) => {
|
|
|
293
308
|
return <CurrentUserContext.Provider value={contextValue}>{children}</CurrentUserContext.Provider>;
|
|
294
309
|
};
|
|
295
310
|
|
|
311
|
+
CurrentUserProvider.displayName = 'CurrentUserProvider';
|
|
312
|
+
|
|
296
313
|
const RootRedirect: FC = () => {
|
|
297
314
|
const app = useApp<Application>();
|
|
298
315
|
const hasToken = !!app?.apiClient?.auth?.token;
|
|
@@ -326,6 +343,7 @@ export class NocoBaseBuildInPlugin extends Plugin<any, Application> {
|
|
|
326
343
|
this.app.flowEngine.registerModels({
|
|
327
344
|
AdminLayoutModel,
|
|
328
345
|
AdminLayoutMenuItemModel,
|
|
346
|
+
AppSwitcherActionPanelModel,
|
|
329
347
|
AdminSettingsLayoutModel,
|
|
330
348
|
});
|
|
331
349
|
this.app.layoutManager.registerLayout({
|
|
@@ -355,7 +373,6 @@ export class NocoBaseBuildInPlugin extends Plugin<any, Application> {
|
|
|
355
373
|
title: this.app.i18n.t('System settings'),
|
|
356
374
|
icon: 'SettingOutlined',
|
|
357
375
|
aclSnippet: 'pm.system-settings.system-settings',
|
|
358
|
-
sort: -100,
|
|
359
376
|
});
|
|
360
377
|
this.app.pluginSettingsManager.addPageTabItem({
|
|
361
378
|
menuKey: 'system-settings',
|
|
@@ -363,7 +380,6 @@ export class NocoBaseBuildInPlugin extends Plugin<any, Application> {
|
|
|
363
380
|
title: this.app.i18n.t('System settings'),
|
|
364
381
|
componentLoader: () => import('../settings-center/SystemSettingsPage'),
|
|
365
382
|
aclSnippet: 'pm.system-settings.system-settings',
|
|
366
|
-
sort: -100,
|
|
367
383
|
});
|
|
368
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.
|
|
369
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
|
>
|
|
@@ -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();
|
|
@@ -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) {
|