Explorar o código

feat(variable): get form context in create effect from variable provider (#924)

Yiwei Mao hai 2 meses
pai
achega
8e2bf52546

+ 40 - 0
apps/docs/components/form-materials/components/sql-editor-with-variables.tsx

@@ -0,0 +1,40 @@
+/**
+ * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
+ * SPDX-License-Identifier: MIT
+ */
+
+import React from 'react';
+
+import { Field } from '@flowgram.ai/fixed-layout-editor';
+
+import { FreeFormMetaStoryBuilder, FormHeader } from '../../free-form-meta-story-builder';
+
+const SQLEditorWithVariables = React.lazy(() =>
+  import('@flowgram.ai/form-materials').then((module) => ({
+    default: module.SQLEditorWithVariables,
+  }))
+);
+
+export const BasicStory = () => (
+  <FreeFormMetaStoryBuilder
+    filterEndNode
+    formMeta={{
+      render: () => (
+        <>
+          <FormHeader />
+          <Field<string | undefined>
+            name="sql_editor_with_variables"
+            defaultValue={'SELECT * FROM users WHERE user_id = {{start_0.str}}'}
+          >
+            {({ field }) => (
+              <SQLEditorWithVariables
+                value={field.value}
+                onChange={(value) => field.onChange(value)}
+              />
+            )}
+          </Field>
+        </>
+      ),
+    }}
+  />
+);

+ 49 - 2
apps/docs/src/en/guide/variable/variable-output.mdx

@@ -1,6 +1,6 @@
 # Outputting Variables
 
-In Flowgram, variables are the key hubs that connect various nodes. The output of one node is often the input of another. Therefore, how to elegantly define and output variables has become a required course. This article will take you to explore the various ways of outputting variables in Flowgram.
+In Flowgram, variables are the key hubs that connect various nodes. The output of one node is often the input of another.
 
 We mainly divide variables into two categories:
 
@@ -22,7 +22,7 @@ Configuring in the node's `form-meta.ts` file is the most common way to define n
 
 #### `provideJsonSchemaOutputs` Material
 
-If the structure of the variable that your node needs to output happens to be consistent with the `JSON Schema` structure of the form, then congratulations, the `provideJsonSchemaOutputs` side effect (Effect) material is tailor-made for you! It is like an automated "variable porter", which can automatically convert the data in the form into the output variable of the node as it is.
+If the structure of the variable that your node needs to output happens to be consistent with the `JSON Schema` structure of the form, then congratulations, the `provideJsonSchemaOutputs` side effect (Effect) material is tailor-made for you! It is like an automated "variable porter", which can automatically convert the json schema in the form into the output variable of the node as it is.
 
 It is very simple to use, just add two lines of configuration in the `effect` of `formMeta`:
 
@@ -117,6 +117,53 @@ export const nodeRegistries: FlowNodeRegistry[] = [
 
 ```
 
+#### Syncing multiple form fields to one variable
+
+If multiple fields need to be synchronized to a single variable, you need to use the `namespace` field of `createEffectFromVariableProvider` to synchronize the variable data of multiple fields to the same namespace.
+
+
+```tsx pure title="form-meta.ts"
+
+/**
+ * Get information of multiple fields from formValues
+ */
+const variableSyncEffect = createEffectFromVariableProvider({
+
+  // Must be added to ensure that the side effects of different fields are synchronized to the same namespace
+  namespace: 'your_namespace',
+
+  // Parse the form value into a variable
+  parse(_, { form }) {
+    return {
+      meta: {
+        title: `Your Output Variable Title`,
+      },
+      key: `your_variable_global_unique_key_${node.id}`,
+      type: createTypeFromValue({
+        value1: form.getValueIn('path.to.value'),
+        value2: form.getValueIn('path.to.value2'),
+      })
+    }
+  }
+})
+
+export const nodeRegistries: FlowNodeRegistry[] = [
+  {
+    type: 'start',
+    formMeta: {
+      effect: {
+        'path.to.value': variableSyncEffect,
+        'path.to.value2': variableSyncEffect,
+      },
+      render: () => (
+       // ...
+      )
+    },
+  }
+]
+
+```
+
 ### Method 2: Use the `node.scope` API
 
 In addition to static configuration in the form, we can also get the scope in the plugin (Plugin) through the `node.scope` API to dynamically operate the variables of the node.

+ 152 - 2
apps/docs/src/en/materials/components/sql-editor-with-variables.mdx

@@ -1,7 +1,157 @@
 import { SourceCode } from '@theme';
+import { BasicStory } from 'components/form-materials/components/sql-editor-with-variables';
 
-# SQLEditorWithVariables (WIP)
+# SQLEditorWithVariables
+
+SQLEditorWithVariables is an enhanced SQL editor that supports inserting variable references in SQL. It is built on top of SQLCodeEditor and integrates a variable selector and variable tag injection, allowing users to reference variables using the `{{variable}}` syntax in SQL strings.
+
+## Examples
+
+### Basic Usage
+
+<BasicStory />
+
+```tsx pure title="form-meta.tsx"
+import { SQLEditorWithVariables } from '@flowgram.ai/form-materials';
+
+const formMeta = {
+  render: () => (
+    <>
+      <FormHeader />
+      <Field<string | undefined>
+        name="sql_editor_with_variables"
+        defaultValue="SELECT * FROM users WHERE user_id = {{start_0.str}}"
+      >
+        {({ field }) => (
+          <SQLEditorWithVariables
+            value={field.value}
+            onChange={(value) => field.onChange(value)}
+          />
+        )}
+      </Field>
+    </>
+  ),
+}
+```
+
+### SQL Example with Variables
+
+```sql
+SELECT
+  id, name, email
+FROM users
+WHERE id = {{start_0.user_id}}
+  AND email LIKE '%{{start_0.domain}}'
+LIMIT 10;
+```
+
+### Variable Insertion
+
+Typing `@` or `{` in the editor can trigger the variable selector.
+
+After typing `@` or `{`, a list of available variables will be displayed. Selecting a variable will automatically insert it in the `{{variable.name}}` format.
+
+## API Reference
+
+### SQLEditorWithVariables Props
+
+| Property | Type | Default | Description |
+|---|---|---|---|
+| `value` | `string` | - | The content of the SQL string |
+| `onChange` | `(value: string) => void` | - | Callback function when the content changes |
+| `theme` | `'dark' \| 'light'` | `'light'` | The theme of the editor |
+| `placeholder` | `string` | - | Placeholder text |
+| `activeLinePlaceholder` | `string` | `'Type @ to select a variable'` | Placeholder hint for the current line |
+| `readonly` | `boolean` | `false` | Whether it is in read-only mode |
+| `options` | `Options` | - | CodeMirror configuration options |
+
+### Variable Syntax
+
+Use double curly braces to reference variables in the SQL string:
+
+```sql
+SELECT * FROM orders WHERE user_id = {{variable.path}}
+```
+
+Supported variable formats:
+- `{{start_0.name}}` - Simple variable
+- `{{start_0.address.city}}` - Nested property
+
+## Source Code Guide
 
 <SourceCode
   href="https://github.com/bytedance/flowgram.ai/tree/main/packages/materials/form-materials/src/components/sql-editor-with-variables"
-/>
+/>
+
+You can copy the source code locally using the CLI command:
+
+```bash
+npx @flowgram.ai/cli@latest materials components/sql-editor-with-variables
+```
+
+### Directory Structure
+
+```
+sql-editor-with-variables/
+├── index.tsx           # Lazy-loaded export file
+├── editor.tsx          # Main component implementation
+└── README.md          # Component documentation
+```
+
+### Core Implementation
+
+#### Variable Selector Integration
+Integrate the `EditorVariableTree` and `EditorVariableTagInject` extensions:
+
+```typescript
+<EditorVariableTree />
+<EditorVariableTagInject />
+```
+
+#### Trigger Characters
+By default, `@` is used as the trigger character for variable selection. If you need to customize the trigger characters, you can extend it using the underlying `SQLCodeEditor`:
+
+```typescript
+import { SQLCodeEditor } from '@flowgram.ai/form-materials';
+
+<SQLCodeEditor>
+  <EditorVariableTree triggerCharacters={["@", "#", "$"]} />
+  <EditorVariableTagInject />
+</SQLCodeEditor>
+```
+
+### Used Flowgram APIs
+
+#### @flowgram.ai/coze-editor
+- `SQLCodeEditor`: SQL code editor
+- `EditorVariableTree`: Variable tree selector
+- `EditorVariableTagInject`: Variable tag injector
+
+#### @flowgram.ai/i18n
+- `I18n`: Internationalization support
+
+#### coze-editor-extensions Materials
+- `EditorVariableTree`: Trigger for variable tree selection
+- `EditorVariableTagInject`: Display for variable tags
+
+### Overall Flow
+
+```mermaid
+graph TD
+    A[SQLEditorWithVariables Component] --> B[Render SQLCodeEditor]
+    B --> C[Load SQL syntax highlighting]
+    B --> D[Integrate variable extensions]
+
+    D --> E[EditorVariableTree]
+    D --> F[EditorVariableTagInject]
+
+    E --> G[Listen for @ trigger]
+    F --> H[Process variable tags]
+
+    G --> I[Display variable list]
+    I --> J[Select and insert variable]
+
+    H --> L[Render variable tag style]
+
+    J --> M[Update editor content]
+```

+ 58 - 4
apps/docs/src/zh/guide/variable/variable-output.mdx

@@ -1,6 +1,6 @@
 # 输出变量
 
-在 Flowgram 中,变量是连接各个节点的关键枢纽。一个节点的输出,往往是另一个节点的输入。因此,如何优雅地定义和输出变量,就成了一门必修课。本文将带你探索在 Flowgram 中输出变量的各种姿势。
+在 Flowgram 中,变量是连接各个节点的关键枢纽。一个节点的输出,往往是另一个节点的输入。
 
 我们主要将变量分为两类:
 
@@ -9,7 +9,9 @@
 
 ## 输出节点变量
 
-输出节点变量,意味着这个变量和当前节点的生命周期是绑定的。当节点被创建时,变量就诞生了;当节点被删除时,变量也随之消逝。我们通常有两种方式来输出节点变量:
+输出节点变量,意味着这个变量和当前节点的生命周期是绑定的。
+
+当节点被创建时,变量就诞生了;当节点被删除时,变量也随之消逝。我们通常有两种方式来输出节点变量:
 
 1.  **通过表单配置**:在节点的设置面板(Form)中,通过一些预设的配置或自定义的逻辑来声明输出变量。这种方式非常直观,所见即所得。
 2.  **通过 API 调用**:在插件(Plugin)中,通过调用 `node.scope` API 来动态地为节点添加、修改或删除变量。这种方式更加灵活,适合处理复杂的、动态的业务场景。
@@ -22,7 +24,8 @@
 
 #### `provideJsonSchemaOutputs` 物料
 
-如果你的节点需要输出的变量,其结构恰好和表单的 `JSON Schema` 结构一致,那么恭喜你,`provideJsonSchemaOutputs` 这个副作用(Effect)物料就是为你量身定做的!它就像一个自动化的“变量搬运工”,能自动将表单中的数据,原封不动地转换为节点的输出变量。
+如果你的节点需要输出的变量,其结构恰好和表单的 `JSON Schema` 结构一致,那么恭喜你,`provideJsonSchemaOutputs` 这个副作用(Effect)物料就是为你量身定做的!
+它就像一个自动化的“变量搬运工”,能自动将表单中的 json schema,原封不动地转换为节点的输出变量。
 
 使用起来非常简单,只需要在 `formMeta` 的 `effect` 中加上两行配置即可:
 
@@ -46,7 +49,8 @@ export const formMeta = {
 
 如果你想要定义自己的一套 Schema,那么你就需要自定义表单的副作用了。
 
-Flowgram 提供了一个强大的辅助函数 `createEffectFromVariableProvider`,它能帮你轻松地创建一个用于提供变量的副作用。你可以把它想象成一个“变量加工厂”,输入的是表单数据,输出的是你精心加工后的变量。
+Flowgram 提供了一个强大的辅助函数 `createEffectFromVariableProvider`,它能帮你轻松地创建一个用于提供变量的副作用。
+你可以把它想象成一个“变量加工厂”,输入的是表单数据,输出的是你精心加工后的变量。
 
 下面这个例子中,我们为表单的两个字段 `path.to.value` 和 `path.to.value2` 分别创建了输出变量。当用户在表单中填写这两个字段时,`parse` 函数就会被触发,将用户输入的值(`v`)转换成一个标准的变量声明对象。
 
@@ -117,6 +121,56 @@ export const nodeRegistries: FlowNodeRegistry[] = [
 
 ```
 
+#### 多个表单字段同步到一个变量上
+
+如果多个字段同步到一个变量,就需要用到 `createEffectFromVariableProvider` 的 `namespace` 字段,将多个字段的变量数据同步到同一个命名空间上。
+
+
+```tsx pure title="form-meta.ts"
+
+/**
+ * 从 formValues 拿到多个字段的信息
+ */
+const variableSyncEffect = createEffectFromVariableProvider({
+
+  // 必须添加,确保不同字段的副作用,同步到同一个命名空间上
+  namespace: 'your_namespace',
+
+  // 将表单值解析为变量
+  parse(_, { form, node }) {
+    return {
+      meta: {
+        title: `Your Output Variable Title`,
+      },
+      key: `your_variable_global_unique_key_${node.id}`,
+      type: createTypeFromValue({
+        value1: form.getValueIn('path.to.value'),
+        value2: form.getValueIn('path.to.value2'),
+      })
+    }
+  }
+})
+
+export const nodeRegistries: FlowNodeRegistry[] = [
+  {
+    type: 'start',
+    formMeta: {
+      effect: {
+        'path.to.value': variableSyncEffect,
+        'path.to.value2': variableSyncEffect,
+      },
+      render: () => (
+       // ...
+      )
+    },
+  }
+]
+
+```
+
+
+
+
 ### 方式二:使用 `node.scope` API
 
 除了在表单中静态配置,我们还可以在插件(Plugin)中,通过 `node.scope` API 来获取作用域,从而动态地操作节点的变量。

+ 151 - 1
apps/docs/src/zh/materials/components/sql-editor-with-variables.mdx

@@ -1,7 +1,157 @@
 import { SourceCode } from '@theme';
+import { BasicStory } from 'components/form-materials/components/sql-editor-with-variables';
 
-# SQLEditorWithVariables (WIP)
+# SQLEditorWithVariables
+
+SQLEditorWithVariables 是一个增强版的 SQL 编辑器,支持在 SQL 中插入变量引用。它基于 SQLCodeEditor 构建,集成了变量选择器和变量标签注入功能,使用户能够在 SQL 字符串中使用 `{{variable}}` 语法引用变量。
+
+## 案例演示
+
+### 基本使用
+
+<BasicStory />
+
+```tsx pure title="form-meta.tsx"
+import { SQLEditorWithVariables } from '@flowgram.ai/form-materials';
+
+const formMeta = {
+  render: () => (
+    <>
+      <FormHeader />
+      <Field<string | undefined>
+        name="sql_editor_with_variables"
+        defaultValue="SELECT * FROM users WHERE user_id = {{start_0.str}}"
+      >
+        {({ field }) => (
+          <SQLEditorWithVariables
+            value={field.value}
+            onChange={(value) => field.onChange(value)}
+          />
+        )}
+      </Field>
+    </>
+  ),
+}
+```
+
+### 带变量的 SQL 示例
+
+```sql
+SELECT
+  id, name, email
+FROM users
+WHERE id = {{start_0.user_id}}
+  AND email LIKE '%{{start_0.domain}}'
+LIMIT 10;
+```
+
+### 变量插入
+
+在编辑器中输入 `@`, `{` 字符可以触发变量选择器。
+
+输入 `@`, `{` 后会显示可用的变量列表,选择变量后会自动插入为 `{{variable.name}}` 格式。
+
+## API 参考
+
+### SQLEditorWithVariables Props
+
+| 属性名 | 类型 | 默认值 | 描述 |
+|--------|------|--------|------|
+| `value` | `string` | - | SQL 字符串内容 |
+| `onChange` | `(value: string) => void` | - | 内容变化时的回调函数 |
+| `theme` | `'dark' \| 'light'` | `'light'` | 编辑器主题 |
+| `placeholder` | `string` | - | 占位符文本 |
+| `activeLinePlaceholder` | `string` | `'按 @ 选择变量'` | 当前行的占位提示 |
+| `readonly` | `boolean` | `false` | 是否为只读模式 |
+| `options` | `Options` | - | CodeMirror 配置选项 |
+
+### 变量语法
+
+在 SQL 字符串中使用双花括号语法引用变量:
+
+```sql
+SELECT * FROM orders WHERE user_id = {{variable.path}}
+```
+
+支持的变量格式:
+- `{{start_0.name}}` - 简单变量
+- `{{start_0.address.city}}` - 嵌套属性
+
+## 源码导读
 
 <SourceCode
   href="https://github.com/bytedance/flowgram.ai/tree/main/packages/materials/form-materials/src/components/sql-editor-with-variables"
 />
+
+使用 CLI 命令可以复制源代码到本地:
+
+```bash
+npx @flowgram.ai/cli@latest materials components/sql-editor-with-variables
+```
+
+### 目录结构讲解
+
+```
+sql-editor-with-variables/
+├── index.tsx           # 懒加载导出文件
+├── editor.tsx          # 主组件实现
+└── README.md          # 组件说明文档
+```
+
+### 核心实现说明
+
+#### 变量选择器集成
+集成 `EditorVariableTree` 和 `EditorVariableTagInject` 扩展:
+
+```typescript
+<EditorVariableTree />
+<EditorVariableTagInject />
+```
+
+#### 触发字符
+默认使用 `@` 作为变量选择的触发字符。若需要自定义触发字符,可使用底层 `SQLCodeEditor` 进行扩展:
+
+```typescript
+import { SQLCodeEditor } from '@flowgram.ai/form-materials';
+
+<SQLCodeEditor>
+  <EditorVariableTree triggerCharacters={["@", "#", "$"]} />
+  <EditorVariableTagInject />
+</SQLCodeEditor>
+```
+
+### 使用到的 flowgram API
+
+#### @flowgram.ai/coze-editor
+- `SQLCodeEditor`: SQL 代码编辑器
+- `EditorVariableTree`: 变量树选择器
+- `EditorVariableTagInject`: 变量标签注入器
+
+#### @flowgram.ai/i18n
+- `I18n`: 国际化支持
+
+#### coze-editor-extensions 物料
+- `EditorVariableTree`: 变量树选择触发
+- `EditorVariableTagInject`: 变量标签展示
+
+### 整体流程
+
+```mermaid
+graph TD
+    A[SQLEditorWithVariables 组件] --> B[渲染 SQLCodeEditor]
+    B --> C[加载 SQL 语法高亮]
+    B --> D[集成变量扩展]
+
+    D --> E[EditorVariableTree]
+    D --> F[EditorVariableTagInject]
+
+    E --> G[监听 @ 触发]
+    F --> H[处理变量标签]
+
+    G --> I[显示变量列表]
+    I --> J[选择并插入变量]
+
+    H --> L[渲染变量标签样式]
+
+    J --> M[更新编辑器内容]
+```

+ 7 - 1
packages/plugins/node-variable-plugin/src/form-v2/create-provider-effect.ts

@@ -26,7 +26,7 @@ export function createEffectFromVariableProvider(
     return variableData.public;
   };
 
-  const transformValueToAST: Effect = ({ value, name, context }) => {
+  const transformValueToAST: Effect = ({ value, name, context, formValues, form }) => {
     if (!context) {
       return;
     }
@@ -39,6 +39,9 @@ export function createEffectFromVariableProvider(
         node,
         scope,
         options,
+        name,
+        formValues,
+        form,
       }),
     });
   };
@@ -54,6 +57,9 @@ export function createEffectFromVariableProvider(
           node: context.node,
           scope,
           options,
+          name: params.name,
+          formValues: params.formValues,
+          form: params.form,
         });
 
         transformValueToAST(params);

+ 4 - 0
packages/plugins/node-variable-plugin/src/types.ts

@@ -9,12 +9,16 @@ import {
   type VariableDeclarationJSON,
 } from '@flowgram.ai/variable-plugin';
 import { Disposable } from '@flowgram.ai/utils';
+import { EffectFuncProps } from '@flowgram.ai/node';
 import { FlowNodeEntity } from '@flowgram.ai/document';
 
 export interface VariableAbilityCommonContext {
   node: FlowNodeEntity; // 节点
   scope: Scope; // 作用域
   options: VariableAbilityOptions;
+  name: string; // 表单字段名
+  formValues: EffectFuncProps['formValues']; // 表单值
+  form: EffectFuncProps['form'];
 }
 
 export interface VariableAbilityInitCtx extends VariableAbilityCommonContext {}