public commit

This commit is contained in:
dswbx
2024-11-16 12:01:47 +01:00
commit 90f80c4280
582 changed files with 49291 additions and 0 deletions

View File

@@ -0,0 +1,48 @@
import { Switch } from "@mantine/core";
import type { FormContextType, RJSFSchema, StrictRJSFSchema, WidgetProps } from "@rjsf/utils";
import { type ChangeEvent, useCallback } from "react";
export function CheckboxWidget<
T = any,
S extends StrictRJSFSchema = RJSFSchema,
F extends FormContextType = any
>({
schema,
uiSchema,
options,
id,
value,
disabled,
readonly,
label,
hideLabel,
autofocus = false,
onBlur,
onFocus,
onChange,
registry,
...props
}: WidgetProps<T, S, F>) {
/*console.log("addprops", value, props, label, {
label,
name: props.name,
hideLabel,
label_lower: label.toLowerCase(),
name_lower: props.name.toLowerCase(),
equals: label.toLowerCase() === props.name.toLowerCase()
});*/
const handleChange = useCallback(
(event: ChangeEvent<HTMLInputElement>) => onChange(event.target.checked),
[onChange]
);
return (
<Switch
id={id}
onChange={handleChange}
defaultChecked={value}
disabled={disabled || readonly}
label={label.toLowerCase() === props.name.toLowerCase() ? undefined : label}
/>
);
}

View File

@@ -0,0 +1,98 @@
import {
type FormContextType,
type RJSFSchema,
type StrictRJSFSchema,
type WidgetProps,
ariaDescribedByIds,
enumOptionsDeselectValue,
enumOptionsIsSelected,
enumOptionsSelectValue,
enumOptionsValueForIndex,
optionId
} from "@rjsf/utils";
import { type ChangeEvent, type FocusEvent, useCallback } from "react";
/** The `CheckboxesWidget` is a widget for rendering checkbox groups.
* It is typically used to represent an array of enums.
*
* @param props - The `WidgetProps` for this component
*/
function CheckboxesWidget<
T = any,
S extends StrictRJSFSchema = RJSFSchema,
F extends FormContextType = any
>({
id,
disabled,
options: { inline = false, enumOptions, enumDisabled, emptyValue },
value,
autofocus = false,
readonly,
onChange,
onBlur,
onFocus
}: WidgetProps<T, S, F>) {
const checkboxesValues = Array.isArray(value) ? value : [value];
const handleBlur = useCallback(
({ target }: FocusEvent<HTMLInputElement>) =>
onBlur(id, enumOptionsValueForIndex<S>(target?.value, enumOptions, emptyValue)),
[onBlur, id]
);
const handleFocus = useCallback(
({ target }: FocusEvent<HTMLInputElement>) =>
onFocus(id, enumOptionsValueForIndex<S>(target?.value, enumOptions, emptyValue)),
[onFocus, id]
);
return (
<div className="checkboxes" id={id}>
{Array.isArray(enumOptions) &&
enumOptions.map((option, index) => {
const checked = enumOptionsIsSelected<S>(option.value, checkboxesValues);
const itemDisabled =
Array.isArray(enumDisabled) && enumDisabled.indexOf(option.value) !== -1;
const disabledCls = disabled || itemDisabled || readonly ? "disabled" : "";
const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
if (event.target.checked) {
onChange(enumOptionsSelectValue<S>(index, checkboxesValues, enumOptions));
} else {
onChange(enumOptionsDeselectValue<S>(index, checkboxesValues, enumOptions));
}
};
const checkbox = (
// biome-ignore lint/correctness/useJsxKeyInIterable: it's wrapped
<span>
<input
type="checkbox"
id={optionId(id, index)}
name={id}
checked={checked}
value={String(index)}
disabled={disabled || itemDisabled || readonly}
autoFocus={autofocus && index === 0}
onChange={handleChange}
onBlur={handleBlur}
onFocus={handleFocus}
aria-describedby={ariaDescribedByIds<T>(id)}
/>
<span>{option.label}</span>
</span>
);
return inline ? (
<label key={index} className={`checkbox-inline ${disabledCls}`}>
{checkbox}
</label>
) : (
<div key={index} className={`checkbox ${disabledCls}`}>
<label>{checkbox}</label>
</div>
);
})}
</div>
);
}
export default CheckboxesWidget;

