@nocobase/client-v2 2.1.0-beta.37 → 2.1.0-beta.38

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.
Files changed (123) hide show
  1. package/es/Application.d.ts +1 -0
  2. package/es/BaseApplication.d.ts +3 -0
  3. package/es/RouterManager.d.ts +1 -0
  4. package/es/components/KeepAlive.d.ts +22 -0
  5. package/es/components/RouterBridge.d.ts +9 -0
  6. package/es/data-source/ExtendCollectionsProvider.d.ts +28 -2
  7. package/es/flow/FlowPage.d.ts +2 -1
  8. package/es/flow/admin-shell/AdminLayoutRouteCoordinator.d.ts +8 -40
  9. package/es/flow/admin-shell/BaseLayoutModel.d.ts +89 -0
  10. package/es/flow/admin-shell/BaseLayoutRouteCoordinator.d.ts +74 -0
  11. package/es/flow/admin-shell/admin-layout/AdminLayoutEntryGuard.d.ts +12 -0
  12. package/es/flow/admin-shell/admin-layout/AdminLayoutModel.d.ts +7 -92
  13. package/es/flow/admin-shell/admin-layout/index.d.ts +2 -0
  14. package/es/flow/admin-shell/useAdminLayoutRoutePage.d.ts +2 -2
  15. package/es/flow/admin-shell/useLayoutRoutePage.d.ts +23 -0
  16. package/es/flow/components/FlowRoute.d.ts +10 -1
  17. package/es/flow/index.d.ts +4 -0
  18. package/es/flow/models/base/PageModel/PageModel.d.ts +3 -1
  19. package/es/flow/models/blocks/form/FormActionGroupModel.d.ts +1 -0
  20. package/es/flow/models/blocks/table/TableBlockModel.d.ts +10 -0
  21. package/es/flow/models/fields/AssociationFieldModel/SubTableFieldModel/index.d.ts +1 -1
  22. package/es/index.d.ts +1 -0
  23. package/es/index.mjs +484 -437
  24. package/es/layout-manager/LayoutContentRoute.d.ts +14 -0
  25. package/es/layout-manager/LayoutManager.d.ts +22 -0
  26. package/es/layout-manager/LayoutRoute.d.ts +14 -0
  27. package/es/layout-manager/index.d.ts +13 -0
  28. package/es/layout-manager/types.d.ts +20 -0
  29. package/es/layout-manager/utils.d.ts +14 -0
  30. package/es/settings-center/index.d.ts +1 -1
  31. package/es/settings-center/plugin-manager/BulkEnableButton.d.ts +15 -0
  32. package/es/settings-center/plugin-manager/PluginCard.d.ts +15 -0
  33. package/es/settings-center/plugin-manager/PluginDetail.d.ts +16 -0
  34. package/es/settings-center/{PluginManagerPage.d.ts → plugin-manager/index.d.ts} +1 -7
  35. package/es/settings-center/plugin-manager/types.d.ts +34 -0
  36. package/lib/index.js +484 -437
  37. package/package.json +8 -7
  38. package/src/Application.tsx +27 -12
  39. package/src/BaseApplication.tsx +6 -0
  40. package/src/PluginSettingsManager.ts +1 -1
  41. package/src/RouterManager.tsx +17 -1
  42. package/src/__tests__/PluginSettingsManager.test.ts +41 -2
  43. package/src/__tests__/app.test.tsx +8 -1
  44. package/src/__tests__/globalDeps.test.ts +1 -0
  45. package/src/__tests__/nocobase-buildin-plugin-auth.test.tsx +45 -2
  46. package/src/__tests__/plugin-manager.test.tsx +177 -0
  47. package/src/__tests__/settings-center.test.tsx +24 -2
  48. package/src/components/KeepAlive.tsx +131 -0
  49. package/src/components/RouterBridge.tsx +28 -4
  50. package/src/components/__tests__/KeepAlive.test.tsx +63 -0
  51. package/src/components/__tests__/RouterBridge.test.tsx +27 -0
  52. package/src/data-source/ExtendCollectionsProvider.tsx +94 -20
  53. package/src/data-source/__tests__/ExtendCollectionsProvider.test.tsx +264 -0
  54. package/src/flow/FlowPage.tsx +35 -7
  55. package/src/flow/__tests__/FlowPage.test.tsx +79 -0
  56. package/src/flow/__tests__/FlowRoute.test.tsx +529 -2
  57. package/src/flow/actions/__tests__/linkageRules.subFormSetFieldProps.test.ts +191 -0
  58. package/src/flow/actions/__tests__/openView.subModelKey.test.tsx +33 -0
  59. package/src/flow/actions/aclCheck.tsx +4 -0
  60. package/src/flow/actions/aclCheckRefresh.tsx +4 -0
  61. package/src/flow/actions/dateTimeFormat.tsx +12 -8
  62. package/src/flow/actions/linkageRules.tsx +122 -0
  63. package/src/flow/actions/openView.tsx +28 -4
  64. package/src/flow/admin-shell/AdminLayoutRouteCoordinator.ts +11 -329
  65. package/src/flow/admin-shell/BaseLayoutModel.tsx +455 -0
  66. package/src/flow/admin-shell/BaseLayoutRouteCoordinator.ts +502 -0
  67. package/src/flow/admin-shell/__tests__/AdminLayoutRouteCoordinator.test.ts +547 -3
  68. package/src/flow/admin-shell/admin-layout/AdminLayoutComponent.tsx +4 -4
  69. package/src/flow/admin-shell/admin-layout/AdminLayoutEntryGuard.tsx +160 -0
  70. package/src/flow/admin-shell/admin-layout/AdminLayoutMenuModels.tsx +0 -12
  71. package/src/flow/admin-shell/admin-layout/AdminLayoutModel.tsx +28 -201
  72. package/src/flow/admin-shell/admin-layout/AdminLayoutSlotModels.tsx +11 -2
  73. package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutMenuModels.test.ts +1 -26
  74. package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutModel.test.tsx +149 -27
  75. package/src/flow/admin-shell/admin-layout/index.ts +2 -0
  76. package/src/flow/admin-shell/useAdminLayoutRoutePage.ts +10 -26
  77. package/src/flow/admin-shell/useLayoutRoutePage.ts +61 -0
  78. package/src/flow/components/AdminLayout.tsx +4 -154
  79. package/src/flow/components/FlowRoute.tsx +105 -15
  80. package/src/flow/index.ts +4 -0
  81. package/src/flow/models/base/ActionModel.tsx +8 -1
  82. package/src/flow/models/base/PageModel/PageModel.tsx +51 -18
  83. package/src/flow/models/base/PageModel/RootPageModel.tsx +6 -13
  84. package/src/flow/models/base/PageModel/__tests__/PageModel.test.ts +102 -1
  85. package/src/flow/models/base/RouteModel.tsx +1 -1
  86. package/src/flow/models/blocks/form/FormActionGroupModel.tsx +14 -0
  87. package/src/flow/models/blocks/form/FormItemModel.tsx +8 -1
  88. package/src/flow/models/blocks/form/__tests__/FormActionGroupModel.test.ts +46 -0
  89. package/src/flow/models/blocks/form/submitValues.ts +4 -1
  90. package/src/flow/models/blocks/table/TableBlockModel.tsx +118 -16
  91. package/src/flow/models/blocks/table/__tests__/TableBlockModel.rowSelection.test.tsx +114 -0
  92. package/src/flow/models/fields/AssociationFieldModel/SubFormFieldModel.tsx +7 -1
  93. package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/SubTableField.tsx +1 -1
  94. package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/index.tsx +6 -5
  95. package/src/flow/models/fields/ClickableFieldModel.tsx +9 -1
  96. package/src/flow/models/fields/DisplayTimeFieldModel.tsx +1 -1
  97. package/src/flow/models/fields/TimeFieldModel.tsx +1 -1
  98. package/src/flow/models/fields/__tests__/TimeFieldModel.test.tsx +61 -0
  99. package/src/flow/models/fields/mobile-components/MobileDatePicker.tsx +19 -3
  100. package/src/flow/models/fields/mobile-components/__tests__/MobileDatePicker.test.tsx +94 -0
  101. package/src/flow/models/topbar/TopbarActionModel.tsx +1 -1
  102. package/src/flow/utils/__tests__/dateTimeFormat.test.ts +91 -0
  103. package/src/index.ts +1 -0
  104. package/src/layout-manager/LayoutContentRoute.tsx +90 -0
  105. package/src/layout-manager/LayoutManager.tsx +185 -0
  106. package/src/layout-manager/LayoutRoute.tsx +138 -0
  107. package/src/layout-manager/__tests__/LayoutManager.test.tsx +335 -0
  108. package/src/layout-manager/__tests__/LayoutRoute.test.tsx +473 -0
  109. package/src/layout-manager/index.ts +14 -0
  110. package/src/layout-manager/types.ts +22 -0
  111. package/src/layout-manager/utils.ts +37 -0
  112. package/src/nocobase-buildin-plugin/index.tsx +56 -48
  113. package/src/settings-center/index.ts +1 -1
  114. package/src/settings-center/plugin-manager/BulkEnableButton.tsx +111 -0
  115. package/src/settings-center/plugin-manager/PluginCard.tsx +270 -0
  116. package/src/settings-center/plugin-manager/PluginDetail.tsx +195 -0
  117. package/src/settings-center/plugin-manager/index.tsx +254 -0
  118. package/src/settings-center/plugin-manager/types.ts +35 -0
  119. package/src/settings-center/utils.tsx +8 -1
  120. package/src/theme/__tests__/globalStyles.test.ts +24 -0
  121. package/src/theme/globalStyles.ts +10 -0
  122. package/src/utils/globalDeps.ts +2 -0
  123. package/src/settings-center/PluginManagerPage.tsx +0 -162
