@nocobase/client-v2 2.2.0-beta.5 → 2.2.0-beta.7

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 (121) hide show
  1. package/es/PluginSettingsManager.d.ts +33 -0
  2. package/es/RouteRepository.d.ts +21 -6
  3. package/es/components/form/JsonTextArea.d.ts +2 -1
  4. package/es/components/form/VariableJsonTextArea.d.ts +19 -0
  5. package/es/components/form/index.d.ts +1 -0
  6. package/es/components/index.d.ts +1 -0
  7. package/es/flow/admin-shell/admin-layout/AdminLayoutEntryGuard.d.ts +2 -0
  8. package/es/flow/admin-shell/admin-layout/AdminLayoutMenuFlowUtils.d.ts +2 -1
  9. package/es/flow/admin-shell/admin-layout/AdminLayoutMenuModels.d.ts +2 -0
  10. package/es/flow/admin-shell/admin-layout/AdminLayoutMenuUtils.d.ts +3 -2
  11. package/es/flow/admin-shell/admin-layout/AdminLayoutSlotModels.d.ts +4 -0
  12. package/es/flow/admin-shell/admin-layout/resolveAdminRouteRuntimeTarget.d.ts +6 -0
  13. package/es/flow/components/FieldAssignRulesEditor.d.ts +3 -1
  14. package/es/flow/models/base/GridModel.d.ts +1 -1
  15. package/es/flow/models/blocks/filter-form/FilterFormBlockModel.d.ts +5 -0
  16. package/es/flow/models/blocks/form/FormBlockModel.d.ts +4 -0
  17. package/es/flow/models/blocks/form/value-runtime/rules.d.ts +21 -0
  18. package/es/flow/models/blocks/form/value-runtime/runtime.d.ts +21 -0
  19. package/es/flow/models/blocks/form/value-runtime/types.d.ts +2 -2
  20. package/es/flow-compat/FieldValidation.d.ts +2 -1
  21. package/es/index.d.ts +2 -1
  22. package/es/index.mjs +145 -119
  23. package/lib/index.js +148 -122
  24. package/package.json +7 -7
  25. package/src/PluginSettingsManager.ts +53 -0
  26. package/src/RouteRepository.ts +126 -24
  27. package/src/__tests__/PluginSettingsManager.test.ts +13 -0
  28. package/src/__tests__/RouteRepository.test.ts +216 -0
  29. package/src/__tests__/exports.test.ts +16 -0
  30. package/src/__tests__/plugin-manager.test.tsx +44 -2
  31. package/src/__tests__/settings-center.test.tsx +40 -1
  32. package/src/components/form/JsonTextArea.tsx +21 -11
  33. package/src/components/form/VariableJsonTextArea.tsx +175 -0
  34. package/src/components/form/__tests__/JsonTextArea.test.tsx +43 -0
  35. package/src/components/form/__tests__/VariableJsonTextArea.test.tsx +86 -0
  36. package/src/components/form/index.tsx +1 -0
  37. package/src/components/index.ts +1 -0
  38. package/src/flow/__tests__/FlowRoute.test.tsx +443 -5
  39. package/src/flow/actions/__tests__/dataScopeFilter.test.ts +58 -0
  40. package/src/flow/actions/__tests__/fieldLinkageRules.scopeDepth.test.ts +150 -1
  41. package/src/flow/actions/__tests__/linkageAssignField.legacy.test.ts +135 -0
  42. package/src/flow/actions/__tests__/linkageRules.hiddenSync.restore.test.ts +19 -5
  43. package/src/flow/actions/__tests__/linkageRules.subFormSetFieldProps.test.ts +92 -0
  44. package/src/flow/actions/customVariable.tsx +1 -2
  45. package/src/flow/actions/dataScopeFilter.ts +10 -0
  46. package/src/flow/actions/linkageRules.tsx +122 -34
  47. package/src/flow/actions/runjs.tsx +2 -14
  48. package/src/flow/actions/validation.tsx +62 -30
  49. package/src/flow/admin-shell/admin-layout/AdminLayoutComponent.tsx +33 -10
  50. package/src/flow/admin-shell/admin-layout/AdminLayoutEntryGuard.test.tsx +310 -0
  51. package/src/flow/admin-shell/admin-layout/AdminLayoutEntryGuard.tsx +44 -8
  52. package/src/flow/admin-shell/admin-layout/AdminLayoutMenuFlowUtils.ts +5 -3
  53. package/src/flow/admin-shell/admin-layout/AdminLayoutMenuModels.tsx +61 -9
  54. package/src/flow/admin-shell/admin-layout/AdminLayoutMenuUtils.tsx +71 -8
  55. package/src/flow/admin-shell/admin-layout/AdminLayoutModel.tsx +1 -1
  56. package/src/flow/admin-shell/admin-layout/AdminLayoutSlotModels.tsx +21 -8
  57. package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutComponent.test.tsx +188 -0
  58. package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutMenuModels.test.ts +99 -0
  59. package/src/flow/admin-shell/admin-layout/__tests__/TopbarActionsBar.test.tsx +166 -2
  60. package/src/flow/admin-shell/admin-layout/resolveAdminRouteRuntimeTarget.test.ts +20 -0
  61. package/src/flow/admin-shell/admin-layout/resolveAdminRouteRuntimeTarget.ts +33 -5
  62. package/src/flow/components/DefaultValue.tsx +1 -2
  63. package/src/flow/components/FieldAssignRulesEditor.tsx +128 -21
  64. package/src/flow/components/FlowRoute.tsx +56 -11
  65. package/src/flow/components/__tests__/FieldAssignRulesEditor.test.tsx +508 -4
  66. package/src/flow/components/__tests__/fieldAssignOptions.test.ts +151 -3
  67. package/src/flow/components/code-editor/__tests__/useCodeRunner.test.tsx +2 -17
  68. package/src/flow/components/code-editor/hooks/useCodeRunner.ts +2 -14
  69. package/src/flow/components/code-editor/runjsDiagnostics.ts +8 -45
  70. package/src/flow/components/fieldAssignOptions.ts +155 -27
  71. package/src/flow/models/actions/JSActionModel.tsx +2 -7
  72. package/src/flow/models/actions/JSCollectionActionModel.tsx +2 -7
  73. package/src/flow/models/actions/JSItemActionModel.tsx +2 -7
  74. package/src/flow/models/actions/JSRecordActionModel.tsx +2 -7
  75. package/src/flow/models/base/ActionModel.tsx +12 -1
  76. package/src/flow/models/base/GridModel.tsx +38 -7
  77. package/src/flow/models/base/PageModel/PageModel.tsx +4 -1
  78. package/src/flow/models/base/PageModel/__tests__/PageModel.test.ts +28 -4
  79. package/src/flow/models/base/__tests__/ActionModel.test.ts +83 -0
  80. package/src/flow/models/base/__tests__/GridModel.dragSnapshotContainer.test.ts +239 -1
  81. package/src/flow/models/base/__tests__/transformRowsToSingleColumn.test.ts +48 -0
  82. package/src/flow/models/blocks/filter-form/FilterFormBlockModel.tsx +31 -3
  83. package/src/flow/models/blocks/filter-form/FilterFormJSActionModel.tsx +2 -7
  84. package/src/flow/models/blocks/filter-form/__tests__/defaultValues.wiring.test.ts +77 -0
  85. package/src/flow/models/blocks/form/EditFormModel.tsx +1 -0
  86. package/src/flow/models/blocks/form/FormBlockModel.tsx +7 -0
  87. package/src/flow/models/blocks/form/JSFormActionModel.tsx +2 -7
  88. package/src/flow/models/blocks/form/__tests__/FormBlockModel.test.tsx +17 -0
  89. package/src/flow/models/blocks/form/value-runtime/__tests__/runtime.test.ts +880 -102
  90. package/src/flow/models/blocks/form/value-runtime/rules.ts +445 -13
  91. package/src/flow/models/blocks/form/value-runtime/runtime.ts +257 -13
  92. package/src/flow/models/blocks/form/value-runtime/types.ts +2 -2
  93. package/src/flow/models/blocks/js-block/JSBlock.tsx +2 -7
  94. package/src/flow/models/blocks/table/JSColumnModel.tsx +1 -9
  95. package/src/flow/models/blocks/table/TableActionsColumnModel.tsx +1 -1
  96. package/src/flow/models/blocks/table/TableBlockModel.tsx +13 -3
  97. package/src/flow/models/blocks/table/__tests__/TableActionsColumnModel.test.tsx +54 -0
  98. package/src/flow/models/blocks/table/__tests__/TableBlockModel.mobileSettingsButtons.test.tsx +78 -0
  99. package/src/flow/models/fields/AssociationFieldModel/AssociationFieldModel.tsx +1 -1
  100. package/src/flow/models/fields/AssociationFieldModel/PopupSubTableFieldModel/PopupSubTableFieldModel.tsx +29 -6
  101. package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/SubTableField.tsx +13 -1
  102. package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/__tests__/SubTableFieldModel.reset.test.ts +180 -0
  103. package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/index.tsx +6 -4
  104. package/src/flow/models/fields/AssociationFieldModel/__tests__/AssociationFieldModel.updateAssociation.test.ts +72 -0
  105. package/src/flow/models/fields/InputFieldModel.tsx +14 -22
  106. package/src/flow/models/fields/JSEditableFieldModel.tsx +2 -11
  107. package/src/flow/models/fields/JSFieldModel.tsx +2 -7
  108. package/src/flow/models/fields/JSItemModel.tsx +2 -14
  109. package/src/flow/models/fields/__tests__/InputFieldModel.test.tsx +17 -0
  110. package/src/flow/models/topbar/TopbarActionModel.tsx +93 -10
  111. package/src/flow-compat/FieldValidation.tsx +122 -60
  112. package/src/flow-compat/Popover.tsx +43 -4
  113. package/src/flow-compat/__tests__/Popover.test.tsx +34 -0
  114. package/src/index.ts +8 -1
  115. package/src/layout-manager/__tests__/LayoutRoute.test.tsx +41 -1
  116. package/src/nocobase-buildin-plugin/index.tsx +0 -2
  117. package/src/settings-center/AdminSettingsLayout.tsx +3 -0
  118. package/src/settings-center/SystemSettingsPage.tsx +0 -1
  119. package/src/settings-center/plugin-manager/PluginCard.tsx +2 -2
  120. package/src/settings-center/plugin-manager/index.tsx +3 -0
  121. package/src/settings-center/utils.tsx +0 -6
