Browse Source

feat: json-schema-editor 常用 field 类型

array 类型还未实现
master
suanmei 5 years ago
committed by 拾邑
parent
commit
150dd8ba38
  1. 20
      packages/json-schema-editor/package-lock.json
  2. 2
      packages/json-schema-editor/package.json
  3. 35
      packages/json-schema-editor/src/components/add-node.tsx
  4. 49
      packages/json-schema-editor/src/components/field-input.tsx
  5. 28
      packages/json-schema-editor/src/components/layout.tsx
  6. 44
      packages/json-schema-editor/src/components/schema-form.tsx
  7. 41
      packages/json-schema-editor/src/components/schema-head.tsx
  8. 152
      packages/json-schema-editor/src/components/schema-item.tsx
  9. 8
      packages/json-schema-editor/src/i18n/translations/en.json
  10. 8
      packages/json-schema-editor/src/i18n/translations/zh.json
  11. 13
      packages/json-schema-editor/src/index.tsx
  12. 1
      packages/json-schema-editor/src/model/index.ts
  13. 45
      packages/json-schema-editor/src/model/schema.ts
  14. 12
      packages/json-schema-editor/src/model/ui.ts
  15. 176
      packages/json-schema-editor/src/reducer/schema.ts
  16. 23
      packages/json-schema-editor/src/store/schema.ts
  17. 9
      packages/json-schema-editor/src/store/ui.ts
  18. 0
      packages/json-schema-editor/src/utils/schema-json.ts
  19. 50
      packages/json-schema-editor/src/utils/schema.ts
  20. 9
      packages/json-schema-editor/src/utils/styles/antd.ts
  21. 26
      packages/json-schema-editor/src/utils/styles/common.ts
  22. 9
      packages/json-schema-editor/src/utils/styles/global.ts

20
packages/json-schema-editor/package-lock.json

@ -231,6 +231,21 @@
"hoist-non-react-statics": "^3.3.0"
}
},
"@types/lodash": {
"version": "4.14.150",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.150.tgz",
"integrity": "sha512-kMNLM5JBcasgYscD9x/Gvr6lTAv2NVgsKtet/hm93qMyf/D1pt+7jeEZklKJKxMVmXjxbRVQQGfqDSfipYCO6w==",
"dev": true
},
"@types/lodash-es": {
"version": "4.17.3",
"resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.3.tgz",
"integrity": "sha512-iHI0i7ZAL1qepz1Y7f3EKg/zUMDwDfTzitx+AlHhJJvXwenP682ZyGbgPSc5Ej3eEAKVbNWKFuwOadCj5vBbYQ==",
"dev": true,
"requires": {
"@types/lodash": "*"
}
},
"@types/parse-json": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
@ -659,6 +674,11 @@
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
"dev": true
},
"lodash-es": {
"version": "4.17.15",
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.15.tgz",
"integrity": "sha512-rlrc3yU3+JNOpZ9zj5pQtxnx2THmvRykwL4Xlxoa8I9lHBlVbbyPhgyPMioxVZ4NqyxaVVtaJnzsyOidQIhyyQ=="
},
"loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",

2
packages/json-schema-editor/package.json

@ -40,6 +40,7 @@
"@ant-design/icons": "^4.1.0",
"@emotion/core": "^10.0.28",
"@emotion/styled": "^10.0.27",
"@types/lodash-es": "^4.17.3",
"@types/react": "^16.9.34",
"@types/react-dom": "^16.9.7",
"antd": "^4.1.5",
@ -58,6 +59,7 @@
"gitHead": "60b114ff262513544204f33a9bbcb6220d6c1c5d",
"dependencies": {
"i18next": "^19.4.4",
"lodash-es": "^4.17.15",
"react-i18next": "^11.4.0",
"react-tracked": "^1.3.0"
}

35
packages/json-schema-editor/src/components/add-node.tsx

