mirror of
https://github.com/shishantbiswas/bknd.git
synced 2026-03-16 12:37:20 +00:00
public commit
This commit is contained in:
@@ -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}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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} />
|
||||
);
|
||||
}
|
||||
@@ -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;
|
||||
105
app/src/ui/components/form/json-schema/widgets/SelectWidget.tsx
Normal file
105
app/src/ui/components/form/json-schema/widgets/SelectWidget.tsx
Normal 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;
|
||||
30
app/src/ui/components/form/json-schema/widgets/index.tsx
Normal file
30
app/src/ui/components/form/json-schema/widgets/index.tsx
Normal 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)
|
||||
};
|
||||
Reference in New Issue
Block a user