diff --git a/packages/json-schema-editor/package-lock.json b/packages/json-schema-editor/package-lock.json
index 43b5fa7..0433955 100644
--- a/packages/json-schema-editor/package-lock.json
+++ b/packages/json-schema-editor/package-lock.json
@@ -619,6 +619,11 @@
"@babel/runtime": "^7.3.1"
}
},
+ "immer": {
+ "version": "6.0.5",
+ "resolved": "https://registry.npmjs.org/immer/-/immer-6.0.5.tgz",
+ "integrity": "sha512-Q2wd90qrgFieIpLzAO2q9NLEdmyp/sr76Ml4Vm5peUKgyTa2CQa3ey8zuzwSKOlKH7grCeGBGUcLLVCVW1aguA=="
+ },
"import-fresh": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz",
diff --git a/packages/json-schema-editor/package.json b/packages/json-schema-editor/package.json
index 58de1d3..be69ca1 100644
--- a/packages/json-schema-editor/package.json
+++ b/packages/json-schema-editor/package.json
@@ -59,6 +59,7 @@
"gitHead": "60b114ff262513544204f33a9bbcb6220d6c1c5d",
"dependencies": {
"i18next": "^19.4.4",
+ "immer": "^6.0.5",
"lodash-es": "^4.17.15",
"react-i18next": "^11.4.0",
"react-tracked": "^1.3.0"
diff --git a/packages/json-schema-editor/src/components/field-input.tsx b/packages/json-schema-editor/src/components/field-input.tsx
index 4721761..4e6b9b6 100644
--- a/packages/json-schema-editor/src/components/field-input.tsx
+++ b/packages/json-schema-editor/src/components/field-input.tsx
@@ -15,7 +15,8 @@ const CustomInput = styled(Input)`
}
`;
-function FieldInput({ value: initValue, changeField, addonAfter }: FieldInputProps): JSX.Element {
+function FieldInput(props: FieldInputProps, ref: React.Ref): JSX.Element {
+ const { value: initValue, changeField, addonAfter } = props;
const [value, setValue] = React.useState(initValue);
const handleChange = (e: React.ChangeEvent): void => {
@@ -37,6 +38,7 @@ function FieldInput({ value: initValue, changeField, addonAfter }: FieldInputPro
return (
(null);
+
const { t } = useTranslation();
const schemaDispatch = useSchemaDispatch();
const setUIState = useSetUIState();
@@ -46,10 +49,15 @@ function SchemaFormItem({
};
const toggleRequired = (): void => {
+ if (!fieldRef.current) return;
+
+ const newField = fieldRef.current.state.value;
+ const fieldKeyRoute = keyRoute.slice(0, -1).concat(newField);
+
if (required) {
- schemaDispatch({ type: 'REMOVE_REQUIRED', keyRoute });
+ schemaDispatch({ type: 'REMOVE_REQUIRED', keyRoute: fieldKeyRoute });
} else {
- schemaDispatch({ type: 'ADD_REQUIRED', keyRoute });
+ schemaDispatch({ type: 'ADD_REQUIRED', keyRoute: fieldKeyRoute });
}
};
@@ -66,9 +74,7 @@ function SchemaFormItem({
};
const changeField = (value: string): void => {
- setTimeout(() => {
- schemaDispatch({ type: 'CHANGE_FIELD', keyRoute, field: value });
- }, 500);
+ schemaDispatch({ type: 'CHANGE_FIELD', keyRoute, field: value });
};
const changeType = (value: SchemaType): void => {
@@ -94,6 +100,7 @@ function SchemaFormItem({
{
return schema;
};
-const addRequired = (state: SchemaState, keyRoute: KeyRoute): SchemaState => {
- const schema = cloneDeep(state);
-
- const field = keyRoute[keyRoute.length - 1];
+const addRequired = (state: SchemaState, keyRoute: KeyRoute): void => {
+ const field = keyRoute.slice(-1)[0];
const requiredKeyRoute = keyRoute.slice(0, -2).concat('required');
- const required = get(schema, requiredKeyRoute) || [];
+ const required = get(state, requiredKeyRoute) || [];
required.push(field);
- set(schema, requiredKeyRoute, required);
-
- return schema;
+ set(state, requiredKeyRoute, required);
};
-const removeRequired = (state: SchemaState, keyRoute: KeyRoute): SchemaState => {
- const schema = cloneDeep(state);
-
- const field = keyRoute[keyRoute.length - 1];
+const removeRequired = (state: SchemaState, keyRoute: KeyRoute): void => {
+ const field = keyRoute.slice(-1)[0];
const requiredKeyRoute = keyRoute.slice(0, -2).concat('required');
- const required = get(schema, requiredKeyRoute) || [];
+ const required = get(state, requiredKeyRoute) || [];
remove(required, (item) => item === field);
- set(schema, requiredKeyRoute, required);
-
- return schema;
+ set(state, requiredKeyRoute, required);
};
-const toggleAllChecked = (state: SchemaState, checked: boolean): SchemaState => {
- const schema = cloneDeep(state);
-
- setAllFieldRequired(schema, checked);
-
- return schema;
+const toggleAllChecked = (state: SchemaState, checked: boolean): void => {
+ setAllFieldRequired(state, checked);
};
-const addChildField = (state: SchemaState, keyRoute: KeyRoute): SchemaState => {
+const addChildField = (state: Draft, keyRoute: KeyRoute): void => {
fieldNum += 1;
const newField = `field_${fieldNum}`;
- let schema = cloneDeep(state);
// add new field - string
const newKeyRoute = keyRoute.concat(newField);
- set(schema, newKeyRoute, defaultSchema.string);
+ set(state, newKeyRoute, defaultSchema.string);
// add new filed to its parent's required
- schema = addRequired(schema, newKeyRoute);
-
- return schema;
+ addRequired(state, newKeyRoute);
};
-const addSiblingField = (state: SchemaState, keyRoute: KeyRoute): SchemaState => {
+const addSiblingField = (state: SchemaState, keyRoute: KeyRoute): void => {
fieldNum += 1;
const newField = `field_${fieldNum}`;
- let schema = cloneDeep(state);
// add new field - string
const newKeyRoute = keyRoute.slice(0, -1).concat(newField);
- set(schema, newKeyRoute, defaultSchema.string);
+ set(state, newKeyRoute, defaultSchema.string);
// add new filed to its parent's required
- schema = addRequired(schema, newKeyRoute);
-
- return schema;
+ addRequired(state, newKeyRoute);
};
-const removeField = (state: SchemaState, keyRoute: KeyRoute): SchemaState => {
- let schema = cloneDeep(state);
-
+const removeField = (state: SchemaState, keyRoute: KeyRoute): void => {
// remove field
- unset(schema, keyRoute);
+ unset(state, keyRoute);
// remove filed from its parent's required
- schema = removeRequired(schema, keyRoute);
-
- return schema;
+ removeRequired(state, keyRoute);
};
-const changeField = (state: SchemaState, keyRoute: KeyRoute, field: string): SchemaState => {
- let schema = cloneDeep(state);
+const changeField = (state: SchemaState, keyRoute: KeyRoute, newField: string): void => {
+ const oldField = keyRoute.slice(-1)[0];
+ const parentKeyRoute = keyRoute.slice(0, -1);
+ const properties = get(state, parentKeyRoute);
+ const newProperties: { [key: string]: SchemaItem } = {};
+
+ // keep the order of fields
+
+ // eslint-disable-next-line no-restricted-syntax
+ for (const field in properties) {
+ if (field === oldField) {
+ newProperties[newField] = properties[field];
+ } else {
+ newProperties[field] = properties[field];
+ }
+ }
- // temp old field's value and remove old field
- const temp = get(schema, keyRoute);
- unset(schema, keyRoute);
+ set(state, parentKeyRoute, newProperties);
// remove old filed from its parent's required
- schema = removeRequired(schema, keyRoute);
-
- // point to new field and add temp to it
- const fieldKeyRoute = keyRoute.slice(0, -1).concat(field);
- set(schema, fieldKeyRoute, temp);
+ removeRequired(state, keyRoute);
// add new filed to its parent's required
- schema = addRequired(schema, fieldKeyRoute);
-
- return schema;
+ const fieldKeyRoute = parentKeyRoute.concat(newField);
+ addRequired(state, fieldKeyRoute);
};
-const changeType = (state: SchemaState, keyRoute: KeyRoute, fieldType: SchemaType): SchemaState => {
- const schema = cloneDeep(state);
-
+const changeType = (state: SchemaState, keyRoute: KeyRoute, fieldType: SchemaType): void => {
// set field new value
- set(schema, keyRoute, defaultSchema[fieldType]);
-
- return schema;
+ set(state, keyRoute, defaultSchema[fieldType]);
};
-const changeTitle = (state: SchemaState, keyRoute: KeyRoute, title: string): SchemaState => {
- const schema = cloneDeep(state);
-
+const changeTitle = (state: SchemaState, keyRoute: KeyRoute, title: string): void => {
// point to title and set new value
const titleKeyRoute = keyRoute.concat('title');
- set(schema, titleKeyRoute, title);
-
- return schema;
+ set(state, titleKeyRoute, title);
};
-const changeDesc = (state: SchemaState, keyRoute: KeyRoute, desc: string): SchemaState => {
- const schema = cloneDeep(state);
-
+const changeDesc = (state: SchemaState, keyRoute: KeyRoute, desc: string): void => {
// point to desc and set new value
const descKeyRoute = keyRoute.concat('description');
- set(schema, descKeyRoute, desc);
-
- return schema;
+ set(state, descKeyRoute, desc);
};
-const reducer = (state: SchemaState, action: SchemaAction): SchemaState => {
- switch (action.type) {
- case 'SET_SCHEMA':
- return setSchema(action.schema);
-
- case 'ADD_CHILD_FIELD':
- return addChildField(state, action.keyRoute);
-
- case 'ADD_SIBLING_FIELD':
- return addSiblingField(state, action.keyRoute);
-
- case 'REMOVE_FIELD':
- return removeField(state, action.keyRoute);
-
- case 'TOGGLE_ALL_CHECKED':
- return toggleAllChecked(state, action.checked);
-
- case 'ADD_REQUIRED':
- return addRequired(state, action.keyRoute);
-
- case 'REMOVE_REQUIRED':
- return removeRequired(state, action.keyRoute);
-
- case 'CHANGE_FIELD':
- return changeField(state, action.keyRoute, action.field);
-
- case 'CHANGE_TYPE':
- return changeType(state, action.keyRoute, action.fieldType);
-
- case 'CHANGE_TITLE':
- return changeTitle(state, action.keyRoute, action.title);
-
- case 'CHANGE_DESC':
- return changeDesc(state, action.keyRoute, action.desc);
-
- default:
- return state;
- }
-};
+const reducer = (state: SchemaState, action: SchemaAction): SchemaState =>
+ produce(state, (draft: Draft) => {
+ switch (action.type) {
+ case 'SET_SCHEMA':
+ setSchema(action.schema);
+ break;
+
+ case 'ADD_CHILD_FIELD':
+ addChildField(draft, action.keyRoute);
+ break;
+
+ case 'ADD_SIBLING_FIELD':
+ addSiblingField(draft, action.keyRoute);
+ break;
+
+ case 'REMOVE_FIELD':
+ removeField(draft, action.keyRoute);
+ break;
+
+ case 'TOGGLE_ALL_CHECKED':
+ toggleAllChecked(draft, action.checked);
+ break;
+
+ case 'ADD_REQUIRED':
+ addRequired(draft, action.keyRoute);
+ break;
+
+ case 'REMOVE_REQUIRED':
+ removeRequired(draft, action.keyRoute);
+ break;
+
+ case 'CHANGE_FIELD':
+ changeField(draft, action.keyRoute, action.field);
+ break;
+
+ case 'CHANGE_TYPE':
+ changeType(draft, action.keyRoute, action.fieldType);
+ break;
+
+ case 'CHANGE_TITLE':
+ changeTitle(draft, action.keyRoute, action.title);
+ break;
+
+ case 'CHANGE_DESC':
+ changeDesc(draft, action.keyRoute, action.desc);
+ break;
+
+ default:
+ break;
+ }
+ });
export default reducer;