@ -0,0 +1,35 @@
import * as React from 'react';
import { Menu, Dropdown } from 'antd';
import { useTranslation } from 'react-i18next';
import { PlusIcon } from '../utils/styles/common';
interface AddNodeProps {
addChildField: () => void;
addSiblingField: () => void;
}
function AddNodeIcon({ addChildField, addSiblingField }: AddNodeProps): JSX.Element {
const { t } = useTranslation();
const menu = (
<Menu>
<Menu.Item>
<div tabIndex={0} role="button" onClick={addChildField} onKeyDown={addChildField}>
{t('child_node')}
</div>
</Menu.Item>
<Menu.Item>
<div tabIndex={0} role="button" onClick={addSiblingField} onKeyDown={addSiblingField}>
{t('sibling_node')}
</div>
</Menu.Item>
</Menu>
);
return (
<Dropdown overlay={menu}>
<PlusIcon />
</Dropdown>
);
}
export default AddNodeIcon;

49
packages/json-schema-editor/src/components/field-input.tsx

@ -0,0 +1,49 @@
import * as React from 'react';
import styled from '@emotion/styled';
import { Input } from 'antd';
interface FieldInputProps {
value: string;
changeField: (value: string) => void;
addonAfter: JSX.Element;
}
const CustomInput = styled(Input)`
.ant-input-group-addon {
background: transparent;
border: none;
}
`;
function FieldInput({ value: initValue, changeField, addonAfter }: FieldInputProps): JSX.Element {
const [value, setValue] = React.useState(initValue);
const handleChange = (e: React.ChangeEvent<HTMLInputElement>): void => {
setValue(e.target.value);
};
const handleBlur = (e: React.ChangeEvent<HTMLInputElement>): void => {
if (e.target.value !== initValue) {
changeField(e.target.value);
}
};
const handleKeyUp = (e: React.KeyboardEvent): void => {
const { value: newValue } = e.target as HTMLInputElement;
if (e.keyCode === 13 && newValue !== initValue) {
changeField(newValue);
}
};
return (
<CustomInput
value={value}
onChange={handleChange}
onBlur={handleBlur}
onKeyUp={handleKeyUp}
addonAfter={addonAfter}
/>
);
}
export default FieldInput;

28
packages/json-schema-editor/src/components/layout.tsx