View File

@@ -0,0 +1,19 @@
import type { WidgetProps } from "@rjsf/utils";
import { useState } from "react";
export default function JsonWidget({ value, onChange, disabled, readonly, ...props }: WidgetProps) {
const [val, setVal] = useState(JSON.stringify(value, null, 2));
function handleChange(e) {
setVal(e.target.value);
try {
onChange(JSON.parse(e.target.value));
} catch (err) {
console.error(err);
}
}
return (
<textarea value={val} rows={10} disabled={disabled || readonly} onChange={handleChange} />
);
}

View File

@@ -0,0 +1,97 @@
import {
type FormContextType,
type RJSFSchema,
type StrictRJSFSchema,
type WidgetProps,
ariaDescribedByIds,
enumOptionsIsSelected,
enumOptionsValueForIndex,
optionId
} from "@rjsf/utils";
import { type FocusEvent, useCallback } from "react";
/** The `RadioWidget` is a widget for rendering a radio group.
* It is typically used with a string property constrained with enum options.
*
* @param props - The `WidgetProps` for this component
*/
function RadioWidget<
T = any,
S extends StrictRJSFSchema = RJSFSchema,
F extends FormContextType = any
>({
options,
value,
required,
disabled,
readonly,
autofocus = false,
onBlur,
onFocus,
onChange,
id
}: WidgetProps<T, S, F>) {
const { enumOptions, enumDisabled, inline, emptyValue } = options;
const handleBlur = useCallback(
({ target: { value } }: FocusEvent<HTMLInputElement>) =>
onBlur(id, enumOptionsValueForIndex<S>(value, enumOptions, emptyValue)),
[onBlur, id]
);
const handleFocus = useCallback(
({ target: { value } }: FocusEvent<HTMLInputElement>) =>
onFocus(id, enumOptionsValueForIndex<S>(value, enumOptions, emptyValue)),
[onFocus, id]
);
return (
<div className="field-radio-group" id={id}>
{Array.isArray(enumOptions) &&
enumOptions.map((option, i) => {
const checked = enumOptionsIsSelected<S>(option.value, value);
const itemDisabled =
Array.isArray(enumDisabled) && enumDisabled.indexOf(option.value) !== -1;
const disabledCls = disabled || itemDisabled || readonly ? "disabled" : "";
const handleChange = () => onChange(option.value);
const radio = (
// biome-ignore lint/correctness/useJsxKeyInIterable: <explanation>
<span>
<input
type="radio"
id={optionId(id, i)}
checked={checked}
name={id}
required={required}
value={String(i)}
disabled={disabled || itemDisabled || readonly}
autoFocus={autofocus && i === 0}
onChange={handleChange}
onBlur={handleBlur}
onFocus={handleFocus}
aria-describedby={ariaDescribedByIds<T>(id)}
/>
<span>{option.label}</span>
</span>
);
return inline ? (
<label
key={i}
className={`radio-inline ${checked ? "checked" : ""} ${disabledCls}`}
>
{radio}
</label>
) : (
<div key={i} className={`radio ${checked ? "checked" : ""} ${disabledCls}`}>
<label>{radio}</label>
</div>
);
})}
</div>
);
}
export default RadioWidget;

View File

