@canard/schema-form-mui-plugin
Version:
Material-UI (MUI) components plugin for @canard/schema-form providing pre-built form inputs with modern MUI styling and MUI X integration
738 lines (714 loc) • 33.6 kB
JavaScript
'use strict';
const jsxRuntime = require('react/jsx-runtime');
const material = require('@mui/material');
const iconsMaterial = require('@mui/icons-material');
const array = require('@winglet/common-utils/array');
const hook = require('@winglet/react-utils/hook');
const react = require('react');
const AdapterDayjs = require('@mui/x-date-pickers/AdapterDayjs');
const DatePicker = require('@mui/x-date-pickers/DatePicker');
const LocalizationProvider = require('@mui/x-date-pickers/LocalizationProvider');
const dayjs = require('dayjs');
const xDatePickers = require('@mui/x-date-pickers');
const FormError = ({ errorMessage }) => (jsxRuntime.jsx(material.FormHelperText, { error: true, children: errorMessage }));
const FormGroup = ({ node, depth, Input, errorMessage, }) => {
if (depth === 0)
return jsxRuntime.jsx(Input, {});
if (node.group === 'branch') {
return (jsxRuntime.jsx(material.Box, { component: "fieldset", sx: {
marginBottom: 1,
marginLeft: depth * 2,
border: '1px solid',
borderColor: 'divider',
borderRadius: 1,
padding: 1,
}, children: jsxRuntime.jsx(Input, {}) }));
}
else {
return (jsxRuntime.jsxs(material.Box, { sx: {
marginBottom: 2,
marginLeft: depth * 2,
}, children: [jsxRuntime.jsx(Input, {}), jsxRuntime.jsx(material.FormHelperText, { error: true, children: errorMessage })] }));
}
};
const FormInput = ({ Input }) => jsxRuntime.jsx(Input, {});
const FormLabel = ({ name, path, required }) => (jsxRuntime.jsx(material.FormLabel, { htmlFor: path, required: required, children: name }));
const Add = (props) => (jsxRuntime.jsx(material.Button, { color: "primary", startIcon: jsxRuntime.jsx(iconsMaterial.Add, {}), ...props, children: "Add" }));
const Remove = (props) => (jsxRuntime.jsx(material.Button, { color: "primary", startIcon: jsxRuntime.jsx(iconsMaterial.Delete, {}), ...props }));
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("div", { style: { flex: 1 }, children: jsxRuntime.jsx(ChildNodeComponent, { hideLabel: true }) }), !readOnly && (jsxRuntime.jsx(Remove, { title: "remove", disabled: disabled, onClick: () => handleRemoveClick(i) }))] }, ChildNodeComponent.key))), !readOnly && (jsxRuntime.jsx("div", { style: { marginLeft: 20 }, children: jsxRuntime.jsx(Add, { title: "add", disabled: disabled, onClick: handleClick }) }))] }));
};
const FormTypeInputArrayDefinition = {
Component: FormTypeInputArray,
test: {
type: 'array',
},
};
const FormTypeInputBoolean = ({ path, name, jsonSchema, required, disabled, defaultValue, onChange, context, label: labelProp, size: sizeProp = 'medium', hideLabel, }) => {
const [label, size] = react.useMemo(() => {
if (hideLabel)
return [undefined, sizeProp || context.size];
return [labelProp || jsonSchema.label || name, sizeProp || context.size];
}, [jsonSchema, context, labelProp, name, sizeProp, hideLabel]);
const [indeterminate, defaultChecked] = react.useMemo(() => {
const isIndeterminate = defaultValue !== undefined && typeof defaultValue !== 'boolean';
return [isIndeterminate, !!defaultValue];
}, [defaultValue]);
const handleChange = hook.useHandle((event) => {
onChange(event.target.checked);
});
return (jsxRuntime.jsx(material.FormControlLabel, { label: label, htmlFor: path, required: required, disabled: disabled, control: jsxRuntime.jsx(material.Checkbox, { id: path, name: name, disabled: disabled, indeterminate: indeterminate, defaultChecked: defaultChecked, onChange: handleChange, size: size }) }));
};
const FormTypeInputBooleanDefinition = {
Component: FormTypeInputBoolean,
test: ({ type }) => type === 'boolean',
};
const FormTypeInputBooleanSwitch = ({ path, name, required, jsonSchema, disabled, defaultValue, onChange, context, label: labelProp, size: sizeProp = 'medium', hideLabel, }) => {
const handleChange = hook.useHandle((event) => {
onChange(event.target.checked);
});
const [label, size] = react.useMemo(() => {
if (hideLabel)
return [undefined, sizeProp || context.size];
return [labelProp || jsonSchema.label || name, sizeProp || context.size];
}, [jsonSchema, context, labelProp, name, sizeProp, hideLabel]);
return (jsxRuntime.jsx(material.FormControlLabel, { label: label, htmlFor: path, required: required, disabled: disabled, labelPlacement: "start", control: jsxRuntime.jsx(material.Switch, { id: path, name: name, defaultChecked: defaultValue, onChange: handleChange, disabled: disabled, size: size }) }));
};
const FormTypeInputBooleanSwitchDefinition = {
Component: FormTypeInputBooleanSwitch,
test: ({ type, formType }) => type === 'boolean' && formType === 'switch',
};
const FormTypeInputDate = ({ path, name, jsonSchema, required, disabled, defaultValue, onChange, context, label: labelProp, size: sizeProp, variant: variantProp, fullWidth: fullWidthProp, hideLabel, }) => {
const [label, size, variant, fullWidth] = react.useMemo(() => {
if (hideLabel)
return [
undefined,
sizeProp || context.size,
variantProp || context.variant,
fullWidthProp ?? context.fullWidth,
];
return [
labelProp || jsonSchema.label || name,
sizeProp || context.size,
variantProp || context.variant,
fullWidthProp ?? context.fullWidth,
];
}, [
jsonSchema,
context,
labelProp,
name,
sizeProp,
variantProp,
fullWidthProp,
hideLabel,
]);
const { minDate, maxDate } = react.useMemo(() => {
return {
minDate: jsonSchema.minimum ? dayjs(jsonSchema.minimum) : undefined,
maxDate: jsonSchema.maximum ? dayjs(jsonSchema.maximum) : undefined,
};
}, [jsonSchema.minimum, jsonSchema.maximum]);
const handleChange = hook.useHandle((newValue) => {
if (newValue && newValue.isValid()) {
onChange(newValue.format('YYYY-MM-DD'));
}
else {
onChange('');
}
});
const disableDate = hook.useHandle((date) => {
if (minDate && date.isBefore(minDate, 'day'))
return true;
if (maxDate && date.isAfter(maxDate, 'day'))
return true;
return false;
});
return (jsxRuntime.jsx(LocalizationProvider.LocalizationProvider, { dateAdapter: AdapterDayjs.AdapterDayjs, children: jsxRuntime.jsx(DatePicker.DatePicker, { label: label, defaultValue: defaultValue ? dayjs(defaultValue) : null, onChange: handleChange, disabled: disabled, minDate: minDate, maxDate: maxDate, shouldDisableDate: disableDate, slotProps: {
textField: {
id: path,
name,
required,
size,
variant,
fullWidth,
},
} }) }));
};
const FormTypeInputDateDefinition = {
Component: FormTypeInputDate,
test: ({ type, format }) => type === 'string' && format === 'date',
};
const FormTypeInputMonth = ({ path, name, jsonSchema, required, disabled, defaultValue, onChange, context, label: labelProp, size: sizeProp, variant: variantProp, fullWidth: fullWidthProp, hideLabel, }) => {
const [label, size, variant, fullWidth] = react.useMemo(() => {
if (hideLabel)
return [
undefined,
sizeProp || context.size,
variantProp || context.variant,
fullWidthProp ?? context.fullWidth,
];
return [
labelProp || jsonSchema.label || name,
sizeProp || context.size,
variantProp || context.variant,
fullWidthProp ?? context.fullWidth,
];
}, [
jsonSchema,
context,
labelProp,
name,
sizeProp,
variantProp,
fullWidthProp,
hideLabel,
]);
const handleChange = hook.useHandle((newValue) => {
if (newValue && newValue.isValid()) {
onChange(newValue.format('YYYY-MM'));
}
else {
onChange('');
}
});
return (jsxRuntime.jsx(LocalizationProvider.LocalizationProvider, { dateAdapter: AdapterDayjs.AdapterDayjs, children: jsxRuntime.jsx(DatePicker.DatePicker, { label: label, defaultValue: defaultValue ? dayjs(defaultValue) : null, onChange: handleChange, disabled: disabled, views: ['year', 'month'], openTo: "month", format: "YYYY-MM", slotProps: {
textField: {
id: path,
name,
required,
size,
variant,
fullWidth,
},
} }) }));
};
const FormTypeInputMonthDefinition = {
Component: FormTypeInputMonth,
test: ({ type, format }) => type === 'string' && format === 'month',
};
const FormTypeInputNumber = ({ path, name, jsonSchema, required, readOnly, disabled, defaultValue, onChange, context, label: labelProp, size: sizeProp, variant: variantProp, fullWidth: fullWidthProp, hideLabel, }) => {
const [label, size, variant, fullWidth] = react.useMemo(() => {
if (hideLabel)
return [
undefined,
sizeProp || context.size,
variantProp || context.variant,
fullWidthProp ?? context.fullWidth,
];
return [
labelProp || jsonSchema.label || name,
sizeProp || context.size,
variantProp || context.variant,
fullWidthProp ?? context.fullWidth,
];
}, [
jsonSchema,
context,
labelProp,
name,
sizeProp,
variantProp,
fullWidthProp,
hideLabel,
]);
const handleChange = hook.useHandle((event) => {
const inputValue = event.target.value;
if (inputValue === '') {
onChange(NaN);
return;
}
const numericValue = jsonSchema.type === 'integer'
? parseInt(inputValue, 10)
: parseFloat(inputValue);
if (!isNaN(numericValue)) {
onChange(numericValue);
}
});
return (jsxRuntime.jsx(material.TextField, { id: path, name: name, type: "number", variant: variant, fullWidth: fullWidth, label: label, required: required, size: size, placeholder: jsonSchema.placeholder, defaultValue: defaultValue, onChange: handleChange, disabled: disabled, slotProps: {
input: {
readOnly,
inputProps: {
min: jsonSchema.minimum,
max: jsonSchema.maximum,
step: jsonSchema.multipleOf ||
(jsonSchema.type === 'integer' ? 1 : 'any'),
},
},
} }));
};
const FormTypeInputNumberDefinition = {
Component: FormTypeInputNumber,
test: {
type: ['number', 'integer'],
},
};
const FormTypeInputRadioGroup = ({ path, name, jsonSchema, required, disabled, defaultValue, onChange, context, label: labelProp, size: sizeProp = 'medium', row = true, hideLabel, }) => {
const [label, size] = react.useMemo(() => {
if (hideLabel)
return [undefined, sizeProp || context.size];
return [labelProp || jsonSchema.label || name, sizeProp || context.size];
}, [jsonSchema, context, labelProp, name, sizeProp, hideLabel]);
const options = react.useMemo(() => {
const enumValues = jsonSchema.enum || [];
const radioLabels = jsonSchema.radioLabels || enumValues.map(String);
return enumValues.map((val, index) => ({
value: String(val),
originalValue: val,
label: radioLabels[index] || String(val),
}));
}, [jsonSchema]);
const handleChange = hook.useHandle((event) => {
const newValue = event.target.value;
const selectedOption = options.find((opt) => opt.value === newValue);
if (selectedOption) {
onChange(selectedOption.originalValue);
}
});
return (jsxRuntime.jsx(material.FormControlLabel, { label: label, htmlFor: path, required: required, disabled: disabled, labelPlacement: "start", style: {
display: 'flex',
alignItems: 'center',
justifyContent: 'start',
gap: 8,
}, control: jsxRuntime.jsx(material.RadioGroup, { name: name, defaultValue: defaultValue, onChange: handleChange, row: row, children: options.map((option) => (jsxRuntime.jsx(material.FormControlLabel, { value: option.value, disabled: disabled, control: jsxRuntime.jsx(material.Radio, { size: size, id: `${path}-${option.value}` }), label: option.label }, option.value))) }) }));
};
const FormTypeInputRadioGroupDefinition = {
Component: FormTypeInputRadioGroup,
test: ({ type, formType, jsonSchema }) => (type === 'string' || type === 'number' || type === 'integer') &&
(formType === 'radio' || formType === 'radiogroup') &&
jsonSchema.enum?.length,
};
const FormTypeInputSlider = ({ path, name, jsonSchema, required, disabled, defaultValue, onChange, context, label: labelProp, size: sizeProp = 'medium', showMarks = false, hideLabel, }) => {
const [label, size] = react.useMemo(() => {
if (hideLabel)
return [undefined, sizeProp || context.size];
return [labelProp || jsonSchema.label || name, sizeProp || context.size];
}, [jsonSchema, context, labelProp, name, sizeProp, hideLabel]);
const min = jsonSchema.minimum ?? 0;
const max = jsonSchema.maximum ?? 100;
const step = jsonSchema.multipleOf ?? 1;
const isLazy = jsonSchema.lazy ?? false;
const handleChange = hook.useHandle((_, newValue) => {
if (!isLazy) {
onChange(Array.isArray(newValue) ? newValue[0] : newValue);
}
});
const handleChangeCommitted = hook.useHandle((_, newValue) => {
if (isLazy) {
onChange(Array.isArray(newValue) ? newValue[0] : newValue);
}
});
return (jsxRuntime.jsxs(material.Box, { sx: { px: 2 }, children: [label && (jsxRuntime.jsxs(material.Typography, { variant: "body2", component: "label", htmlFor: path, sx: { mb: 1, display: 'block' }, children: [label, required && ' *'] })), jsxRuntime.jsx(material.Slider, { id: path, name: name, defaultValue: typeof defaultValue === 'number' ? defaultValue : min, min: min, max: max, step: step, onChange: handleChange, onChangeCommitted: handleChangeCommitted, disabled: disabled, size: size, marks: showMarks, valueLabelDisplay: "auto", sx: { mt: 1 } }), jsxRuntime.jsxs(material.Box, { sx: { display: 'flex', justifyContent: 'space-between', mt: 1 }, children: [jsxRuntime.jsx(material.Typography, { variant: "caption", color: "text.secondary", children: min }), jsxRuntime.jsx(material.Typography, { variant: "caption", color: "text.secondary", children: max })] })] }));
};
const FormTypeInputSliderDefinition = {
Component: FormTypeInputSlider,
test: ({ type, formType }) => (type === 'number' || type === 'integer') && formType === 'slider',
};
const FormTypeInputString = ({ path, name, jsonSchema, required, readOnly, disabled, defaultValue, onChange, context, label: labelProp, size: sizeProp, variant: variantProp, fullWidth: fullWidthProp, hideLabel, }) => {
const [label, size, variant, fullWidth] = react.useMemo(() => {
if (hideLabel)
return [
undefined,
sizeProp || context.size,
variantProp || context.variant,
fullWidthProp ?? context.fullWidth,
];
return [
labelProp || jsonSchema.label || name,
sizeProp || context.size,
variantProp || context.variant,
fullWidthProp ?? context.fullWidth,
];
}, [
jsonSchema,
context,
labelProp,
name,
sizeProp,
variantProp,
fullWidthProp,
hideLabel,
]);
const isPassword = react.useMemo(() => {
return (jsonSchema.format === 'password' || jsonSchema.formType === 'password');
}, [jsonSchema]);
const handleChange = hook.useHandle((event) => {
onChange(event.target.value);
});
return (jsxRuntime.jsx(material.TextField, { id: path, name: name, type: isPassword ? 'password' : 'text', variant: variant, fullWidth: fullWidth, placeholder: jsonSchema.placeholder, label: label, required: required, disabled: disabled, defaultValue: defaultValue, onChange: handleChange, size: size, slotProps: {
input: {
readOnly,
},
} }));
};
const FormTypeInputStringDefinition = {
Component: FormTypeInputString,
test: {
type: 'string',
},
};
const FormTypeInputStringCheckbox = ({ path, name, jsonSchema, required, disabled, defaultValue = [], onChange, context, label: labelProp, size: sizeProp = 'medium', row = true, hideLabel, }) => {
const [label, size] = react.useMemo(() => {
if (hideLabel)
return [undefined, sizeProp || context.size];
return [labelProp || jsonSchema.label || name, sizeProp || context.size];
}, [hideLabel, sizeProp, context.size, labelProp, jsonSchema, name]);
const options = react.useMemo(() => {
const enumValues = jsonSchema.items.enum || [];
const checkboxLabels = jsonSchema.items?.options?.alias;
return enumValues.map((value) => ({
value,
label: checkboxLabels?.[value] || value,
}));
}, [jsonSchema]);
const handleToggle = hook.useHandle((optionValue) => {
const currentValues = Array.isArray(defaultValue) ? defaultValue : [];
const isSelected = currentValues.includes(optionValue);
let newValues;
if (isSelected) {
newValues = currentValues.filter((val) => val !== optionValue);
}
else {
newValues = [...currentValues, optionValue];
}
onChange(newValues);
});
return (jsxRuntime.jsx(material.FormControlLabel, { label: label, htmlFor: path, required: required, disabled: disabled, labelPlacement: "start", style: {
display: 'flex',
alignItems: 'center',
justifyContent: 'start',
gap: 8,
}, control: jsxRuntime.jsx(material.FormGroup, { row: row, children: options.map((option) => {
const isChecked = Array.isArray(defaultValue)
? defaultValue.includes(option.value)
: false;
return (jsxRuntime.jsx(material.FormControlLabel, { disabled: disabled, control: jsxRuntime.jsx(material.Checkbox, { id: `${path}-${option.value}`, name: `${name}[]`, defaultChecked: isChecked, onChange: () => handleToggle(option.value), size: size }), label: option.label }, option.value));
}) }) }));
};
const FormTypeInputStringCheckboxDefinition = {
Component: FormTypeInputStringCheckbox,
test: ({ type, formType, jsonSchema }) => type === 'array' &&
formType === 'checkbox' &&
jsonSchema.items?.type === 'string' &&
jsonSchema.items?.enum &&
jsonSchema.items.enum.length > 0,
};
const FormTypeInputStringEnum = ({ path, name, jsonSchema, required, readOnly, disabled, defaultValue, onChange, context, label: labelProp, size: sizeProp, variant: variantProp, fullWidth: fullWidthProp, hideLabel, }) => {
const [label, size, variant, fullWidth] = react.useMemo(() => {
if (hideLabel)
return [
undefined,
sizeProp || context.size,
variantProp || context.variant,
fullWidthProp ?? context.fullWidth,
];
return [
labelProp || jsonSchema.label || name,
sizeProp || context.size,
variantProp || context.variant,
fullWidthProp ?? context.fullWidth,
];
}, [
jsonSchema,
context,
labelProp,
name,
sizeProp,
variantProp,
fullWidthProp,
hideLabel,
]);
const options = react.useMemo(() => jsonSchema.enum.map((value) => ({
value,
label: jsonSchema.options?.alias?.[value] || value,
})), [jsonSchema]);
const handleChange = hook.useHandle((event) => {
onChange(event.target.value);
});
const labelId = react.useMemo(() => `label-${path}`, [path]);
return (jsxRuntime.jsxs(material.FormControl, { fullWidth: fullWidth, variant: variant, children: [jsxRuntime.jsx(material.InputLabel, { id: labelId, children: label }), jsxRuntime.jsx(material.Select, { id: path, name: name, labelId: labelId, label: label, required: required, readOnly: readOnly, defaultValue: defaultValue, onChange: handleChange, disabled: disabled, size: size, children: options.map((option) => (jsxRuntime.jsx(material.MenuItem, { value: option.value, children: option.label }, option.value))) })] }));
};
const FormTypeInputStringEnumDefinition = {
Component: FormTypeInputStringEnum,
test: ({ type, jsonSchema }) => type === 'string' && jsonSchema.enum && jsonSchema.enum.length > 0,
};
const FormTypeInputStringSwitch = ({ path, name, jsonSchema, required, disabled, defaultValue, onChange, context, label: labelProp, size: sizeProp = 'medium', hideLabel, }) => {
const [label, size] = react.useMemo(() => {
if (hideLabel)
return [undefined, sizeProp || context.size];
return [labelProp || jsonSchema.label || name, sizeProp || context.size];
}, [hideLabel, sizeProp, context.size, labelProp, jsonSchema, name]);
const { offValue, onValue, offLabel, onLabel } = react.useMemo(() => {
const [first, second] = jsonSchema.enum;
const [firstLabel, secondLabel] = jsonSchema.switchLabels || [
first,
second,
];
return {
offValue: first,
onValue: second,
offLabel: firstLabel,
onLabel: secondLabel,
};
}, [jsonSchema.enum, jsonSchema.switchLabels]);
const handleChange = hook.useHandle((event) => {
onChange(event.target.checked ? onValue : offValue);
});
const switchSize = jsonSchema.switchSize || size;
return (jsxRuntime.jsx(material.FormControlLabel, { label: label, htmlFor: path, required: required, disabled: disabled, labelPlacement: "start", sx: {
alignItems: 'center',
gap: 1,
}, control: jsxRuntime.jsxs(material.Stack, { direction: "row", spacing: 1, sx: { alignItems: 'center' }, children: [jsxRuntime.jsx(material.Typography, { children: offLabel }), jsxRuntime.jsx(material.Switch, { id: path, name: name, defaultChecked: defaultValue === onValue, onChange: handleChange, disabled: disabled, size: switchSize }), jsxRuntime.jsx(material.Typography, { children: onLabel })] }) }));
};
const FormTypeInputStringSwitchDefinition = {
Component: FormTypeInputStringSwitch,
test: ({ type, formType, jsonSchema }) => type === 'string' &&
formType === 'switch' &&
jsonSchema.enum &&
jsonSchema.enum.length === 2,
};
const FormTypeInputTextarea = ({ path, name, jsonSchema, required, readOnly, disabled, defaultValue, onChange, context, label: labelProp, size: sizeProp, variant: variantProp, fullWidth: fullWidthProp, minRows, maxRows, hideLabel, }) => {
const [label, size, variant, fullWidth] = react.useMemo(() => {
if (hideLabel)
return [
undefined,
sizeProp || context.size,
variantProp || context.variant,
fullWidthProp ?? context.fullWidth,
];
return [
labelProp || jsonSchema.label || name,
sizeProp || context.size,
variantProp || context.variant,
fullWidthProp ?? context.fullWidth,
];
}, [
jsonSchema,
context,
labelProp,
name,
sizeProp,
variantProp,
fullWidthProp,
hideLabel,
]);
const handleChange = hook.useHandle((event) => {
onChange(event.target.value);
});
const finalMinRows = minRows ?? jsonSchema.minRows ?? 3;
const finalMaxRows = maxRows ?? jsonSchema.maxRows ?? 8;
return (jsxRuntime.jsx(material.TextField, { id: path, name: name, multiline: true, variant: variant, fullWidth: fullWidth, label: label, required: required, size: size, placeholder: jsonSchema.placeholder, defaultValue: defaultValue, onChange: handleChange, disabled: disabled, minRows: finalMinRows, maxRows: finalMaxRows, slotProps: {
input: {
readOnly,
},
}, sx: {
'& .MuiInputBase-root': {
alignItems: 'flex-start',
},
} }));
};
const FormTypeInputTextareaDefinition = {
Component: FormTypeInputTextarea,
test: ({ type, format, formType }) => type === 'string' && (format === 'textarea' || formType === 'textarea'),
};
const FormTypeInputTime = ({ path, name, jsonSchema, required, disabled, defaultValue, onChange, context, label: labelProp, size: sizeProp, variant: variantProp, fullWidth: fullWidthProp, ampm: ampmProp, hideLabel, }) => {
const [label, size, variant, fullWidth, ampm] = react.useMemo(() => {
if (hideLabel)
return [
undefined,
sizeProp || context.size,
variantProp || context.variant,
fullWidthProp ?? context.fullWidth,
ampmProp ?? jsonSchema.ampm,
];
return [
labelProp || jsonSchema.label || name,
sizeProp || context.size,
variantProp || context.variant,
fullWidthProp ?? context.fullWidth,
ampmProp ?? jsonSchema.ampm,
];
}, [
jsonSchema,
context,
labelProp,
name,
sizeProp,
variantProp,
fullWidthProp,
ampmProp,
hideLabel,
]);
const timeValue = react.useMemo(() => {
if (!defaultValue)
return null;
const today = dayjs().format('YYYY-MM-DD');
return dayjs(`${today}T${defaultValue}`);
}, [defaultValue]);
const handleChange = hook.useHandle((newValue) => {
if (newValue && newValue.isValid()) {
onChange(newValue.format('HH:mm:ss'));
}
else {
onChange('');
}
});
return (jsxRuntime.jsx(xDatePickers.LocalizationProvider, { dateAdapter: AdapterDayjs.AdapterDayjs, children: jsxRuntime.jsx(xDatePickers.TimePicker, { label: label, defaultValue: timeValue, onChange: handleChange, disabled: disabled, ampm: ampm, slotProps: {
textField: {
id: path,
name,
required,
size,
variant,
fullWidth,
},
} }) }));
};
const FormTypeInputTimeDefinition = {
Component: FormTypeInputTime,
test: ({ type, format }) => type === 'string' && format === 'time',
};
const DEFAULT_PROTOCOLS = ['http', 'https'];
const normalizeProtocol = (protocol) => {
return protocol.replace(/:\/\/$/, '').replace(/:$/, '');
};
const getProtocolSeparator = (protocol) => {
const normalizedProtocol = normalizeProtocol(protocol);
return ['mailto', 'tel'].includes(normalizedProtocol) ? ':' : '://';
};
const formatProtocolDisplay = (protocol) => {
const normalizedProtocol = normalizeProtocol(protocol);
const separator = getProtocolSeparator(normalizedProtocol);
return `${normalizedProtocol}${separator}`;
};
const parseUri = (uri) => {
const singleColonMatch = uri.match(/^([a-zA-Z][a-zA-Z0-9+.-]*):(.*)$/);
if (singleColonMatch && ['mailto', 'tel'].includes(singleColonMatch[1])) {
return {
protocol: singleColonMatch[1],
path: singleColonMatch[2],
};
}
const doubleColonMatch = uri.match(/^([a-zA-Z][a-zA-Z0-9+.-]*):\/\/(.*)$/);
if (doubleColonMatch) {
return {
protocol: doubleColonMatch[1],
path: doubleColonMatch[2],
};
}
return null;
};
const FormTypeInputUri = ({ path, name, jsonSchema, required, readOnly, disabled, defaultValue, onChange, context, label: labelProp, size: sizeProp, variant: variantProp, fullWidth: fullWidthProp, protocols: protocolsProp, hideLabel, }) => {
const [label, size, variant, fullWidth] = react.useMemo(() => {
if (hideLabel)
return [
undefined,
sizeProp || context.size,
variantProp || context.variant,
fullWidthProp ?? context.fullWidth,
];
return [
labelProp || jsonSchema.label || name,
sizeProp || context.size,
variantProp || context.variant,
fullWidthProp ?? context.fullWidth,
];
}, [
jsonSchema,
context,
labelProp,
name,
sizeProp,
variantProp,
fullWidthProp,
hideLabel,
]);
const normalizedProtocols = react.useMemo(() => {
const rawProtocols = protocolsProp || jsonSchema.options?.protocols || DEFAULT_PROTOCOLS;
return rawProtocols.map(normalizeProtocol);
}, [protocolsProp, jsonSchema]);
const textFieldRef = react.useRef(null);
const [protocol, setProtocol] = react.useState(normalizedProtocols[0]);
const { initialProtocol, initialUri } = react.useMemo(() => {
if (!defaultValue) {
return {
initialProtocol: normalizedProtocols[0],
initialUri: '',
};
}
const parsed = parseUri(defaultValue);
if (parsed) {
const normalizedDetected = normalizeProtocol(parsed.protocol);
return {
initialProtocol: normalizedProtocols.includes(normalizedDetected)
? normalizedDetected
: normalizedProtocols[0],
initialUri: parsed.path,
};
}
return {
initialProtocol: normalizedProtocols[0],
initialUri: defaultValue,
};
}, [defaultValue, normalizedProtocols]);
react.useEffect(() => {
setProtocol(initialProtocol);
}, [initialProtocol]);
const handleProtocolChange = hook.useHandle((event) => {
const newProtocol = event.target.value;
setProtocol(newProtocol);
const currentUri = textFieldRef.current?.value || '';
const separator = getProtocolSeparator(newProtocol);
const newValue = currentUri
? `${newProtocol}${separator}${currentUri}`
: `${newProtocol}${separator}`;
onChange(newValue);
});
const handleUriChange = hook.useHandle((event) => {
const newUri = event.target.value;
const separator = getProtocolSeparator(protocol);
const newValue = newUri ? `${protocol}${separator}${newUri}` : '';
onChange(newValue);
});
return (jsxRuntime.jsxs(material.Box, { children: [jsxRuntime.jsx(material.InputLabel, { htmlFor: path, required: required, children: label }), jsxRuntime.jsxs(material.Box, { sx: { display: 'flex', gap: 1, alignItems: 'flex-start' }, children: [jsxRuntime.jsx(material.Select, { value: protocol, onChange: handleProtocolChange, disabled: disabled || readOnly, size: size, displayEmpty: true, children: normalizedProtocols.map((prot) => (jsxRuntime.jsx(material.MenuItem, { value: prot, children: formatProtocolDisplay(prot) }, prot))) }), jsxRuntime.jsx(material.TextField, { inputRef: textFieldRef, id: path, name: name, variant: variant, fullWidth: fullWidth, size: size, placeholder: jsonSchema.placeholder, defaultValue: initialUri, onChange: handleUriChange, disabled: disabled, slotProps: {
input: {
readOnly,
},
} })] })] }));
};
const FormTypeInputUriDefinition = {
Component: FormTypeInputUri,
test: ({ type, format, formType }) => type === 'string' && (format === 'uri' || formType === 'uri'),
};
const formTypeInputDefinitions = [
FormTypeInputBooleanSwitchDefinition,
FormTypeInputStringCheckboxDefinition,
FormTypeInputStringSwitchDefinition,
FormTypeInputUriDefinition,
FormTypeInputMonthDefinition,
FormTypeInputDateDefinition,
FormTypeInputTimeDefinition,
FormTypeInputRadioGroupDefinition,
FormTypeInputStringEnumDefinition,
FormTypeInputArrayDefinition,
FormTypeInputSliderDefinition,
FormTypeInputTextareaDefinition,
FormTypeInputStringDefinition,
FormTypeInputNumberDefinition,
FormTypeInputBooleanDefinition,
];
const plugin = {
FormGroup,
FormLabel,
FormInput,
FormError,
formTypeInputDefinitions,
};
exports.plugin = plugin;