@ -1,15 +1,35 @@
/** @jsx jsx */
import { useEffect } from 'react';
import { jsx, css } from '@emotion/core';
import { Button } from 'antd';
import { useTranslation } from 'react-i18next';
import EditorHead from './editor-head';
import EditorHead from './schema-head';
import SchemaForm from './schema-form';
import { useUIState } from '../store/ui';
import { useSchemaState } from '../store/schema';
import { SchemaState } from '../model';
function Layout(): JSX.Element {
let temp: SchemaState | null = null;
interface AA {
onChange: (value: SchemaState) => void;
}
function Layout({ onChange }: AA): JSX.Element {
const { t } = useTranslation();
const uiState = useUIState();
const { formVisible } = uiState;
const key = 'properties';
const visible = uiState[key] !== false;
const schemaState = useSchemaState();
useEffect(() => {
if (temp !== schemaState) {
temp = schemaState;
onChange(schemaState);
}
});
return (
<div
@ -20,7 +40,7 @@ function Layout(): JSX.Element {
>
<Button type="primary">{t('import_json')}</Button>
<EditorHead />
{formVisible && <SchemaForm />}
{visible && <SchemaForm data={schemaState} keyRoute={['properties']} />}
</div>
);
}

44
packages/json-schema-editor/src/components/schema-form.tsx

@ -1,22 +1,40 @@
/** @jsx jsx */
import { useEffect, memo, useRef } from 'react';
import { jsx } from '@emotion/core';
import { useUIState } from '../store/ui';
import { isUndefined } from 'lodash-es';
import SchemaItem from './schema-item';
import { ObjectSchemaItem, KeyRoute } from '../model';
interface SchemaFormProps {
data: ObjectSchemaItem;
keyRoute: KeyRoute;
}
function SchemaForm({ data, keyRoute }: SchemaFormProps): JSX.Element {
const { required, properties } = data;
function SchemaForm(): JSX.Element {
const uiState = useUIState();
const { formVisible } = uiState;
const renders = useRef(1);
useEffect(() => {
console.log(11111);
});
renders.current += 1;
return (
<div>
{formVisible ? 3 : 0}
(renders:{renders.current})
{Object.keys(properties).map((field) => {
const itemData = properties[field];
const isRequired = required && required.includes(field);
const fieldKeyRoute = keyRoute.concat(field);
return (
<SchemaItem
key={field}
field={field}
data={itemData}
required={isRequired}
keyRoute={fieldKeyRoute}
>
{itemData.type === 'object' && !isUndefined(itemData.properties) ? (
<SchemaForm data={itemData} keyRoute={fieldKeyRoute.concat('properties')} />
) : null}
</SchemaItem>
);
})}
</div>
);
}
export default memo(SchemaForm);
export default SchemaForm;

41
packages/json-schema-editor/src/components/editor-head.tsx → packages/json-schema-editor/src/components/schema-head.tsx

@ -1,9 +1,12 @@
/** @jsx jsx */
import { jsx, css } from '@emotion/core';
import { useState } from 'react';
import { jsx } from '@emotion/core';
import styled from '@emotion/styled';
import { Row, Col, Tooltip, Input, Checkbox, Select } from 'antd';
import { CheckboxChangeEvent } from 'antd/lib/checkbox';
import { useTranslation } from 'react-i18next';
import { useUIState, useSetUIState } from '../store/ui';
import { useSchemaDispatch } from '../store/schema';
import {
CaretDownIcon,
CaretRightIcon,
@ -23,26 +26,35 @@ const FieldInput = styled(Input)`
function EditorHead(): JSX.Element {
const { t } = useTranslation();
const uiState = useUIState();
const schemaDispatch = useSchemaDispatch();
const setUIState = useSetUIState();
const { formVisible } = uiState;
const uiState = useUIState();
const key = 'properties';
const visible = uiState[key];
const [checked, setChecked] = useState(false);
const toggleFormVisible = (): void => {
setUIState((prev) => ({ ...prev, formVisible: !formVisible }));
setUIState((prev) => ({ ...prev, [key]: !visible }));
};
const addChildField = (): void => {
schemaDispatch({ type: 'ADD_CHILD_FIELD', keyRoute: ['properties'] });
};
const toggleAllChecked = (e: CheckboxChangeEvent): void => {
setChecked(e.target.checked);
schemaDispatch({ type: 'TOGGLE_ALL_CHECKED', checked: e.target.checked });
};
return (
<Row
css={css`
margin-top: 10px;
`}
>
<Row css={{ marginTop: '10px' }}>
<Col span="24">
<Row align="middle">
<Col span="8">
<Row align="middle">
<Col span="2">
{formVisible ? (
{visible ? (
<CaretDownIcon onClick={toggleFormVisible} />
) : (
<CaretRightIcon onClick={toggleFormVisible} />
@ -54,12 +66,7 @@ function EditorHead(): JSX.Element {
value="root"
addonAfter={
<Tooltip placement="top" title={t('select_all')}>
<Checkbox
checked={false}
onChange={(): void => {
// code
}}
/>
<Checkbox checked={checked} onChange={toggleAllChecked} />
</Tooltip>
}
/>
@ -82,7 +89,7 @@ function EditorHead(): JSX.Element {
<SettingIcon />
</Tooltip>
<Tooltip placement="top" title={t('add_child_node')}>
<PlusIcon />
<PlusIcon onClick={addChildField} />
</Tooltip>
</Col>
</Row>

152
packages/json-schema-editor/src/components/schema-item.tsx

@ -0,0 +1,152 @@
/** @jsx jsx */
import { jsx } from '@emotion/core';
import { Row, Col, Tooltip, Input, Checkbox, Select } from 'antd';
import { useTranslation } from 'react-i18next';
import { useUIState, useSetUIState } from '../store/ui';
import { useSchemaDispatch } from '../store/schema';
import { schemaType } from '../utils/schema';
import { SchemaType, SchemaItem, KeyRoute } from '../model';
import FieldInput from './field-input';
import AddNode from './add-node';
import {
CaretDownIcon,
CaretRightIcon,
EditIcon,
SettingIcon,
PlusIcon,
CloseIcon,
} from '../utils/styles/common';
const { Option } = Select;
interface SchemaItemProps {
field: string;
data: SchemaItem;
required: boolean;
keyRoute: KeyRoute;
children: React.ReactChild | null;
}
function SchemaFormItem({
field,
data,
required,
keyRoute,
children,
}: SchemaItemProps): JSX.Element {
const { t } = useTranslation();
const schemaDispatch = useSchemaDispatch();
const setUIState = useSetUIState();
const uiState = useUIState();
const key = keyRoute.join('-');
const visible = uiState[key] !== false;
const toggleFormVisible = (): void => {
setUIState((prev) => ({ ...prev, [key]: !visible }));
};
const toggleRequired = (): void => {
if (required) {
schemaDispatch({ type: 'REMOVE_REQUIRED', keyRoute });
} else {
schemaDispatch({ type: 'ADD_REQUIRED', keyRoute });
}
};
const addChildField = (): void => {
schemaDispatch({ type: 'ADD_CHILD_FIELD', keyRoute: keyRoute.concat('properties') });
};
const addSiblingField = (): void => {
schemaDispatch({ type: 'ADD_SIBLING_FIELD', keyRoute });
};
const removeField = (): void => {
schemaDispatch({ type: 'REMOVE_FIELD', keyRoute });
};
const changeField = (value: string): void => {
setTimeout(() => {
schemaDispatch({ type: 'CHANGE_FIELD', keyRoute, field: value });
}, 500);
};
const changeType = (value: SchemaType): void => {
schemaDispatch({ type: 'CHANGE_TYPE', keyRoute, fieldType: value });
};
const changeTitle = (e: React.ChangeEvent<HTMLInputElement>): void => {
schemaDispatch({ type: 'CHANGE_TITLE', keyRoute, title: e.target.value });
};
const changeDesc = (e: React.ChangeEvent<HTMLInputElement>): void => {
schemaDispatch({ type: 'CHANGE_DESC', keyRoute, desc: e.target.value });
};
return (
<div css={{ marginTop: '10px' }}>
<Row align="middle">
<Col span="8" css={{ paddingLeft: `${keyRoute.length * 10}px` }}>
<Row align="middle">
<Col span="2">
{data.type === 'object' && visible && <CaretDownIcon onClick={toggleFormVisible} />}
{data.type === 'object' && !visible && <CaretRightIcon onClick={toggleFormVisible} />}
</Col>
<Col span="22">
<FieldInput
value={field}
changeField={changeField}
addonAfter={
<Tooltip placement="top" title={t('required')}>
<Checkbox checked={required} onChange={toggleRequired} />
</Tooltip>
}
/>
</Col>
</Row>
</Col>
<Col span="3">
<Select value={data.type} onChange={changeType}>
{schemaType.map((type) => (
<Option key={type} value={type}>
{type}
</Option>
))}
</Select>
</Col>
<Col span="5">
<Input
value={data.title}
onChange={changeTitle}
placeholder={t('title')}
addonAfter={<EditIcon />}
/>
</Col>
<Col span="5">
<Input
value={data.description}
onChange={changeDesc}
placeholder={t('description')}
addonAfter={<EditIcon />}
/>
</Col>
<Col span="3">
<Tooltip placement="top" title={t('setting')}>
<SettingIcon />
</Tooltip>
<CloseIcon onClick={removeField} />
{data.type === 'object' ? (
<AddNode addChildField={addChildField} addSiblingField={addSiblingField} />
) : (
<Tooltip placement="top" title={t('add_sibling_node')}>
<PlusIcon onClick={addSiblingField} />
</Tooltip>
)}
</Col>
</Row>
{visible && children ? <div css={{ marginTop: '10px' }}>{children}</div> : null}
</div>
);
}
export default SchemaFormItem;

8
packages/json-schema-editor/src/i18n/translations/en.json

@ -2,7 +2,13 @@
"translation": {
"import_json": "Import JSON",
"select_all": "Select All",
"required": "required",
"title": "Title",
"description": "Description",
"setting": "Advanced Setting",
"add_child_node": "Add child node"
"child_node": "Child node",
"sibling_node": "Sibling node",
"add_child_node": "Add child node",
"add_sibling_node": "Add sibling node"
}
}

8
packages/json-schema-editor/src/i18n/translations/zh.json

@ -2,7 +2,13 @@
"translation": {
"import_json": "导入 JSON",
"select_all": "全选",
"required": "必选",
"title": "标题",
"description": "描述",
"setting": "高级设置",
"add_child_node": "添加子节点"
"child_node": "子节点",
"sibling_node": "兄弟节点",
"add_child_node": "添加子节点",
"add_sibling_node": "添加兄弟节点"
}
}

13
packages/json-schema-editor/src/index.tsx

@ -4,14 +4,19 @@ import { Global, jsx } from '@emotion/core';
import { useTranslation } from 'react-i18next';
import Layout from './components/layout';
import { UIProvider } from './store/ui';
import { SchemaProvider } from './store/schema';
import globalStyles from './utils/styles/global';
import { SchemaState } from './model';
import './utils/styles/antd';
import './i18n';
interface EditorProps {
lang?: string;
data: SchemaState;
onChange: (value: SchemaState) => void;
}
function JsonSchemaEditor({ lang }: EditorProps): JSX.Element {
function JsonSchemaEditor({ lang, data, onChange }: EditorProps): JSX.Element {
const { i18n } = useTranslation();
useEffect(() => {
@ -21,8 +26,10 @@ function JsonSchemaEditor({ lang }: EditorProps): JSX.Element {
return (
<UIProvider>
<Global styles={globalStyles} />
<Layout />
<SchemaProvider initialData={data}>
<Global styles={globalStyles} />
<Layout onChange={onChange} />
</SchemaProvider>
</UIProvider>
);
}

1
packages/json-schema-editor/src/model/index.ts

@ -1 +1,2 @@
export * from './ui';
export * from './schema';

45
packages/json-schema-editor/src/model/schema.ts

@ -0,0 +1,45 @@
export type SchemaType = 'string' | 'number' | 'object' | 'array' | 'boolean' | 'integer';
type basicSchemaType = 'string' | 'number' | 'boolean' | 'integer';
interface BasicSchemaItem {
title?: string;
description?: string;
type: basicSchemaType;
}
export interface ObjectSchemaItem {
title?: string;
description?: string;
type: 'object';
properties: {
[field: string]: SchemaItem;
};
required: string[];
}
export interface ArraySchemaItem {
title?: string;
description?: string;
type: 'array';
items: SchemaItem;
}
export type SchemaItem = BasicSchemaItem | ObjectSchemaItem | ArraySchemaItem;
export type SchemaState = ObjectSchemaItem;
export type KeyRoute = (string | number)[];
export type SchemaAction =
| { type: 'SET_SCHEMA'; schema: SchemaState }
| { type: 'ADD_CHILD_FIELD'; keyRoute: KeyRoute }
| { type: 'ADD_SIBLING_FIELD'; keyRoute: KeyRoute }
| { type: 'REMOVE_FIELD'; keyRoute: KeyRoute }
| { type: 'TOGGLE_ALL_CHECKED'; checked: boolean }
| { type: 'ADD_REQUIRED'; keyRoute: KeyRoute }
| { type: 'REMOVE_REQUIRED'; keyRoute: KeyRoute }
| { type: 'CHANGE_FIELD'; keyRoute: KeyRoute; field: string }
| { type: 'CHANGE_TYPE'; keyRoute: KeyRoute; fieldType: SchemaType }
| { type: 'CHANGE_TITLE'; keyRoute: KeyRoute; title: string }
| { type: 'CHANGE_DESC'; keyRoute: KeyRoute; desc: string };

12
packages/json-schema-editor/src/model/ui.ts

@ -1,13 +1,3 @@
export interface UIState {
formVisible: boolean;
aaa: boolean;
}
export interface UIAction {
type: string;
}
export interface UIContext {
uiState: UIState;
uiDispatch: React.Dispatch<UIAction>;
[key: string]: boolean;
}

176
packages/json-schema-editor/src/reducer/schema.ts

@ -0,0 +1,176 @@
import { get, set, unset, remove, cloneDeep } from 'lodash-es';
import { SchemaType, SchemaState, SchemaAction, KeyRoute } from '../model';
import { defaultSchema, setAllFieldRequired } from '../utils/schema';
let fieldNum = 0;
const setSchema = (schema: SchemaState): SchemaState => {
return schema;
};
const addRequired = (state: SchemaState, keyRoute: KeyRoute): SchemaState => {
const schema = cloneDeep(state);
const field = keyRoute[keyRoute.length - 1];
const requiredKeyRoute = keyRoute.slice(0, -2).concat('required');
const required = get(schema, requiredKeyRoute) || [];
required.push(field);
set(schema, requiredKeyRoute, required);
return schema;
};
const removeRequired = (state: SchemaState, keyRoute: KeyRoute): SchemaState => {
const schema = cloneDeep(state);
const field = keyRoute[keyRoute.length - 1];
const requiredKeyRoute = keyRoute.slice(0, -2).concat('required');
const required = get(schema, requiredKeyRoute) || [];
remove(required, (item) => item === field);
set(schema, requiredKeyRoute, required);
return schema;
};
const toggleAllChecked = (state: SchemaState, checked: boolean): SchemaState => {
const schema = cloneDeep(state);
setAllFieldRequired(schema, checked);
return schema;
};
const addChildField = (state: SchemaState, keyRoute: KeyRoute): SchemaState => {
fieldNum += 1;
const newField = `field_${fieldNum}`;
let schema = cloneDeep(state);
// add new field - string
const newKeyRoute = keyRoute.concat(newField);
set(schema, newKeyRoute, defaultSchema.string);
// add new filed to its parent's required
schema = addRequired(schema, newKeyRoute);
return schema;
};
const addSiblingField = (state: SchemaState, keyRoute: KeyRoute): SchemaState => {
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);
// add new filed to its parent's required
schema = addRequired(schema, newKeyRoute);
return schema;
};
const removeField = (state: SchemaState, keyRoute: KeyRoute): SchemaState => {
let schema = cloneDeep(state);
// remove field
unset(schema, keyRoute);
// remove filed from its parent's required
schema = removeRequired(schema, keyRoute);
return schema;
};
const changeField = (state: SchemaState, keyRoute: KeyRoute, field: string): SchemaState => {
let schema = cloneDeep(state);
// temp old field's value and remove old field
const temp = get(schema, keyRoute);
unset(schema, keyRoute);
// 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);
// add new filed to its parent's required
schema = addRequired(schema, fieldKeyRoute);
return schema;
};
const changeType = (state: SchemaState, keyRoute: KeyRoute, fieldType: SchemaType): SchemaState => {
const schema = cloneDeep(state);
// set field new value
set(schema, keyRoute, defaultSchema[fieldType]);
return schema;
};
const changeTitle = (state: SchemaState, keyRoute: KeyRoute, title: string): SchemaState => {
const schema = cloneDeep(state);
// point to title and set new value
const titleKeyRoute = keyRoute.concat('title');
set(schema, titleKeyRoute, title);
return schema;
};
const changeDesc = (state: SchemaState, keyRoute: KeyRoute, desc: string): SchemaState => {
const schema = cloneDeep(state);
// point to desc and set new value
const descKeyRoute = keyRoute.concat('description');
set(schema, descKeyRoute, desc);
return schema;
};
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;
}
};
export default reducer;

23
packages/json-schema-editor/src/store/schema.ts

@ -0,0 +1,23 @@
import { Dispatch, useReducer } from 'react';
import { createContainer } from 'react-tracked';
import { SchemaState, SchemaAction } from '../model';
import reducer from '../reducer/schema';
interface StoreProps {
initialData: SchemaState;
}
const defaultData: SchemaState = {
type: 'object',
properties: {},
required: [],
};
const useValue = ({ initialData }: StoreProps): [SchemaState, Dispatch<SchemaAction>] =>
useReducer(reducer, initialData || defaultData);
export const {
Provider: SchemaProvider,
useTrackedState: useSchemaState,
useUpdate: useSchemaDispatch,
} = createContainer(useValue);

9
packages/json-schema-editor/src/store/ui.ts

@ -1,13 +1,10 @@
import { useState, Dispatch, SetStateAction } from 'react';
import { createContainer } from 'react-tracked';
import { UIState } from '../model';
export type State = {
formVisible: boolean;
};
const initialData = { properties: true };
const initialData = { formVisible: true };
const useValue = (): [State, Dispatch<SetStateAction<State>>] => useState<State>(initialData);
const useValue = (): [UIState, Dispatch<SetStateAction<UIState>>] => useState<UIState>(initialData);
export const {
Provider: UIProvider,

0
packages/json-schema-editor/src/utils/schema-json.ts

50
packages/json-schema-editor/src/utils/schema.ts

@ -0,0 +1,50 @@
import { SchemaItem } from '../model';
export const defaultSchema = {
string: {
type: 'string',
},
number: {
type: 'number',
},
array: {
type: 'array',
items: {
type: 'string',
},
},
object: {
type: 'object',
properties: {},
},
boolean: {
type: 'boolean',
},
integer: {
type: 'integer',
},
};
export const schemaType = ['string', 'number', 'array', 'object', 'boolean', 'integer'];
export const setAllFieldRequired = (schema: SchemaItem, checked: boolean): void => {
if (schema.type === 'object') {
const { properties } = schema;
const fields = Object.keys(properties);
if (checked) {
schema.required = fields;
} else {
delete schema.required;
}
fields.forEach((field) => {
const childSchema = properties[field];
if (['object', 'array'].includes(childSchema.type)) {
setAllFieldRequired(childSchema, checked);
}
});
} else if (schema.type === 'array') {
setAllFieldRequired(schema.items, checked);
}
};

9
packages/json-schema-editor/src/utils/styles/antd.ts

@ -0,0 +1,9 @@
import 'antd/lib/row/style';
import 'antd/lib/col/style';
import 'antd/lib/menu/style';
import 'antd/lib/input/style';
import 'antd/lib/select/style';
import 'antd/lib/button/style';
import 'antd/lib/tooltip/style';
import 'antd/lib/dropdown/style';
import 'antd/lib/checkbox/style';

26
packages/json-schema-editor/src/utils/styles/common.ts

@ -6,13 +6,15 @@ import {
EditOutlined,
SettingOutlined,
PlusOutlined,
CloseOutlined,
} from '@ant-design/icons';
const cursorStyle = css`
const iconStyle = css`
vertical-align: middle;
cursor: pointer;
`;
const IconStyle = css`
const actionStyle = css`
margin: 0 5px;
svg {
font-size: 14px;
@ -20,25 +22,31 @@ const IconStyle = css`
`;
export const CaretDownIcon = styled(CaretDownOutlined)`
${cursorStyle}
${iconStyle}
`;
export const CaretRightIcon = styled(CaretRightOutlined)`
${cursorStyle}
${iconStyle}
`;
export const EditIcon = styled(EditOutlined)`
${cursorStyle}
${iconStyle}
`;
export const SettingIcon = styled(SettingOutlined)`
color: #00a854;
${cursorStyle}
${IconStyle}
${iconStyle}
${actionStyle}
`;
export const PlusIcon = styled(PlusOutlined)`
color: #2395f1;
${cursorStyle}
${IconStyle}
${iconStyle}
${actionStyle}
`;
export const CloseIcon = styled(CloseOutlined)`
color: #ff561b;
${iconStyle}
${actionStyle}
`;

9
packages/json-schema-editor/src/utils/styles/global.ts

@ -25,15 +25,6 @@ const globalStyles = css`
width: 80%;
text-align: left;
}
.editor-icon {
cursor: pointer;
svg {
margin: 0 5px;
font-size: 14px;
}
}
}
`;

Loading…
Cancel
Save