/** * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates * SPDX-License-Identifier: MIT */ import React, { useCallback } from 'react'; import { IJsonSchema, JsonSchemaTypeManager, JsonSchemaUtils, useTypeManager, } from '@flowgram.ai/json-schema'; import { ASTMatch, BaseVariableField, useAvailableVariables } from '@flowgram.ai/editor'; import { TreeNodeData } from '@douyinfe/semi-ui/lib/es/tree'; import { Icon } from '@douyinfe/semi-ui'; import { useVariableSelectorContext } from './context'; type VariableField = BaseVariableField<{ icon?: string | JSX.Element; title?: string; disabled?: boolean; }>; export function useVariableTree(params: { includeSchema?: IJsonSchema | IJsonSchema[]; excludeSchema?: IJsonSchema | IJsonSchema[]; skipVariable?: (variable: VariableField) => boolean; }): TreeNodeData[] { const context = useVariableSelectorContext(); const { includeSchema = context.includeSchema, excludeSchema = context.excludeSchema, skipVariable = context.skipVariable, } = params; const typeManager = useTypeManager() as JsonSchemaTypeManager; const variables = useAvailableVariables(); const getVariableTypeIcon = useCallback((variable: VariableField) => { if (variable.meta?.icon) { if (typeof variable.meta.icon === 'string') { return ; } return variable.meta.icon; } const schema = JsonSchemaUtils.astToSchema(variable.type, { drilldownObject: false }); return ; }, []); const renderVariable = ( variable: VariableField, parentFields: VariableField[] = [] ): TreeNodeData | null => { let type = variable?.type; if (!type) { return null; } let children: TreeNodeData[] | undefined; if (ASTMatch.isObject(type)) { children = (type.properties || []) .map((_property) => renderVariable(_property as VariableField, [...parentFields, variable])) .filter(Boolean) as TreeNodeData[]; } const keyPath = [...parentFields.map((_field) => _field.key), variable.key]; const key = keyPath.join('.'); const isSchemaInclude = includeSchema ? JsonSchemaUtils.isASTMatchSchema(type, includeSchema) : true; const isSchemaExclude = excludeSchema ? JsonSchemaUtils.isASTMatchSchema(type, excludeSchema) : false; const isCustomSkip = skipVariable ? skipVariable(variable) : false; // disabled in meta when created const isMetaDisabled = variable.meta?.disabled; const isSchemaMatch = isSchemaInclude && !isSchemaExclude && !isCustomSkip && !isMetaDisabled; // If not match, and no children, return null if (!isSchemaMatch && !children?.length) { return null; } return { key: key, label: variable.meta?.title || variable.key, value: key, keyPath, icon: getVariableTypeIcon(variable), children, disabled: !isSchemaMatch, rootMeta: parentFields[0]?.meta || variable.meta, isRoot: !parentFields?.length, }; }; return [...variables.slice(0).reverse()] .map((_variable) => renderVariable(_variable as VariableField)) .filter(Boolean) as TreeNodeData[]; }