Просмотр исходного кода

feat(materials): json schema editor performance optimize (#681)

* feat(material): json-schema-editor performance

* chore: blur input

* fix: json schema editor

* feat: enable multi line str

* refactor(json-schema-editor): remove useless attributes

* feat: ui tree name update
Yiwei Mao 5 месяцев назад
Родитель
Сommit
6e49cd5fcc
16 измененных файлов с 95 добавлено и 175 удалено
  1. 0 0
      packages/materials/form-materials/src/components/blur-input/index.tsx
  2. 14 4
      packages/materials/form-materials/src/components/dynamic-value-input/styles.tsx
  3. 1 0
      packages/materials/form-materials/src/components/index.ts
  4. 1 1
      packages/materials/form-materials/src/components/inputs-values/index.tsx
  5. 1 1
      packages/materials/form-materials/src/components/inputs-values/styles.tsx
  6. 0 27
      packages/materials/form-materials/src/components/json-schema-editor/components/blur-input.tsx
  7. 1 3
      packages/materials/form-materials/src/components/json-schema-editor/default-value.tsx
  8. 13 2
      packages/materials/form-materials/src/components/json-schema-editor/hooks.tsx
  9. 17 57
      packages/materials/form-materials/src/components/json-schema-editor/index.tsx
  10. 12 55
      packages/materials/form-materials/src/components/json-schema-editor/styles.tsx
  11. 0 1
      packages/materials/form-materials/src/components/json-schema-editor/types.ts
  12. 16 1
      packages/materials/form-materials/src/hooks/use-object-list/index.tsx
  13. 0 5
      packages/materials/form-materials/src/plugins/disable-declaration-plugin/config.json
  14. 0 9
      packages/materials/form-materials/src/plugins/json-schema-preset/config.json
  15. 1 0
      packages/materials/form-materials/src/plugins/json-schema-preset/manager.ts
  16. 18 9
      packages/materials/form-materials/src/plugins/json-schema-preset/type-definition/string.tsx

+ 0 - 0
packages/materials/form-materials/src/components/inputs-values/components/blur-input.tsx → packages/materials/form-materials/src/components/blur-input/index.tsx


+ 14 - 4
packages/materials/form-materials/src/components/dynamic-value-input/styles.tsx

@@ -20,6 +20,8 @@ export const UIMain = styled.div`
   flex-grow: 1;
   overflow: hidden;
   min-width: 0;
+  border-left: 1px solid var(--semi-color-border);
+  border-right: 1px solid var(--semi-color-border);
 
   & .semi-tree-select,
   & .semi-input-number,
@@ -33,19 +35,27 @@ export const UIMain = styled.div`
     border: none;
     border-radius: 0;
   }
+
+  & .semi-input-textarea-wrapper {
+    border: none;
+    border-radius: 0;
+  }
+
+  & .semi-input-textarea {
+    padding: 2px 6px;
+    border: none;
+    border-radius: 0;
+    word-break: break-all;
+  }
 `;
 
 export const UIType = styled.div`
-  border-right: 1px solid #e5e5e5;
-
   & .semi-button {
     border-radius: 0;
   }
 `;
 
 export const UITrigger = styled.div`
-  border-left: 1px solid #e5e5e5;
-
   & .semi-button {
     border-radius: 0;
   }

+ 1 - 0
packages/materials/form-materials/src/components/index.ts

@@ -25,3 +25,4 @@ export * from './display-flow-value';
 export * from './display-inputs-values';
 export * from './assign-rows';
 export * from './assign-row';
+export * from './blur-input';

+ 1 - 1
packages/materials/form-materials/src/components/inputs-values/index.tsx

@@ -12,10 +12,10 @@ import { IconDelete, IconPlus } from '@douyinfe/semi-icons';
 import { IFlowConstantRefValue, IFlowValue } from '@/typings';
 import { useObjectList } from '@/hooks';
 import { InjectDynamicValueInput } from '@/components/dynamic-value-input';
+import { BlurInput } from '@/components/blur-input';
 
 import { PropsType } from './types';
 import { UIRow, UIRows } from './styles';
