use-variable-tree.tsx 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. /**
  2. * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
  3. * SPDX-License-Identifier: MIT
  4. */
  5. import React, { useCallback } from 'react';
  6. import { IJsonSchema, JsonSchemaUtils } from '@flowgram.ai/json-schema';
  7. import { ASTMatch, BaseVariableField, useAvailableVariables } from '@flowgram.ai/editor';
  8. import { TreeNodeData } from '@douyinfe/semi-ui/lib/es/tree';
  9. import { Icon } from '@douyinfe/semi-ui';
  10. import { useTypeManager } from '@/plugins';
  11. type VariableField = BaseVariableField<{
  12. icon?: string | JSX.Element;
  13. title?: string;
  14. disabled?: boolean;
  15. }>;
  16. export function useVariableTree(params: {
  17. includeSchema?: IJsonSchema | IJsonSchema[];
  18. excludeSchema?: IJsonSchema | IJsonSchema[];
  19. customSkip?: (variable: VariableField) => boolean;
  20. }): TreeNodeData[] {
  21. const { includeSchema, excludeSchema, customSkip } = params;
  22. const typeManager = useTypeManager();
  23. const variables = useAvailableVariables();
  24. const getVariableTypeIcon = useCallback((variable: VariableField) => {
  25. if (variable.meta?.icon) {
  26. if (typeof variable.meta.icon === 'string') {
  27. return <img style={{ marginRight: 8 }} width={12} height={12} src={variable.meta.icon} />;
  28. }
  29. return variable.meta.icon;
  30. }
  31. const schema = JsonSchemaUtils.astToSchema(variable.type, { drilldownObject: false });
  32. return <Icon size="small" svg={typeManager.getDisplayIcon(schema || {})} />;
  33. }, []);
  34. const renderVariable = (
  35. variable: VariableField,
  36. parentFields: VariableField[] = []
  37. ): TreeNodeData | null => {
  38. let type = variable?.type;
  39. if (!type) {
  40. return null;
  41. }
  42. let children: TreeNodeData[] | undefined;
  43. if (ASTMatch.isObject(type)) {
  44. children = (type.properties || [])
  45. .map((_property) => renderVariable(_property as VariableField, [...parentFields, variable]))
  46. .filter(Boolean) as TreeNodeData[];
  47. }
  48. const keyPath = [...parentFields.map((_field) => _field.key), variable.key];
  49. const key = keyPath.join('.');
  50. const isSchemaInclude = includeSchema
  51. ? JsonSchemaUtils.isASTMatchSchema(type, includeSchema)
  52. : true;
  53. const isSchemaExclude = excludeSchema
  54. ? JsonSchemaUtils.isASTMatchSchema(type, excludeSchema)
  55. : false;
  56. const isCustomSkip = customSkip ? customSkip(variable) : false;
  57. // disabled in meta when created
  58. const isMetaDisabled = variable.meta?.disabled;
  59. const isSchemaMatch = isSchemaInclude && !isSchemaExclude && !isCustomSkip && !isMetaDisabled;
  60. // If not match, and no children, return null
  61. if (!isSchemaMatch && !children?.length) {
  62. return null;
  63. }
  64. return {
  65. key: key,
  66. label: variable.meta?.title || variable.key,
  67. value: key,
  68. keyPath,
  69. icon: getVariableTypeIcon(variable),
  70. children,
  71. disabled: !isSchemaMatch,
  72. rootMeta: parentFields[0]?.meta || variable.meta,
  73. isRoot: !parentFields?.length,
  74. };
  75. };
  76. return [...variables.slice(0).reverse()]
  77. .map((_variable) => renderVariable(_variable as VariableField))
  78. .filter(Boolean) as TreeNodeData[];
  79. }