@flowgram.ai/form-materials 0.5.6 → 1.0.0

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 (29) hide show
  1. package/dist/cjs/components/blur-input/index.js +4 -1
  2. package/dist/cjs/components/condition-context/hooks/use-condition.js +17 -1
  3. package/dist/cjs/components/condition-row/index.js +13 -1
  4. package/dist/cjs/components/coze-editor-extensions/extensions/inputs-tree.js +2 -2
  5. package/dist/cjs/components/coze-editor-extensions/extensions/variable-tag.js +16 -5
  6. package/dist/cjs/components/db-condition-row/index.js +13 -1
  7. package/dist/cjs/components/display-outputs/index.js +2 -2
  8. package/dist/cjs/effects/validate-when-variable-sync/index.js +3 -3
  9. package/dist/cjs/shared/inject-material/index.js +1 -1
  10. package/dist/esm/components/blur-input/index.mjs +4 -1
  11. package/dist/esm/components/condition-context/hooks/use-condition.mjs +18 -2
  12. package/dist/esm/components/condition-row/index.mjs +13 -1
  13. package/dist/esm/components/coze-editor-extensions/extensions/inputs-tree.mjs +3 -3
  14. package/dist/esm/components/coze-editor-extensions/extensions/variable-tag.mjs +16 -5
  15. package/dist/esm/components/db-condition-row/index.mjs +13 -1
  16. package/dist/esm/components/display-outputs/index.mjs +2 -2
  17. package/dist/esm/effects/validate-when-variable-sync/index.mjs +3 -3
  18. package/dist/esm/shared/inject-material/index.mjs +1 -1
  19. package/dist/types/components/condition-context/hooks/use-condition.d.ts +16 -1
  20. package/package.json +6 -6
  21. package/src/components/blur-input/index.tsx +4 -1
  22. package/src/components/condition-context/hooks/use-condition.tsx +51 -7
  23. package/src/components/condition-row/index.tsx +12 -0
  24. package/src/components/coze-editor-extensions/extensions/inputs-tree.tsx +3 -3
  25. package/src/components/coze-editor-extensions/extensions/variable-tag.tsx +17 -9
  26. package/src/components/db-condition-row/index.tsx +12 -0
  27. package/src/components/display-outputs/index.tsx +2 -2
  28. package/src/effects/validate-when-variable-sync/index.ts +7 -3
  29. package/src/shared/inject-material/index.tsx +1 -1
@@ -43,7 +43,10 @@ function BlurInput(props) {
43
43
  onChange: (value)=>{
44
44
  setValue(value);
45
45
  },
46
- onBlur: (e)=>props.onChange?.(value, e)
46
+ onBlur: (e)=>{
47
+ props.onChange?.(value, e);
48
+ props.onBlur?.(e);
49
+ }
47
50
  });
48
51
  }
49
52
  exports.BlurInput = __webpack_exports__.BlurInput;
@@ -30,7 +30,7 @@ const external_react_namespaceObject = require("react");
30
30
  const editor_namespaceObject = require("@flowgram.ai/editor");
31
31
  const index_js_namespaceObject = require("../../../plugins/index.js");
32
32
  const external_context_js_namespaceObject = require("../context.js");
