@nocobase/client-v2 2.2.0-beta.13 → 2.2.0-beta.15

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 (91) hide show
  1. package/es/APIClient.d.ts +1 -3
  2. package/es/components/category-tabs/SortableCategoryTabs.d.ts +55 -0
  3. package/es/components/category-tabs/index.d.ts +9 -0
  4. package/es/components/form/ScanInput/useCodeScanner.d.ts +2 -1
  5. package/es/components/form/TypedVariableInput.d.ts +31 -5
  6. package/es/components/form/filter/CollectionFilter.d.ts +2 -0
  7. package/es/components/form/filter/CollectionFilterItem.d.ts +11 -1
  8. package/es/components/form/filter/CollectionFilterPanel.d.ts +2 -0
  9. package/es/components/form/filter/index.d.ts +2 -0
  10. package/es/components/form/filter/useFilterActionProps.d.ts +2 -0
  11. package/es/components/index.d.ts +1 -0
  12. package/es/flow/actions/index.d.ts +1 -1
  13. package/es/flow/actions/linkageRules.d.ts +2 -0
  14. package/es/flow/admin-shell/admin-layout/AdminLayoutMenuModels.d.ts +1 -0
  15. package/es/flow/components/FieldAssignExactDatePicker.d.ts +1 -0
  16. package/es/flow/components/FieldAssignValueInput.d.ts +9 -0
  17. package/es/flow/components/RunJSValueEditor.d.ts +1 -0
  18. package/es/flow/components/filter/FilterGroup.d.ts +1 -0
  19. package/es/flow/components/filter/VariableFilterItem.d.ts +6 -1
  20. package/es/flow/index.d.ts +1 -0
  21. package/es/flow/models/base/PageModel/PageModel.d.ts +12 -0
  22. package/es/flow/models/base/PageModel/PageModelTabBar.d.ts +22 -0
  23. package/es/flow/models/base/PageModel/PageTabModel.d.ts +12 -0
  24. package/es/flow-compat/fieldValidationConstants.d.ts +1 -1
  25. package/es/flow-compat/routeTypes.d.ts +1 -0
  26. package/es/index.d.ts +4 -0
  27. package/es/index.mjs +144 -109
  28. package/es/utils/getRouteRuntimeVersion.d.ts +23 -0
  29. package/es/utils/index.d.ts +1 -0
  30. package/lib/index.js +153 -118
  31. package/lib/locale/languageCodes.js +2 -1
  32. package/package.json +7 -7
  33. package/src/APIClient.ts +1 -10
  34. package/src/Application.tsx +4 -0
  35. package/src/BaseApplication.tsx +1 -1
  36. package/src/__tests__/app.test.tsx +18 -0
  37. package/src/__tests__/browserChecker.test.ts +61 -0
  38. package/src/__tests__/getRouteRuntimeVersion.test.ts +68 -0
  39. package/src/__tests__/nocobase-buildin-plugin-auth.test.tsx +28 -0
  40. package/src/collection-manager/field-validation.ts +1 -1
  41. package/src/components/README.md +7 -1
  42. package/src/components/README.zh-CN.md +6 -1
  43. package/src/components/category-tabs/SortableCategoryTabs.tsx +210 -0
  44. package/src/components/category-tabs/index.ts +10 -0
  45. package/src/components/form/JsonTextArea.tsx +4 -0
  46. package/src/components/form/ScanInput/CodeScanner.tsx +23 -6
  47. package/src/components/form/ScanInput/__tests__/useCodeScanner.test.tsx +35 -1
  48. package/src/components/form/ScanInput/useCodeScanner.ts +16 -3
  49. package/src/components/form/TypedVariableInput.tsx +452 -101
  50. package/src/components/form/__tests__/TypedVariableInput.test.tsx +364 -12
  51. package/src/components/form/filter/CollectionFilter.tsx +4 -0
  52. package/src/components/form/filter/CollectionFilterItem.tsx +32 -7
  53. package/src/components/form/filter/CollectionFilterPanel.tsx +4 -0
  54. package/src/components/form/filter/__tests__/CollectionFilterItem.test.tsx +38 -0
  55. package/src/components/form/filter/__tests__/useFilterActionProps.test.tsx +95 -0
  56. package/src/components/form/filter/index.ts +2 -0
  57. package/src/components/form/filter/useFilterActionProps.ts +37 -6
  58. package/src/components/index.ts +1 -0
  59. package/src/flow/actions/__tests__/linkageRules.tab.test.ts +171 -0
  60. package/src/flow/actions/index.ts +2 -0
  61. package/src/flow/actions/linkageRules.tsx +86 -11
  62. package/src/flow/admin-shell/admin-layout/AdminLayoutComponent.tsx +0 -1
  63. package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutComponent.test.tsx +253 -6
  64. package/src/flow/components/FieldAssignExactDatePicker.tsx +25 -11
  65. package/src/flow/components/FieldAssignValueInput.tsx +76 -9
  66. package/src/flow/components/RunJSValueEditor.tsx +9 -1
  67. package/src/flow/components/__tests__/FieldAssignValueInput.context.test.tsx +84 -2
  68. package/src/flow/components/filter/FilterGroup.tsx +33 -5
  69. package/src/flow/components/filter/VariableFilterItem.tsx +158 -10
  70. package/src/flow/components/filter/__tests__/FilterGroup.test.tsx +45 -0
  71. package/src/flow/components/filter/__tests__/VariableFilterItem.leftMetaTree.test.tsx +9 -0
  72. package/src/flow/components/filter/__tests__/VariableFilterItem.test.tsx +70 -1
  73. package/src/flow/index.ts +1 -0
  74. package/src/flow/models/base/PageModel/PageModel.tsx +387 -53
  75. package/src/flow/models/base/PageModel/PageModelTabBar.tsx +114 -0
  76. package/src/flow/models/base/PageModel/PageTabModel.tsx +184 -3
  77. package/src/flow/models/base/PageModel/__tests__/PageModel.test.ts +790 -10
  78. package/src/flow/models/base/PageModel/__tests__/PageModelTabBar.module-isolation.test.tsx +130 -0
  79. package/src/flow/models/base/PageModel/__tests__/PageModelTabBar.test.tsx +118 -0
  80. package/src/flow/models/base/PageModel/__tests__/PageTabModel.test.ts +572 -1
  81. package/src/flow/models/blocks/table/TableBlockModel.tsx +1 -1
  82. package/src/flow/models/fields/AssociationFieldModel/PopupSubTableFieldModel/PopupSubTableFieldModel.tsx +1 -1
  83. package/src/flow/models/fields/VariableFieldFormModel.tsx +3 -3
  84. package/src/flow/models/fields/__tests__/VariableFieldFormModel.test.tsx +51 -0
  85. package/src/flow-compat/fieldValidationConstants.ts +1 -1
  86. package/src/flow-compat/routeTypes.ts +1 -0
  87. package/src/index.ts +4 -0
  88. package/src/locale/languageCodes.ts +2 -1
  89. package/src/nocobase-buildin-plugin/index.tsx +12 -0
  90. package/src/utils/getRouteRuntimeVersion.ts +185 -0
  91. package/src/utils/index.tsx +1 -0
