@nocobase/client-v2 2.1.0-alpha.26 → 2.1.0-alpha.27

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 (45) hide show
  1. package/es/BaseApplication.d.ts +1 -0
  2. package/es/flow/actions/dataScopeFilter.d.ts +9 -0
  3. package/es/flow/components/Grid/index.d.ts +5 -3
  4. package/es/flow/components/code-editor/types.d.ts +1 -0
  5. package/es/flow/internal/utils/rebuildFieldSubModel.d.ts +2 -1
  6. package/es/flow/models/base/GridModel.d.ts +19 -2
  7. package/es/flow/models/blocks/filter-form/FilterFormGridModel.d.ts +1 -0
  8. package/es/flow/models/fields/JSFieldModel.d.ts +5 -0
  9. package/es/index.mjs +83 -83
  10. package/lib/index.js +83 -83
  11. package/package.json +5 -5
  12. package/src/BaseApplication.tsx +4 -0
  13. package/src/flow/actions/__tests__/dataScopeFilter.test.ts +158 -0
  14. package/src/flow/actions/dataScope.tsx +6 -4
  15. package/src/flow/actions/dataScopeFilter.ts +70 -0
  16. package/src/flow/actions/setTargetDataScope.tsx +6 -5
  17. package/src/flow/components/Grid/index.tsx +66 -20
  18. package/src/flow/components/code-editor/__tests__/linter.test.ts +18 -0
  19. package/src/flow/components/code-editor/__tests__/runjsDiagnostics.test.ts +23 -0
  20. package/src/flow/components/code-editor/index.tsx +18 -17
  21. package/src/flow/components/code-editor/linter.ts +222 -158
  22. package/src/flow/components/code-editor/runjsDiagnostics.ts +161 -97
  23. package/src/flow/components/code-editor/types.ts +1 -0
  24. package/src/flow/internal/utils/__tests__/rebuildFieldSubModel.test.ts +77 -2
  25. package/src/flow/internal/utils/rebuildFieldSubModel.ts +21 -5
  26. package/src/flow/models/base/BlockGridModel.tsx +2 -2
  27. package/src/flow/models/base/GridModel.tsx +428 -195
  28. package/src/flow/models/base/__tests__/BlockGridModel.dragOverlayConfig.test.ts +44 -0
  29. package/src/flow/models/base/__tests__/GridModel.computeOverlayRect.test.ts +29 -0
  30. package/src/flow/models/base/__tests__/GridModel.dragSnapshotContainer.test.ts +181 -2
  31. package/src/flow/models/base/__tests__/GridModel.resizeLayout.test.ts +124 -0
  32. package/src/flow/models/base/__tests__/GridModel.visibleLayout.test.ts +55 -15
  33. package/src/flow/models/blocks/details/DetailsGridModel.tsx +6 -6
  34. package/src/flow/models/blocks/filter-form/FilterFormBlockModel.tsx +9 -5
  35. package/src/flow/models/blocks/filter-form/FilterFormGridModel.tsx +54 -14
  36. package/src/flow/models/blocks/filter-form/__tests__/FilterFormBlockModel.cleanup.test.ts +138 -0
  37. package/src/flow/models/blocks/filter-form/__tests__/FilterFormGridModel.toggleFormFieldsCollapse.test.ts +45 -0
  38. package/src/flow/models/blocks/form/FormGridModel.tsx +6 -6
  39. package/src/flow/models/blocks/form/__tests__/FormBlockModel.test.tsx +22 -0
  40. package/src/flow/models/blocks/table/JSColumnModel.tsx +30 -2
  41. package/src/flow/models/blocks/table/TableBlockModel.tsx +8 -1
  42. package/src/flow/models/blocks/table/TableColumnModel.tsx +1 -0
  43. package/src/flow/models/blocks/table/__tests__/JSColumnModel.test.tsx +51 -0
  44. package/src/flow/models/blocks/table/__tests__/TableBlockModel.quickEditRefresh.test.ts +49 -0
  45. package/src/flow/models/fields/JSFieldModel.tsx +54 -14
@@ -8,7 +8,7 @@
8
8
  */
9
9
 
10
10
  import { ElementProxy, tExpr, createSafeWindow, createSafeDocument, createSafeNavigator } from '@nocobase/flow-engine';
11
- import React, { useEffect, useRef } from 'react';
11
+ import React, { useEffect } from 'react';
12
12
  import { FieldModel } from '../base/FieldModel';
13
13
  import { resolveRunJsParams } from '../utils/resolveRunJsParams';
14
14
  import { CodeEditor } from '../../components/code-editor';
@@ -41,6 +41,10 @@ ctx.render(<JsReadonlyField />);
41
41
  */
