Skip to content

Commit

Permalink
refactor: form interface
Browse files Browse the repository at this point in the history
  • Loading branch information
danielwii committed Sep 17, 2020
1 parent 7f352e5 commit 68e958f
Show file tree
Hide file tree
Showing 6 changed files with 19 additions and 319 deletions.
282 changes: 3 additions & 279 deletions src/components/EasyForm/index.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,15 @@
/** @jsx jsx */
import { AppContext } from '@asuna-admin/core';
import { DebugInfo, WithDebugInfo } from '@asuna-admin/helpers';
import { createLogger } from '@asuna-admin/logger';
import { css, jsx } from '@emotion/core';
import { FormControl, FormControlLabel, FormHelperText, Switch, TextField } from '@material-ui/core';
import { jsx } from '@emotion/core';
import { FormControl, FormHelperText } from '@material-ui/core';
import * as antd from 'antd';
import { Divider, Popconfirm } from 'antd';
import { DynamicJsonArrayTable, ObjectJsonTableHelper, StringTmpl, Uploader, WithVariable } from 'asuna-components';
import { changeAntdTheme, generateThemeColor } from 'dynamic-antd-theme';
import { FormFieldDef, FormFieldsGroup, RenderInputComponent } from 'asuna-components';
import * as formik from 'formik';
import { FieldInputProps, FormikProps } from 'formik';
import * as _ from 'lodash';
import * as React from 'react';
import { SketchPicker } from 'react-color';
import Highlight from 'react-highlight';
import * as util from 'util';
import { FileUploaderAdapterImpl } from '../DynamicForm/elements/Image';
import { FormField, FormFieldDef, FormFields, FormFieldsGroup, FormFieldType } from './interfaces';

export * from './interfaces';

const logger = createLogger('components:easy-form');

Expand All @@ -28,273 +19,6 @@ interface FormProps<FieldsType> {
fields: FieldsType;
}

interface EasyFormProps extends FormProps<FormFields> {
// initialValues(props): any;
onSubmit: (values: any) => Promise<any> | void;
onClear?: () => Promise<any>;
}

