committed by
拾邑
22 changed files with 686 additions and 74 deletions
@ -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; |
@ -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; |
@ -1,22 +1,40 @@ |
|||||
/** @jsx jsx */ |
/** @jsx jsx */ |
||||
import { useEffect, memo, useRef } from 'react'; |
|
||||
import { jsx } from '@emotion/core'; |
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 ( |
return ( |
||||
<div> |
<div> |
||||
{formVisible ? 3 : 0} |
{Object.keys(properties).map((field) => { |
||||
(renders:{renders.current}) |
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> |
</div> |
||||
); |
); |
||||
} |
} |
||||
|
|
||||
export default memo(SchemaForm); |
export default SchemaForm; |
||||
|
@ -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; |
@ -1 +1,2 @@ |
|||||
export * from './ui'; |
export * from './ui'; |
||||
|
export * from './schema'; |
||||
|
@ -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 }; |
@ -1,13 +1,3 @@ |
|||||
export interface UIState { |
export interface UIState { |
||||
formVisible: boolean; |
[key: string]: boolean; |
||||
aaa: boolean; |
|
||||
} |
|
||||
|
|
||||
export interface UIAction { |
|
||||
type: string; |
|
||||
} |
|
||||
|
|
||||
export interface UIContext { |
|
||||
uiState: UIState; |
|
||||
uiDispatch: React.Dispatch<UIAction>; |
|
||||
} |
} |
||||
|
@ -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; |
@ -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); |
@ -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); |
||||
|
} |
||||
|
}; |
@ -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'; |
Loading…
Reference in new issue