UNPKG

@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

248 lines (229 loc) 11.7 kB
'use strict'; const jsxRuntime = require('react/jsx-runtime'); const antdMobile = require('antd-mobile'); 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?.type !== 'array' && (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) => (jsxRuntime.jsxs("div", { style: { display: 'flex' }, children: [jsxRuntime.jsx(ChildNodeComponent, {}), !readOnly && (jsxRuntime.jsx(Remove, { disabled: disabled, onClick: () => handleRemoveClick(i) }))] }, ChildNodeComponent.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], [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, jsonSchema, disabled, value, onChange, context, }) => { const [checkedLabel, uncheckedLabel] = react.useMemo(() => { const alias = context?.checkboxLabels || jsonSchema.options?.alias || {}; return [alias.checked, alias.unchecked]; }, [context, jsonSchema]); const handleChange = hook.useHandle(onChange); return (jsxRuntime.jsx(antdMobile.Switch, { disabled: disabled, checked: value, 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) => { if (value === null) onChange(NaN); else 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, onChange: handleChange })); }; const FormTypeInputNumberDefinition = { Component: FormTypeInputNumber, test: { type: ['number', 'integer'], }, }; const FormTypeInputRadioGroup = ({ jsonSchema, disabled, defaultValue, onChange, context, style, }) => { const options = react.useMemo(() => { const alias = context.radioLabels || jsonSchema.options?.alias || {}; return jsonSchema.enum ? array.map(jsonSchema.enum, (value) => ({ label: alias[value] || value, value, })) : []; }, [context, jsonSchema]); const handleChange = hook.useHandle(onChange); return (jsxRuntime.jsx("div", { style: { display: 'flex', flexDirection: 'row', gap: 8, ...style }, children: jsxRuntime.jsx(antdMobile.Radio.Group, { disabled: disabled, defaultValue: defaultValue, 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, ...changeHandler })); }; const FormTypeInputSliderDefinition = { Component: FormTypeInputSlider, test: ({ type, jsonSchema, format }) => { return (((type === 'number' || type === 'integer') && format === 'slider') || (type === 'array' && jsonSchema.items && (jsonSchema.items.type === 'number' || jsonSchema.items.type === 'integer') && format === 'slider')); }, }; const FormTypeInputString = ({ path, name, jsonSchema, readOnly, disabled, defaultValue, onChange, }) => { 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: jsonSchema.placeholder, defaultValue: defaultValue, onChange: handleChange })); }; const FormTypeInputStringDefinition = { Component: FormTypeInputString, test: { type: 'string', }, }; const FormTypeInputStringCheckbox = ({ jsonSchema, disabled, defaultValue, onChange, context, }) => { const options = react.useMemo(() => { const alias = context.checkboxLabels || jsonSchema.items?.options?.alias || jsonSchema.options?.alias || {}; return jsonSchema.items?.enum ? array.map(jsonSchema.items.enum, (value) => ({ label: alias[value] || value, value, })) : []; }, [context, jsonSchema]); const handleChange = hook.useHandle((value) => { onChange(array.map(value, (v) => v.toString())); }); return (jsxRuntime.jsx("div", { style: { display: 'flex', flexDirection: 'row', gap: 8 }, children: jsxRuntime.jsx(antdMobile.Checkbox.Group, { disabled: disabled, defaultValue: defaultValue, 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' && jsonSchema.items.type === 'string' && jsonSchema.items.enum?.length); }, }; const FormTypeInputStringSwitch = ({ path, jsonSchema, disabled, value, onChange, context, }) => { const [checked, unchecked] = react.useMemo(() => { const [checked, unchecked] = jsonSchema.enum || []; return [checked || 'on', unchecked || 'off']; }, [jsonSchema]); const [checkedLabel, uncheckedLabel] = react.useMemo(() => { const alias = context.switchLabels || jsonSchema.options?.alias || {}; return [alias[checked] || checked, alias[unchecked] || unchecked]; }, [checked, unchecked, context, jsonSchema]); 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, }) => { 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: jsonSchema.placeholder, defaultValue: defaultValue, 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;