@nocobase/client-v2 2.1.0-alpha.30 → 2.1.0-alpha.32

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 (103) hide show
  1. package/es/components/form/JsonTextArea.d.ts +18 -0
  2. package/es/components/index.d.ts +1 -0
  3. package/es/flow/actions/dateRangeLimit.d.ts +9 -0
  4. package/es/flow/actions/index.d.ts +1 -0
  5. package/es/flow/actions/linkageRulesFormValueRefresh.d.ts +10 -0
  6. package/es/flow/index.d.ts +1 -0
  7. package/es/flow/models/actions/AssociateActionModel.d.ts +19 -0
  8. package/es/flow/models/actions/AssociationActionUtils.d.ts +17 -0
  9. package/es/flow/models/actions/DisassociateActionModel.d.ts +16 -0
  10. package/es/flow/models/actions/index.d.ts +3 -0
  11. package/es/flow/models/base/GridModel.d.ts +3 -1
  12. package/es/flow/models/base/PageModel/PageModel.d.ts +4 -0
  13. package/es/flow/models/base/PageModel/RootPageModel.d.ts +9 -0
  14. package/es/flow/models/blocks/filter-form/FilterFormGridModel.d.ts +15 -6
  15. package/es/flow/models/blocks/form/value-runtime/runtime.d.ts +7 -0
  16. package/es/flow/models/blocks/shared/filterOperators.d.ts +9 -0
  17. package/es/flow/models/fields/AssociationFieldModel/SubTableFieldModel/SubTableColumnModel.d.ts +3 -0
  18. package/es/flow/models/fields/AssociationFieldModel/recordSelectSettingsUtils.d.ts +9 -0
  19. package/es/flow/models/fields/DateTimeFieldModel/dateLimit.d.ts +20 -0
  20. package/es/flow/models/fields/JSEditableFieldModel.d.ts +4 -0
  21. package/es/flow-compat/data.d.ts +9 -2
  22. package/es/flow-compat/index.d.ts +1 -1
  23. package/es/index.d.ts +1 -0
  24. package/es/index.mjs +100 -93
  25. package/lib/index.js +101 -94
  26. package/package.json +6 -5
  27. package/src/BaseApplication.tsx +1 -1
  28. package/src/__tests__/app.test.tsx +23 -6
  29. package/src/__tests__/globalDeps.test.ts +5 -0
  30. package/src/components/form/JsonTextArea.tsx +129 -0
  31. package/src/components/index.ts +1 -0
  32. package/src/flow/actions/__tests__/fieldLinkageRules.scopeDepth.test.ts +478 -0
  33. package/src/flow/actions/__tests__/linkageRules.formValueDrivenRefresh.test.ts +438 -0
  34. package/src/flow/actions/__tests__/linkageRulesRefresh.test.ts +42 -0
  35. package/src/flow/actions/__tests__/pattern.test.ts +190 -0
  36. package/src/flow/actions/dateRangeLimit.tsx +66 -0
  37. package/src/flow/actions/index.ts +1 -0
  38. package/src/flow/actions/linkageRules.tsx +119 -14
  39. package/src/flow/actions/linkageRulesFormValueRefresh.ts +492 -0
  40. package/src/flow/actions/linkageRulesRefresh.tsx +4 -2
  41. package/src/flow/actions/openView.tsx +2 -1
  42. package/src/flow/actions/pattern.tsx +25 -2
  43. package/src/flow/actions/titleField.tsx +8 -3
  44. package/src/flow/admin-shell/AdminLayoutRouteCoordinator.ts +7 -1
  45. package/src/flow/admin-shell/__tests__/AdminLayoutRouteCoordinator.test.ts +117 -0
  46. package/src/flow/components/FieldAssignValueInput.tsx +1 -0
  47. package/src/flow/components/filter/LinkageFilterItem.tsx +6 -5
  48. package/src/flow/components/filter/VariableFilterItem.tsx +14 -13
  49. package/src/flow/components/filter/__tests__/LinkageFilterItem.test.tsx +33 -0
  50. package/src/flow/components/filter/__tests__/VariableFilterItem.test.tsx +48 -5
  51. package/src/flow/index.ts +1 -0
  52. package/src/flow/internal/utils/__tests__/titleFieldQuickSync.test.ts +1 -0
  53. package/src/flow/internal/utils/titleFieldQuickSync.ts +2 -2
  54. package/src/flow/models/actions/AssociateActionModel.tsx +196 -0
  55. package/src/flow/models/actions/AssociationActionUtils.ts +90 -0
  56. package/src/flow/models/actions/DisassociateActionModel.tsx +57 -0
  57. package/src/flow/models/actions/FilterActionModel.tsx +17 -9
  58. package/src/flow/models/actions/__tests__/AssociationActionModel.test.ts +250 -0
  59. package/src/flow/models/actions/index.ts +3 -0
  60. package/src/flow/models/base/GridModel.tsx +21 -1
  61. package/src/flow/models/base/PageModel/PageModel.tsx +15 -3
  62. package/src/flow/models/base/PageModel/RootPageModel.tsx +37 -2
  63. package/src/flow/models/base/PageModel/__tests__/PageModel.test.ts +73 -0
  64. package/src/flow/models/base/PageModel/__tests__/RootPageModel.test.ts +116 -0
  65. package/src/flow/models/base/__tests__/GridModel.dragSnapshotContainer.test.ts +98 -0
  66. package/src/flow/models/blocks/details/DetailsItemModel.tsx +3 -0
  67. package/src/flow/models/blocks/filter-form/FilterFormGridModel.tsx +200 -36
  68. package/src/flow/models/blocks/filter-form/__tests__/FilterFormGridModel.toggleFormFieldsCollapse.test.ts +270 -1
  69. package/src/flow/models/blocks/filter-form/__tests__/customFieldOperators.test.tsx +23 -0
  70. package/src/flow/models/blocks/filter-form/customFieldOperators.ts +12 -1
  71. package/src/flow/models/blocks/filter-form/fields/FieldComponentProps.tsx +22 -8
  72. package/src/flow/models/blocks/filter-form/fields/__tests__/FilterFormCustomFieldModel.recordSelect.test.tsx +18 -0
  73. package/src/flow/models/blocks/filter-manager/FilterManager.ts +51 -1
  74. package/src/flow/models/blocks/filter-manager/__tests__/FilterManager.test.ts +75 -0
  75. package/src/flow/models/blocks/form/FormItemModel.tsx +48 -28
  76. package/src/flow/models/blocks/form/value-runtime/__tests__/runtime.test.ts +167 -1
  77. package/src/flow/models/blocks/form/value-runtime/runtime.ts +103 -11
  78. package/src/flow/models/blocks/shared/filterOperators.ts +14 -0
  79. package/src/flow/models/blocks/table/TableBlockModel.tsx +19 -3
  80. package/src/flow/models/fields/AssociationFieldModel/RecordSelectFieldModel.tsx +5 -1
  81. package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/SubTableColumnModel.tsx +48 -8
  82. package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/SubTableField.tsx +47 -0
  83. package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/__tests__/SubTableColumnModel.rowRecord.test.ts +42 -0
  84. package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/__tests__/SubTableField.refresh.test.tsx +122 -0
  85. package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/index.tsx +2 -0
  86. package/src/flow/models/fields/AssociationFieldModel/recordSelectSettingsUtils.ts +20 -0
  87. package/src/flow/models/fields/ClickableFieldModel.tsx +21 -9
  88. package/src/flow/models/fields/DateTimeFieldModel/DateOnlyFieldModel.tsx +9 -0
  89. package/src/flow/models/fields/DateTimeFieldModel/DateTimeFieldModel.tsx +4 -0
  90. package/src/flow/models/fields/DateTimeFieldModel/DateTimeNoTzFieldModel.tsx +9 -0
  91. package/src/flow/models/fields/DateTimeFieldModel/DateTimeTzFieldModel.tsx +9 -0
  92. package/src/flow/models/fields/DateTimeFieldModel/__tests__/DateTimeNoTzFieldModel.dateLimit.test.tsx +242 -0
  93. package/src/flow/models/fields/DateTimeFieldModel/dateLimit.ts +152 -0
  94. package/src/flow/models/fields/DividerItemModel.tsx +30 -15
  95. package/src/flow/models/fields/JSEditableFieldModel.tsx +110 -14
  96. package/src/flow/models/fields/__tests__/ClickableFieldModel.test.ts +87 -0
  97. package/src/flow/models/fields/__tests__/JSEditableFieldModel.test.tsx +210 -0
  98. package/src/flow/models/fields/mobile-components/MobileSelect.tsx +11 -3
  99. package/src/flow/models/fields/mobile-components/__tests__/MobileSelect.test.tsx +235 -0
  100. package/src/flow-compat/data.ts +25 -3
  101. package/src/flow-compat/index.ts +7 -1
  102. package/src/index.ts +1 -0
  103. package/src/utils/globalDeps.ts +6 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nocobase/client-v2",