@@ -8,15 +8,54 @@
8
8
  */
9
9
 
10
10
  import { Popover as AntdPopover, type PopoverProps } from 'antd';
11
- import React, { useCallback, useRef } from 'react';
11
+ import React, { useCallback, useLayoutEffect, useRef, useState } from 'react';
12
12
  import { getZIndex, useZIndexContext, zIndexContext } from './zIndexContext';
13
13
 
14
14
  export const ICON_POPUP_Z_INDEX = 2000;
15
15
 
16
+ function parseZIndex(value?: string | null) {
17
+ if (!value || value === 'auto') {
18
+ return;
19
+ }
20
+
21
+ const zIndex = Number(value);
22
+ return Number.isFinite(zIndex) ? zIndex : undefined;
23
+ }
24
+
25
+ function getElementZIndex(element: HTMLElement) {
26
+ return parseZIndex(window.getComputedStyle(element).zIndex) ?? parseZIndex(element.style.zIndex);
27
+ }
28
+
29
+ function getContainingPopupZIndex(element: HTMLElement | null) {
30
+ if (!element || typeof window === 'undefined') {
31
+ return 0;
32
+ }
33
+
34
+ let current: HTMLElement | null = element;
35
+ let maxZIndex = 0;
36
+
37
+ while (current && current !== document.documentElement) {
38
+ const zIndex = getElementZIndex(current);
39
+ if (typeof zIndex === 'number' && zIndex > maxZIndex) {
40
+ maxZIndex = zIndex;
41
+ }
42
+ current = current.parentElement;
43
+ }
44
+
45
+ return maxZIndex;
46
+ }
47
+
16
48
  export const StablePopover = (props: PopoverProps) => {
17
49
  const parentZIndex = useZIndexContext();
18
- const zIndex = getZIndex('drawer', parentZIndex, 1);
19
- const target = useRef(null);
50
+ const [containingPopupZIndex, setContainingPopupZIndex] = useState(parentZIndex);
51
+ const target = useRef<EventTarget | null>(null);
52
+ const triggerRef = useRef<HTMLDivElement | null>(null);
53
+ const zIndex = getZIndex('drawer', Math.max(parentZIndex, containingPopupZIndex), 1);
54
+
55
+ useLayoutEffect(() => {
56
+ const nextZIndex = Math.max(parentZIndex, getContainingPopupZIndex(triggerRef.current));
57
+ setContainingPopupZIndex((previousZIndex) => (previousZIndex === nextZIndex ? previousZIndex : nextZIndex));
58
+ }, [parentZIndex]);
20
59
 
21
60
  const avoidClose = useCallback((e: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
22
61
  target.current = e.target;
@@ -36,7 +75,7 @@ export const StablePopover = (props: PopoverProps) => {
36
75
  );
37
76
 
38
77
  return (
39
- <div className="popover-with-stop-propagation" onClick={avoidClose}>
78
+ <div ref={triggerRef} className="popover-with-stop-propagation" onClick={avoidClose}>
40
79
  <zIndexContext.Provider value={zIndex}>
41
80
  <AntdPopover {...props} zIndex={zIndex} onOpenChange={onOpenChange} />
42
81
  </zIndexContext.Provider>
@@ -0,0 +1,34 @@
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 { render, screen, waitFor } from '@testing-library/react';
11
+ import React from 'react';
12
+ import { describe, expect, it } from 'vitest';
13
+ import { StablePopover } from '../Popover';
14
+
15
+ describe('StablePopover', () => {
16
+ it('renders above the containing popup layer', async () => {
17
+ render(
18
+ <div style={{ position: 'relative', zIndex: 5000 }}>
19
+ <StablePopover open content={<div>Icon picker popup</div>}>
20
+ <button type="button">Select icon</button>
21
+ </StablePopover>
22
+ </div>,
23
+ );
24
+
25
+ expect(screen.getByText('Icon picker popup')).toBeInTheDocument();
26
+
27
+ await waitFor(() => {
28
+ const popover = document.querySelector<HTMLElement>('.ant-popover');
29
+
30
+ expect(popover).toBeInTheDocument();
31
+ expect(Number(popover?.style.zIndex)).toBeGreaterThan(5000);
32
+ });
33
+ });
34
+ });
package/src/index.ts CHANGED
@@ -39,5 +39,12 @@ export * from './collection-manager/interfaces';
39
39
  export * from './collection-manager/template-fields';
40
40
  export * from './data-source';
41
41
  export * from './flow';
42
- export { DEFAULT_DATA_SOURCE_KEY, isTitleField, isTitleFieldInterface } from './flow-compat';
42
+ export {
43
+ DEFAULT_DATA_SOURCE_KEY,
44
+ IconPicker,
45
+ isTitleField,
46
+ isTitleFieldInterface,
47
+ NocoBaseDesktopRouteType,
48
+ } from './flow-compat';
49
+ export type { NocoBaseDesktopRoute } from './flow-compat';
43
50
  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,
@@ -355,7 +355,6 @@ export class NocoBaseBuildInPlugin extends Plugin<any, Application> {
355
355
  title: this.app.i18n.t('System settings'),
356
356
  icon: 'SettingOutlined',
357
357
  aclSnippet: 'pm.system-settings.system-settings',
358
- sort: -100,
359
358
  });
360
359
  this.app.pluginSettingsManager.addPageTabItem({
361
360
  menuKey: 'system-settings',
@@ -363,7 +362,6 @@ export class NocoBaseBuildInPlugin extends Plugin<any, Application> {
363
362
  title: this.app.i18n.t('System settings'),
364
363
  componentLoader: () => import('../settings-center/SystemSettingsPage'),
365
364
  aclSnippet: 'pm.system-settings.system-settings',
366
- sort: -100,
367
365
  });
368
366
  // 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
367
  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
@@ -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,
@@ -116,7 +116,7 @@ export const PluginCard: FC<PluginCardProps> = ({ data }) => {
116
116
  });
117
117
 
118
118
  const openSettings = useMemoizedFn(() => {
119
- navigate(app.pluginSettingsManager.getRoutePath(name));
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.has(name) && (
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
  }