-import { BlurInput } from './components/blur-input';
 
 export function InputsValues({
   value,

+ 1 - 1
packages/materials/form-materials/src/components/inputs-values/styles.tsx

@@ -14,6 +14,6 @@ export const UIRows = styled.div`
 
 export const UIRow = styled.div`
   display: flex;
-  align-items: center;
+  align-items: flex-start;
   gap: 5px;
 `;

+ 0 - 27
packages/materials/form-materials/src/components/json-schema-editor/components/blur-input.tsx

@@ -1,27 +0,0 @@
-/**
- * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
- * SPDX-License-Identifier: MIT
- */
-
-import React, { useEffect, useState } from 'react';
-
-import Input, { InputProps } from '@douyinfe/semi-ui/lib/es/input';
-
-export function BlurInput(props: InputProps) {
-  const [value, setValue] = useState('');
-
-  useEffect(() => {
-    setValue(props.value as string);
-  }, [props.value]);
-
-  return (
-    <Input
-      {...props}
-      value={value}
-      onChange={(value) => {
-        setValue(value);
-      }}
-      onBlur={(e) => props.onChange?.(value, e)}
-    />
-  );
-}

+ 1 - 3
packages/materials/form-materials/src/components/json-schema-editor/default-value.tsx

@@ -20,10 +20,7 @@ import { ConstantInputWrapper } from './styles';
 export function DefaultValue(props: {
   value: any;
   schema?: IJsonSchema;
-  name?: string;
-  type?: string;
   placeholder?: string;
-  jsonFormatText?: string;
   onChange: (value: any) => void;
 }) {
   const { value, schema, onChange, placeholder } = props;
@@ -35,6 +32,7 @@ export function DefaultValue(props: {
         onChange={(_v) => onChange(_v)}
         schema={schema || { type: 'string' }}
         placeholder={placeholder ?? I18n.t('Default value if parameter is not provided')}
+        enableMultiLineStr
       />
     </ConstantInputWrapper>
   );

+ 13 - 2
packages/materials/form-materials/src/components/json-schema-editor/hooks.tsx

@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: MIT
  */
 
-import { useEffect, useState } from 'react';
+import { useEffect, useRef, useState } from 'react';
 
 import { difference, omit } from 'lodash';
 import { produce } from 'immer';
@@ -28,7 +28,16 @@ export function usePropertiesEdit(
 
   const [propertyList, setPropertyList] = useState<PropertyValueType[]>([]);
 
+  const effectVersion = useRef(0);
+  const changeVersion = useRef(0);
+
   useEffect(() => {
+    effectVersion.current = effectVersion.current + 1;
+    if (effectVersion.current === changeVersion.current) {
+      return;
+    }
+    effectVersion.current = changeVersion.current;
+
     // If the value is changed, update the property list
     setPropertyList((_list) => {
       const newNames = Object.entries(drilldownSchema?.properties || {})
@@ -44,7 +53,7 @@ export function usePropertiesEdit(
           key: item.key,
           name: item.name,
           isPropertyRequired: drilldownSchema?.required?.includes(item.name || '') || false,
-          ...item,
+          ...(drilldownSchema?.properties?.[item.name || ''] || item || {}),
         }))
         .concat(
           addNames.map((_name) => ({
@@ -58,6 +67,8 @@ export function usePropertiesEdit(
   }, [drilldownSchema]);
 
   const updatePropertyList = (updater: (list: PropertyValueType[]) => PropertyValueType[]) => {
+    changeVersion.current = changeVersion.current + 1;
+
     setPropertyList((_list) => {
       const next = updater(_list);
 

+ 17 - 57
packages/materials/form-materials/src/components/json-schema-editor/index.tsx

@@ -18,6 +18,7 @@ import {
 } from '@douyinfe/semi-icons';
 
 import { InjectTypeSelector } from '@/components/type-selector';
+import { BlurInput } from '@/components/blur-input';
 
 import { ConfigType, PropertyValueType } from './types';
 import {
@@ -28,10 +29,10 @@ import {
   UIContainer,
   UIExpandDetail,
   UILabel,
-  UIProperties,
-  UIPropertyLeft,
-  UIPropertyMain,
-  UIPropertyRight,
+  UITreeItems,
+  UITreeItemLeft,
+  UITreeItemMain,
+  UITreeItemRight,
   UIRequired,
   UIType,
 } from './styles';
@@ -39,7 +40,6 @@ import { UIName } from './styles';
 import { DefaultValueWrapper, UIRow } from './styles';
 import { usePropertiesEdit } from './hooks';
 import { DefaultValue } from './default-value';
-import { BlurInput } from './components/blur-input';
 
 const DEFAULT = { type: 'object' };
 
@@ -58,14 +58,13 @@ export function JsonSchemaEditor(props: {
 
   return (
     <UIContainer className={props.className}>
-      <UIProperties>
-        {propertyList.map((_property, index) => (
+      <UITreeItems>
+        {propertyList.map((_property) => (
           <PropertyEdit
             readonly={readonly}
             key={_property.key}
             value={_property}
             config={config}
-            $index={index}
             onChange={(_v) => {
               onEditProperty(_property.key!, _v);
             }}
@@ -74,7 +73,7 @@ export function JsonSchemaEditor(props: {
             }}
           />
         ))}
-      </UIProperties>
+      </UITreeItems>
       <Button
         disabled={readonly}
         size="small"
@@ -95,27 +94,9 @@ function PropertyEdit(props: {
   onRemove?: () => void;
   readonly?: boolean;
   $isLast?: boolean;
-  $index?: number;
-  $isFirst?: boolean;
-  $parentExpand?: boolean;
-  $parentType?: string;
-  $showLine?: boolean;
   $level?: number; // 添加层级属性
 }) {
-  const {
-    value,
-    config,
-    readonly,
-    $level = 0,
-    onChange: onChangeProps,
-    onRemove,
-    $index,
-    $isFirst,
-    $isLast,
-    $parentExpand = false,
-    $parentType = '',
-    $showLine,
-  } = props;
+  const { value, config, readonly, $level = 0, onChange: onChangeProps, onRemove, $isLast } = props;
 
   const [expand, setExpand] = useState(false);
   const [collapse, setCollapse] = useState(false);
@@ -138,29 +119,15 @@ function PropertyEdit(props: {
 
   return (
     <>
-      <UIPropertyLeft
-        type={type}
-        $index={$index}
-        $isFirst={$isFirst}
-        $isLast={$isLast}
-        $showLine={$showLine}
-        $isExpand={expand}
-        $parentExpand={$parentExpand}
-        $parentType={$parentType}
-      >
+      <UITreeItemLeft $isLast={$isLast} $showLine={$level > 0} $showCollapse={showCollapse}>
         {showCollapse && (
           <UICollapseTrigger onClick={() => setCollapse((_collapse) => !_collapse)}>
             {collapse ? <IconChevronDown size="small" /> : <IconChevronRight size="small" />}
           </UICollapseTrigger>
         )}
-      </UIPropertyLeft>
-      <UIPropertyRight>
-        <UIPropertyMain
-          $showCollapse={showCollapse}
-          $collapse={collapse}
-          $expand={expand}
-          type={type}
-        >
+      </UITreeItemLeft>
+      <UITreeItemRight>
+        <UITreeItemMain>
           <UIRow>
             <UIName>
               <BlurInput
@@ -242,9 +209,7 @@ function PropertyEdit(props: {
                     <DefaultValue
                       value={defaultValue}
                       schema={value}
-                      type={type}
                       placeholder={config?.defaultValuePlaceholder ?? I18n.t('Default Value')}
-                      jsonFormatText={config?.jsonFormatText}
                       onChange={(value) => onChange('default', value)}
                     />
                   </DefaultValueWrapper>
@@ -252,10 +217,10 @@ function PropertyEdit(props: {
               )}
             </UIExpandDetail>
           )}
-        </UIPropertyMain>
+        </UITreeItemMain>
         {showCollapse && (
           <UICollapsible $collapse={collapse}>
-            <UIProperties $shrink={true}>
+            <UITreeItems $shrink={true}>
               {propertyList.map((_property, index) => (
                 <PropertyEdit
                   readonly={readonly}
@@ -263,8 +228,6 @@ function PropertyEdit(props: {
                   value={_property}
                   config={config}
                   $level={$level + 1} // 传递递增的层级
-                  $parentExpand={expand}
-                  $parentType={type}
                   onChange={(_v) => {
                     onEditProperty(_property.key!, _v);
                   }}
@@ -272,15 +235,12 @@ function PropertyEdit(props: {
                     onRemoveProperty(_property.key!);
                   }}
                   $isLast={index === propertyList.length - 1}
-                  $isFirst={index === 0}
-                  $index={index}
-                  $showLine={true}
                 />
               ))}
-            </UIProperties>
+            </UITreeItems>
           </UICollapsible>
         )}
-      </UIPropertyRight>
+      </UITreeItemRight>
     </>
   );
 }

+ 12 - 55
packages/materials/form-materials/src/components/json-schema-editor/styles.tsx

@@ -39,37 +39,30 @@ export const UILabel = styled.div`
   margin-bottom: 2px;
 `;
 
-export const UIProperties = styled.div<{ $shrink?: boolean }>`
+export const UITreeItems = styled.div<{ $shrink?: boolean }>`
   display: grid;
   grid-template-columns: auto 1fr;
 
   ${({ $shrink }) =>
     $shrink &&
     css`
-      padding-left: 10px;
+      padding-left: 3px;
       margin-top: 10px;
     `}
 `;
 
-export const UIPropertyLeft = styled.div<{
+export const UITreeItemLeft = styled.div<{
   $isLast?: boolean;
   $showLine?: boolean;
-  $isExpand?: boolean;
-  type?: string;
-  $isFirst?: boolean;
-  $index?: number;
-  $parentExpand?: boolean;
-  $parentType?: string;
+  $showCollapse?: boolean;
 }>`
   grid-column: 1;
   position: relative;
   width: 16px;
 
-  ${({ $showLine, $isLast, $parentType }) => {
-    let height = '100%';
-    if ($parentType && $isLast) {
-      height = '24px';
-    }
+  ${({ $showLine, $isLast, $showCollapse }) => {
+    let height = $isLast ? '24px' : '100%';
+    let width = $showCollapse ? '12px' : '30px';
 
     return (
       $showLine &&
@@ -79,7 +72,7 @@ export const UIPropertyLeft = styled.div<{
           content: '';
           height: ${height};
           position: absolute;
-          left: -22px;
+          left: -14px;
           top: -16px;
           width: 1px;
           background: #d9d9d9;
@@ -90,9 +83,9 @@ export const UIPropertyLeft = styled.div<{
           /* 横线 */
           content: '';
           position: absolute;
-          left: -22px; // 横线起点和竖线对齐
+          left: -14px; // 横线起点和竖线对齐
           top: 8px; // 跟随你的行高调整
-          width: 18px; // 横线长度
+          width: ${width}; // 横线长度
           height: 1px;
           background: #d9d9d9;
           display: block;
@@ -102,7 +95,7 @@ export const UIPropertyLeft = styled.div<{
   }}
 `;
 
-export const UIPropertyRight = styled.div`
+export const UITreeItemRight = styled.div`
   grid-column: 2;
   margin-bottom: 10px;
 
@@ -111,47 +104,11 @@ export const UIPropertyRight = styled.div`
   }
 `;
 
-export const UIPropertyMain = styled.div<{
-  $expand?: boolean;
-  type?: string;
-  $collapse?: boolean;
-  $showCollapse?: boolean;
-}>`
+export const UITreeItemMain = styled.div<{}>`
   display: flex;
   flex-direction: column;
   gap: 10px;
   position: relative;
-
-  ${({ $expand, type, $collapse, $showCollapse }) => {
-    const beforeElement = `
-      &::before {
-        /* 竖线 */
-        content: '';
-        height: 100%;
-        position: absolute;
-        left: -12px;
-        top: 18px;
-        width: 1px;
-        background: #d9d9d9;
-        display: block;
-      }`;
-
-    return (
-      $expand &&
-      css`
-        background-color: #f5f5f5;
-        padding: 10px;
-        border-radius: 4px;
-
-        ${$showCollapse &&
-        $collapse &&
-        (type === 'array' || type === 'object') &&
-        css`
-          ${beforeElement}
-        `}
-      `
-    );
-  }}
 `;
 
 export const UICollapsible = styled.div<{ $collapse?: boolean }>`

+ 0 - 1
packages/materials/form-materials/src/components/json-schema-editor/types.ts

@@ -22,5 +22,4 @@ export interface ConfigType {
   defaultValueTitle?: string;
   defaultValuePlaceholder?: string;
   addButtonText?: string;
-  jsonFormatText?: string;
 }

+ 16 - 1
packages/materials/form-materials/src/hooks/use-object-list/index.tsx

@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: MIT
  */
 
-import { useEffect, useState } from 'react';
+import { useEffect, useRef, useState } from 'react';
 
 import { nanoid } from 'nanoid';
 import { difference, get, isObject, set } from 'lodash';
@@ -31,7 +31,16 @@ export function useObjectList<ValueType>({
 }) {
   const [list, setList] = useState<ListItem<ValueType>[]>([]);
 
+  const effectVersion = useRef(0);
+  const changeVersion = useRef(0);
+
   useEffect(() => {
+    effectVersion.current = effectVersion.current + 1;
+    if (effectVersion.current === changeVersion.current) {
+      return;
+    }
+    effectVersion.current = changeVersion.current;
+
     setList((_prevList) => {
       const newKeys = Object.entries(value || {})
         .sort((a, b) => get(a[1], sortIndexKey || 0) - get(b[1], sortIndexKey || 0))
@@ -67,6 +76,8 @@ export function useObjectList<ValueType>({
   };
 
   const updateValue = (itemId: string, value: ValueType) => {
+    changeVersion.current = changeVersion.current + 1;
+
     setList((prevList) => {
       const nextList = prevList.map((_item) => {
         if (_item.id === itemId) {
@@ -97,6 +108,8 @@ export function useObjectList<ValueType>({
   };
 
   const updateKey = (itemId: string, key: string) => {
+    changeVersion.current = changeVersion.current + 1;
+
     setList((prevList) => {
       const nextList = prevList.map((_item) => {
         if (_item.id === itemId) {
@@ -119,6 +132,8 @@ export function useObjectList<ValueType>({
   };
 
   const remove = (itemId: string) => {
+    changeVersion.current = changeVersion.current + 1;
+
     setList((prevList) => {
       const nextList = prevList.filter((_item) => _item.id !== itemId);
 

+ 0 - 5
packages/materials/form-materials/src/plugins/disable-declaration-plugin/config.json

@@ -1,5 +0,0 @@
-{
-  "name": "disable-declaration-plugin",
-  "depMaterials": [],
-  "depPackages": []
-}

+ 0 - 9
packages/materials/form-materials/src/plugins/json-schema-preset/config.json

@@ -1,9 +0,0 @@
-{
-  "name": "json-schema-preset",
-  "depMaterials": [
-    "components/code-editor-mini"
-  ],
-  "depPackages": [
-    "@flowgram.ai/json-schema"
-  ]
-}

+ 1 - 0
packages/materials/form-materials/src/plugins/json-schema-preset/manager.ts

@@ -9,6 +9,7 @@ export interface ConstantRendererProps<Value = any> {
   value?: Value;
   onChange?: (value: Value) => void;
   readonly?: boolean;
+  [key: string]: any;
 }
 export interface JsonSchemaTypeRegistry<Value = any> extends OriginJsonSchemaTypeRegistry {
   /**

+ 18 - 9
packages/materials/form-materials/src/plugins/json-schema-preset/type-definition/string.tsx

@@ -7,18 +7,27 @@
 import React from 'react';
 
 import { I18n } from '@flowgram.ai/editor';
-import { Input } from '@douyinfe/semi-ui';
+import { Input, TextArea } from '@douyinfe/semi-ui';
 
 import { type JsonSchemaTypeRegistry } from '../manager';
 
 export const stringRegistry: Partial<JsonSchemaTypeRegistry> = {
   type: 'string',
-  ConstantRenderer: (props) => (
-    <Input
-      placeholder={I18n.t('Please Input String')}
-      size="small"
-      disabled={props.readonly}
-      {...props}
-    />
-  ),
+  ConstantRenderer: (props) =>
+    props?.enableMultiLineStr ? (
+      <TextArea
+        autosize
+        rows={1}
+        placeholder={I18n.t('Please Input String')}
+        disabled={props.readonly}
+        {...props}
+      />
+    ) : (
+      <Input
+        size="small"
+        placeholder={I18n.t('Please Input String')}
+        disabled={props.readonly}
+        {...props}
+      />
+    ),
 };