3
- "version": "2.1.0-alpha.30",
3
+ "version": "2.1.0-alpha.32",
4
4
  "license": "Apache-2.0",
5
5
  "main": "lib/index.js",
6
6
  "module": "es/index.mjs",
@@ -24,17 +24,18 @@
24
24
  "@formily/antd-v5": "1.2.3",
25
25
  "@formily/react": "^2.2.27",
26
26
  "@formily/shared": "^2.2.27",
27
- "@nocobase/flow-engine": "2.1.0-alpha.30",
28
- "@nocobase/sdk": "2.1.0-alpha.30",
29
- "@nocobase/shared": "2.1.0-alpha.30",
27
+ "@nocobase/flow-engine": "2.1.0-alpha.32",
28
+ "@nocobase/sdk": "2.1.0-alpha.32",
29
+ "@nocobase/shared": "2.1.0-alpha.32",
30
30
  "ahooks": "^3.7.2",
31
31
  "antd": "5.24.2",
32
32
  "classnames": "^2.3.1",
33
33
  "dayjs": "^1.11.10",
34
34
  "i18next": "^22.4.9",
35
+ "json5": "^2.2.3",
35
36
  "lodash": "4.17.21",
36
37
  "react-i18next": "^11.15.1",
37
38
  "react-router-dom": "^6.30.1"
