UNPKG

@vtaits/react-form-schema-base-ui

Version:

Structure of base ui-components to construct complex forms using @vtaits/form-schema

584 lines (583 loc) 14.2 kB
// src/BaseUIContext.tsx import { format } from "date-fns/format"; import { isValid } from "date-fns/isValid"; import { parse } from "date-fns/parse"; import { createContext, useMemo } from "react"; import { useSelectAsyncPaginate } from "use-select-async-paginate"; import { Fragment, jsx, jsxs } from "react/jsx-runtime"; var DATE_FORMAT = "yyyy-MM-dd"; var DATETIME_FORMAT = "yyyy-MM-dd'T'HH:mm"; function AsyncOptions({ initialAdditional, additional, getOptionLabel, getOptionValue, loadOptions, selectedValuesSet, render }) { const { currentCache } = useSelectAsyncPaginate({ autoload: true, initialAdditional, additional, loadOptions }); const { options } = currentCache; const children = useMemo( () => options.map((option) => { const optionValue = getOptionValue(option); return /* @__PURE__ */ jsx( "option", { selected: selectedValuesSet ? selectedValuesSet.has(optionValue) : void 0, value: optionValue, children: getOptionLabel(option) }, optionValue ); }), [getOptionLabel, getOptionValue, options, selectedValuesSet] ); return render({ children }); } var BaseUIContext = createContext({ renderAsyncSelect: ({ clearable, disabled, autoFocus, name, initialAdditional, additional, optionsCacheRef, loadOptions, placeholder, value, onChange, getOptionLabel, getOptionValue }) => /* @__PURE__ */ jsx( AsyncOptions, { initialAdditional, additional, getOptionLabel, getOptionValue, loadOptions, render: ({ children }) => /* @__PURE__ */ jsxs( "select", { autoFocus, disabled, name, onChange: (event) => { const nextValue = event.target.value; const selectedOption = optionsCacheRef.current[nextValue]; onChange(selectedOption); }, value: value ? getOptionValue(value) : "", children: [ clearable && /* @__PURE__ */ jsx("option", { value: "", children: placeholder }), children ] } ) } ), renderAsyncMultiSelect: ({ disabled, autoFocus, name, initialAdditional, additional, optionsCacheRef, loadOptions, value, onChange, getOptionLabel, getOptionValue }) => { const selectedValuesSet = new Set( (value || []).map((option) => getOptionValue(option)) ); return /* @__PURE__ */ jsx( AsyncOptions, { initialAdditional, additional, getOptionLabel, getOptionValue, loadOptions, selectedValuesSet, render: ({ children }) => /* @__PURE__ */ jsx( "select", { autoFocus, disabled, multiple: true, name, onChange: (event) => { const nextValue = []; for (const option of Array.from(event.target.options)) { if (option.selected) { const selectedOption = optionsCacheRef.current[option.value]; if (selectedOption) { nextValue.push(selectedOption); } } } onChange(nextValue); }, children } ) } ); }, renderCheckbox: ({ checked, disabled, autoFocus, name, onChange, children }) => /* @__PURE__ */ jsxs("label", { children: [ /* @__PURE__ */ jsx( "input", { autoFocus, disabled, name, type: "checkbox", checked, onChange: (event) => { onChange(event.target.checked); } } ), children ] }), renderCheckboxGroup: ({ disabled, name, value, onChange, options, getOptionLabel, getOptionValue }) => { const selectedValuesSet = new Set( (value || []).map((option) => getOptionValue(option)) ); return /* @__PURE__ */ jsx("div", { children: options.map((option) => { const optionValue = getOptionValue(option); const checked = selectedValuesSet.has(optionValue); return /* @__PURE__ */ jsxs("label", { children: [ /* @__PURE__ */ jsx( "input", { disabled, name, type: "checkbox", checked, onChange: () => { if (checked) { onChange( value.filter( (valueItem) => getOptionValue(valueItem) !== optionValue ) ); } else { onChange([...value, option]); } } } ), getOptionLabel(option) ] }, optionValue); }) }); }, renderDatePicker: ({ disabled, inputProps, autoFocus, onChange, value }) => /* @__PURE__ */ jsx( "input", { autoFocus, disabled, ...inputProps, type: "date", value: value ? format(value, DATE_FORMAT) : "", onChange: (event) => { const dateRaw = event.target.value; if (!dateRaw) { onChange(null); return; } const date = parse(dateRaw, DATE_FORMAT, /* @__PURE__ */ new Date()); if (!isValid(date)) { onChange(null); return; } onChange(date); } } ), renderDateTimePicker: ({ disabled, inputProps, autoFocus, onChange, value }) => /* @__PURE__ */ jsx( "input", { autoFocus, disabled, ...inputProps, type: "datetime-local", value: value ? format(value, DATETIME_FORMAT) : "", onChange: (event) => { const dateRaw = event.target.value; if (!dateRaw) { onChange(null); return; } const date = parse(dateRaw, DATETIME_FORMAT, /* @__PURE__ */ new Date()); if (!isValid(date)) { onChange(null); return; } onChange(date); } } ), renderFileInput: ({ accept, disabled, name, onSelectFile, selectedFile }) => /* @__PURE__ */ jsxs(Fragment, { children: [ /* @__PURE__ */ jsx( "input", { accept, disabled, name, type: "file", onChange: (event) => { onSelectFile(event.target.files?.[0] || null); event.target.value = ""; } } ), selectedFile && /* @__PURE__ */ jsxs("p", { children: [ selectedFile, " ", /* @__PURE__ */ jsx( "button", { disabled, type: "button", onClick: () => { onSelectFile(null); }, children: "Remove" } ) ] }) ] }), renderForm: ({ actions, error, fields, formProps, title }) => /* @__PURE__ */ jsxs("form", { ...formProps, children: [ title && /* @__PURE__ */ jsx("h1", { children: title }), fields, error && /* @__PURE__ */ jsx( "p", { style: { color: "red", fontSize: "1.5em" }, "data-testid": "@@form/error", children: error } ), /* @__PURE__ */ jsx("div", { children: actions }) ] }), renderInput: ({ disabled, inputProps, autoFocus, onChange, options, name }) => { if (options && options.length > 0) { const listId = `${name}-datalist`; return /* @__PURE__ */ jsxs(Fragment, { children: [ /* @__PURE__ */ jsx( "input", { autoFocus, list: `${name}-datalist`, name, disabled, ...inputProps, onChange: ({ target: { value } }) => { onChange(value); } } ), /* @__PURE__ */ jsx("datalist", { id: listId, children: options.map((option) => /* @__PURE__ */ jsx("option", { value: option }, option)) }) ] }); } return /* @__PURE__ */ jsx( "input", { name, disabled, ...inputProps, onChange: ({ target: { value } }) => { onChange(value); } } ); }, renderListAddButton: ({ children, onClick, disabled }) => /* @__PURE__ */ jsx( "button", { disabled, "data-testid": "@@list/add", type: "button", onClick, children } ), renderListItemWrapper: ({ children, disabled, handleRemove, title, name }) => /* @__PURE__ */ jsxs("fieldset", { "data-testid": `@@list-item/${name}`, children: [ title && /* @__PURE__ */ jsx("legend", { children: title }), children, handleRemove && /* @__PURE__ */ jsx("button", { disabled, type: "button", onClick: handleRemove, children: "Remove" }) ] }), renderListWrapper: ({ actions, error, hint, items, label, name, required }) => /* @__PURE__ */ jsxs("div", { "data-testid": `@@list/${name}`, children: [ (label || required) && // biome-ignore lint/a11y/noLabelWithoutControl: list has no associated input /* @__PURE__ */ jsxs("label", { children: [ label, " ", required && /* @__PURE__ */ jsx( "span", { style: { color: "red" }, children: "*" } ) ] }), /* @__PURE__ */ jsx("div", { role: "list", children: items }), hint && /* @__PURE__ */ jsx( "p", { style: { color: "gray" }, "data-testid": `@@hint/${name}`, children: hint } ), actions && /* @__PURE__ */ jsx("div", { children: actions }), error && /* @__PURE__ */ jsx( "p", { style: { color: "red" }, "data-testid": `@@error/${name}`, children: error } ) ] }), renderMultiSelect: ({ disabled, autoFocus, name, options, optionsCacheRef, value, onChange, getOptionLabel, getOptionValue }) => { const selectedValuesSet = new Set( (value || []).map((option) => getOptionValue(option)) ); return /* @__PURE__ */ jsx( "select", { autoFocus, disabled, multiple: true, name, onChange: (event) => { const nextValue = []; for (const option of Array.from(event.target.options)) { if (option.selected) { const selectedOption = optionsCacheRef.current[option.value]; if (selectedOption) { nextValue.push(selectedOption); } } } onChange(nextValue); }, children: options.map((option) => { const optionValue = getOptionValue(option); return /* @__PURE__ */ jsx( "option", { selected: selectedValuesSet.has(optionValue), value: optionValue, children: getOptionLabel(option) }, optionValue ); }) } ); }, renderRadioGroup: ({ disabled, name, value, onChange, options, getOptionLabel, getOptionValue }) => { const selectedValue = value ? getOptionValue(value) : null; return /* @__PURE__ */ jsx("div", { children: options.map((option) => { const optionValue = getOptionValue(option); return /* @__PURE__ */ jsxs("label", { children: [ /* @__PURE__ */ jsx( "input", { disabled, name, type: "radio", checked: optionValue === selectedValue, onChange: () => { onChange(option); } } ), getOptionLabel(option) ] }, optionValue); }) }); }, renderSelect: ({ clearable, disabled, autoFocus, name, options, optionsCacheRef, placeholder, value, onChange, getOptionLabel, getOptionValue }) => /* @__PURE__ */ jsxs( "select", { autoFocus, disabled, name, onChange: (event) => { const nextValue = event.target.value; const selectedOption = optionsCacheRef.current[nextValue]; onChange(selectedOption); }, value: value ? getOptionValue(value) : "", children: [ clearable && /* @__PURE__ */ jsx("option", { value: "", children: placeholder }), options.map((option) => { const optionValue = getOptionValue(option); return /* @__PURE__ */ jsx("option", { value: optionValue, children: getOptionLabel(option) }, optionValue); }) ] } ), renderTags: ({ disabled, autoFocus, name, onChange, value }) => /* @__PURE__ */ jsx( "input", { autoFocus, disabled, name, onChange: (event) => { onChange(event.target.value.split(",")); }, value: value.join(",") } ), renderTextArea: ({ disabled, autoFocus, name, textAreaProps }) => /* @__PURE__ */ jsx( "textarea", { autoFocus, disabled, name, ...textAreaProps } ), renderWrapper: ({ children, error, hint, label, name, required }) => /* @__PURE__ */ jsxs("div", { children: [ (label || required) && // biome-ignore lint/a11y/noLabelWithoutControl: TO DO /* @__PURE__ */ jsxs("label", { children: [ label, " ", required && /* @__PURE__ */ jsx( "span", { style: { color: "red" }, children: "*" } ) ] }), children, hint && /* @__PURE__ */ jsx( "p", { style: { color: "gray" }, "data-testid": `@@hint/${name}`, children: hint } ), /* @__PURE__ */ jsx( "p", { style: { color: "red" }, "data-testid": `@@error/${name}`, children: error } ) ] }) }); // src/useUI.ts import { useContext } from "react"; function useUI() { return useContext(BaseUIContext); } export { BaseUIContext, useUI }; //# sourceMappingURL=index.js.map