@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
JavaScript
// 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