42
42
  export class JSFieldModel extends FieldModel {
43
43
  private _mountedOnce = false; // prevent first-mount double-run
44
+ private _lastRenderedElement?: HTMLSpanElement | null;
45
+ private _pendingRenderedElement?: HTMLSpanElement | null;
46
+ private _lastRunJs?: { code: string; value: any; element: HTMLSpanElement | null };
47
+
44
48
  getInputArgs() {
45
49
  const field = this.context.collectionField;
46
50
  if (field?.isAssociationField?.()) {
@@ -75,19 +79,10 @@ export class JSFieldModel extends FieldModel {
75
79
  * 说明:fork 实例在表格逐行渲染时会复用该逻辑,确保按值更新。
76
80
  */
77
81
  useHooksBeforeRender() {
78
- // 单一副作用:当 code 或 value 变化,且二者都已就绪时执行一次
79
- // 通过记忆上次运行的输入,避免相同输入导致的重复执行
80
82
  const codeParam = this.getStepParams('jsSettings', 'runJs')?.code as string | undefined;
81
83
  // eslint-disable-next-line react-hooks/rules-of-hooks
82
- const lastRunRef = useRef<{ code: string; value: any } | null>(null);
83
- // eslint-disable-next-line react-hooks/rules-of-hooks
84
84
  useEffect(() => {
85
- const valueNow = this.props.value;
86
- const codeNow = (typeof codeParam === 'string' && codeParam.trim().length ? codeParam : DEFAULT_CODE).trim();
87
- const last = lastRunRef.current;
88
- if (last && last.code === codeNow && last.value === valueNow) return;
89
- lastRunRef.current = { code: codeNow, value: valueNow };
90
- this.applyFlow('jsSettings');
85
+ this.refreshRenderedElement(this.context.ref?.current as HTMLSpanElement | null);
91
86
  // eslint-disable-next-line react-hooks/exhaustive-deps
92
87
  }, [codeParam, this.props.value]);
93
88
  }
@@ -95,8 +90,52 @@ export class JSFieldModel extends FieldModel {
95
90
  /**
96
91
  * 渲染一个占位容器,供 JS 脚本写入内容
97
92
  */
93
+ private getRunJsCode() {
94
+ const codeParam = this.getStepParams('jsSettings', 'runJs')?.code as string | undefined;
95
+ return (typeof codeParam === 'string' && codeParam.trim().length ? codeParam : DEFAULT_CODE).trim();
96
+ }
97
+
98
+ private refreshRenderedElement(element: HTMLSpanElement | null) {
99
+ if (!element || this._pendingRenderedElement === element) {
100
+ return;
101
+ }
102
+
103
+ this._pendingRenderedElement = element;
104
+ const ref = this.context.ref as React.MutableRefObject<HTMLSpanElement | null>;
105
+
106
+ queueMicrotask(() => {
107
+ if (this._pendingRenderedElement === element) {
108
+ this._pendingRenderedElement = null;
109
+ }
110
+ if (ref.current !== element) {
111
+ return;
112
+ }
113
+ const code = this.getRunJsCode();
114
+ const value = this.props.value;
115
+ const last = this._lastRunJs;
116
+ if (last && last.element === element && last.code === code && Object.is(last.value, value)) {
117
+ return;
118
+ }
119
+ this._lastRunJs = { code, value, element };
120
+ void this.applyFlow('jsSettings');
121
+ });
122
+ }
123
+
98
124
  render() {
99
- return <span ref={this.context.ref} style={{ display: 'inline-block', maxWidth: '100%' }} />;
125
+ const ref = this.context.ref as React.MutableRefObject<HTMLSpanElement | null>;
126
+ const assignRef = (element: HTMLSpanElement | null) => {
127
+ ref.current = element;
128
+ if (!element) {
129
+ return;
130
+ }
131
+
132
+ const elementChanged = this._lastRenderedElement && this._lastRenderedElement !== element;
133
+ this._lastRenderedElement = element;
134
+ if (elementChanged || !this._mountedOnce) {
135
+ this.refreshRenderedElement(element);
136
+ }
137
+ };
138
+ return <span ref={assignRef} style={{ display: 'inline-block', maxWidth: '100%' }} />;
100
139
  }
101
140
 
102
141
  /**
@@ -106,7 +145,7 @@ export class JSFieldModel extends FieldModel {
106
145
  protected onMount() {
107
146
  if (this._mountedOnce) {
108
147
  if (this.context.ref?.current) {
109
- this.rerender();
148
+ this.refreshRenderedElement(this.context.ref.current as HTMLSpanElement);
110
149
  }
111
150
  }
112
151
  this._mountedOnce = true;
@@ -165,7 +204,8 @@ JSFieldModel.registerFlow({
165
204
  // 暴露 element 与 value 到运行上下文
166
205
  ctx.onRefReady(ctx.ref, async (element) => {
167
206
  ctx.defineProperty('element', {
168
- get: () => new ElementProxy(element),
207
+ get: () => new ElementProxy((ctx.ref?.current as HTMLElement | null) || element),
208
+ cache: false,
169
209
  });
170
210
  ctx.defineProperty('value', {
171
211
  get: () => ctx.model.props?.value,