Browse Source

fix: nested arrayfield

pointhalo 2 years ago
parent
commit
5301cc788a

+ 2 - 1
package.json

@@ -82,6 +82,7 @@
         "react-infinite-scroller": "^1.2.4",
         "react-inlinesvg": "^1.2.0",
         "react-intl": "^3.12.1",
+        "react-json-tree": "^0.18.0",
         "react-lazyload": "^3.2.0",
         "react-resizable": "^1.11.1",
         "react-scroll-parallax": "^2.4.0",
@@ -227,4 +228,4 @@
         ]
     },
     "license": "MIT"
-}
+}

+ 45 - 20
packages/semi-foundation/form/foundation.ts

@@ -131,30 +131,51 @@ export default class FormFoundation extends BaseFoundation<BaseFormAdapter> {
         this._adapter.forceUpdate();
     }
 
-    // in order to slove byted-issue-289
     registerArrayField(arrayFieldPath: string, { initValue, forceUpdate }: Pick<ArrayFieldStaff, 'initValue' | 'forceUpdate'> ): void {
         //  save initValue of arrayField, will be use when calling rest
-        // this.updateArrayField(arrayFieldPath, {
-        //     updateKey: new Date().valueOf(),
-        //     initValue: initValue,
-        // });
         this.registeredArrayField.set(arrayFieldPath, { field: arrayFieldPath, initValue: initValue, forceUpdate });
     }
+    
 