@@ -0,0 +1,105 @@
import {
type FormContextType,
type RJSFSchema,
type StrictRJSFSchema,
type WidgetProps,
ariaDescribedByIds,
enumOptionsIndexForValue,
enumOptionsValueForIndex
} from "@rjsf/utils";
import { type ChangeEvent, type FocusEvent, type SyntheticEvent, useCallback } from "react";
function getValue(event: SyntheticEvent<HTMLSelectElement>, multiple: boolean) {
if (multiple) {
return Array.from((event.target as HTMLSelectElement).options)
.slice()
.filter((o) => o.selected)
.map((o) => o.value);
}
return (event.target as HTMLSelectElement).value;
}
/** The `SelectWidget` is a widget for rendering dropdowns.
* It is typically used with string properties constrained with enum options.
*
* @param props - The `WidgetProps` for this component
*/
function SelectWidget<
T = any,
S extends StrictRJSFSchema = RJSFSchema,
F extends FormContextType = any
>({
schema,
id,
options,
value,
required,
disabled,
readonly,
multiple = false,
autofocus = false,
onChange,
onBlur,
onFocus,
placeholder
}: WidgetProps<T, S, F>) {
const { enumOptions, enumDisabled, emptyValue: optEmptyVal } = options;
const emptyValue = multiple ? [] : "";
const handleFocus = useCallback(
(event: FocusEvent<HTMLSelectElement>) => {
const newValue = getValue(event, multiple);
return onFocus(id, enumOptionsValueForIndex<S>(newValue, enumOptions, optEmptyVal));
},
[onFocus, id, schema, multiple, enumOptions, optEmptyVal]
);
const handleBlur = useCallback(
(event: FocusEvent<HTMLSelectElement>) => {
const newValue = getValue(event, multiple);
return onBlur(id, enumOptionsValueForIndex<S>(newValue, enumOptions, optEmptyVal));
},
[onBlur, id, schema, multiple, enumOptions, optEmptyVal]
);
const handleChange = useCallback(
(event: ChangeEvent<HTMLSelectElement>) => {
const newValue = getValue(event, multiple);
return onChange(enumOptionsValueForIndex<S>(newValue, enumOptions, optEmptyVal));
},
[onChange, schema, multiple, enumOptions, optEmptyVal]
);
const selectedIndexes = enumOptionsIndexForValue<S>(value, enumOptions, multiple);
const showPlaceholderOption = !multiple && schema.default === undefined;
return (
<select
id={id}
name={id}
multiple={multiple}
className="form-control"
value={typeof selectedIndexes === "undefined" ? emptyValue : selectedIndexes}
required={required}
disabled={disabled || readonly}
autoFocus={autofocus}
onBlur={handleBlur}
onFocus={handleFocus}
onChange={handleChange}
aria-describedby={ariaDescribedByIds<T>(id)}
>
{showPlaceholderOption && <option value="">{placeholder}</option>}
{Array.isArray(enumOptions) &&
enumOptions.map(({ value, label }, i) => {
const disabled = enumDisabled && enumDisabled.indexOf(value) !== -1;
return (
<option key={i} value={String(i)} disabled={disabled}>
{label}
</option>
);
})}
</select>
);
}
export default SelectWidget;

View File

@@ -0,0 +1,30 @@
import { Label } from "../templates/FieldTemplate";
import { CheckboxWidget } from "./CheckboxWidget";
import CheckboxesWidget from "./CheckboxesWidget";
import JsonWidget from "./JsonWidget";
import RadioWidget from "./RadioWidget";
import SelectWidget from "./SelectWidget";
const WithLabel = (WrappedComponent, kind?: string) => {
return (props) => {
const hideLabel =
!props.label ||
props.uiSchema["ui:options"]?.hideLabel ||
props.options?.hideLabel ||
props.hideLabel;
return (
<>
{!hideLabel && <Label label={props.label} required={props.required} id={props.id} />}
<WrappedComponent {...props} />
</>
);
};
};
export const widgets = {
RadioWidget: RadioWidget,
CheckboxWidget: WithLabel(CheckboxWidget),
SelectWidget: WithLabel(SelectWidget, "select"),
CheckboxesWidget: WithLabel(CheckboxesWidget),
JsonWidget: WithLabel(JsonWidget)
};