export function RenderInputComponent({
form,
fieldDef,
field,
value,
}: {
form: FormikProps<any>;
fieldDef: FormFieldDef;
field: FieldInputProps<any>;
value: any;
}) {
logger.log('[RenderInputComponent]', field, { fieldDef, value });
switch (fieldDef.field.type) {
case FormFieldType.boolean: {
return (
<FormControlLabel
control={
<Switch
onChange={(event, checked) =>
field.onChange({ target: { id: field.name, name: field.name, value: checked } })
}
defaultChecked={value}
// value={value}
color="primary"
/>
}
label={fieldDef.name}
/>
);
}
case FormFieldType.color: {
return (
<>
<label>{field.name}</label>
<SketchPicker
css={css`
margin: 1rem;
`}
color={value}
onChange={(color) => {
changeAntdTheme(generateThemeColor(color.hex));
field.onChange({ target: { id: field.name, name: field.name, value: color } });
}}
/>
</>
);
}
case FormFieldType.image: {
return (
<>
<label>{field.name}</label>
<br />
<Uploader
adapter={new FileUploaderAdapterImpl()}
value={value}
onChange={(newValue) => {
logger.log('[RenderInputComponent]', { fieldDef, value, newValue });
field.onChange({ target: { id: field.name, name: field.name, value: newValue } });
}}
/>
</>
);
}
case FormFieldType.string:
case FormFieldType.text: {
const label = field.name === fieldDef.name ? field.name : `${field.name} / ${fieldDef.name}`;
return (
<>
{/*<antd.Input.TextArea id={field.name} {...field} autoSize rows={4} value={value} />*/}
<TextField id={field.name} multiline {...field} value={value} label={label} />
{/*<DebugInfo data={{ field, fieldDef, value }} type="json" />*/}
</>
);
}
case FormFieldType.stringTmpl: {
const label = field.name === fieldDef.name ? field.name : `${field.name} / ${fieldDef.name}`;
return (
<WithDebugInfo info={{ field, fieldDef, value }}>
<label>{label}</label>
<StringTmpl tmpl={value} {...field} fields={[]} {...fieldDef.field.extra} />
</WithDebugInfo>
);
}
case FormFieldType.emailTmplData: {
const label = field.name === fieldDef.name ? field.name : `${field.name} / ${fieldDef.name}`;
return (
<>
<label>{label}</label>
<DynamicJsonArrayTable
adapter={ObjectJsonTableHelper}
value={value}
preview={(item) => <div>{util.inspect(ObjectJsonTableHelper.keyParser(item))}</div>}
render={({ fieldOpts, index }) => (
<antd.Card>
<TextField {...fieldOpts('key', index)} label="key" />
<TextField {...fieldOpts('subject', index)} label="subject" />
{/*<TextField {...fieldOpts('template', index)} label="template" />*/}
<WithVariable variable={fieldOpts('template', index)}>
{({ name, value, onChange }) => (
<StringTmpl
tmpl={value}
fields={[]}
onChange={(value) => onChange({ target: { id: name, name, value } } as any)}
htmlMode
/>
)}
</WithVariable>
<pre>{JSON.stringify(field, null, 2)}</pre>
</antd.Card>
)}
onChange={(values) => form.setFieldValue(field.name, values)}
/>
{/*<DebugInfo data={value} type="util" />*/}
</>
);
}
case FormFieldType.wxTmplData: {
const label = field.name === fieldDef.name ? field.name : `${field.name} / ${fieldDef.name}`;
return (
<>
<label>{label}</label>
<DynamicJsonArrayTable
adapter={ObjectJsonTableHelper}
value={value}
preview={(item) => <div>{util.inspect(ObjectJsonTableHelper.keyParser(item))}</div>}
render={({ fieldOpts, index }) => (
<antd.Card>
<TextField {...fieldOpts('key', index)} label="key" />{' '}
<TextField {...fieldOpts('color', index)} label="color" />
<TextField {...fieldOpts('value', index)} label="value" fullWidth multiline />
</antd.Card>
)}
onChange={(values) => form.setFieldValue(field.name, values)}
/>
{/*<DebugInfo data={value} type="util" />*/}
</>
);
}
case FormFieldType.wxSubscribeData: {
const label = field.name === fieldDef.name ? field.name : `${field.name} / ${fieldDef.name}`;
return (
<>
<label>{label}</label>
<DynamicJsonArrayTable
adapter={ObjectJsonTableHelper}
value={value}
preview={(item) => <div>{util.inspect(ObjectJsonTableHelper.keyParser(item))}</div>}
render={({ fieldOpts, index }) => (
<antd.Card>
<TextField {...fieldOpts('key', index)} label="key" />
<TextField {...fieldOpts('value', index)} label="value" fullWidth multiline />
</antd.Card>
)}
onChange={(values) => {
console.log('onChange', field.name, values);
form.setFieldValue(field.name, values);
}}
/>
<DebugInfo data={value} type="util" />
</>
);
}
default: {
const label = field.name === fieldDef.name ? field.name : `${field.name} / ${fieldDef.name}`;
return (
<WithDebugInfo info={{ field, fieldDef, value, label }}>
<TextField id={field.name} type={fieldDef.field.type} {...field} value={value} label={label} />
{/*<DebugInfo data={{ field, fieldDef, value }} />*/}
</WithDebugInfo>
);
}
}
}

const InnerForm = (props: EasyFormProps & formik.FormikProps<formik.FormikValues>) => {
const { touched, errors, isSubmitting, message, body, fields, handleSubmit, handleReset, values, onClear } = props;
logger.log('[InnerForm]', props.values);
return (
<formik.Form>
{message && <h1>{message}</h1>}
{_.map(fields, (formField: FormField, key: string) => (
<formik.Field key={key} name={key}>
{({ field, form }: formik.FieldProps<formik.FormikValues>) => {
const hasError = !!(form.touched[formField.name] && form.errors[formField.name]);
const value = field.value ?? formField.defaultValue;
logger.log('render field', field, { hasError, value }, formField);
return (
<FormControl key={field.name} error={hasError} fullWidth={true}>
<RenderInputComponent
form={form}
fieldDef={{ field: formField, name: formField.name }}
field={field}
value={value}
/>
{/*<Input id={field.name} type={formField.type} {...field} value={value} />*/}
{formField.help && <FormHelperText>{formField.help}</FormHelperText>}
{hasError && <FormHelperText>{form.errors[formField.name]}</FormHelperText>}
<Divider dashed style={{ margin: '0.5rem 0' }} />
</FormControl>
);
}}
</formik.Field>
))}
<antd.Divider />
<antd.Button type="primary" htmlType="submit" onSubmit={handleSubmit} disabled={isSubmitting}>
{isSubmitting ? 'Submitting' : 'Submit'}
</antd.Button>{' '}
{onClear && (
<antd.Button onClick={handleReset} disabled={isSubmitting}>
{isSubmitting ? 'Resetting' : 'Reset'}
</antd.Button>
)}
</formik.Form>
);
};