33
- function useCondition({ leftSchema, operator, ruleConfig }) {
33
+ function useCondition({ leftSchema, operator, onClearOp, onClearRight, ruleConfig }) {
34
34
  const typeManager = (0, index_js_namespaceObject.useTypeManager)();
35
35
  const { rules: contextRules, ops: contextOps } = (0, external_context_js_namespaceObject.useConditionContext)();
36
36
  const userRules = (0, external_react_namespaceObject.useMemo)(()=>ruleConfig?.rules || contextRules || {}, [
@@ -63,6 +63,14 @@ function useCondition({ leftSchema, operator, ruleConfig }) {
63
63
  rule,
64
64
  allOps
65
65
  ]);
66
+ (0, external_react_namespaceObject.useEffect)(()=>{
67
+ if (!operator || !rule) return;
68
+ if (!opOptionList.find((item)=>item.value === operator)) onClearOp?.();
69
+ }, [
70
+ operator,
71
+ opOptionList,
72
+ onClearOp
73
+ ]);
66
74
  const targetSchema = (0, external_react_namespaceObject.useMemo)(()=>{
67
75
  const targetType = rule?.[operator || ''] || null;
68
76
  if (!targetType) return;
@@ -77,6 +85,14 @@ function useCondition({ leftSchema, operator, ruleConfig }) {
77
85
  rule,
78
86
  operator
79
87
  ]);
88
+ const prevTargetSchemaRef = (0, external_react_namespaceObject.useRef)(void 0);
89
+ (0, external_react_namespaceObject.useEffect)(()=>{
90
+ if (prevTargetSchemaRef.current?.type !== targetSchema?.type) onClearRight?.();
91
+ prevTargetSchemaRef.current = targetSchema;
92
+ }, [
93
+ targetSchema,
94
+ onClearRight
95
+ ]);
80
96
  const opConfig = (0, external_react_namespaceObject.useMemo)(()=>allOps[operator || ''], [
81
97
  operator,
82
98
  allOps
@@ -57,7 +57,19 @@ function ConditionRow({ style, value, onChange, readonly, ruleConfig }) {
57
57
  const { rule, opConfig, opOptionList, targetSchema } = (0, external_condition_context_index_js_namespaceObject.useCondition)({
58
58
  leftSchema,
59
59
  operator,
60
- ruleConfig
60
+ ruleConfig,
61
+ onClearOp () {
62
+ onChange({
63
+ ...value,
64
+ operator: void 0
65
+ });
66
+ },
67
+ onClearRight () {
68
+ onChange({
69
+ ...value,
70
+ right: void 0
71
+ });
72
+ }
61
73
  });
62
74
  const renderOpSelect = ()=>/*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(semi_ui_namespaceObject.Select, {
63
75
  style: {
@@ -35,7 +35,7 @@ const react_namespaceObject = require("@flowgram.ai/coze-editor/react");
35
35
  const semi_ui_namespaceObject = require("@douyinfe/semi-ui");
36
36
  const index_js_namespaceObject = require("../../../shared/index.js");
37
37
  function InputsPicker({ inputsValues, onSelect }) {
38
- const available = (0, editor_namespaceObject.useScopeAvailable)();
38
+ const scope = (0, editor_namespaceObject.useCurrentScope)();
39
39
  const getArrayDrilldown = (type, depth = 1)=>{
40
40
  if (editor_namespaceObject.ASTMatch.isArray(type.items)) return getArrayDrilldown(type.items, depth + 1);
41
41
  return {
@@ -70,7 +70,7 @@ function InputsPicker({ inputsValues, onSelect }) {
70
70
  const currKey = keyPath.join('.');
71
71
  if (index_js_namespaceObject.FlowValueUtils.isFlowValue(value)) {
72
72
  if (index_js_namespaceObject.FlowValueUtils.isRef(value)) {
73
- const variable = available.getByKeyPath(value.content || []);
73
+ const variable = scope?.available?.getByKeyPath(value.content || []);
74
74
  if (variable) return renderVariable(variable, keyPath);
75
75
  }
76
76
  return {
@@ -109,12 +109,21 @@ class VariableTagWidget extends view_namespaceObject.WidgetType {
109
109
  this.toDispose.push(editor_namespaceObject.Disposable.create(()=>{
110
110
  this.root.unmount();
111
111
  }));
112
- this.toDispose.push(this.scope.available.trackByKeyPath(this.keyPath, (v)=>{
113
- this.renderVariable(v);
114
- }, {
112
+ const refresh = ()=>{
113
+ this.renderVariable(this.scope.available.getByKeyPath(this.keyPath));
114
+ };
115
+ this.toDispose.push(this.scope.available.trackByKeyPath(this.keyPath, refresh, {
116
+ triggerOnInit: false
117
+ }));
118
+ if (this.keyPath?.[0]) this.toDispose.push(this.scope.available.trackByKeyPath([
119
+ this.keyPath[0]
120
+ ], refresh, {
121
+ selector: (curr)=>({
122
+ ...curr?.meta
123
+ }),
115
124
  triggerOnInit: false
116
125
  }));
117
- this.renderVariable(this.scope.available.getByKeyPath(this.keyPath));
126
+ refresh();
118
127
  return dom;
119
128
  }
120
129
  eq(other) {
@@ -129,7 +138,9 @@ class VariableTagWidget extends view_namespaceObject.WidgetType {
129
138
  }
130
139
  function VariableTagInject() {
131
140
  const injector = (0, react_namespaceObject.useInjector)();
132
- const scope = (0, editor_namespaceObject.useCurrentScope)();
141
+ const scope = (0, editor_namespaceObject.useCurrentScope)({
142
+ strict: true
143
+ });
133
144
  (0, external_react_namespaceObject.useLayoutEffect)(()=>{
134
145
  const atMatcher = new view_namespaceObject.MatchDecorator({
135
146
  regexp: /\{\{([^\}\{]+)\}\}/g,
@@ -45,7 +45,19 @@ function DBConditionRow({ style, value, onChange, readonly, options, ruleConfig
45
45
  const { opConfig, rule, opOptionList, targetSchema } = (0, external_condition_context_index_js_namespaceObject.useCondition)({
46
46
  leftSchema,
47
47
  operator,
48
- ruleConfig
48
+ ruleConfig,
49
+ onClearOp () {
50
+ onChange({
51
+ ...value,
52
+ operator: void 0
53
+ });
54
+ },
55
+ onClearRight () {
56
+ onChange({
57
+ ...value,
58
+ right: void 0
59
+ });
60
+ }
49
61
  });
50
62
  const renderDBOptionSelect = ()=>/*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(semi_ui_namespaceObject.Select, {
51
63
  className: "gedit-m-db-condition-row-select",
@@ -36,7 +36,7 @@ function DisplayOutputs({ value, showIconInTree, displayFromScope }) {
36
36
  const scope = (0, editor_namespaceObject.useCurrentScope)();
37
37
  const refresh = (0, editor_namespaceObject.useRefresh)();
38
38
  (0, external_react_namespaceObject.useEffect)(()=>{
39
- if (!displayFromScope) return ()=>null;
39
+ if (!displayFromScope || !scope) return ()=>null;
40
40
  const disposable = scope.output.onListOrAnyVarChange(()=>{
41
41
  refresh();
42
42
  });
@@ -46,7 +46,7 @@ function DisplayOutputs({ value, showIconInTree, displayFromScope }) {
46
46
  }, [
47
47
  displayFromScope
48
48
  ]);
49
- const properties = displayFromScope ? scope.output.variables?.reduce((acm, curr)=>{
49
+ const properties = displayFromScope ? (scope?.output.variables || []).reduce((acm, curr)=>{
50
50
  acm = {
51
51
  ...acm,
52
52
  ...json_schema_namespaceObject.JsonSchemaUtils.astToSchema(curr.type)?.properties || {}
@@ -26,15 +26,15 @@ __webpack_require__.r(__webpack_exports__);
26
26
  __webpack_require__.d(__webpack_exports__, {
27
27
  validateWhenVariableSync: ()=>validateWhenVariableSync
28
28
  });
29
- const external_lodash_es_namespaceObject = require("lodash-es");
30
29
  const editor_namespaceObject = require("@flowgram.ai/editor");
31
30
  const validateWhenVariableSync = ({ scope } = {})=>[
32
31
  {
33
32
  event: editor_namespaceObject.DataEvent.onValueInit,
34
- effect: ({ context, form })=>{
33
+ effect: ({ context, form, name })=>{
35
34
  const nodeScope = 'private' === scope ? (0, editor_namespaceObject.getNodePrivateScope)(context.node) : (0, editor_namespaceObject.getNodeScope)(context.node);
36
35
  const disposable = nodeScope.available.onListOrAnyVarChange(()=>{
37
- if (!(0, external_lodash_es_namespaceObject.isEmpty)(form.state.errors)) form.validate();
36
+ const errorKeys = Object.entries(form.state.errors || {}).filter(([_, errors])=>errors?.length > 0).filter(([key])=>key.startsWith(name) || name.startsWith(key)).map(([key])=>key);
37
+ if (errorKeys.length > 0) form.validate();
38
38
  });
39
39
  return ()=>disposable.dispose();
40
40
  }
@@ -42,7 +42,7 @@ function createInjectMaterial(Component, params) {
42
42
  const renderKey = params?.renderKey || Component.renderKey || Component.name || '';
43
43
  const InjectComponent = (props)=>{
44
44
  const container = (0, editor_namespaceObject.usePlaygroundContainer)();
45
- if (!container?.isBound(editor_namespaceObject.FlowRendererRegistry)) return /*#__PURE__*/ external_react_default().createElement(Component, {
45
+ if (!container?.isBound?.(editor_namespaceObject.FlowRendererRegistry)) return /*#__PURE__*/ external_react_default().createElement(Component, {
46
46
  ...props
47
47
  });
48
48
  const rendererRegistry = container.get(editor_namespaceObject.FlowRendererRegistry);
@@ -15,7 +15,10 @@ function BlurInput(props) {
15
15
  onChange: (value)=>{
16
16
  setValue(value);
17
17
  },
18
- onBlur: (e)=>props.onChange?.(value, e)
18
+ onBlur: (e)=>{
19
+ props.onChange?.(value, e);
20
+ props.onBlur?.(e);
21
+ }
19
22
  });
20
23
  }
21
24
  export { BlurInput };
@@ -1,8 +1,8 @@
1
- import { useMemo } from "react";
1
+ import { useEffect, useMemo, useRef } from "react";
2
2
  import { I18n } from "@flowgram.ai/editor";
3
3
  import { useTypeManager } from "../../../plugins/index.mjs";
4
4
  import { useConditionContext } from "../context.mjs";
5
- function useCondition({ leftSchema, operator, ruleConfig }) {
5
+ function useCondition({ leftSchema, operator, onClearOp, onClearRight, ruleConfig }) {
6
6
  const typeManager = useTypeManager();
7
7
  const { rules: contextRules, ops: contextOps } = useConditionContext();
8
8
  const userRules = useMemo(()=>ruleConfig?.rules || contextRules || {}, [
@@ -35,6 +35,14 @@ function useCondition({ leftSchema, operator, ruleConfig }) {
35
35
  rule,
36
36
  allOps
37
37
  ]);
38
+ useEffect(()=>{
39
+ if (!operator || !rule) return;
40
+ if (!opOptionList.find((item)=>item.value === operator)) onClearOp?.();
41
+ }, [
42
+ operator,
43
+ opOptionList,
44
+ onClearOp
45
+ ]);
38
46
  const targetSchema = useMemo(()=>{
39
47
  const targetType = rule?.[operator || ''] || null;
40
48
  if (!targetType) return;
@@ -49,6 +57,14 @@ function useCondition({ leftSchema, operator, ruleConfig }) {
49
57
  rule,
50
58
  operator
51
59
  ]);
60
+ const prevTargetSchemaRef = useRef(void 0);
61
+ useEffect(()=>{
62
+ if (prevTargetSchemaRef.current?.type !== targetSchema?.type) onClearRight?.();
63
+ prevTargetSchemaRef.current = targetSchema;
64
+ }, [
65
+ targetSchema,
66
+ onClearRight
67
+ ]);
52
68
  const opConfig = useMemo(()=>allOps[operator || ''], [
53
69
  operator,
54
70
  allOps
@@ -29,7 +29,19 @@ function ConditionRow({ style, value, onChange, readonly, ruleConfig }) {
29
29
  const { rule, opConfig, opOptionList, targetSchema } = useCondition({
30
30
  leftSchema,
31
31
  operator,
32
- ruleConfig
32
+ ruleConfig,
33
+ onClearOp () {
34
+ onChange({
35
+ ...value,
36
+ operator: void 0
37
+ });
38
+ },
39
+ onClearRight () {
40
+ onChange({
41
+ ...value,
42
+ right: void 0
43
+ });
44
+ }
33
45
  });
34
46
  const renderOpSelect = ()=>/*#__PURE__*/ jsx(Select, {
35
47
  style: {
@@ -1,12 +1,12 @@
1
1
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
2
  import { useEffect, useMemo, useState } from "react";
3
3
  import { isPlainObject, last } from "lodash-es";
4
- import { ASTMatch, useScopeAvailable } from "@flowgram.ai/editor";
4
+ import { ASTMatch, useCurrentScope } from "@flowgram.ai/editor";
5
5
  import { Mention, PositionMirror, getCurrentMentionReplaceRange, useEditor } from "@flowgram.ai/coze-editor/react";
6
6
  import { Popover, Tree } from "@douyinfe/semi-ui";
7
7
  import { FlowValueUtils } from "../../../shared/index.mjs";
8
8
  function InputsPicker({ inputsValues, onSelect }) {
9
- const available = useScopeAvailable();
9
+ const scope = useCurrentScope();
10
10
  const getArrayDrilldown = (type, depth = 1)=>{
11
11
  if (ASTMatch.isArray(type.items)) return getArrayDrilldown(type.items, depth + 1);
12
12
  return {
@@ -41,7 +41,7 @@ function InputsPicker({ inputsValues, onSelect }) {
41
41
  const currKey = keyPath.join('.');
42
42
  if (FlowValueUtils.isFlowValue(value)) {
43
43
  if (FlowValueUtils.isRef(value)) {
44
- const variable = available.getByKeyPath(value.content || []);
44
+ const variable = scope?.available?.getByKeyPath(value.content || []);
45
45
  if (variable) return renderVariable(variable, keyPath);
46
46
  }
47
47
  return {
@@ -81,12 +81,21 @@ class VariableTagWidget extends WidgetType {
81
81
  this.toDispose.push(Disposable.create(()=>{
82
82
  this.root.unmount();
83
83
  }));
84
- this.toDispose.push(this.scope.available.trackByKeyPath(this.keyPath, (v)=>{
85
- this.renderVariable(v);
86
- }, {
84
+ const refresh = ()=>{
85
+ this.renderVariable(this.scope.available.getByKeyPath(this.keyPath));
86
+ };
87
+ this.toDispose.push(this.scope.available.trackByKeyPath(this.keyPath, refresh, {
88
+ triggerOnInit: false
89
+ }));
90
+ if (this.keyPath?.[0]) this.toDispose.push(this.scope.available.trackByKeyPath([
91
+ this.keyPath[0]
92
+ ], refresh, {
93
+ selector: (curr)=>({
94
+ ...curr?.meta
95
+ }),
87
96
  triggerOnInit: false
88
97
  }));
89
- this.renderVariable(this.scope.available.getByKeyPath(this.keyPath));
98
+ refresh();
90
99
  return dom;
91
100
  }
92
101
  eq(other) {
@@ -101,7 +110,9 @@ class VariableTagWidget extends WidgetType {
101
110
  }
102
111
  function VariableTagInject() {
103
112
  const injector = useInjector();
104
- const scope = useCurrentScope();
113
+ const scope = useCurrentScope({
114
+ strict: true
115
+ });
105
116
  useLayoutEffect(()=>{
106
117
  const atMatcher = new MatchDecorator({
107
118
  regexp: /\{\{([^\}\{]+)\}\}/g,
@@ -17,7 +17,19 @@ function DBConditionRow({ style, value, onChange, readonly, options, ruleConfig
17
17
  const { opConfig, rule, opOptionList, targetSchema } = useCondition({
18
18
  leftSchema,
19
19
  operator,
20
- ruleConfig
20
+ ruleConfig,
21
+ onClearOp () {
22
+ onChange({
23
+ ...value,
24
+ operator: void 0
25
+ });
26
+ },
27
+ onClearRight () {
28
+ onChange({
29
+ ...value,
30
+ right: void 0
31
+ });
32
+ }
21
33
  });
22
34
  const renderDBOptionSelect = ()=>/*#__PURE__*/ jsx(Select, {
23
35
  className: "gedit-m-db-condition-row-select",
@@ -8,7 +8,7 @@ function DisplayOutputs({ value, showIconInTree, displayFromScope }) {
8
8
  const scope = useCurrentScope();
9
9
  const refresh = useRefresh();
10
10
  useEffect(()=>{
11
- if (!displayFromScope) return ()=>null;
11
+ if (!displayFromScope || !scope) return ()=>null;
12
12
  const disposable = scope.output.onListOrAnyVarChange(()=>{
13
13
  refresh();
14
14
  });
@@ -18,7 +18,7 @@ function DisplayOutputs({ value, showIconInTree, displayFromScope }) {
18
18
  }, [
19
19
  displayFromScope
20
20
  ]);
21
- const properties = displayFromScope ? scope.output.variables?.reduce((acm, curr)=>{
21
+ const properties = displayFromScope ? (scope?.output.variables || []).reduce((acm, curr)=>{
22
22
  acm = {
23
23
  ...acm,
24
24
  ...JsonSchemaUtils.astToSchema(curr.type)?.properties || {}
@@ -1,12 +1,12 @@
1
- import { isEmpty } from "lodash-es";
2
1
  import { DataEvent, getNodePrivateScope, getNodeScope } from "@flowgram.ai/editor";
3
2
  const validateWhenVariableSync = ({ scope } = {})=>[
4
3
  {
5
4
  event: DataEvent.onValueInit,
6
- effect: ({ context, form })=>{
5
+ effect: ({ context, form, name })=>{
7
6
  const nodeScope = 'private' === scope ? getNodePrivateScope(context.node) : getNodeScope(context.node);
8
7
  const disposable = nodeScope.available.onListOrAnyVarChange(()=>{
9
- if (!isEmpty(form.state.errors)) form.validate();
8
+ const errorKeys = Object.entries(form.state.errors || {}).filter(([_, errors])=>errors?.length > 0).filter(([key])=>key.startsWith(name) || name.startsWith(key)).map(([key])=>key);
9
+ if (errorKeys.length > 0) form.validate();
10
10
  });
11
11
  return ()=>disposable.dispose();
12
12
  }
@@ -4,7 +4,7 @@ function createInjectMaterial(Component, params) {
4
4
  const renderKey = params?.renderKey || Component.renderKey || Component.name || '';
5
5
  const InjectComponent = (props)=>{
6
6
  const container = usePlaygroundContainer();
7
- if (!container?.isBound(FlowRendererRegistry)) return /*#__PURE__*/ react.createElement(Component, {
7
+ if (!container?.isBound?.(FlowRendererRegistry)) return /*#__PURE__*/ react.createElement(Component, {
8
8
  ...props
9
9
  });
10
10
  const rendererRegistry = container.get(FlowRendererRegistry);
@@ -5,9 +5,24 @@
5
5
  import { IJsonSchema } from '@flowgram.ai/json-schema';
6
6
  import { IConditionRule, ConditionOpConfigs } from '../types';
7
7
  interface HooksParams {
8
+ /**
9
+ * Left schema of condition
10
+ */
8
11
  leftSchema?: IJsonSchema;
12
+ /**
13
+ * Operator of condition
14
+ */
9
15
  operator?: string;
10
16
  /**
17
+ * If op is not in opOptionList, clear it
18
+ */
19
+ onClearOp?: () => void;
20
+ /**
21
+ * If targetSchema updated, clear it
22
+ */
23
+ onClearRight?: () => void;
24
+ /**
25
+ * @deprecated use ConditionProvider instead
11
26
  * custom rule config
12
27
  */
13
28
  ruleConfig?: {
@@ -15,7 +30,7 @@ interface HooksParams {
15
30
  rules?: Record<string, IConditionRule>;
16
31
  };
17
32
  }
18
- export declare function useCondition({ leftSchema, operator, ruleConfig }: HooksParams): {
33
+ export declare function useCondition({ leftSchema, operator, onClearOp, onClearRight, ruleConfig, }: HooksParams): {
19
34
  rule: IConditionRule | undefined;
20
35
  opConfig: import("..").ConditionOpConfig;
21
36
  opOptionList: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowgram.ai/form-materials",
3
- "version": "0.5.6",
3
+ "version": "1.0.0",
4
4
  "homepage": "https://flowgram.ai/",
5
5
  "repository": "https://github.com/bytedance/flowgram.ai",
6
6
  "license": "MIT",
@@ -67,9 +67,9 @@
67
67
  "@codemirror/view": "~6.38.0",
68
68
  "@codemirror/state": "~6.5.2",
69
69
  "zod": "^3.24.4",
70
- "@flowgram.ai/editor": "0.5.6",
71
- "@flowgram.ai/json-schema": "0.5.6",
72
- "@flowgram.ai/coze-editor": "0.5.6"
70
+ "@flowgram.ai/editor": "1.0.0",
71
+ "@flowgram.ai/json-schema": "1.0.0",
72
+ "@flowgram.ai/coze-editor": "1.0.0"
73
73
  },
74
74
  "devDependencies": {
75
75
  "@types/lodash-es": "^4.17.12",
@@ -86,8 +86,8 @@
86
86
  "cross-env": "~7.0.3",
87
87
  "@rsbuild/plugin-react": "^1.1.1",
88
88
  "date-fns": "~4.1.0",
89
- "@flowgram.ai/eslint-config": "0.5.6",
90
- "@flowgram.ai/ts-config": "0.5.6"
89
+ "@flowgram.ai/eslint-config": "1.0.0",
90
+ "@flowgram.ai/ts-config": "1.0.0"
91
91
  },
92
92
  "peerDependencies": {
93
93
  "react": ">=16.8",
@@ -30,7 +30,10 @@ export function BlurInput(props: InputProps) {
30
30
  onChange={(value) => {
31
31
  setValue(value);
32
32
  }}
33
- onBlur={(e) => props.onChange?.(value, e)}
33
+ onBlur={(e) => {
34
+ props.onChange?.(value, e);
35
+ props.onBlur?.(e);
36
+ }}
34
37
  />
35
38
  );
36
39
  }
@@ -3,7 +3,7 @@
3
3
  * SPDX-License-Identifier: MIT
4
4
  */
5
5
 
6
- import { useMemo } from 'react';
6
+ import { useEffect, useMemo, useRef } from 'react';
7
7
 
8
8
  import { IJsonSchema } from '@flowgram.ai/json-schema';
9
9
  import { I18n } from '@flowgram.ai/editor';
@@ -14,10 +14,28 @@ import { IConditionRule, ConditionOpConfigs } from '../types';
14
14
  import { useConditionContext } from '../context';
15
15
 
16
16
  interface HooksParams {
17
+ /**
18
+ * Left schema of condition
19
+ */
17
20
  leftSchema?: IJsonSchema;
21
+
22
+ /**
23
+ * Operator of condition
24
+ */
18
25
  operator?: string;
19
26
 
20
27
  /**
28
+ * If op is not in opOptionList, clear it
29
+ */
30
+ onClearOp?: () => void;
31
+
32
+ /**
33
+ * If targetSchema updated, clear it
34
+ */
35
+ onClearRight?: () => void;
36
+
37
+ /**
38
+ * @deprecated use ConditionProvider instead
21
39
  * custom rule config
22
40
  */
23
41
  ruleConfig?: {
@@ -26,26 +44,32 @@ interface HooksParams {
26
44
  };
27
45
  }
28
46
 
29
- export function useCondition({ leftSchema, operator, ruleConfig }: HooksParams) {
47
+ export function useCondition({
48
+ leftSchema,
49
+ operator,
50
+ onClearOp,
51
+ onClearRight,
52
+ ruleConfig,
53
+ }: HooksParams) {
30
54
  const typeManager = useTypeManager();
31
55
  const { rules: contextRules, ops: contextOps } = useConditionContext();
32
56
 
33
- // 合并用户规则和上下文规则
57
+ // Merge user rules and context rules
34
58
  const userRules = useMemo(
35
59
  () => ruleConfig?.rules || contextRules || {},
36
60
  [contextRules, ruleConfig?.rules]
37
61
  );
38
62
 
39
- // 合并用户操作符和上下文操作符
63
+ // Merge user operators and context operators
40
64
  const allOps = useMemo(() => ruleConfig?.ops || contextOps || {}, [contextOps, ruleConfig?.ops]);
41
65
 
42
- // 获取类型配置
66
+ // Get type configuration
43
67
  const config = useMemo(
44
68
  () => (leftSchema ? typeManager.getTypeBySchema(leftSchema) : undefined),
45
69
  [leftSchema, typeManager]
46
70
  );
47
71
 
48
- // 计算规则
72
+ // Calculate rule
49
73
  const rule = useMemo(() => {
50
74
  if (!config) {
51
75
  return undefined;
@@ -59,7 +83,7 @@ export function useCondition({ leftSchema, operator, ruleConfig }: HooksParams)
59
83
  return config.conditionRule;
60
84
  }, [userRules, leftSchema, config]);
61
85
 
62
- // 计算操作符选项列表
86
+ // Calculate operator option list
63
87
  const opOptionList = useMemo(
64
88
  () =>
65
89
  Object.keys(rule || {})
@@ -72,6 +96,16 @@ export function useCondition({ leftSchema, operator, ruleConfig }: HooksParams)
72
96
  [rule, allOps]
73
97
  );
74
98
 
99
+ // When op not in list, clear it
100
+ useEffect(() => {
101
+ if (!operator || !rule) {
102
+ return;
103
+ }
104
+ if (!opOptionList.find((item) => item.value === operator)) {
105
+ onClearOp?.();
106
+ }
107
+ }, [operator, opOptionList, onClearOp]);
108
+
75
109
  // get target schema
76
110
  const targetSchema = useMemo(() => {
77
111
  const targetType: string | IJsonSchema | null = rule?.[operator || ''] || null;
@@ -87,6 +121,16 @@ export function useCondition({ leftSchema, operator, ruleConfig }: HooksParams)
87
121
  return targetType;
88
122
  }, [rule, operator]);
89
123
 
124
+ const prevTargetSchemaRef = useRef<IJsonSchema | undefined>(undefined);
125
+
126
+ // When type of target schema updated, clear it
127
+ useEffect(() => {
128
+ if (prevTargetSchemaRef.current?.type !== targetSchema?.type) {
129
+ onClearRight?.();
130
+ }
131
+ prevTargetSchemaRef.current = targetSchema;
132
+ }, [targetSchema, onClearRight]);
133
+
90
134
  // get current operator config
91
135
  const opConfig = useMemo(() => allOps[operator || ''], [operator, allOps]);
92
136
 
@@ -50,6 +50,18 @@ export function ConditionRow({ style, value, onChange, readonly, ruleConfig }: P
50
50
  leftSchema,
51
51
  operator,
52
52
  ruleConfig,
53
+ onClearOp() {
54
+ onChange({
55
+ ...value,
56
+ operator: undefined,
57
+ });
58
+ },
59
+ onClearRight() {
60
+ onChange({
61
+ ...value,
62
+ right: undefined,
63
+ });
64
+ },
53
65
  });
54
66
 
55
67
  const renderOpSelect = () => (
@@ -11,7 +11,7 @@ import {
11
11
  ASTMatch,
12
12
  type BaseType,
13
13
  type BaseVariableField,
14
- useScopeAvailable,
14
+ useCurrentScope,
15
15
  } from '@flowgram.ai/editor';
16
16
  import {
17
17
  Mention,
@@ -36,7 +36,7 @@ export function InputsPicker({
36
36
  inputsValues: IInputsValues;
37
37
  onSelect: (v: string) => void;
38
38
  }) {
39
- const available = useScopeAvailable();
39
+ const scope = useCurrentScope();
40
40
 
41
41
  const getArrayDrilldown = (type: ArrayType, depth = 1): { type: BaseType; depth: number } => {
42
42
  if (ASTMatch.isArray(type.items)) {
@@ -90,7 +90,7 @@ export function InputsPicker({
90
90
 
91
91
  if (FlowValueUtils.isFlowValue(value)) {
92
92
  if (FlowValueUtils.isRef(value)) {
93
- const variable = available.getByKeyPath(value.content || []);
93
+ const variable = scope?.available?.getByKeyPath(value.content || []);
94
94
  if (variable) {
95
95
  return renderVariable(variable, keyPath);
96
96
  }
@@ -107,17 +107,25 @@ class VariableTagWidget extends WidgetType {
107
107
  })
108
108
  );
109
109
 
110
+ const refresh = () => {
111
+ this.renderVariable(this.scope.available.getByKeyPath(this.keyPath));
112
+ };
113
+
110
114
  this.toDispose.push(
111
- this.scope.available.trackByKeyPath(
112
- this.keyPath,
113
- (v) => {
114
- this.renderVariable(v);
115
- },
116
- { triggerOnInit: false }
117
- )
115
+ this.scope.available.trackByKeyPath(this.keyPath, refresh, { triggerOnInit: false })
118
116
  );
119
117
 
120
- this.renderVariable(this.scope.available.getByKeyPath(this.keyPath));
118
+ if (this.keyPath?.[0]) {
119
+ this.toDispose.push(
120
+ // listen to root title changed
121
+ this.scope.available.trackByKeyPath<{ title?: string }>([this.keyPath[0]], refresh, {
122
+ selector: (curr) => ({ ...curr?.meta }),
123
+ triggerOnInit: false,
124
+ })
125
+ );
126
+ }
127
+
128
+ refresh();
121
129
 
122
130
  return dom;
123
131
  }
@@ -138,7 +146,7 @@ class VariableTagWidget extends WidgetType {
138
146
  export function VariableTagInject() {
139
147
  const injector = useInjector();
140
148
 
141
- const scope = useCurrentScope();
149
+ const scope = useCurrentScope({ strict: true });
142
150
 
143
151
  // 基于 {{var}} 的正则进行匹配,匹配后进行自定义渲染
144
152
  useLayoutEffect(() => {
@@ -56,6 +56,18 @@ export function DBConditionRow({
56
56
  leftSchema,
57
57
  operator,
58
58
  ruleConfig,
59
+ onClearOp() {
60
+ onChange({
61
+ ...value,
62
+ operator: undefined,
63
+ });
64
+ },
65
+ onClearRight() {
66
+ onChange({
67
+ ...value,
68
+ right: undefined,
69
+ });
70
+ },
59
71
  });
60
72
 
61
73
  const renderDBOptionSelect = () => (
@@ -24,7 +24,7 @@ export function DisplayOutputs({ value, showIconInTree, displayFromScope }: Prop
24
24
  const refresh = useRefresh();
25
25
 
26
26
  useEffect(() => {
27
- if (!displayFromScope) {
27
+ if (!displayFromScope || !scope) {
28
28
  return () => null;
29
29
  }
30
30
 
@@ -38,7 +38,7 @@ export function DisplayOutputs({ value, showIconInTree, displayFromScope }: Prop
38
38
  }, [displayFromScope]);
39
39
 
40
40
  const properties: IJsonSchema['properties'] = displayFromScope
41
- ? scope.output.variables?.reduce((acm, curr) => {
41
+ ? (scope?.output.variables || []).reduce((acm, curr) => {
42
42
  acm = {
43
43
  ...acm,
44
44
  ...(JsonSchemaUtils.astToSchema(curr.type)?.properties || {}),
@@ -3,7 +3,6 @@
3
3
  * SPDX-License-Identifier: MIT
4
4
  */
5
5
 
6
- import { isEmpty } from 'lodash-es';
7
6
  import {
8
7
  DataEvent,
9
8
  Effect,
@@ -19,12 +18,17 @@ export const validateWhenVariableSync = ({
19
18
  } = {}): EffectOptions[] => [
20
19
  {
21
20
  event: DataEvent.onValueInit,
22
- effect: (({ context, form }) => {
21
+ effect: (({ context, form, name }) => {
23
22
  const nodeScope =
24
23
  scope === 'private' ? getNodePrivateScope(context.node) : getNodeScope(context.node);
25
24
 
26
25
  const disposable = nodeScope.available.onListOrAnyVarChange(() => {
27
- if (!isEmpty(form.state.errors)) {
26
+ const errorKeys = Object.entries(form.state.errors || {})
27
+ .filter(([_, errors]) => errors?.length > 0)
28
+ .filter(([key]) => key.startsWith(name) || name.startsWith(key))
29
+ .map(([key]) => key);
30
+
31
+ if (errorKeys.length > 0) {
28
32
  form.validate();
29
33
  }
30
34
  });
@@ -62,7 +62,7 @@ export function createInjectMaterial<Props>(
62
62
  const container = usePlaygroundContainer();
63
63
 
64
64
  // Check if renderer registry is bound in container
65
- if (!container?.isBound(FlowRendererRegistry)) {
65
+ if (!container?.isBound?.(FlowRendererRegistry)) {
66
66
  // If no registry, use default component directly
67
67
  return React.createElement(Component as (props?: any) => any, { ...props });
68
68
  }