38
39
  },
39
- "gitHead": "292ae0ad87f195ed201b274902d21ecd96f5ddd0"
40
+ "gitHead": "1ba7d717e156651db17c615f9b9c48edd669d19b"
40
41
  }
@@ -356,7 +356,7 @@ export abstract class BaseApplication<
356
356
  addRoutes() {
357
357
  this.router.add('not-found', {
358
358
  path: '*',
359
- Component: this.components['AppNotFound'],
359
+ Component: () => null,
360
360
  });
361
361
  }
362
362
 
@@ -72,29 +72,46 @@ describe('app', () => {
72
72
  });
73
73
  });
74
74
 
75
- it('should mount the app and display "Not Found"', async () => {
75
+ it('should mount the base app with blank fallback route', async () => {
76
76
  const app = createMockClient();
77
77
  const element = document.createElement('div');
78
78
  act(() => {
79
79
  app.mount(element);
80
80
  });
81
- await waitFor(() => expect(element.textContent).toContain('Sorry, the page you visited does not exist.'));
81
+ await waitFor(() => expect(element.querySelector('.ant-app')).not.toBeNull());
82
+ expect(element.textContent).toBe('');
82
83
  });
83
84
 
84
- it('should render default "Not Found" view', async () => {
85
+ it('should render blank fallback route by default', async () => {
85
86
  const app = createMockClient();
86
87
  await renderApp(app);
87
- expect(screen.getByText('Sorry, the page you visited does not exist.')).toBeInTheDocument();
88
+ expect(screen.queryByText('Sorry, the page you visited does not exist.')).not.toBeInTheDocument();
88
89
  });
89
90
 
90
- it('should render custom "Not Found" component', async () => {
91
+ it('should not render custom "Not Found" component before builtin routes are added', async () => {
91
92
  class PluginHelloClient extends Plugin {}
92
93
  const app = createMockClient({
93
94
  plugins: [PluginHelloClient],
94
95
  components: { AppNotFound: () => <div>Not Found2</div> },
95
96
  });
96
97
  await renderApp(app);
97
- expect(screen.getByText('Not Found2')).toBeInTheDocument();
98
+ expect(screen.queryByText('Not Found2')).not.toBeInTheDocument();
99
+ });
100
+
101
+ it('should render builtin "Not Found" view after builtin routes are added', async () => {
102
+ const app = createMockClient({
103
+ plugins: [NocoBaseBuildInPlugin as any],
104
+ router: { type: 'memory', initialEntries: ['/missing'] },
105
+ });
106
+ app.apiMock.onGet('app:getLang').reply(200, {
107
+ data: {
108
+ lang: 'en-US',
109
+ resources: { client: {} },
110
+ cron: {},
111
+ },
112
+ });
113
+ await renderApp(app);
114
+ expect(screen.getByText('Sorry, the page you visited does not exist.')).toBeInTheDocument();
98
115
  });
99
116
 
100
117
  it('should support app provider functionality', async () => {
@@ -9,6 +9,8 @@
9
9
 
10
10
  import { defineGlobalDeps } from '../utils/globalDeps';
11
11
 
12
+ vi.mock('../index', () => ({}));
13
+
12
14
  describe('client-v2 defineGlobalDeps', () => {
13
15
  it('should register shared AMD dependencies for remote plugins', () => {
14
16
  const define = vi.fn();
@@ -24,5 +26,8 @@ describe('client-v2 defineGlobalDeps', () => {
24
26
  expect(define).toHaveBeenCalledWith('@nocobase/client-v2', expect.any(Function));
25
27
  expect(define).toHaveBeenCalledWith('@nocobase/flow-engine', expect.any(Function));
26
28
  expect(define).toHaveBeenCalledWith('ahooks', expect.any(Function));
29
+ expect(define).toHaveBeenCalledWith('dayjs', expect.any(Function));
30
+ expect(define).toHaveBeenCalledWith('lodash', expect.any(Function));
31
+ expect(define).toHaveBeenCalledWith('@emotion/css', expect.any(Function));
27
32
  });
28
33
  });
@@ -0,0 +1,129 @@
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 { css, cx } from '@emotion/css';
11
+ import { Input, Typography } from 'antd';
12
+ import type { TextAreaProps } from 'antd/es/input';
13
+ import JSON5 from 'json5';
14
+ import React, { useCallback, useEffect, useMemo, useState } from 'react';
15
+
16
+ export interface JsonTextAreaProps extends Omit<TextAreaProps, 'value' | 'onChange'> {
17
+ value?: unknown;
18
+ onChange?: (value: unknown) => void;
19
+ space?: number;
20
+ json5?: boolean;
21
+ showError?: boolean;
22
+ }
23
+
24
+ const jsonTextAreaClassName = css`
25
+ font-size: 80%;
26
+ font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
27
+ `;
28
+
29
+ function stringifyJsonValue(value: unknown, json: typeof JSON | typeof JSON5, space: number) {
30
+ if (value == null) {
31
+ return '';
32
+ }
33
+
34
+ if (typeof value === 'string') {
35
+ try {
36
+ json.parse(value);
37
+ return value;
38
+ } catch {
39
+ return json.stringify(value, undefined, space);
40
+ }
41
+ }
42
+
43
+ return json.stringify(value, undefined, space);
44
+ }
45
+
46
+ export const JsonTextArea = React.memo((props: JsonTextAreaProps) => {
47
+ const {
48
+ value,
49
+ onChange,
50
+ space = 2,
51
+ json5 = false,
52
+ showError = true,
53
+ className,
54
+ status,
55
+ onBlur,
56
+ ...textAreaProps
57
+ } = props;
58
+ const json = json5 ? JSON5 : JSON;
59
+ const [text, setText] = useState(() => stringifyJsonValue(value, json, space));
60
+ const [error, setError] = useState<string>();
61
+
62
+ useEffect(() => {
63
+ setText(stringifyJsonValue(value, json, space));
64
+ }, [json, space, value]);
65
+
66
+ const parseText = useCallback(
67
+ (nextText: string) => {
68
+ if (nextText.trim() === '') {
69
+ return null;
70
+ }
71
+
72
+ return json.parse(nextText);
73
+ },
74
+ [json],
75
+ );
76
+
77
+ const handleChange = useCallback(
78
+ (event: React.ChangeEvent<HTMLTextAreaElement>) => {
79
+ const nextText = event.target.value;
80
+ setText(nextText);
81
+
82
+ try {
83
+ parseText(nextText);
84
+ setError(undefined);
85
+ } catch (err) {
86
+ setError(err instanceof Error ? err.message : String(err));
87
+ }
88
+ },
89
+ [parseText],
90
+ );
91
+
92
+ const handleBlur = useCallback(
93
+ (event: React.FocusEvent<HTMLTextAreaElement>) => {
94
+ try {
95
+ const parsed = parseText(event.target.value);
96
+ setError(undefined);
97
+ setText(parsed == null ? '' : json.stringify(parsed, undefined, space));
98
+ onChange?.(parsed);
99
+ } catch (err) {
100
+ setError(err instanceof Error ? err.message : String(err));
101
+ }
102
+
103
+ onBlur?.(event);
104
+ },
105
+ [json, onBlur, onChange, parseText, space],
106
+ );
107
+
108
+ const mergedStatus = useMemo(() => status || (error ? 'error' : undefined), [error, status]);
109
+
110
+ return (
111
+ <>
112
+ <Input.TextArea
113
+ {...textAreaProps}
114
+ value={text}
115
+ onChange={handleChange}
116
+ onBlur={handleBlur}
117
+ status={mergedStatus}
118
+ className={cx(jsonTextAreaClassName, className)}
119
+ />
120
+ {showError && error ? (
121
+ <Typography.Text type="danger" style={{ display: 'block', marginTop: 4 }}>
122
+ {error}
123
+ </Typography.Text>
124
+ ) : null}
125
+ </>
126
+ );
127
+ });
128
+
129
+ JsonTextArea.displayName = 'JsonTextArea';
@@ -10,4 +10,5 @@
10
10
  export * from './AppComponents';
11
11
  export * from './BlankComponent';
12
12
  export * from './Icon';
13
+ export * from './form/JsonTextArea';
13
14
  export * from './RouterContextCleaner';