@@ -43,6 +43,7 @@ import {
43
43
  type FlowModel,
44
44
  } from '@nocobase/flow-engine';
45
45
  import { AdminLayoutModel, getAdminLayoutModel } from '..';
46
+ import { getLayoutPageRouteName, getLayoutPageViewRouteName } from '../../../../layout-manager/utils';
46
47
  import { TopbarActionModel } from '../../../models/topbar/TopbarActionModel';
47
48
  import { UserCenterTopbarActionModel } from '../../../models/topbar/UserCenterTopbarActionModel';
48
49
  import { TopbarActionsBar } from '../TopbarActionsBar';
@@ -113,7 +114,7 @@ describe('AdminLayoutModel runtime', () => {
113
114
  }).toThrowError(/admin-layout-model/);
114
115
  });
115
116
 
116
- it('should expose live layoutContentElement on engine context', async () => {
117
+ it('should expose live layoutContentElement on layout context', async () => {
117
118
  const engine = new FlowEngine();
118
119
 
119
120
  render(
@@ -131,29 +132,99 @@ describe('AdminLayoutModel runtime', () => {
131
132
  model.setLayoutContentElement(element);
132
133
  });
133
134
 
134
- expect(engine.context.layoutContentElement).toBe(element);
135
+ expect(model.context.layoutContentElement).toBe(element);
135
136
 
136
137
  act(() => {
137
138
  model.setLayoutContentElement(null);
138
139
  });
139
140
 
140
- expect(engine.context.layoutContentElement).toBeNull();
141
+ expect(model.context.layoutContentElement).toBeNull();
141
142
  });
142
143
 
143
- it('should expose live engine currentRoute when active page changes', async () => {
144
+ it('should expose layout definition only while mounted', async () => {
145
+ const engine = new FlowEngine();
146
+ const { unmount } = render(
147
+ <FlowEngineProvider engine={engine}>
148
+ <TestAdminLayoutHost />
149
+ </FlowEngineProvider>,
150
+ );
151
+ const model = engine.getModel<TestAdminLayoutModel>('admin-layout-model');
152
+ expect(model).toBeTruthy();
153
+
154
+ expect(model.context.layout).toMatchObject({
155
+ routeName: 'admin',
156
+ routePath: '/admin',
157
+ rootRouteName: 'admin',
158
+ rootPageModelClass: 'RootPageModel',
159
+ childPageModelClass: 'ChildPageModel',
160
+ });
161
+ expect(engine.context.layout).toBeUndefined();
162
+
163
+ unmount();
164
+
165
+ expect(model.context.layout).toBeUndefined();
166
+ expect(model.context.currentRoute).toEqual({});
167
+ expect(model.context.layoutContentElement).toBeNull();
168
+ });
169
+
170
+ it('should expose layoutRoute from local layout route sync', async () => {
144
171
  const engine = new FlowEngine();
145
- const routeMap = {
146
- 'page-1': { title: 'Page 1' },
147
- 'page-2': { title: 'Page 2' },
148
- };
149
172
  engine.context.defineProperty('routeRepository', {
150
173
  value: {
151
- getRouteBySchemaUid: (pageUid: string) => routeMap[pageUid],
174
+ getRouteBySchemaUid: (pageUid: string) => ({ title: pageUid }),
152
175
  },
153
176
  });
177
+
178
+ render(
179
+ <FlowEngineProvider engine={engine}>
180
+ <TestAdminLayoutHost />
181
+ </FlowEngineProvider>,
182
+ );
183
+ const model = engine.getModel<TestAdminLayoutModel>('admin-layout-model');
184
+ expect(model).toBeTruthy();
185
+
186
+ act(() => {
187
+ model.syncLayoutRoute({
188
+ name: getLayoutPageViewRouteName('admin'),
189
+ pathname: '/admin/page-1/view/popup',
190
+ layoutBasePathname: '/admin',
191
+ });
192
+ });
193
+
194
+ await waitFor(() => {
195
+ expect(model.context.layoutRoute).toMatchObject({
196
+ type: 'page',
197
+ pageUid: 'page-1',
198
+ viewStack: [{ viewUid: 'page-1' }, { viewUid: 'popup' }],
199
+ });
200
+ expect(model.context.currentRoute.title).toBe('page-1');
201
+ });
202
+
203
+ act(() => {
204
+ model.syncLayoutRoute({
205
+ name: 'admin.settings',
206
+ pathname: '/admin/settings',
207
+ });
208
+ });
209
+
210
+ await waitFor(() => {
211
+ expect(model.context.layoutRoute).toBeNull();
212
+ expect(model.context.currentRoute).toEqual({});
213
+ });
214
+ });
215
+
216
+ it('should not consume global routes that belong to nested layouts', async () => {
217
+ const engine = new FlowEngine();
154
218
  const routeRef = observable.ref({
155
- params: { name: 'page-1' },
156
- pathname: '/admin/page-1',
219
+ name: getLayoutPageViewRouteName('admin.settings.publicForms'),
220
+ pathname: '/admin/settings/public-forms/form-1/view/popup',
221
+ params: { name: 'form-1' },
222
+ layoutBasePathname: '/admin/settings/public-forms',
223
+ });
224
+ engine.context.defineProperty('routeRepository', {
225
+ value: {
226
+ getRouteBySchemaUid: (pageUid: string) => ({ title: pageUid }),
227
+ },
157
228
  });
158
229
  engine.context.defineProperty('route', {
159
230
  get: () => routeRef.value,
@@ -169,6 +240,55 @@ describe('AdminLayoutModel runtime', () => {
169
240
  const model = engine.getModel<TestAdminLayoutModel>('admin-layout-model');
170
241
  expect(model).toBeTruthy();
171
242
 
243
+ await waitFor(() => {
244
+ expect(model.context.layoutRoute).toBeNull();
245
+ expect(model.context.currentRoute).toEqual({});
246
+ });
247
+
248
+ act(() => {
249
+ routeRef.value = {
250
+ name: getLayoutPageViewRouteName('admin.settings.publicForms'),
251
+ pathname: '/admin/settings/public-forms/form-2/view/popup',
252
+ params: { name: 'form-2' },
253
+ layoutBasePathname: '/admin/settings/public-forms',
254
+ };
255
+ });
256
+
257
+ await waitFor(() => {
258
+ expect(model.context.layoutRoute).toBeNull();
259
+ expect(model.context.currentRoute).toEqual({});
260
+ });
261
+ });
262
+
263
+ it('should expose live layout currentRoute when active page changes', async () => {
264
+ const engine = new FlowEngine();
265
+ const routeMap = {
266
+ 'page-1': { title: 'Page 1' },
267
+ 'page-2': { title: 'Page 2' },
268
+ };
269
+ engine.context.defineProperty('routeRepository', {
270
+ value: {
271
+ getRouteBySchemaUid: (pageUid: string) => routeMap[pageUid],
272
+ },
273
+ });
274
+
275
+ render(
276
+ <FlowEngineProvider engine={engine}>
277
+ <TestAdminLayoutHost />
278
+ </FlowEngineProvider>,
279
+ );
280
+
281
+ const model = engine.getModel<TestAdminLayoutModel>('admin-layout-model');
282
+ expect(model).toBeTruthy();
283
+
284
+ act(() => {
285
+ model.syncLayoutRoute({
286
+ name: getLayoutPageRouteName('admin'),
287
+ pathname: '/admin/page-1',
288
+ layoutBasePathname: '/admin',
289
+ });
290
+ });
291
+
172
292
  model.registerRoutePage('page-1', {
173
293
  active: true,
174
294
  });
@@ -177,36 +297,29 @@ describe('AdminLayoutModel runtime', () => {
177
297
  });
178
298
 
179
299
  await waitFor(() => {
180
- expect(engine.context.currentRoute.title).toBe('Page 1');
300
+ expect(model.context.currentRoute.title).toBe('Page 1');
181
301
  });
182
302
 
183
303
  act(() => {
184
- routeRef.value = {
185
- params: { name: 'page-2' },
304
+ model.syncLayoutRoute({
305
+ name: getLayoutPageRouteName('admin'),
186
306
  pathname: '/admin/page-2',
187
- };
307
+ layoutBasePathname: '/admin',
308
+ });
188
309
  });
189
310
 
190
311
  await waitFor(() => {
191
- expect(engine.context.currentRoute.title).toBe('Page 2');
312
+ expect(model.context.currentRoute.title).toBe('Page 2');
192
313
  });
193
314
  });
194
315
 
195
316
  it('should keep pageActive in sync after non-active route page updates', async () => {
196
317
  const engine = new FlowEngine();
197
- const routeRef = observable.ref({
198
- params: { name: 'page-1' },
199
- pathname: '/admin/page-1',
200
- });
201
318
  engine.context.defineProperty('routeRepository', {
202
319
  value: {
203
320
  getRouteBySchemaUid: (pageUid: string) => ({ title: pageUid }),
204
321
  },
205
322
  });
206
- engine.context.defineProperty('route', {
207
- get: () => routeRef.value,
208
- cache: false,
209
- });
210
323
 
211
324
  render(
212
325
  <FlowEngineProvider engine={engine}>
@@ -217,6 +330,14 @@ describe('AdminLayoutModel runtime', () => {
217
330
  const model = engine.getModel<TestAdminLayoutModel>('admin-layout-model');
218
331
  expect(model).toBeTruthy();
219
332
 
333
+ act(() => {
334
+ model.syncLayoutRoute({
335
+ name: getLayoutPageRouteName('admin'),
336
+ pathname: '/admin/page-1',
337
+ layoutBasePathname: '/admin',
338
+ });
339
+ });
340
+
220
341
  model.registerRoutePage('page-1', {
221
342
  active: false,
222
343
  });
@@ -236,10 +357,11 @@ describe('AdminLayoutModel runtime', () => {
236
357
  expect(routeModel.context.pageActive.value).toBe(true);
237
358
 
238
359
  act(() => {
239
- routeRef.value = {
240
- params: { name: 'page-2' },
360
+ model.syncLayoutRoute({
361
+ name: getLayoutPageRouteName('admin'),
241
362
  pathname: '/admin/page-2',
242
- };
363
+ layoutBasePathname: '/admin',
364
+ });
243
365
  });
244
366
 
245
367
  await waitFor(() => {
@@ -9,6 +9,8 @@
9
9
 
10
10
  export * from './AdminLayoutComponent';
11
11
  export * from './AdminLayoutModel';
12
+ export * from '../BaseLayoutModel';
13
+ export * from '../BaseLayoutRouteCoordinator';
12
14
  export * from './AdminLayoutSlotModels';
13
15
  export * from './AdminLayoutMenuModels';
14
16
  export * from './AdminLayoutMenuFlowUtils';
@@ -8,14 +8,15 @@
8
8
  */
9
9
 
10
10
  import type { FlowEngine } from '@nocobase/flow-engine';
11
- import { useEffect, useRef } from 'react';
11
+ import type { RefObject } from 'react';
12
12
  import { getAdminLayoutModel, type AdminLayoutModel } from './admin-layout/AdminLayoutModel';
13
+ import { useLayoutRoutePage } from './useLayoutRoutePage';
13
14
 
14
15
  type UseAdminLayoutRoutePageOptions = {
15
16
  flowEngine: FlowEngine;
16
17
  pageUid: string;
17
18
  refreshDesktopRoutes?: () => Promise<unknown>;
18
- layoutContentRef: React.RefObject<HTMLDivElement>;
19
+ layoutContentRef: RefObject<HTMLDivElement>;
19
20
  };
20
21
 
21
22
  /**
@@ -29,28 +30,11 @@ type UseAdminLayoutRoutePageOptions = {
29
30
  */
30
31
  export function useAdminLayoutRoutePage(options: UseAdminLayoutRoutePageOptions) {
31
32
  const { flowEngine, pageUid, refreshDesktopRoutes, layoutContentRef } = options;
32
- const adminLayoutModel = getAdminLayoutModel<AdminLayoutModel>(flowEngine, { required: true });
33
- const refreshRef = useRef(refreshDesktopRoutes);
34
-
35
- refreshRef.current = refreshDesktopRoutes;
36
-
37
- useEffect(() => {
38
- adminLayoutModel.registerRoutePage(pageUid, {
39
- active: false,
40
- refreshDesktopRoutes: refreshRef.current,
41
- layoutContentElement: layoutContentRef.current,
42
- });
43
- return () => {
44
- adminLayoutModel.unregisterRoutePage(pageUid);
45
- };
46
- }, [adminLayoutModel, pageUid, layoutContentRef]);
47
-
48
- useEffect(() => {
49
- adminLayoutModel.updateRoutePage(pageUid, {
50
- refreshDesktopRoutes,
51
- layoutContentElement: layoutContentRef.current,
52
- });
53
- }, [adminLayoutModel, pageUid, refreshDesktopRoutes, layoutContentRef]);
54
-
55
- return adminLayoutModel;
33
+ return useLayoutRoutePage<AdminLayoutModel>({
34
+ flowEngine,
35
+ pageUid,
36
+ refreshDesktopRoutes,
37
+ layoutContentRef,
38
+ getLayoutModel: (engine) => getAdminLayoutModel<AdminLayoutModel>(engine, { required: true }),
39
+ });
56
40
  }
@@ -0,0 +1,61 @@
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 type { FlowEngine } from '@nocobase/flow-engine';
11
+ import { useEffect, useRef, type RefObject } from 'react';
12
+ import type { BaseLayoutModel } from './BaseLayoutModel';
13
+
14
+ export type UseLayoutRoutePageOptions<TModel extends BaseLayoutModel = BaseLayoutModel> = {
15
+ flowEngine: FlowEngine;
16
+ pageUid: string;
17
+ active?: boolean;
18
+ refreshDesktopRoutes?: () => Promise<unknown>;
19
+ layoutContentRef: RefObject<HTMLElement>;
20
+ getLayoutModel: (flowEngine: FlowEngine) => TModel | undefined;
21
+ };
22
+
23
+ /**
24
+ * 把 FlowRoute 页面的生命周期桥接到指定 Layout host model。
25
+ */
26
+ export function useLayoutRoutePage<TModel extends BaseLayoutModel = BaseLayoutModel>(
27
+ options: UseLayoutRoutePageOptions<TModel>,
28
+ ) {
29
+ const { flowEngine, pageUid, active, refreshDesktopRoutes, layoutContentRef, getLayoutModel } = options;
30
+ const layoutModel = getLayoutModel(flowEngine);
31
+ const activeRef = useRef(active);
32
+ const refreshRef = useRef(refreshDesktopRoutes);
33
+
34
+ if (!layoutModel) {
35
+ throw new Error('[NocoBase] FlowRoute requires layout model. Please render FlowRoute under Layout.');
36
+ }
37
+
38
+ activeRef.current = active;
39
+ refreshRef.current = refreshDesktopRoutes;
40
+
41
+ useEffect(() => {
42
+ layoutModel.registerRoutePage(pageUid, {
43
+ active: activeRef.current ?? false,
44
+ refreshDesktopRoutes: refreshRef.current,
45
+ layoutContentElement: layoutContentRef.current,
46
+ });
47
+ return () => {
48
+ layoutModel.unregisterRoutePage(pageUid);
49
+ };
50
+ }, [layoutModel, pageUid, layoutContentRef]);
51
+
52
+ useEffect(() => {
53
+ layoutModel.updateRoutePage(pageUid, {
54
+ active,
55
+ refreshDesktopRoutes,
56
+ layoutContentElement: layoutContentRef.current,
57
+ });
58
+ }, [active, layoutModel, pageUid, refreshDesktopRoutes, layoutContentRef]);
59
+
60
+ return layoutModel;
61
+ }
@@ -7,161 +7,11 @@
7
7
  * For more information, please refer to: https://www.nocobase.com/agreement.
8
8
  */
9
9
 
10
- import { useFlowEngine, FlowModelRenderer } from '@nocobase/flow-engine';
11
- import React, { type FC, useEffect, useMemo, useRef, useState } from 'react';
12
- import { getAdminLayoutModel, AdminLayoutModel } from '..';
13
- import { useApp } from '../../hooks/useApp';
14
- import { useLocation, useNavigate, useParams } from 'react-router-dom';
15
- import { NocoBaseDesktopRouteType } from '../../flow-compat';
16
- import {
17
- findFirstV2LandingRoute,
18
- resolveAdminRouteRuntimeTarget,
19
- toRouterNavigationPath,
20
- } from '../admin-shell/admin-layout/resolveAdminRouteRuntimeTarget';
10
+ import React, { type FC } from 'react';
11
+ import { LayoutRoute } from '../../layout-manager/LayoutRoute';
21
12
 
22
- const AdminLayoutEntryGuard: FC<{ children: React.ReactNode }> = ({ children }) => {
23
- const flowEngine = useFlowEngine();
24
- const app = useApp();
25
- const navigate = useNavigate();
26
- const location = useLocation();
27
- const params = useParams();
28
- const [ready, setReady] = useState(false);
29
- const replaceTriggeredRef = useRef(false);
30
- const routeRepository = flowEngine.context.routeRepository;
31
- const isAdminRoot = useMemo(() => {
32
- const pathname = toRouterNavigationPath(location.pathname, app.router.getBasename());
33
- return pathname === '/admin';
34
- }, [app, location.pathname]);
35
-
36
- useEffect(() => {
37
- replaceTriggeredRef.current = false;
38
- }, [location.pathname, location.search, location.hash, params.name]);
39
-
40
- useEffect(() => {
41
- let active = true;
42
-
43
- const run = async () => {
44
- setReady(false);
45
-
46
- if (!isAdminRoot && !params.name) {
47
- if (active) {
48
- setReady(true);
49
- }
50
- return;
51
- }
52
-
53
- if (!routeRepository?.isAccessibleLoaded?.()) {
54
- try {
55
- await routeRepository?.ensureAccessibleLoaded?.();
56
- } catch (_error) {
57
- if (active) {
58
- setReady(true);
59
- }
60
- return;
61
- }
62
- }
63
-
64
- if (!active || replaceTriggeredRef.current) {
65
- return;
66
- }
67
-
68
- if (params.name) {
69
- const currentRoute = routeRepository?.getRouteBySchemaUid?.(params.name);
70
- if (currentRoute?.type === NocoBaseDesktopRouteType.page) {
71
- const target = resolveAdminRouteRuntimeTarget({
72
- app,
73
- route: currentRoute,
74
- location: {
75
- pathname: window.location.pathname,
76
- search: window.location.search,
77
- hash: window.location.hash,
78
- },
79
- preserveLocationState: true,
80
- });
81
-
82
- if (target.navigationMode === 'document' && target.runtimePath) {
83
- replaceTriggeredRef.current = true;
84
- window.location.replace(target.runtimePath);
85
- return;
86
- }
87
- }
88
-
89
- if (active) {
90
- setReady(true);
91
- }
92
- return;
93
- }
94
-
95
- const firstAccessibleRoute = findFirstV2LandingRoute(routeRepository?.listAccessible?.() || []);
96
- if (!firstAccessibleRoute) {
97
- if (active) {
98
- setReady(true);
99
- }
100
- return;
101
- }
102
-
103
- const target = resolveAdminRouteRuntimeTarget({
104
- app,
105
- route: firstAccessibleRoute,
106
- });
107
-
108
- if (!target.runtimePath) {
109
- if (active) {
110
- setReady(true);
111
- }
112
- return;
113
- }
114
-
115
- replaceTriggeredRef.current = true;
116
- if (target.navigationMode === 'document') {
117
- window.location.replace(target.runtimePath);
118
- return;
119
- }
120
-
121
- navigate(toRouterNavigationPath(target.runtimePath, app.router.getBasename()), { replace: true });
122
- };
123
-
124
- void run();
125
-
126
- return () => {
127
- active = false;
128
- };
129
- }, [
130
- app,
131
- flowEngine,
132
- isAdminRoot,
133
- location.hash,
134
- location.pathname,
135
- location.search,
136
- navigate,
137
- params.name,
138
- routeRepository,
139
- ]);
140
-
141
- if (!ready) {
142
- return null;
143
- }
144
-
145
- return <>{children}</>;
146
- };
147
-
148
- const AdminLayout: FC = (props) => {
149
- const flowEngine = useFlowEngine();
150
- const model = getAdminLayoutModel(flowEngine, {
151
- create: true,
152
- props,
153
- use: AdminLayoutModel,
154
- });
155
-
156
- if (!model) {
157
- throw new Error('[NocoBase] Failed to create admin-layout-model.');
158
- }
159
-
160
- return (
161
- <AdminLayoutEntryGuard>
162
- <FlowModelRenderer model={model} />
163
- </AdminLayoutEntryGuard>
164
- );
13
+ const AdminLayout: FC = () => {
14
+ return <LayoutRoute layoutRouteName="admin" />;
165
15
  };
166
16
 
167
17
  export default AdminLayout;