-    unRegisterArrayField(arrayField: string): void {
-        this.registeredArrayField.delete(arrayField);
+    unRegisterArrayField(arrayFieldPath: string): void {
+        this.registeredArrayField.delete(arrayFieldPath);
+        console.log('remain', this.registeredArrayField);
     }
 
-    getArrayField(arrayField: string): ArrayFieldStaff {
-        return this.registeredArrayField.get(arrayField);
-    }
-
-    updateArrayField(arrayField: string, updateStaff?: Omit<ArrayFieldStaff, 'field'>): void {
-        const arrayFieldStaff = this.getArrayField(arrayField);
-        const mergeStaff = { ...arrayFieldStaff, ...updateStaff };
-        this.registeredArrayField.set(arrayField, mergeStaff);
-        mergeStaff.forceUpdate(mergeStaff?.updateValue);
-    }
+    getArrayField(arrayFieldPath?: string): ArrayFieldStaff | Map<string, ArrayFieldStaff> {
+        if (!arrayFieldPath) {
+            return this.registeredArrayField;
+        }
+        return this.registeredArrayField.get(arrayFieldPath);
+    }
+
+    // updateArrayField(arrayFieldPath: string, updateStaff?: Omit<ArrayFieldStaff, 'field'>): void {
+    //     const arrayFieldStaff = this.getArrayField(arrayFieldPath);
+    //     const mergeStaff = { ...arrayFieldStaff, ...updateStaff };
+    //     this.registeredArrayField.set(arrayFieldPath, mergeStaff);
+    //     mergeStaff.forceUpdate(mergeStaff?.updateValue);
+    // }
+
+    updateArrayField(arrayFieldPath: string, { newValue, oldValue }: any): void {
+        const arrayFieldStaff = this.getArrayField(arrayFieldPath);
+        arrayFieldStaff.forceUpdate({ newValue, oldValue });
+        // // check is nested ArrayField
+        // const nested = this._getNestedField(arrayFieldPath);
+        // const hasParent;
+        // const hasChild;
+        // if (hasParent) {
+        //     // update parent cache value
+        // }
+        // if (hasChild) {
+        //     // update child cache value
+        // }
+        // mergeStaff.forceUpdate(updateDetail);
+    }
+
+    // 使用formApi.setValue等手段,父改子,或者子改父时,需要更新 cache
+    // updateArrayFieldCache(arrayFieldPath: string, ): void {
+    //     const arrayFieldStaff = this.getArrayField(arrayFieldPath);
+    // }
 
     validate(fieldPaths?: Array<string>): Promise<unknown> {
         const { validateFields } = this.getProps();
@@ -341,10 +362,11 @@ export default class FormFoundation extends BaseFoundation<BaseFormAdapter> {
         const arrayFieldPaths = [...this.registeredArrayField.keys()];
         arrayFieldPaths.forEach(path => {
             const arrayFieldState = this.registeredArrayField.get(path);
+            const oldValue= ObjectUtil.get(this.data.values, path);
             // clone prevent dom unmounted cause initValue lost
             const arrayFieldInitValue = this._adapter.cloneDeep(arrayFieldState.initValue);
             this.updateStateValue(path, arrayFieldInitValue, { notNotify: true, notUpdate: true });
-            this.updateArrayField(path, { updateValue: arrayFieldInitValue });
+            this.updateArrayField(path, { newValue: arrayFieldInitValue, oldValue });
         });
     }
 
@@ -404,7 +426,7 @@ export default class FormFoundation extends BaseFoundation<BaseFormAdapter> {
             arrayFieldPaths.forEach(path => {
                 this.updateArrayField(path, {
                     // updateKey: new Date().valueOf(),
-                    updateValue: ObjectUtil.get(_values, path)
+                    newValue: ObjectUtil.get(_values, path)
                 });
             });
         }
@@ -518,6 +540,9 @@ export default class FormFoundation extends BaseFoundation<BaseFormAdapter> {
                 // At this time, first modify formState directly, then find out the subordinate fields and drive them to update
                 // Eg: peoples: [0, 2, 3]. Each value of the peoples array corresponds to an Input Field
                 // When the user directly calls formA pi.set Value ('peoples', [2,3])
+                
+                const oldValue = ObjectUtil.get(this.data.values, field);
+
                 this.updateStateValue(field, newValue, opts, () => {
                     let nestedFields = this._getNestedField(field);
                     if (nestedFields.size) {
@@ -532,7 +557,7 @@ export default class FormFoundation extends BaseFoundation<BaseFormAdapter> {
 
                 // If the reset happens to be, then update the updateKey corresponding to ArrayField to render it again
                 if (this.getArrayField(field)) {
-                    this.updateArrayField(field, { updateValue: newValue });
+                    this.updateArrayField(field, { newValue: newValue, oldValue });
                 }
             }
         };

+ 7 - 2
packages/semi-foundation/form/interface.ts

@@ -111,12 +111,17 @@ export interface FieldStaff {
     allowEmpty: boolean
 }
 
+interface forceUpdateParams {
+    value?: any;
+    oldValue?: any
+}
+
 export interface ArrayFieldStaff {
     field: string;
     // updateKey?: string;
-    updateValue?: any;
+    // updateValue?: any;
     initValue?: any;
-    forceUpdate?: (value: any) => void
+    forceUpdate?: (forceUpdateParams: forceUpdateParams) => void
 }
 export interface FormUpdaterContextType {
     register: (field: string, fieldState: FieldState, fieldStuff: FieldStaff) => void;

+ 60 - 23
packages/semi-ui/form/_story/ArrayField/6-nestedBasicUsage.jsx

@@ -1,6 +1,18 @@
 import React, { useState, useRef } from 'react';
-import { Form, Col, Row, Button, ArrayField, Space } from '@douyinfe/semi-ui';
+import { Form, Col, Row, Button, ArrayField, Space, useFormState } from '@douyinfe/semi-ui';
 import { IconMinusCircle, IconPlusCircle, IconRefresh } from '@douyinfe/semi-icons';
+import { JSONTree } from 'react-json-tree';
+
+const FormStateTree = () => {
+    const formState = useFormState();
+    return (
+        <JSONTree
+            shouldExpandNodeInitially={() => true}
+            hideRoot
+            data={formState}
+        />
+    );
+};
 
 const NestDemo = () => {
     const formRef = useRef();
@@ -28,10 +40,45 @@ const NestDemo = () => {
         ]
     };
 
-    // const change = () => {
-        
-    // };
+    const setValueChangeOutSide = () => {
+        const formApi = getFormApi();
+        formApi.setValue('data', [
+            {
+                name: 'new-0',
+                rules: [
+                    { desc: 'new-0-0-desc', type: 'new-0-0-type' },
+                    { desc: 'new-0-1-desc', type: 'new-0-1-type' },
+                ],
+            },
+            {
+                name: 'new-1',
+                rules: [
+                    { desc: 'new-1-0-desc', type: 'new-1-0-type' },
+                ],
+            },
+            {
+                name: 'new-2',
+                rules: [
+                    { desc: 'new-2-0-desc', type: 'new-2-0-type' },
+                    { desc: 'new-2-1-desc', type: 'new-2-1-type' },
+                    { desc: 'new-2-2-desc', type: 'new-2-2-type' },
+                ],
+            }
+        ]);
+    };
+
+    // ❌ something still wrong when first click outside and click inside after
+    // ❌ something still wrong when first click outside and click inside after and click outside again
+    const setValueChangeInside = () => {
+        const formApi = getFormApi();
+        formApi.setValue('data[0].rules', [{ desc: 'new-0-0-desc-in', type: 'new-0-0-type-in' }]);
+    };
 
+    const setValueChangeRow = () => {
+        const formApi = getFormApi();
+        formApi.setValue('data[0].rules[0]', { name: 'special-row-name', rules: [{ desc: 'abc', type: 'efg' }] });
+    };
+    
     return (
         <Form
             ref={formRef}
@@ -43,11 +90,13 @@ const NestDemo = () => {
                     <React.Fragment>
                         <Space>
                             <Button htmlType='reset' theme='solid' type='secondary' icon={<IconRefresh />}>Reset</Button>
+                            <Button id='changeOutSide' theme='solid' type='primary' onClick={() => setValueChangeOutSide()}>ChangeOutSide</Button>
+                            <Button id='changeInside' theme='solid' type='tertiary' onClick={() => setValueChangeInside()}>ChangeInsideSide</Button>
                         </Space>
                         {
                             arrayFields.map(({ field, key, remove }, i) => (
-                                <div key={key} style={{ width: 800, display: 'flex', flexWrap: 'wrap' }} id={`data-${i}`} className='line'>
-                                    {/* {key} */}
+                                <div key={key} style={{ width: 1000, display: 'flex', flexWrap: 'wrap' }} id={`data-${i}`} className='line'>
+                                    {key.slice(0, 10)}
                                     <Space>
                                         <Form.Input
                                             field={`${field}.name`}
@@ -82,15 +131,15 @@ const NestDemo = () => {
                                                     nestedArrayFields.map(({ field: f, key: k, remove: r }, n) => (
                                                         <section className='rules' key={k} style={{ display: 'flex', flexWrap: 'wrap', marginLeft: 36 }}>
                                                             <Space>
-                                                                {/* {k} */}
+                                                                {k.slice(0, 10)}
                                                                 <Form.Input
-                                                                    style={{ width: 280, marginRight: 12 }}
+                                                                    style={{ width: 300, marginRight: 12 }}
                                                                     field={`${f}.type`}
                                                                     label={`${f}.type`}
                                                                     id={`data-${i}-rule-${n}-type`} 
                                                                 />
                                                                 <Form.Input
-                                                                    style={{ width: 280 }}
+                                                                    style={{ width: 300 }}
                                                                     field={`${f}.desc`}
                                                                     label={`${f}.desc`}
                                                                     id={`data-${i}-rule-${n}-desc`} 
@@ -122,23 +171,11 @@ const NestDemo = () => {
                     </React.Fragment>
                 )}
             </ArrayField>
+            <FormStateTree></FormStateTree>
         </Form>
     );
 };
 
 NestDemo.storyName = 'ArrayField-Nested Level 2';
 
-export default NestDemo;
-
-// class NestArrayField extends React.Component {
-//     change = () => {
-//         let number = this.formApi.getValue('number');
-//         let newData = {
-//             group: [
-//                 { name: Math.random().toString().slice(0, 3), items: [ { itemName: Math.random(), type: '0-1' } ] },
-//                 // { name: Math.random(), items: [ { itemName: Math.random(), type: '0-1' } ] },
-//             ]
-//         };
-//         this.formApi.setValues(newData, { isOverride: true });
-//     }
-// }
+export default NestDemo;

+ 18 - 3
packages/semi-ui/form/_story/DynamicField/nestArrayField.jsx

@@ -120,12 +120,21 @@ class NestArrayField extends React.Component {
 
 
 class Child extends React.Component {
-    constructor(props){
+    constructor(props) {
         super(props);
         this.state = {
             ts: format(new Date(), 'yyyy-MM-dd HH:mm:ss')
         };
     }
+
+    componentDidMount () {
+        console.log('did mount');
+    };
+
+    componentWillUnmount() {
+        console.log('will un mount');
+    }
+
     render() {
         return <div>
             {this.state.ts}
@@ -134,7 +143,7 @@ class Child extends React.Component {
 }
 
 class Parent extends React.Component {
-    constructor(props){
+    constructor(props) {
         super(props);
         this.click = this.click.bind(this);
     }
@@ -142,6 +151,11 @@ class Parent extends React.Component {
     click() {
         this.forceUpdate();
     }
+
+    changeLineKey() {
+        this.forceUpdate();
+    }
+
     render() {
         return <div>
             {[1, 2, 3].map(i => (
@@ -149,7 +163,8 @@ class Parent extends React.Component {
                     <Child></Child>
                 </div>
             ))}
-            <Button onClick={this.click}>change</Button>
+            <Button onClick={this.click}>forceUpdate</Button>
+            <Button onClick={this.changeLineKey}>changeLineKey</Button>
         </div>;
     }
 }

+ 56 - 51
packages/semi-ui/form/arrayField.tsx

@@ -30,17 +30,6 @@ const filterArrayByIndex = (array: any[], index: number) => array.filter((item,
 
 const getUuidByArray = (array: any[]) => array.map(() => getUuidv4());
 
-// const getUpdateKey = (arrayField: ArrayFieldStaff): string | undefined => {
-//     if (!arrayField) {
-//         return undefined;
-//     }
-//     // TODO
-//     if (arrayField && arrayField.updateKey) {
-//         return arrayField.updateKey;
-//     }
-//     return undefined;
-// };
-
 const initValueAdapter = (initValue: any) => {
     const iv: any[] = [];
     if (Array.isArray(initValue)) {
@@ -60,14 +49,23 @@ const initValueAdapter = (initValue: any) => {
  * @param {string[]} oldKeys
  * @param {any[]} cacheValue
  * @returns string[]
+ * 
+ * 2 -> 3,理想情况,看加的哪个,复用2个
+ * 3 -> 2, 理想情况,看删的哪个,减少,
+ * 2 -> 2, 看值变不变
+ *   嵌套场景,外部变不变无所谓,里面尽量不变,否则会走卸载。除非判断出嵌套时候
+ *   外部 setValue、setValues 变更,不易判断改的是哪行
+ *    如果只有自己一层,那都变问题也不大。如果有两层以上,变就会导致里面一层卸载再挂载。
  */
+
 const generateKeys = (value: any[] = [], oldKeys?: string[], cacheValues: any[] = []) => {
     const val = initValueAdapter(value);
     const newKeys = getUuidByArray(val);
-    const keys = [];
+    // const keys = newKeys.map((key, i) => (oldKeys && oldKeys[i] ? oldKeys[i] : key));
 
-    //  todo cacheValue 与 oldKeys 是对不上的
+    const keys = [];
 
+    // Keys 的复用机制需要
     value.forEach((newRow, i) => {
         const cacheRow = get(cacheValues, i);
         if (!isEqual(newRow, cacheRow)) {
@@ -75,17 +73,15 @@ const generateKeys = (value: any[] = [], oldKeys?: string[], cacheValues: any[]
         } else {
             keys[i] = oldKeys && oldKeys[i] ? oldKeys[i] : newKeys[i];
         }
-
     });
+
     return keys;
 };
 
 class ArrayFieldComponent extends Component<ArrayFieldProps, ArrayFieldState> {
     static contextType = FormUpdaterContext;
 
-    cacheFieldValues: any[] | null;
-    shouldUseInitValue: boolean;
-    // cacheUpdateKey: string;
+    hasMounted: boolean;
     context: FormUpdaterContextType;
 
     constructor(props: ArrayFieldProps, context: FormUpdaterContextType) {
@@ -94,20 +90,20 @@ class ArrayFieldComponent extends Component<ArrayFieldProps, ArrayFieldState> {
         const { field } = this.props;
         const initValueInForm = context.getValue(field);
         const initValue = initValueInProps || initValueInForm;
+
+        this.hasMounted = Boolean(context.getArrayField(field));
+
         this.state = {
             keys: generateKeys(initValue),
         };
         this.add = this.add.bind(this);
         this.addWithInitValue = this.addWithInitValue.bind(this);
         this.remove = this.remove.bind(this);
-        this.cacheFieldValues = initValue;
-
         // /*
         //     If updateKey exists, it means that the arrayField (usually a nested ArrayField not at the first level) is only re-mounted due to setValues,
         //     and the fields it contains do not need to consume initValue
         // */
         // // whether the fields inside arrayField should use props.initValue in current render process
-        // this.shouldUseInitValue = !context.getArrayField(field);
 
         // Separate the arrays that reset and the usual add and remove modify, otherwise they will affect each other
         const initValueCopyForFormState = cloneDeep(initValue);
@@ -117,49 +113,57 @@ class ArrayFieldComponent extends Component<ArrayFieldProps, ArrayFieldState> {
     }
 
     componentDidMount() {
-        // const { field } = this.props;
-        // const updater = this.context;
-    //     updater.updateArrayField(field, { forceUpdate: this.forceUpdate });
+        const { field } = this.props;
+        const initValueInArrayFieldProps = this.props.initValue;
+        const updater = this.context;
+
+        const formProps = updater.getFormProps(['initValues']);
+        const initValueInFormProps = get(formProps, field);
+
+        const initValueInFormState = updater.getValue(field);
+        
+        const initValue = initValueInArrayFieldProps || initValueInFormProps || initValueInFormState;
+
+        const initValueCopyForFormState = cloneDeep(initValue);
+        const initValueCopyForReset = cloneDeep(initValue);
+
+        // 如果首次挂载,应该使用初始值,如果不是首次挂载,例如嵌套场景下,level 1 keys变更导致的 level 2子级重新挂载,那应该直接使用formState 中的值 , 且无需注册 initValue(否则会影响保存的 initValue)
+        if (!this.hasMounted) {
+            updater.registerArrayField(field, { initValue: initValueCopyForReset, forceUpdate: this.forceKeysUpdate });
+            updater.updateStateValue(field, initValueCopyForFormState, { notNotify: true, notUpdate: true });
+        } else {
+            if (field === 'data[0].rules') {
+                const a = updater.getArrayField();
+                console.log('data[0].rules didmount');
+            }
+            updater.registerArrayField(field, { forceUpdate: this.forceKeysUpdate });
+        }
+        console.log('didMount', this.props.field);
     }
 
     componentWillUnmount() {
         const updater = this.context;
         const { field } = this.props;
+        // 卸载时,不卸载 initValue?
+        // 嵌套的不做卸载?
         updater.unRegisterArrayField(field);
+        console.log('unmount', this.props.field);
     }
 
-    // componentDidUpdate() {
-    //     const updater = this.context;
-    //     // const { field } = this.props;
-    //     // const { keys } = this.state;
-    //     // const fieldValues = updater.getValue(field);
-    //     // const updateKey = getUpdateKey(updater.getArrayField(field));
-    //     // // when update form outside, like use formApi.setValue('field', [{newItem1, newItem2}]),  formApi.setValues
-    //     // // re generate keys to update arrayField;
-    //     // if (updateKey !== this.cacheUpdateKey) {
-    //     //     const newKeys = generateKeys(fieldValues, keys);
-    //     //     // eslint-disable-next-line
-    //     //     this.setState({ keys: newKeys });
-    //     //     this.cacheUpdateKey = updateKey;
-    //     //     if (this.cacheUpdateKey !== null) {
-    //     //         this.shouldUseInitValue = false;
-    //     //     }
-    //     // } else {
-    //     //     console.log('not update');
-    //     // }
-    // }
-
-    forceUpdate = (value?: any): void => {
+    forceKeysUpdate = ({ newValue, oldValue }): void => {
         const updater = this.context;
         const { field } = this.props;
         const { keys } = this.state;
-        console.log(this.cacheFieldValues);
-        const fieldValues = value ? value : updater.getValue(field);
-        // TODO fieldValues 如果长度相同,keys目前仍会相同,需要为新的
-        const newKeys = generateKeys(fieldValues, keys, this.cacheFieldValues);
+
+        const fieldValues = newValue ? newValue : updater.getValue(field);
+        const newKeys = generateKeys(fieldValues, keys, oldValue);
+        // const newKeys = generateKeys(fieldValues, keys, this.cacheFieldValues);
+
         // eslint-disable-next-line
         this.setState({ keys: newKeys });
-        this.cacheFieldValues = [...value];
+        // A: A1、A2   ->  A: A1-new  -> A: A1、A2
+        // 如果 forecUpdate child,改变了 child 的length,但是这个无法更新父级的 cacheFieldValue。会有问题, cache不是一个好的设计,要做完善还得在 setValue时检测潜在的对父子 cacheValue的影响
+        // this.cacheFieldValues = cloneDeep(value);
     }
 
     add() {
@@ -204,7 +208,7 @@ class ArrayFieldComponent extends Component<ArrayFieldProps, ArrayFieldState> {
             updater.updateStateValue(field, newArrayFieldValue);
         }
         this.setState({ keys: newKeys });
-        this.cacheFieldValues = [...newArrayFieldValue];
+        // this.cacheFieldValues = cloneDeep(newArrayFieldValue);
     }
 
     render() {
@@ -218,6 +222,7 @@ class ArrayFieldComponent extends Component<ArrayFieldProps, ArrayFieldState> {
         const { add } = this;
         const { addWithInitValue } = this;
         const contextVal = {
+            isInArrayField: true,
             // shouldUseInitValue: this.shouldUseInitValue,
         };
         return (

+ 1 - 1
packages/semi-ui/form/hoc/withField.tsx

@@ -101,7 +101,7 @@ function withField<
         }
 
         // To prevent user forgetting to pass the field, use undefined as the key, and updater.getValue will get the wrong value.
-        let initValueInFormOpts = typeof field !== 'undefined' ? updater.getValue(field) : undefined; // Get the init value of form from formP rops.init Values Get the initial value set in the initValues of Form
+        let initValueInFormOpts = typeof field !== 'undefined' ? updater.getValue(field) : undefined; // Get the init value of form from formProps.init Values Get the initial value set in the initValues of Form
         let initVal = typeof initValue !== 'undefined' ? initValue : initValueInFormOpts;
 
         // use arrayFieldState to fix issue 615

+ 60 - 1
yarn.lock

@@ -1199,6 +1199,13 @@
   dependencies:
     regenerator-runtime "^0.13.4"
 
+"@babel/runtime@^7.16.7", "@babel/runtime@^7.20.6":
+  version "7.21.5"
+  resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.5.tgz#8492dddda9644ae3bda3b45eabe87382caee7200"
+  integrity sha512-8jI69toZqqcsnqGGqwGS4Qb1VwLOEp4hz+CXPywcvjs60u3B4Pom/U/7rm4W8tMOYEB+E9wgD0mW1l3r8qlI9Q==
+  dependencies:
+    regenerator-runtime "^0.13.11"
+
 "@babel/standalone@^7.12.6":
   version "7.17.9"
   resolved "https://registry.npmjs.org/@babel/standalone/-/standalone-7.17.9.tgz"
@@ -4824,6 +4831,11 @@
   dependencies:
     "@babel/types" "^7.3.0"
 
+"@types/base16@^1.0.2":
+  version "1.0.2"
+  resolved "https://registry.npmjs.org/@types/base16/-/base16-1.0.2.tgz#eb3a07db52309bfefb9ba010dfdb3c0784971f65"
+  integrity sha512-oYO/U4VD1DavwrKuCSQWdLG+5K22SLPem2OQaHmFcQuwHoVeGC+JGVRji2MUqZUAIQZHEonOeVfAX09hYiLsdg==
+
 "@types/buble@^0.20.0":
   version "0.20.1"
   resolved "https://registry.npmjs.org/@types/buble/-/buble-0.20.1.tgz"
@@ -5082,6 +5094,11 @@
   resolved "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.182.tgz"
   integrity sha512-/THyiqyQAP9AfARo4pF+aCGcyiQ94tX/Is2I7HofNRqoYLgN1PBoOWu2/zTA5zMxzP5EFutMtWtGAFRKUe961Q==
 
+"@types/lodash@^4.14.178", "@types/lodash@^4.14.191":
+  version "4.14.194"
+  resolved "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.194.tgz#b71eb6f7a0ff11bff59fc987134a093029258a76"
+  integrity sha512-r22s9tAS7imvBt2lyHC9B8AGwWnXaYb1tY09oyLkXDs4vArpYJzw09nj8MLx5VfciBPGIb+ZwG0ssYnEPJxn/g==
+
 "@types/lolex@^2.1.3":
   version "2.1.3"
   resolved "https://registry.npmjs.org/@types/lolex/-/lolex-2.1.3.tgz"
@@ -6979,6 +6996,11 @@ balanced-match@^2.0.0:
   resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-2.0.0.tgz"
   integrity sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==
 
+base16@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.npmjs.org/base16/-/base16-1.0.0.tgz#e297f60d7ec1014a7a971a39ebc8a98c0b681e70"
+  integrity sha512-pNdYkNPiJUnEhnfXV56+sQy8+AaPcG3POZAUnwr4EeqCUZFz4u2PePbo3e5Gj4ziYPCWGUZT9RHisvJKnwFuBQ==
+
 [email protected]:
   version "0.1.4"
   resolved "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz"
@@ -8259,7 +8281,7 @@ color-support@^1.1.2, color-support@^1.1.3:
   resolved "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz"
   integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==
 
-color@^3.0.0:
+color@^3.0.0, color@^3.2.1:
   version "3.2.1"
   resolved "https://registry.npmjs.org/color/-/color-3.2.1.tgz"
   integrity sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==
@@ -9210,6 +9232,11 @@ csstype@^2.5.7:
   resolved "https://registry.npmjs.org/csstype/-/csstype-2.6.20.tgz"
   integrity sha512-/WwNkdXfckNgw6S5R125rrW8ez139lBHWouiBvX8dfMFtcn6V81REDqnH7+CRpRipfYlyU1CmOnOxrmGcFOjeA==
 
+csstype@^3.0.10:
+  version "3.1.2"
+  resolved "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz#1d4bf9d572f11c14031f0436e1c10bc1f571f50b"
+  integrity sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==
+
 csstype@^3.0.2:
   version "3.0.11"
   resolved "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz"
@@ -16323,6 +16350,11 @@ [email protected], lodash.clonedeep@^4.5.0:
   resolved "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz"
   integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=
 
+lodash.curry@^4.1.1:
+  version "4.1.1"
+  resolved "https://registry.npmjs.org/lodash.curry/-/lodash.curry-4.1.1.tgz#248e36072ede906501d75966200a86dab8b23170"
+  integrity sha512-/u14pXGviLaweY5JI0IUzgzF2J6Ne8INyzAZjImcryjgkZ+ebruBxy2/JaOOkTqScddcYtakjhSaeemV8lR0tA==
+
 lodash.debounce@^4.0.6, lodash.debounce@^4.0.8:
   version "4.0.8"
   resolved "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz"
@@ -20511,6 +20543,19 @@ re-resizable@^6.9.0:
   resolved "https://registry.npmjs.org/re-resizable/-/re-resizable-6.9.9.tgz"
   integrity sha512-l+MBlKZffv/SicxDySKEEh42hR6m5bAHfNu3Tvxks2c4Ah+ldnWjfnVRwxo/nxF27SsUsxDS0raAzFuJNKABXA==
 
+react-base16-styling@^0.9.1:
+  version "0.9.1"
+  resolved "https://registry.npmjs.org/react-base16-styling/-/react-base16-styling-0.9.1.tgz#4906b4c0a51636f2dca2cea8b682175aa8bd0c92"
+  integrity sha512-1s0CY1zRBOQ5M3T61wetEpvQmsYSNtWEcdYzyZNxKa8t7oDvaOn9d21xrGezGAHFWLM7SHcktPuPTrvoqxSfKw==
+  dependencies:
+    "@babel/runtime" "^7.16.7"
+    "@types/base16" "^1.0.2"
+    "@types/lodash" "^4.14.178"
+    base16 "^1.0.0"
+    color "^3.2.1"
+    csstype "^3.0.10"
+    lodash.curry "^4.1.1"
+
 react-clipboard.js@^2.0.16:
   version "2.0.16"
   resolved "https://registry.npmjs.org/react-clipboard.js/-/react-clipboard.js-2.0.16.tgz"
@@ -20740,6 +20785,15 @@ react-is@^17.0.2:
   resolved "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz"
   integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
 
+react-json-tree@^0.18.0:
+  version "0.18.0"
+  resolved "https://registry.npmjs.org/react-json-tree/-/react-json-tree-0.18.0.tgz#3c4bec7b091f50dcc9c09652d89c8f4373ebf3ea"
+  integrity sha512-Qe6HKSXrr++n9Y31nkRJ3XvQMATISpqigH1vEKhLwB56+nk5thTP0ITThpjxY6ZG/ubpVq/aEHIcyLP/OPHxeA==
+  dependencies:
+    "@babel/runtime" "^7.20.6"
+    "@types/lodash" "^4.14.191"
+    react-base16-styling "^0.9.1"
+
 react-lazyload@^3.2.0:
   version "3.2.0"
   resolved "https://registry.npmjs.org/react-lazyload/-/react-lazyload-3.2.0.tgz"
@@ -21226,6 +21280,11 @@ regenerator-runtime@^0.11.0:
   resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz"
   integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==
 
+regenerator-runtime@^0.13.11:
+  version "0.13.11"
+  resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9"
+  integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==
+
 regenerator-runtime@^0.13.4, regenerator-runtime@^0.13.7:
   version "0.13.9"
   resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz"