@canard/schema-form-antd-mobile-plugin
Version:
Ant Design Mobile components plugin for @canard/schema-form providing mobile-optimized form inputs for React Native and mobile web
277 lines (258 loc) • 12.9 kB
JavaScript
'use strict';
const jsxRuntime = require('react/jsx-runtime');
const antdMobile = require('antd-mobile');
const filter = require('@winglet/json-schema/filter');
const array = require('@winglet/common-utils/array');
const hook = require('@winglet/react-utils/hook');
const react = require('react');
const FormError = ({ errorMessage }) => errorMessage;
const FormGroup = ({ node, depth, path, name, required, Input, errorMessage, }) => {
if (depth === 0)
return jsxRuntime.jsx(Input, {});
if (node.group === 'branch') {
return (jsxRuntime.jsx(antdMobile.Space, { direction: "vertical", block: true, children: jsxRuntime.jsx(Input, {}) }));
}
else {
return (jsxRuntime.jsxs(antdMobile.Space, { direction: "vertical", block: true, children: [jsxRuntime.jsxs("div", { children: [node.parentNode && filter.isArraySchema(node.parentNode) === false && (jsxRuntime.jsxs("label", { htmlFor: path, children: [name, required && (jsxRuntime.jsx("span", { style: { marginLeft: 4, color: 'red' }, children: "*" }))] })), jsxRuntime.jsx(Input, {})] }), errorMessage] }));
}
};
const FormInput = ({ Input }) => jsxRuntime.jsx(Input, {});
const FormLabel = ({ name, path, required }) => (jsxRuntime.jsxs("label", { htmlFor: path, children: [name, required && jsxRuntime.jsx("span", { style: { marginLeft: 4, color: 'red' }, children: "*" })] }));
const Add = (props) => (jsxRuntime.jsx("div", { children: jsxRuntime.jsx(antdMobile.Button, { size: "mini", ...props, children: "+" }) }));
const Remove = (props) => (jsxRuntime.jsx("div", { children: jsxRuntime.jsx(antdMobile.Button, { size: "mini", ...props, children: "-" }) }));
const FormTypeInputArray = ({ node, readOnly, disabled, ChildNodeComponents, style, }) => {
const handleClick = hook.useHandle(() => {
node.push();
});
const handleRemoveClick = hook.useHandle((index) => {
node.remove(index);
});
return (jsxRuntime.jsxs("div", { style: style, children: [ChildNodeComponents &&
array.map(ChildNodeComponents, (ChildNodeComponent, i) => {
const key = ChildNodeComponent.key;
return (jsxRuntime.jsxs("div", { style: { display: 'flex' }, children: [jsxRuntime.jsx(ChildNodeComponent, {}, key), !readOnly && (jsxRuntime.jsx(Remove, { disabled: disabled, onClick: () => handleRemoveClick(i) }))] }, key));
}), !readOnly && (jsxRuntime.jsx("div", { children: jsxRuntime.jsx(Add, { disabled: disabled, onClick: handleClick }) }))] }));
};
const FormTypeInputArrayDefinition = {
Component: FormTypeInputArray,
test: {
type: 'array',
},
};
const FormTypeInputBoolean = ({ path, disabled, value, onChange, }) => {
const [indeterminate, checked] = react.useMemo(() => [
value !== undefined && typeof value !== 'boolean',
value ?? undefined,
], [value]);
const handleChange = hook.useHandle(onChange);
return (jsxRuntime.jsx(antdMobile.Checkbox, { id: path, disabled: disabled, indeterminate: indeterminate, checked: checked, onChange: handleChange, style: {
'--icon-size': '24px',
'--font-size': '20px',
} }));
};
const FormTypeInputBooleanDefinition = {
Component: FormTypeInputBoolean,
test: {
type: 'boolean',
},
};
const FormTypeInputBooleanSwitch = ({ path, disabled, value, onChange, context, alias, }) => {
const [checkedLabel, uncheckedLabel] = react.useMemo(() => {
const labels = context?.checkboxLabels || alias || {};
return [labels.checked, labels.unchecked];
}, [context, alias]);
const handleChange = hook.useHandle(onChange);
return (jsxRuntime.jsx(antdMobile.Switch, { disabled: disabled, checked: value ?? undefined, checkedText: checkedLabel, uncheckedText: uncheckedLabel, onChange: handleChange }, path));
};
const FormTypeInputBooleanSwitchDefinition = {
Component: FormTypeInputBooleanSwitch,
test: ({ type, formType }) => type === 'boolean' && formType === 'switch',
};
const FormTypeInputNumber = ({ jsonSchema, readOnly, disabled, defaultValue, onChange, formatter, parser, }) => {
const handleChange = hook.useHandle((value) => {
onChange(value);
});
return (jsxRuntime.jsx(antdMobile.Stepper, { inputReadOnly: readOnly, disabled: disabled, min: jsonSchema.minimum, max: jsonSchema.maximum, step: jsonSchema.multipleOf, formatter: formatter, parser: parser, defaultValue: defaultValue ?? undefined, onChange: handleChange }));
};
const FormTypeInputNumberDefinition = {
Component: FormTypeInputNumber,
test: {
type: ['number', 'integer'],
},
};
const FormTypeInputRadioGroup = ({ jsonSchema, disabled, defaultValue, onChange, context, alias, style, }) => {
const options = react.useMemo(() => {
const labels = context.radioLabels || alias || {};
return jsonSchema.enum
? array.map(jsonSchema.enum, (rawValue) => {
const value = '' + rawValue;
return {
value,
rawValue,
label: labels[value] || value,
};
})
: [];
}, [context, jsonSchema, alias]);
const initialValue = react.useMemo(() => options.find((option) => option.rawValue === defaultValue)?.value, [defaultValue, options]);
const handleChange = hook.useHandle((value) => {
const selectedOption = options.find((opt) => opt.value === value);
if (selectedOption === undefined)
return;
onChange(selectedOption.rawValue);
});
return (jsxRuntime.jsx("div", { style: { display: 'flex', flexDirection: 'row', gap: 8, ...style }, children: jsxRuntime.jsx(antdMobile.Radio.Group, { disabled: disabled, defaultValue: initialValue, onChange: handleChange, children: array.map(options, (option) => (jsxRuntime.jsx(antdMobile.Radio, { value: option.value, style: {
'--icon-size': '24px',
'--font-size': '20px',
'--gap': '6px',
}, children: option.label }, option.value))) }) }));
};
const FormTypeInputRadioGroupDefinition = {
Component: FormTypeInputRadioGroup,
test: ({ type, formType, jsonSchema }) => {
return ((type === 'string' || type === 'number' || type === 'integer') &&
(formType === 'radio' || formType === 'radiogroup') &&
!!jsonSchema.enum?.length);
},
};
const FormTypeInputSlider = ({ jsonSchema, disabled, defaultValue, onChange, }) => {
const handleChange = react.useCallback((value) => {
if (value === null)
onChange(NaN);
else if (typeof value === 'number')
onChange(value);
else
onChange(value);
}, [onChange]);
const { min, max, step, range, marks, ...changeHandler } = react.useMemo(() => {
return {
min: jsonSchema.minimum,
max: jsonSchema.maximum,
step: jsonSchema.multipleOf,
range: jsonSchema.options?.range,
marks: jsonSchema.options?.marks,
...(jsonSchema.options?.lazy === false
? { onChange: handleChange }
: { onAfterChange: handleChange }),
};
}, [handleChange, jsonSchema]);
return (jsxRuntime.jsx(antdMobile.Slider, { disabled: disabled, min: min, max: max, step: step, range: range, marks: marks, defaultValue: defaultValue ?? undefined, ...changeHandler }));
};
const FormTypeInputSliderDefinition = {
Component: FormTypeInputSlider,
test: ({ type, jsonSchema, format }) => {
return (((type === 'number' || type === 'integer') && format === 'slider') ||
(type === 'array' &&
jsonSchema.items &&
filter.isNumberSchema(jsonSchema.items) &&
format === 'slider'));
},
};
const FormTypeInputString = ({ path, name, jsonSchema, readOnly, disabled, defaultValue, onChange, placeholder, }) => {
const type = jsonSchema.format === 'password' ? 'password' : 'text';
const handleChange = hook.useHandle(onChange);
return (jsxRuntime.jsx(antdMobile.Input, { id: path, name: name, type: type, readOnly: readOnly, disabled: disabled, placeholder: placeholder, defaultValue: defaultValue ?? undefined, onChange: handleChange }));
};
const FormTypeInputStringDefinition = {
Component: FormTypeInputString,
test: {
type: 'string',
},
};
const FormTypeInputStringCheckbox = ({ jsonSchema, disabled, defaultValue, onChange, context, alias, }) => {
const options = react.useMemo(() => {
const labels = context.checkboxLabels || alias || {};
return jsonSchema.items?.enum
? array.map(jsonSchema.items.enum, (rawValue) => {
const value = '' + rawValue;
return {
value,
rawValue,
label: labels[value] || value,
};
})
: [];
}, [context, jsonSchema, alias]);
const initialValue = react.useMemo(() => {
if (defaultValue === undefined)
return undefined;
return array.map(defaultValue, (v) => '' + v);
}, [defaultValue]);
const handleChange = hook.useHandle((value) => {
const convertedValues = array.map(value, (v) => {
const option = options.find((opt) => opt.value === v.toString());
return option ? option.rawValue : v.toString();
});
onChange(convertedValues);
});
return (jsxRuntime.jsx("div", { style: { display: 'flex', flexDirection: 'row', gap: 8 }, children: jsxRuntime.jsx(antdMobile.Checkbox.Group, { disabled: disabled, defaultValue: initialValue, onChange: handleChange, children: array.map(options, (option) => (jsxRuntime.jsx(antdMobile.Checkbox, { value: option.value, style: {
'--icon-size': '24px',
'--font-size': '20px',
'--gap': '6px',
}, children: option.label }, option.value))) }) }));
};
const FormTypeInputStringCheckboxDefinition = {
Component: FormTypeInputStringCheckbox,
test: ({ type, formType, jsonSchema }) => {
return (type === 'array' &&
formType === 'checkbox' &&
filter.isStringSchema(jsonSchema.items) &&
!!jsonSchema.items.enum?.length);
},
};
const FormTypeInputStringSwitch = ({ path, jsonSchema, disabled, value, onChange, context, alias, }) => {
const [checked, unchecked] = react.useMemo(() => {
const [checked, unchecked] = jsonSchema.enum || [];
return [
checked !== undefined ? checked : 'on',
unchecked !== undefined ? unchecked : 'off',
];
}, [jsonSchema]);
const [checkedLabel, uncheckedLabel] = react.useMemo(() => {
const labels = context.switchLabels || alias || {};
return [
labels['' + checked] || checked,
labels['' + unchecked] || unchecked,
];
}, [checked, unchecked, context, alias]);
const handleChange = hook.useHandle((input) => {
onChange(input ? checked : unchecked);
});
return (jsxRuntime.jsx(antdMobile.Switch, { disabled: disabled, checked: value === checked, checkedText: checkedLabel, uncheckedText: uncheckedLabel, onChange: handleChange }, path));
};
const FormTypeInputStringSwitchDefinition = {
Component: FormTypeInputStringSwitch,
test: ({ type, formType, jsonSchema }) => type === 'string' && formType === 'switch' && jsonSchema.enum?.length === 2,
};
const FormTypeInputTextarea = ({ path, name, jsonSchema, readOnly, disabled, defaultValue, onChange, placeholder, }) => {
const handleChange = hook.useHandle(onChange);
const autoSize = react.useMemo(() => ({
minRows: jsonSchema.minRows,
maxRows: jsonSchema.maxRows,
}), [jsonSchema]);
return (jsxRuntime.jsx(antdMobile.TextArea, { id: path, name: name, disabled: disabled, readOnly: readOnly, autoSize: autoSize, placeholder: placeholder, defaultValue: defaultValue ?? undefined, onChange: handleChange }));
};
const FormTypeInputTextareaDefinition = {
Component: FormTypeInputTextarea,
test: ({ type, format, formType }) => type === 'string' && (format === 'textarea' || formType === 'textarea'),
};
const formTypeInputDefinitions = [
FormTypeInputBooleanSwitchDefinition,
FormTypeInputStringCheckboxDefinition,
FormTypeInputStringSwitchDefinition,
FormTypeInputRadioGroupDefinition,
FormTypeInputArrayDefinition,
FormTypeInputSliderDefinition,
FormTypeInputTextareaDefinition,
FormTypeInputStringDefinition,
FormTypeInputNumberDefinition,
FormTypeInputBooleanDefinition,
];
const plugin = {
FormGroup,
FormLabel,
FormInput,
FormError,
formTypeInputDefinitions,
};
exports.plugin = plugin;