/* creatable: (key, actions, extras) => {
// Modal.info({ content: JSON.stringify({ key, actions, extras }) });
Modal.info({
okText: '取消',
content: (
<EasyForm
fields={{
change: {
name: 'change',
defaultValue: 0,
type: 'number',
validate: value => (_.isNumber(value) && value === 0 ? '不为 0 的数字' : null),
} as FormField,
reason: { name: 'reason', defaultValue: '', type: 'string' } as FormField,
}}
/>
),
});
},*/
export const EasyForm = formik.withFormik<EasyFormProps, any>({
// Transform outer props into form values
mapPropsToValues: (props) =>
Object.assign({}, ..._.map(props.fields, (field: FormField, name: string) => ({ [name]: field.defaultValue }))),

validate: (values: formik.FormikValues, props) => {
const errors: formik.FormikErrors<formik.FormikValues> = {};

_.forEach(props.fields, (field: FormField, name: string) => {
if (field.required && !values[name]) {
errors[name] = 'Required';
} else if (field.validate) {
const error = field.validate(values[name]);
if (error) errors[name] = error;
}
});

return errors;
},

handleSubmit: (values, { props, setSubmitting }) => {
const submitted = props.onSubmit(values);
if (submitted && submitted.then) submitted.finally(() => setSubmitting(false));
},
})(InnerForm) as any;

// --------------------------------------------------------------
// GroupForm
// --------------------------------------------------------------
Expand Down
28 changes: 0 additions & 28 deletions src/components/EasyForm/interfaces.ts

This file was deleted.

12 changes: 7 additions & 5 deletions src/components/KV/form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,16 @@ export function FormKVComponent(props: {
// initialState: { body: FormBody | any };
enableClear?: boolean;
info?: React.ReactChild;
fields: (state) => FormFields;
fields?: (state) => FormFields;
}) {
const { kvCollection: collection, kvKey: key, info, /*initialState, */ fields } = props;
const { loading, error, data, refetch } = KVHelper.loadByKey(key, collection);
const body = _.get(data, 'kv.value', {});
useLogger('FormKVComponent', props, body, { loading, error, data });
const body = _.get(data, 'kv.value', {}) as { type: string; fields: FormFields; values: Record<string, unknown> };

const initialValues = body.values;
const fieldValues = fields ? fields(body) : body.fields;

const fieldValues = fields(body);
useLogger('FormKVComponent', props, body, { loading, error, data }, { initialValues, fieldValues });
logger.log('render', props, body, { loading, error, data, fields, /*initialState, */ fieldValues });

return (
Expand All @@ -70,7 +72,7 @@ export function FormKVComponent(props: {
<Row gutter={16}>
<Col span={18}>
<EasyForm
initialValues={body}
initialValues={initialValues}
fields={fieldValues}
onSubmit={(values) => KVHelper.save({ key, collection }, values, refetch)}
onClear={() => KVHelper.clear({ key, collection }, refetch)}
Expand Down
6 changes: 4 additions & 2 deletions src/modules/content/query.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { AsunaDataView, EasyForm, FormFieldType } from '@asuna-admin/components';
import { AsunaDataView } from '@asuna-admin/components';
import { AppContext } from '@asuna-admin/core';
import { extractModelNameFromPane, resolveModelInPane, useAsunaModels } from '@asuna-admin/helpers';
import { Divider, PageHeader } from 'antd';
import { EasyForm, FormFieldType } from 'asuna-components';
import 'highlight.js/styles/default.css';
import * as _ from 'lodash';
import * as fp from 'lodash/fp';
import * as React from 'react';
import { useEffect, useState } from 'react';
import { useState } from 'react';
import { ModulesLoaderProps } from '..';

export type QueryFieldsColumnProps<EntitySchema> = (keyof EntitySchema)[];
Expand Down Expand Up @@ -56,6 +57,7 @@ const ContentSearch: React.FC<ModulesLoaderProps> = (props) => {

<PageHeader title={pane.title}>
<EasyForm
initialValues={{}}
fields={fields}
onSubmit={async (values) => {
const valuesByFields = _.mapValues(fields, (v, k) => {
Expand Down
Loading

0 comments on commit 68e958f

Please sign in to comment.