@@ -49,6 +49,21 @@ function CodeScannerContent({ visible, formatsToSupport, onClose, onScanSuccess
49
49
  message.error(t('Code recognition failed, please scan again'));
50
50
  }, [t]);
51
51
 
52
+ const showCameraStartFailure = useCallback(
53
+ (error: unknown) => {
54
+ const errorName = error instanceof Error ? error.name : '';
55
+ const errorMessage = error instanceof Error ? error.message : '';
56
+ const errorMap: Record<string, string> = {
57
+ NotFoundError: t('No camera device detected'),
58
+ NotAllowedError: t('You have not granted permission to use the camera'),
59
+ };
60
+
61
+ message.error(errorMap[errorName] || errorMessage || t('You have not granted permission to use the camera'));
62
+ onClose();
63
+ },
64
+ [onClose, t],
65
+ );
66
+
52
67
  const handleScanSuccess = useCallback(
53
68
  (text: string) => {
54
69
  onScanSuccess(text);
@@ -64,6 +79,7 @@ function CodeScannerContent({ visible, formatsToSupport, onClose, onScanSuccess
64
79
  scanBoxSize,
65
80
  onScanSuccess: handleScanSuccess,
66
81
  onScanFailure: showScanFailure,
82
+ onCameraStartFailure: showCameraStartFailure,
67
83
  });
68
84
 
69
85
  useEffect(() => {
@@ -129,7 +145,7 @@ function CodeScannerContent({ visible, formatsToSupport, onClose, onScanSuccess
129
145
  inset: 0;
130
146
  z-index: ${token.zIndexPopupBase + 1000};
131
147
  overflow: hidden;
132
- background: ${token.colorBgMask};
148
+ background: #000;
133
149
  `;
134
150
  const scannerWrapperClass = css`
135
151
  position: absolute;
@@ -137,16 +153,17 @@ function CodeScannerContent({ visible, formatsToSupport, onClose, onScanSuccess
137
153
  overflow: hidden;
138
154
  `;
139
155
  const scannerClass = css`
156
+ position: relative;
140
157
  width: 100%;
141
158
  height: 100%;
142
- display: flex;
143
- justify-content: center;
144
- align-items: center;
159
+ overflow: hidden;
145
160
 
146
161
  video {
147
- width: auto !important;
162
+ position: absolute !important;
163
+ inset: 0 !important;
164
+ width: 100% !important;
148
165
  height: 100% !important;
149
- max-width: none !important;
166
+ object-fit: cover !important;
150
167
  }
151
168
 
152
169
  #qr-shaded-region {
@@ -74,13 +74,23 @@ vi.mock('html5-qrcode', () => ({
74
74
 
75
75
  vi.mock('jsqr', () => jsQrMocks);
76
76
 
77
- function ScannerHost({ scanBoxSize }: { scanBoxSize?: { width: number; height: number } } = {}) {
77
+ function ScannerHost({
78
+ onCameraStartFailure,
79
+ onScanFailure,
80
+ scanBoxSize,
81
+ }: {
82
+ onCameraStartFailure?: (error: unknown) => void;
83
+ onScanFailure?: () => void;
84
+ scanBoxSize?: { width: number; height: number };
85
+ } = {}) {
78
86
  const handleScanSuccess = useCallback(() => undefined, []);
79
87
 
80
88
  useCodeScanner({
81
89
  elementId: 'scanner',
82
90
  enabled: true,
83
91
  scanBoxSize,
92
+ onCameraStartFailure,
93
+ onScanFailure,
84
94
  onScanSuccess: handleScanSuccess,
85
95
  });
86
96
 
@@ -194,6 +204,30 @@ describe('useCodeScanner', () => {
194
204
  expect(config?.qrbox?.(1200, 844)).toEqual({ width: 319, height: 240 });
195
205
  });
196
206
 
207
+ it('clamps the scan box to the camera viewfinder size', async () => {
208
+ render(<ScannerHost scanBoxSize={{ width: 319, height: 240 }} />);
209
+
210
+ await waitFor(() => expect(mocks.start).toHaveBeenCalled());
211
+
212
+ const config = mocks.start.mock.calls[0]?.[1] as
213
+ | { qrbox?: (width: number, height: number) => { width: number; height: number } }
214
+ | undefined;
215
+
216
+ expect(config?.qrbox?.(200, 160)).toEqual({ width: 200, height: 160 });
217
+ });
218
+
219
+ it('reports camera start failures through the dedicated handler', async () => {
220
+ const cameraStartError = Object.assign(new Error('Camera failed'), { name: 'NotAllowedError' });
221
+ const handleCameraStartFailure = vi.fn();
222
+ const handleScanFailure = vi.fn();
223
+ mocks.start.mockRejectedValueOnce(cameraStartError);
224
+
225
+ render(<ScannerHost onCameraStartFailure={handleCameraStartFailure} onScanFailure={handleScanFailure} />);
226
+
227
+ await waitFor(() => expect(handleCameraStartFailure).toHaveBeenCalledWith(cameraStartError));
228
+ expect(handleScanFailure).not.toHaveBeenCalled();
229
+ });
230
+
197
231
  it('uses jsQR first for Safari uploaded QR images', async () => {
198
232
  const handleScanSuccess = vi.fn();
199
233
  jsQrMocks.default.mockReturnValueOnce({ data: 'JSQR-CODE' });
@@ -25,6 +25,7 @@ type UseCodeScannerOptions = {
25
25
  onScannerSizeChanged?: (size: ScannerSize) => void;
26
26
  onScanSuccess: (text: string) => void;
27
27
  onScanFailure?: () => void;
28
+ onCameraStartFailure?: (error: unknown) => void;
28
29
  };
29
30
 
30
31
  type ImageDataVariant = {
@@ -70,6 +71,13 @@ export function getCodeScanBoxSize(width: number, height: number) {
70
71
  };
71
72
  }
72
73
 
74
+ function clampCodeScanBoxSize(scanBoxSize: ScannerSize, width: number, height: number) {
75
+ return {
76
+ width: Math.min(scanBoxSize.width, width),
77
+ height: Math.min(scanBoxSize.height, height),
78
+ };
79
+ }
80
+
73
81
  async function stopScanner(scanner?: Html5Qrcode, options: { clear?: boolean } = {}) {
74
82
  if (!scanner) {
75
83
  return;
@@ -212,6 +220,7 @@ export function useCodeScanner({
212
220
  onScannerSizeChanged,
213
221
  onScanSuccess,
214
222
  onScanFailure,
223
+ onCameraStartFailure,
215
224
  }: UseCodeScannerOptions) {
216
225
  const [scanner, setScanner] = useState<Html5Qrcode>();
217
226
 
@@ -223,7 +232,7 @@ export function useCodeScanner({
223
232
  fps: 10,
224
233
  qrbox(width, height) {
225
234
  onScannerSizeChanged?.({ width, height });
226
- return scanBoxSize ?? getCodeScanBoxSize(width, height);
235
+ return clampCodeScanBoxSize(scanBoxSize ?? getCodeScanBoxSize(width, height), width, height);
227
236
  },
228
237
  },
229
238
  (decodedText) => {
@@ -273,14 +282,18 @@ export function useCodeScanner({
273
282
  verbose: false,
274
283
  });
275
284
  setScanner(scannerInstance);
276
- startScanCamera(scannerInstance).catch(() => {
285
+ startScanCamera(scannerInstance).catch((error: unknown) => {
286
+ if (onCameraStartFailure) {
287
+ onCameraStartFailure(error);
288
+ return;
289
+ }
277
290
  onScanFailure?.();
278
291
  });
279
292
 
280
293
  return () => {
281
294
  stopScanner(scannerInstance, { clear: true }).catch(() => undefined);
282
295
  };
283
- }, [elementId, enabled, formatsToSupport, onScanFailure, startScanCamera]);
296
+ }, [elementId, enabled, formatsToSupport, onCameraStartFailure, onScanFailure, startScanCamera]);
284
297
 
285
298
  return {
286
299
  startScanFile,