@rjsf/daisyui 6.0.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +231 -0
- package/dist/chakra-ui.esm.js +2192 -0
- package/dist/chakra-ui.esm.js.map +7 -0
- package/dist/chakra-ui.umd.js +2011 -0
- package/dist/index.js +2180 -0
- package/dist/index.js.map +7 -0
- package/lib/DaisyUIForm.d.ts +5 -0
- package/lib/DaisyUIForm.js +8 -0
- package/lib/DaisyUIForm.js.map +1 -0
- package/lib/DaisyUIFrameProvider.d.ts +35 -0
- package/lib/DaisyUIFrameProvider.js +93 -0
- package/lib/DaisyUIFrameProvider.js.map +1 -0
- package/lib/daisyForm/DaisyForm.d.ts +12 -0
- package/lib/daisyForm/DaisyForm.js +15 -0
- package/lib/daisyForm/DaisyForm.js.map +1 -0
- package/lib/daisyForm/index.d.ts +2 -0
- package/lib/daisyForm/index.js +3 -0
- package/lib/daisyForm/index.js.map +1 -0
- package/lib/index.d.ts +10 -0
- package/lib/index.js +11 -0
- package/lib/index.js.map +1 -0
- package/lib/templates/ArrayFieldDescriptionTemplate/ArrayFieldDescriptionTemplate.d.ts +7 -0
- package/lib/templates/ArrayFieldDescriptionTemplate/ArrayFieldDescriptionTemplate.js +11 -0
- package/lib/templates/ArrayFieldDescriptionTemplate/ArrayFieldDescriptionTemplate.js.map +1 -0
- package/lib/templates/ArrayFieldDescriptionTemplate/index.d.ts +2 -0
- package/lib/templates/ArrayFieldDescriptionTemplate/index.js +3 -0
- package/lib/templates/ArrayFieldDescriptionTemplate/index.js.map +1 -0
- package/lib/templates/ArrayFieldItemButtonsTemplate/ArrayFieldItemButtonsTemplate.d.ts +5 -0
- package/lib/templates/ArrayFieldItemButtonsTemplate/ArrayFieldItemButtonsTemplate.js +20 -0
- package/lib/templates/ArrayFieldItemButtonsTemplate/ArrayFieldItemButtonsTemplate.js.map +1 -0
- package/lib/templates/ArrayFieldItemButtonsTemplate/index.d.ts +2 -0
- package/lib/templates/ArrayFieldItemButtonsTemplate/index.js +3 -0
- package/lib/templates/ArrayFieldItemButtonsTemplate/index.js.map +1 -0
- package/lib/templates/ArrayFieldItemTemplate/ArrayFieldItemTemplate.d.ts +14 -0
- package/lib/templates/ArrayFieldItemTemplate/ArrayFieldItemTemplate.js +27 -0
- package/lib/templates/ArrayFieldItemTemplate/ArrayFieldItemTemplate.js.map +1 -0
- package/lib/templates/ArrayFieldItemTemplate/index.d.ts +2 -0
- package/lib/templates/ArrayFieldItemTemplate/index.js +3 -0
- package/lib/templates/ArrayFieldItemTemplate/index.js.map +1 -0
- package/lib/templates/ArrayFieldTemplate/ArrayFieldTemplate.d.ts +15 -0
- package/lib/templates/ArrayFieldTemplate/ArrayFieldTemplate.js +37 -0
- package/lib/templates/ArrayFieldTemplate/ArrayFieldTemplate.js.map +1 -0
- package/lib/templates/ArrayFieldTemplate/index.d.ts +2 -0
- package/lib/templates/ArrayFieldTemplate/index.js +3 -0
- package/lib/templates/ArrayFieldTemplate/index.js.map +1 -0
- package/lib/templates/ArrayFieldTitleTemplate/ArrayFieldTitleTemplate.d.ts +7 -0
- package/lib/templates/ArrayFieldTitleTemplate/ArrayFieldTitleTemplate.js +11 -0
- package/lib/templates/ArrayFieldTitleTemplate/ArrayFieldTitleTemplate.js.map +1 -0
- package/lib/templates/ArrayFieldTitleTemplate/index.d.ts +2 -0
- package/lib/templates/ArrayFieldTitleTemplate/index.js +3 -0
- package/lib/templates/ArrayFieldTitleTemplate/index.js.map +1 -0
- package/lib/templates/BaseInputTemplate/BaseInputTemplate.d.ts +16 -0
- package/lib/templates/BaseInputTemplate/BaseInputTemplate.js +33 -0
- package/lib/templates/BaseInputTemplate/BaseInputTemplate.js.map +1 -0
- package/lib/templates/BaseInputTemplate/index.d.ts +2 -0
- package/lib/templates/BaseInputTemplate/index.js +3 -0
- package/lib/templates/BaseInputTemplate/index.js.map +1 -0
- package/lib/templates/ButtonTemplates/AddButton.d.ts +6 -0
- package/lib/templates/ButtonTemplates/AddButton.js +15 -0
- package/lib/templates/ButtonTemplates/AddButton.js.map +1 -0
- package/lib/templates/ButtonTemplates/DaisyUIButton.d.ts +18 -0
- package/lib/templates/ButtonTemplates/DaisyUIButton.js +15 -0
- package/lib/templates/ButtonTemplates/DaisyUIButton.js.map +1 -0
- package/lib/templates/ButtonTemplates/IconButton.d.ts +5 -0
- package/lib/templates/ButtonTemplates/IconButton.js +21 -0
- package/lib/templates/ButtonTemplates/IconButton.js.map +1 -0
- package/lib/templates/ButtonTemplates/SubmitButton.d.ts +4 -0
- package/lib/templates/ButtonTemplates/SubmitButton.js +12 -0
- package/lib/templates/ButtonTemplates/SubmitButton.js.map +1 -0
- package/lib/templates/ButtonTemplates/index.d.ts +15 -0
- package/lib/templates/ButtonTemplates/index.js +17 -0
- package/lib/templates/ButtonTemplates/index.js.map +1 -0
- package/lib/templates/DescriptionField/DescriptionField.d.ts +8 -0
- package/lib/templates/DescriptionField/DescriptionField.js +16 -0
- package/lib/templates/DescriptionField/DescriptionField.js.map +1 -0
- package/lib/templates/DescriptionField/index.d.ts +2 -0
- package/lib/templates/DescriptionField/index.js +3 -0
- package/lib/templates/DescriptionField/index.js.map +1 -0
- package/lib/templates/ErrorList/ErrorList.d.ts +7 -0
- package/lib/templates/ErrorList/ErrorList.js +11 -0
- package/lib/templates/ErrorList/ErrorList.js.map +1 -0
- package/lib/templates/ErrorList/index.d.ts +2 -0
- package/lib/templates/ErrorList/index.js +3 -0
- package/lib/templates/ErrorList/index.js.map +1 -0
- package/lib/templates/FieldErrorTemplate/FieldErrorTemplate.d.ts +10 -0
- package/lib/templates/FieldErrorTemplate/FieldErrorTemplate.js +15 -0
- package/lib/templates/FieldErrorTemplate/FieldErrorTemplate.js.map +1 -0
- package/lib/templates/FieldErrorTemplate/index.d.ts +2 -0
- package/lib/templates/FieldErrorTemplate/index.js +3 -0
- package/lib/templates/FieldErrorTemplate/index.js.map +1 -0
- package/lib/templates/FieldHelpTemplate/FieldHelpTemplate.d.ts +11 -0
- package/lib/templates/FieldHelpTemplate/FieldHelpTemplate.js +15 -0
- package/lib/templates/FieldHelpTemplate/FieldHelpTemplate.js.map +1 -0
- package/lib/templates/FieldHelpTemplate/index.d.ts +2 -0
- package/lib/templates/FieldHelpTemplate/index.js +3 -0
- package/lib/templates/FieldHelpTemplate/index.js.map +1 -0
- package/lib/templates/FieldTemplate/FieldTemplate.d.ts +15 -0
- package/lib/templates/FieldTemplate/FieldTemplate.js +23 -0
- package/lib/templates/FieldTemplate/FieldTemplate.js.map +1 -0
- package/lib/templates/FieldTemplate/index.d.ts +2 -0
- package/lib/templates/FieldTemplate/index.js +3 -0
- package/lib/templates/FieldTemplate/index.js.map +1 -0
- package/lib/templates/GridTemplate/GridTemplate.d.ts +7 -0
- package/lib/templates/GridTemplate/GridTemplate.js +16 -0
- package/lib/templates/GridTemplate/GridTemplate.js.map +1 -0
- package/lib/templates/GridTemplate/index.d.ts +2 -0
- package/lib/templates/GridTemplate/index.js +3 -0
- package/lib/templates/GridTemplate/index.js.map +1 -0
- package/lib/templates/ObjectFieldTemplate/ObjectFieldTemplate.d.ts +13 -0
- package/lib/templates/ObjectFieldTemplate/ObjectFieldTemplate.js +25 -0
- package/lib/templates/ObjectFieldTemplate/ObjectFieldTemplate.js.map +1 -0
- package/lib/templates/ObjectFieldTemplate/index.d.ts +2 -0
- package/lib/templates/ObjectFieldTemplate/index.js +3 -0
- package/lib/templates/ObjectFieldTemplate/index.js.map +1 -0
- package/lib/templates/Templates.d.ts +19 -0
- package/lib/templates/Templates.js +56 -0
- package/lib/templates/Templates.js.map +1 -0
- package/lib/templates/TitleField/TitleField.d.ts +13 -0
- package/lib/templates/TitleField/TitleField.js +19 -0
- package/lib/templates/TitleField/TitleField.js.map +1 -0
- package/lib/templates/TitleField/index.d.ts +2 -0
- package/lib/templates/TitleField/index.js +3 -0
- package/lib/templates/TitleField/index.js.map +1 -0
- package/lib/templates/WrapIfAdditionalTemplate/WrapIfAdditionalTemplate.d.ts +7 -0
- package/lib/templates/WrapIfAdditionalTemplate/WrapIfAdditionalTemplate.js +20 -0
- package/lib/templates/WrapIfAdditionalTemplate/WrapIfAdditionalTemplate.js.map +1 -0
- package/lib/templates/WrapIfAdditionalTemplate/index.d.ts +2 -0
- package/lib/templates/WrapIfAdditionalTemplate/index.js +3 -0
- package/lib/templates/WrapIfAdditionalTemplate/index.js.map +1 -0
- package/lib/templates/index.d.ts +19 -0
- package/lib/templates/index.js +20 -0
- package/lib/templates/index.js.map +1 -0
- package/lib/theme/Theme.d.ts +43 -0
- package/lib/theme/Theme.js +68 -0
- package/lib/theme/Theme.js.map +1 -0
- package/lib/theme/index.d.ts +2 -0
- package/lib/theme/index.js +3 -0
- package/lib/theme/index.js.map +1 -0
- package/lib/tsconfig.tsbuildinfo +1 -0
- package/lib/types/DaisyProps.d.ts +5 -0
- package/lib/types/DaisyProps.js +2 -0
- package/lib/types/DaisyProps.js.map +1 -0
- package/lib/utils.d.ts +13 -0
- package/lib/utils.js +5 -0
- package/lib/utils.js.map +1 -0
- package/lib/widgets/AltDateTimeWidget/AltDateTimeWidget.d.ts +6 -0
- package/lib/widgets/AltDateTimeWidget/AltDateTimeWidget.js +10 -0
- package/lib/widgets/AltDateTimeWidget/AltDateTimeWidget.js.map +1 -0
- package/lib/widgets/AltDateTimeWidget/index.d.ts +2 -0
- package/lib/widgets/AltDateTimeWidget/index.js +3 -0
- package/lib/widgets/AltDateTimeWidget/index.js.map +1 -0
- package/lib/widgets/AltDateWidget/AltDateWidget.d.ts +15 -0
- package/lib/widgets/AltDateWidget/AltDateWidget.js +138 -0
- package/lib/widgets/AltDateWidget/AltDateWidget.js.map +1 -0
- package/lib/widgets/AltDateWidget/index.d.ts +2 -0
- package/lib/widgets/AltDateWidget/index.js +3 -0
- package/lib/widgets/AltDateWidget/index.js.map +1 -0
- package/lib/widgets/CheckboxWidget/CheckboxWidget.d.ts +13 -0
- package/lib/widgets/CheckboxWidget/CheckboxWidget.js +43 -0
- package/lib/widgets/CheckboxWidget/CheckboxWidget.js.map +1 -0
- package/lib/widgets/CheckboxWidget/index.d.ts +2 -0
- package/lib/widgets/CheckboxWidget/index.js +3 -0
- package/lib/widgets/CheckboxWidget/index.js.map +1 -0
- package/lib/widgets/CheckboxesWidget/CheckboxesWidget.d.ts +16 -0
- package/lib/widgets/CheckboxesWidget/CheckboxesWidget.js +73 -0
- package/lib/widgets/CheckboxesWidget/CheckboxesWidget.js.map +1 -0
- package/lib/widgets/CheckboxesWidget/index.d.ts +2 -0
- package/lib/widgets/CheckboxesWidget/index.js +3 -0
- package/lib/widgets/CheckboxesWidget/index.js.map +1 -0
- package/lib/widgets/DateTimeWidget/DateTimeWidget.d.ts +14 -0
- package/lib/widgets/DateTimeWidget/DateTimeWidget.js +207 -0
- package/lib/widgets/DateTimeWidget/DateTimeWidget.js.map +1 -0
- package/lib/widgets/DateTimeWidget/index.d.ts +2 -0
- package/lib/widgets/DateTimeWidget/index.js +3 -0
- package/lib/widgets/DateTimeWidget/index.js.map +1 -0
- package/lib/widgets/DateWidget/DateWidget.d.ts +14 -0
- package/lib/widgets/DateWidget/DateWidget.js +274 -0
- package/lib/widgets/DateWidget/DateWidget.js.map +1 -0
- package/lib/widgets/DateWidget/index.d.ts +2 -0
- package/lib/widgets/DateWidget/index.js +3 -0
- package/lib/widgets/DateWidget/index.js.map +1 -0
- package/lib/widgets/FileWidget/FileWidget.d.ts +12 -0
- package/lib/widgets/FileWidget/FileWidget.js +57 -0
- package/lib/widgets/FileWidget/FileWidget.js.map +1 -0
- package/lib/widgets/FileWidget/index.d.ts +2 -0
- package/lib/widgets/FileWidget/index.js +3 -0
- package/lib/widgets/FileWidget/index.js.map +1 -0
- package/lib/widgets/RadioWidget/RadioWidget.d.ts +14 -0
- package/lib/widgets/RadioWidget/RadioWidget.js +67 -0
- package/lib/widgets/RadioWidget/RadioWidget.js.map +1 -0
- package/lib/widgets/RadioWidget/index.d.ts +2 -0
- package/lib/widgets/RadioWidget/index.js +3 -0
- package/lib/widgets/RadioWidget/index.js.map +1 -0
- package/lib/widgets/RangeWidget/RangeWidget.d.ts +13 -0
- package/lib/widgets/RangeWidget/RangeWidget.js +42 -0
- package/lib/widgets/RangeWidget/RangeWidget.js.map +1 -0
- package/lib/widgets/RangeWidget/index.d.ts +2 -0
- package/lib/widgets/RangeWidget/index.js +3 -0
- package/lib/widgets/RangeWidget/index.js.map +1 -0
- package/lib/widgets/RatingWidget/RatingWidget.d.ts +17 -0
- package/lib/widgets/RatingWidget/RatingWidget.js +64 -0
- package/lib/widgets/RatingWidget/RatingWidget.js.map +1 -0
- package/lib/widgets/RatingWidget/index.d.ts +2 -0
- package/lib/widgets/RatingWidget/index.js +3 -0
- package/lib/widgets/RatingWidget/index.js.map +1 -0
- package/lib/widgets/SelectWidget/SelectWidget.d.ts +14 -0
- package/lib/widgets/SelectWidget/SelectWidget.js +74 -0
- package/lib/widgets/SelectWidget/SelectWidget.js.map +1 -0
- package/lib/widgets/SelectWidget/index.d.ts +2 -0
- package/lib/widgets/SelectWidget/index.js +3 -0
- package/lib/widgets/SelectWidget/index.js.map +1 -0
- package/lib/widgets/TextareaWidget/TextareaWidget.d.ts +12 -0
- package/lib/widgets/TextareaWidget/TextareaWidget.js +44 -0
- package/lib/widgets/TextareaWidget/TextareaWidget.js.map +1 -0
- package/lib/widgets/TextareaWidget/index.d.ts +2 -0
- package/lib/widgets/TextareaWidget/index.js +3 -0
- package/lib/widgets/TextareaWidget/index.js.map +1 -0
- package/lib/widgets/TimeWidget/TimeWidget.d.ts +12 -0
- package/lib/widgets/TimeWidget/TimeWidget.js +42 -0
- package/lib/widgets/TimeWidget/TimeWidget.js.map +1 -0
- package/lib/widgets/TimeWidget/index.d.ts +2 -0
- package/lib/widgets/TimeWidget/index.js +3 -0
- package/lib/widgets/TimeWidget/index.js.map +1 -0
- package/lib/widgets/ToggleWidget/ToggleWidget.d.ts +13 -0
- package/lib/widgets/ToggleWidget/ToggleWidget.js +40 -0
- package/lib/widgets/ToggleWidget/ToggleWidget.js.map +1 -0
- package/lib/widgets/ToggleWidget/index.d.ts +2 -0
- package/lib/widgets/ToggleWidget/index.js +3 -0
- package/lib/widgets/ToggleWidget/index.js.map +1 -0
- package/lib/widgets/Widgets.d.ts +18 -0
- package/lib/widgets/Widgets.js +35 -0
- package/lib/widgets/Widgets.js.map +1 -0
- package/lib/widgets/index.d.ts +2 -0
- package/lib/widgets/index.js +3 -0
- package/lib/widgets/index.js.map +1 -0
- package/package.json +95 -0
- package/src/DaisyUIForm.tsx +9 -0
- package/src/DaisyUIFrameProvider.tsx +107 -0
- package/src/daisyForm/DaisyForm.tsx +20 -0
- package/src/daisyForm/index.ts +2 -0
- package/src/index.ts +13 -0
- package/src/styles.css +9 -0
- package/src/templates/ArrayFieldDescriptionTemplate/ArrayFieldDescriptionTemplate.tsx +19 -0
- package/src/templates/ArrayFieldDescriptionTemplate/index.ts +2 -0
- package/src/templates/ArrayFieldItemButtonsTemplate/ArrayFieldItemButtonsTemplate.tsx +89 -0
- package/src/templates/ArrayFieldItemButtonsTemplate/index.ts +2 -0
- package/src/templates/ArrayFieldItemTemplate/ArrayFieldItemTemplate.tsx +55 -0
- package/src/templates/ArrayFieldItemTemplate/index.ts +2 -0
- package/src/templates/ArrayFieldTemplate/ArrayFieldTemplate.tsx +120 -0
- package/src/templates/ArrayFieldTemplate/index.ts +2 -0
- package/src/templates/ArrayFieldTitleTemplate/ArrayFieldTitleTemplate.tsx +15 -0
- package/src/templates/ArrayFieldTitleTemplate/index.ts +2 -0
- package/src/templates/BaseInputTemplate/BaseInputTemplate.tsx +104 -0
- package/src/templates/BaseInputTemplate/index.ts +2 -0
- package/src/templates/ButtonTemplates/AddButton.tsx +38 -0
- package/src/templates/ButtonTemplates/DaisyUIButton.tsx +31 -0
- package/src/templates/ButtonTemplates/IconButton.tsx +62 -0
- package/src/templates/ButtonTemplates/SubmitButton.tsx +25 -0
- package/src/templates/ButtonTemplates/index.ts +19 -0
- package/src/templates/DescriptionField/DescriptionField.tsx +26 -0
- package/src/templates/DescriptionField/index.ts +2 -0
- package/src/templates/ErrorList/ErrorList.tsx +21 -0
- package/src/templates/ErrorList/index.ts +2 -0
- package/src/templates/FieldErrorTemplate/FieldErrorTemplate.tsx +22 -0
- package/src/templates/FieldErrorTemplate/index.ts +2 -0
- package/src/templates/FieldHelpTemplate/FieldHelpTemplate.tsx +23 -0
- package/src/templates/FieldHelpTemplate/index.ts +2 -0
- package/src/templates/FieldTemplate/FieldTemplate.tsx +66 -0
- package/src/templates/FieldTemplate/index.ts +2 -0
- package/src/templates/GridTemplate/GridTemplate.tsx +26 -0
- package/src/templates/GridTemplate/index.ts +2 -0
- package/src/templates/ObjectFieldTemplate/ObjectFieldTemplate.tsx +108 -0
- package/src/templates/ObjectFieldTemplate/index.ts +2 -0
- package/src/templates/Templates.tsx +63 -0
- package/src/templates/TitleField/TitleField.tsx +26 -0
- package/src/templates/TitleField/index.ts +2 -0
- package/src/templates/WrapIfAdditionalTemplate/WrapIfAdditionalTemplate.tsx +74 -0
- package/src/templates/WrapIfAdditionalTemplate/index.ts +2 -0
- package/src/templates/index.ts +22 -0
- package/src/theme/Theme.tsx +93 -0
- package/src/theme/index.ts +2 -0
- package/src/tsconfig.json +26 -0
- package/src/types/DaisyProps.ts +5 -0
- package/src/types.d.ts +4 -0
- package/src/utils.ts +18 -0
- package/src/widgets/AltDateTimeWidget/AltDateTimeWidget.tsx +14 -0
- package/src/widgets/AltDateTimeWidget/index.ts +2 -0
- package/src/widgets/AltDateWidget/AltDateWidget.tsx +328 -0
- package/src/widgets/AltDateWidget/index.ts +2 -0
- package/src/widgets/CheckboxWidget/CheckboxWidget.tsx +108 -0
- package/src/widgets/CheckboxWidget/index.ts +2 -0
- package/src/widgets/CheckboxesWidget/CheckboxesWidget.tsx +124 -0
- package/src/widgets/CheckboxesWidget/index.ts +2 -0
- package/src/widgets/DateTimeWidget/DateTimeWidget.tsx +343 -0
- package/src/widgets/DateTimeWidget/index.ts +2 -0
- package/src/widgets/DateWidget/DateWidget.tsx +393 -0
- package/src/widgets/DateWidget/index.ts +2 -0
- package/src/widgets/FileWidget/FileWidget.tsx +86 -0
- package/src/widgets/FileWidget/index.ts +2 -0
- package/src/widgets/RadioWidget/RadioWidget.tsx +114 -0
- package/src/widgets/RadioWidget/index.ts +2 -0
- package/src/widgets/RangeWidget/RangeWidget.tsx +78 -0
- package/src/widgets/RangeWidget/index.ts +2 -0
- package/src/widgets/RatingWidget/RatingWidget.tsx +120 -0
- package/src/widgets/RatingWidget/index.ts +2 -0
- package/src/widgets/SelectWidget/SelectWidget.tsx +161 -0
- package/src/widgets/SelectWidget/index.ts +2 -0
- package/src/widgets/TextareaWidget/TextareaWidget.tsx +77 -0
- package/src/widgets/TextareaWidget/index.ts +2 -0
- package/src/widgets/TimeWidget/TimeWidget.tsx +72 -0
- package/src/widgets/TimeWidget/index.ts +2 -0
- package/src/widgets/ToggleWidget/ToggleWidget.tsx +70 -0
- package/src/widgets/ToggleWidget/index.ts +2 -0
- package/src/widgets/Widgets.tsx +58 -0
- package/src/widgets/index.ts +2 -0
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
import { memo, RefObject, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
2
|
+
import { faCalendar } from '@fortawesome/free-solid-svg-icons';
|
|
3
|
+
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
|
4
|
+
import { FormContextType, RJSFSchema, StrictRJSFSchema, WidgetProps } from '@rjsf/utils';
|
|
5
|
+
import { format, isSameDay, isToday, isValid } from 'date-fns';
|
|
6
|
+
import { ClassNames, DayPicker, ModifiersClassNames, UI } from 'react-day-picker';
|
|
7
|
+
import 'react-day-picker/dist/style.css';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Props for the DatePicker popup component
|
|
11
|
+
*/
|
|
12
|
+
interface DatePickerProps {
|
|
13
|
+
/** Currently selected date */
|
|
14
|
+
selectedDate?: Date;
|
|
15
|
+
/** Currently displayed month */
|
|
16
|
+
month: Date;
|
|
17
|
+
/** Handler for month changes */
|
|
18
|
+
onMonthChange: (date: Date) => void;
|
|
19
|
+
/** Handler for date selection */
|
|
20
|
+
onSelect: (date: Date | undefined) => void;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Custom hook to manage the picker's popup state and displayed month
|
|
25
|
+
*
|
|
26
|
+
* @param initialDate - Initial date to display, defaults to today
|
|
27
|
+
* @returns State and handlers for the date picker
|
|
28
|
+
*/
|
|
29
|
+
function useDatePickerState(initialDate?: Date) {
|
|
30
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
31
|
+
const [month, setMonth] = useState<Date>(initialDate ?? new Date());
|
|
32
|
+
return { isOpen, setIsOpen, month, setMonth };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Custom hook to detect clicks outside an element and run a callback
|
|
37
|
+
*
|
|
38
|
+
* @param ref - React ref to the element to monitor
|
|
39
|
+
* @param callback - Function to call when a click outside is detected
|
|
40
|
+
*/
|
|
41
|
+
function useClickOutside(ref: RefObject<HTMLDivElement>, callback: () => void) {
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
const handleClickOutside = (event: MouseEvent) => {
|
|
44
|
+
if (ref.current && !ref.current.contains(event.target as Node)) {
|
|
45
|
+
callback();
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
document.addEventListener('mousedown', handleClickOutside);
|
|
49
|
+
return () => document.removeEventListener('mousedown', handleClickOutside);
|
|
50
|
+
}, [ref, callback]);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Predefined DayPicker styles using DaisyUI classes
|
|
55
|
+
*/
|
|
56
|
+
const dayPickerStyles: { classNames: Partial<ClassNames>; modifiers: Partial<ModifiersClassNames> } = {
|
|
57
|
+
classNames: {
|
|
58
|
+
[UI.Root]: 'relative',
|
|
59
|
+
[UI.Nav]: 'hidden',
|
|
60
|
+
[UI.Chevron]: 'hidden',
|
|
61
|
+
[UI.CaptionLabel]: 'hidden',
|
|
62
|
+
[UI.Dropdowns]: 'flex justify-between gap-4 px-4 pb-4',
|
|
63
|
+
[UI.Dropdown]: 'select select-bordered select-sm w-32',
|
|
64
|
+
[UI.MonthsDropdown]: 'select select-bordered select-sm',
|
|
65
|
+
[UI.YearsDropdown]: 'select select-bordered select-sm',
|
|
66
|
+
[UI.Months]: 'flex justify-center',
|
|
67
|
+
[UI.Month]: 'w-full',
|
|
68
|
+
[UI.MonthCaption]: 'flex justify-center',
|
|
69
|
+
[UI.MonthGrid]: 'w-full',
|
|
70
|
+
[UI.Weekdays]: 'grid grid-cols-7 text-center border-b mb-2 pb-1 text-base-content/60 uppercase',
|
|
71
|
+
[UI.Weekday]: 'p-1 font-medium text-base-content/60 text-sm',
|
|
72
|
+
[UI.Week]: 'grid grid-cols-7',
|
|
73
|
+
[UI.Day]: 'w-10 h-8 p-0 relative rounded-md',
|
|
74
|
+
[UI.DayButton]:
|
|
75
|
+
'btn btn-ghost absolute inset-0 flex items-center justify-center w-full h-full cursor-pointer rounded-md hover:btn-primary',
|
|
76
|
+
},
|
|
77
|
+
modifiers: {
|
|
78
|
+
selected: 'btn btn-accent min-h-0 h-full',
|
|
79
|
+
outside: 'text-base-content/30 hover:btn-ghost',
|
|
80
|
+
disabled: 'opacity-50 cursor-not-allowed hover:btn-disabled',
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Popup component for the calendar
|
|
86
|
+
*
|
|
87
|
+
* Renders a DayPicker calendar for selecting dates
|
|
88
|
+
*
|
|
89
|
+
* @param props - The DatePickerProps for this component
|
|
90
|
+
*/
|
|
91
|
+
function DatePickerPopup({ selectedDate, month, onMonthChange, onSelect }: DatePickerProps) {
|
|
92
|
+
const customDayModifiers = {
|
|
93
|
+
selected: selectedDate,
|
|
94
|
+
'custom-today': (date: Date) => isToday(date) && !(selectedDate && isSameDay(date, selectedDate)),
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const customModifiersClassNames: ModifiersClassNames = {
|
|
98
|
+
...dayPickerStyles.modifiers,
|
|
99
|
+
'custom-today': 'btn btn-outline btn-info min-h-0 h-full',
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
return (
|
|
103
|
+
<div className='p-3' style={{ minWidth: '320px', minHeight: '350px' }}>
|
|
104
|
+
<DayPicker
|
|
105
|
+
mode='single'
|
|
106
|
+
selected={selectedDate}
|
|
107
|
+
month={month}
|
|
108
|
+
onMonthChange={onMonthChange}
|
|
109
|
+
onSelect={onSelect}
|
|
110
|
+
captionLayout='dropdown'
|
|
111
|
+
fromYear={1900}
|
|
112
|
+
toYear={new Date().getFullYear() + 10}
|
|
113
|
+
showOutsideDays
|
|
114
|
+
classNames={dayPickerStyles.classNames}
|
|
115
|
+
modifiers={customDayModifiers}
|
|
116
|
+
modifiersClassNames={customModifiersClassNames}
|
|
117
|
+
/>
|
|
118
|
+
</div>
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Use React.memo to optimize re-renders
|
|
123
|
+
const MemoizedDatePickerPopup = memo(DatePickerPopup);
|
|
124
|
+
|
|
125
|
+
/** The `DateWidget` component provides a date picker with DaisyUI styling.
|
|
126
|
+
*
|
|
127
|
+
* Features:
|
|
128
|
+
* - Calendar popup with month/year dropdown navigation
|
|
129
|
+
* - Accessible keyboard navigation
|
|
130
|
+
* - Date formatting using date-fns
|
|
131
|
+
* - Date-only selection (time component set to 00:00:00)
|
|
132
|
+
* - Manages focus and blur events for accessibility
|
|
133
|
+
*
|
|
134
|
+
* @param props - The `WidgetProps` for this component
|
|
135
|
+
*/
|
|
136
|
+
export default function DateWidget<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(
|
|
137
|
+
props: WidgetProps<T, S, F>,
|
|
138
|
+
) {
|
|
139
|
+
const { id, value, onChange, onFocus, onBlur, schema } = props;
|
|
140
|
+
// Initialize the local date from the parent's value.
|
|
141
|
+
const initialDate = useMemo(() => (value ? new Date(value) : undefined), [value]);
|
|
142
|
+
const [localDate, setLocalDate] = useState<Date | undefined>(initialDate);
|
|
143
|
+
|
|
144
|
+
// When the parent's value changes externally, update local state.
|
|
145
|
+
useEffect(() => {
|
|
146
|
+
setLocalDate(initialDate);
|
|
147
|
+
}, [initialDate]);
|
|
148
|
+
|
|
149
|
+
const { isOpen, setIsOpen, month, setMonth } = useDatePickerState(initialDate);
|
|
150
|
+
const containerRef = useRef<HTMLDivElement>(null);
|
|
151
|
+
const inputRef = useRef<HTMLDivElement>(null);
|
|
152
|
+
|
|
153
|
+
// Close the popup when clicking outside and commit changes.
|
|
154
|
+
useClickOutside(containerRef, () => {
|
|
155
|
+
if (isOpen) {
|
|
156
|
+
setIsOpen(false);
|
|
157
|
+
onChange(localDate ? localDate.toISOString() : '');
|
|
158
|
+
// Manually invoke the blur handler to ensure blur event is triggered
|
|
159
|
+
if (onBlur) {
|
|
160
|
+
onBlur(id, value);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
// When the local date changes, update the displayed month.
|
|
166
|
+
useEffect(() => {
|
|
167
|
+
if (localDate) {
|
|
168
|
+
setMonth(localDate);
|
|
169
|
+
}
|
|
170
|
+
}, [localDate, setMonth]);
|
|
171
|
+
|
|
172
|
+
// Update the month when the user navigates the calendar.
|
|
173
|
+
const handleMonthChange = useCallback((date: Date) => setMonth(date), [setMonth]);
|
|
174
|
+
|
|
175
|
+
// Update local state on day selection (but do not commit immediately).
|
|
176
|
+
const handleSelect = useCallback((date: Date | undefined) => {
|
|
177
|
+
if (date) {
|
|
178
|
+
// Remove any time component by setting hours, minutes, seconds, and milliseconds to zero.
|
|
179
|
+
date.setHours(0, 0, 0, 0);
|
|
180
|
+
setLocalDate(date);
|
|
181
|
+
}
|
|
182
|
+
}, []);
|
|
183
|
+
|
|
184
|
+
// Add a portal container to the document body if it doesn't exist
|
|
185
|
+
useEffect(() => {
|
|
186
|
+
// Check if the portal container exists, create it if not
|
|
187
|
+
let portalContainer = document.getElementById('date-picker-portal');
|
|
188
|
+
if (!portalContainer) {
|
|
189
|
+
portalContainer = document.createElement('div');
|
|
190
|
+
portalContainer.id = 'date-picker-portal';
|
|
191
|
+
document.body.appendChild(portalContainer);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Clean up on unmount
|
|
195
|
+
return () => {
|
|
196
|
+
// Only remove if no other date pickers are using it and if portalContainer exists
|
|
197
|
+
const container = document.getElementById('date-picker-portal');
|
|
198
|
+
if (container && document.querySelectorAll('.date-picker-popup').length === 0) {
|
|
199
|
+
container.remove();
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
}, []);
|
|
203
|
+
|
|
204
|
+
// Get the document and window objects (will work in iframes too)
|
|
205
|
+
const getDocumentAndWindow = () => {
|
|
206
|
+
// Try to get the iframe's document and window if we're in one
|
|
207
|
+
let doc = document;
|
|
208
|
+
let win = window;
|
|
209
|
+
|
|
210
|
+
try {
|
|
211
|
+
// If we're in an iframe, try to access the parent
|
|
212
|
+
if (window.frameElement) {
|
|
213
|
+
// We're in an iframe
|
|
214
|
+
const iframe = window.frameElement as HTMLIFrameElement;
|
|
215
|
+
// Get the iframe's contentDocument and contentWindow
|
|
216
|
+
if (iframe.contentDocument) {
|
|
217
|
+
doc = iframe.contentDocument;
|
|
218
|
+
}
|
|
219
|
+
if (iframe.contentWindow) {
|
|
220
|
+
win = iframe.contentWindow as typeof window;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
} catch (e) {
|
|
224
|
+
// Security error, we're in a cross-origin iframe
|
|
225
|
+
console.log('Unable to access parent frame:', e);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return { doc, win };
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
// Render the calendar at a specific position
|
|
232
|
+
const renderCalendar = useCallback(() => {
|
|
233
|
+
if (!containerRef.current || !inputRef.current) {
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Get the proper document and window
|
|
238
|
+
const { win } = getDocumentAndWindow();
|
|
239
|
+
|
|
240
|
+
const inputRect = inputRef.current.getBoundingClientRect();
|
|
241
|
+
const containerWidth = 320; // Minimum width we've set
|
|
242
|
+
|
|
243
|
+
// Position the calendar relative to the input but with fixed positioning
|
|
244
|
+
containerRef.current.style.position = 'fixed';
|
|
245
|
+
containerRef.current.style.top = `${inputRect.bottom + 5}px`;
|
|
246
|
+
|
|
247
|
+
// Prevent it from going off-screen on the right
|
|
248
|
+
const rightEdge = inputRect.left + containerWidth;
|
|
249
|
+
const windowWidth = win.innerWidth;
|
|
250
|
+
|
|
251
|
+
if (rightEdge > windowWidth - 20) {
|
|
252
|
+
// Align to the right edge if it would overflow
|
|
253
|
+
containerRef.current.style.left = `${Math.max(20, windowWidth - 20 - containerWidth)}px`;
|
|
254
|
+
} else {
|
|
255
|
+
// Otherwise align to the left edge of the input
|
|
256
|
+
containerRef.current.style.left = `${inputRect.left}px`;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Ensure the calendar is visible
|
|
260
|
+
containerRef.current.style.zIndex = '99999';
|
|
261
|
+
}, [containerRef, inputRef]);
|
|
262
|
+
|
|
263
|
+
// Handle window resize to reposition the calendar
|
|
264
|
+
useEffect(() => {
|
|
265
|
+
if (!isOpen) {
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Position initially
|
|
270
|
+
renderCalendar();
|
|
271
|
+
|
|
272
|
+
// Update position on resize
|
|
273
|
+
window.addEventListener('resize', renderCalendar);
|
|
274
|
+
window.addEventListener('scroll', renderCalendar);
|
|
275
|
+
|
|
276
|
+
return () => {
|
|
277
|
+
window.removeEventListener('resize', renderCalendar);
|
|
278
|
+
window.removeEventListener('scroll', renderCalendar);
|
|
279
|
+
};
|
|
280
|
+
}, [isOpen, renderCalendar]);
|
|
281
|
+
|
|
282
|
+
// Toggle popup visibility.
|
|
283
|
+
const togglePicker = useCallback(
|
|
284
|
+
(e: React.MouseEvent) => {
|
|
285
|
+
e.stopPropagation();
|
|
286
|
+
setIsOpen((prev) => !prev);
|
|
287
|
+
if (!isOpen && onFocus) {
|
|
288
|
+
onFocus(id, value);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// Position calculation will happen in the effect hook
|
|
292
|
+
},
|
|
293
|
+
[isOpen, id, onFocus, setIsOpen, value],
|
|
294
|
+
);
|
|
295
|
+
|
|
296
|
+
// Handle focus event
|
|
297
|
+
const handleFocus = useCallback(() => {
|
|
298
|
+
if (onFocus) {
|
|
299
|
+
onFocus(id, value);
|
|
300
|
+
}
|
|
301
|
+
}, [id, onFocus, value]);
|
|
302
|
+
|
|
303
|
+
// Handle blur event
|
|
304
|
+
const handleBlur = useCallback(() => {
|
|
305
|
+
if (!isOpen && onBlur) {
|
|
306
|
+
onBlur(id, value);
|
|
307
|
+
}
|
|
308
|
+
}, [id, onBlur, value, isOpen]);
|
|
309
|
+
|
|
310
|
+
// Close popup on escape key
|
|
311
|
+
useEffect(() => {
|
|
312
|
+
const handleEscape = (e: KeyboardEvent) => {
|
|
313
|
+
if (e.key === 'Escape' && isOpen) {
|
|
314
|
+
setIsOpen(false);
|
|
315
|
+
if (onBlur) {
|
|
316
|
+
onBlur(id, value);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
document.addEventListener('keydown', handleEscape);
|
|
322
|
+
return () => document.removeEventListener('keydown', handleEscape);
|
|
323
|
+
}, [id, isOpen, onBlur, value]);
|
|
324
|
+
|
|
325
|
+
// Add the handleDoneClick callback near the top of the component, with the other event handlers
|
|
326
|
+
/** Handle clicking the "Done" button
|
|
327
|
+
*/
|
|
328
|
+
const handleDoneClick = useCallback(() => {
|
|
329
|
+
setIsOpen(false);
|
|
330
|
+
onChange(localDate ? localDate.toISOString() : '');
|
|
331
|
+
if (onBlur) {
|
|
332
|
+
onBlur(id, value);
|
|
333
|
+
}
|
|
334
|
+
inputRef.current?.focus();
|
|
335
|
+
}, [localDate, onChange, onBlur, id, value]);
|
|
336
|
+
|
|
337
|
+
return (
|
|
338
|
+
<div className='form-control my-4 w-full relative'>
|
|
339
|
+
<div
|
|
340
|
+
className='w-full'
|
|
341
|
+
tabIndex={0}
|
|
342
|
+
onKeyDown={(e) => {
|
|
343
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
344
|
+
togglePicker(e as unknown as React.MouseEvent);
|
|
345
|
+
}
|
|
346
|
+
}}
|
|
347
|
+
onFocus={handleFocus}
|
|
348
|
+
onBlur={handleBlur}
|
|
349
|
+
ref={inputRef}
|
|
350
|
+
>
|
|
351
|
+
<div
|
|
352
|
+
id={id}
|
|
353
|
+
className={`input input-bordered w-full flex items-center justify-between cursor-pointer ${
|
|
354
|
+
isOpen ? 'ring-2 ring-primary/50' : ''
|
|
355
|
+
}`}
|
|
356
|
+
onClick={togglePicker}
|
|
357
|
+
role='button'
|
|
358
|
+
aria-haspopup='true'
|
|
359
|
+
aria-expanded={isOpen}
|
|
360
|
+
tabIndex={-1}
|
|
361
|
+
>
|
|
362
|
+
<span className={localDate && isValid(localDate) ? '' : 'text-base-content/50'}>
|
|
363
|
+
{localDate && isValid(localDate) ? format(localDate, 'PP') : schema.title}
|
|
364
|
+
</span>
|
|
365
|
+
<FontAwesomeIcon icon={faCalendar} className='ml-2 h-4 w-4 text-primary' />
|
|
366
|
+
</div>
|
|
367
|
+
{isOpen && (
|
|
368
|
+
<div
|
|
369
|
+
ref={containerRef}
|
|
370
|
+
className='date-picker-popup fixed z-[99999] w-full max-w-xs bg-base-100 border border-base-300 shadow-lg rounded-box'
|
|
371
|
+
style={{
|
|
372
|
+
maxHeight: 'none',
|
|
373
|
+
overflow: 'visible',
|
|
374
|
+
}}
|
|
375
|
+
onClick={(e) => e.stopPropagation()}
|
|
376
|
+
>
|
|
377
|
+
<MemoizedDatePickerPopup
|
|
378
|
+
selectedDate={localDate}
|
|
379
|
+
month={month}
|
|
380
|
+
onMonthChange={handleMonthChange}
|
|
381
|
+
onSelect={handleSelect}
|
|
382
|
+
/>
|
|
383
|
+
<div className='p-3 flex justify-end border-t border-base-300'>
|
|
384
|
+
<button type='button' className='btn btn-sm btn-primary' onClick={handleDoneClick}>
|
|
385
|
+
Done
|
|
386
|
+
</button>
|
|
387
|
+
</div>
|
|
388
|
+
</div>
|
|
389
|
+
)}
|
|
390
|
+
</div>
|
|
391
|
+
</div>
|
|
392
|
+
);
|
|
393
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { ChangeEvent, FocusEvent, useCallback } from 'react';
|
|
2
|
+
import { FormContextType, RJSFSchema, StrictRJSFSchema, WidgetProps } from '@rjsf/utils';
|
|
3
|
+
|
|
4
|
+
/** The `FileWidget` component provides a file input with DaisyUI styling
|
|
5
|
+
*
|
|
6
|
+
* Features:
|
|
7
|
+
* - Handles both single and multiple file uploads
|
|
8
|
+
* - Supports file type filtering via accept attribute
|
|
9
|
+
* - Properly manages disabled and readonly states
|
|
10
|
+
* - Handles focus and blur events
|
|
11
|
+
*
|
|
12
|
+
* @param props - The `WidgetProps` for this component
|
|
13
|
+
*/
|
|
14
|
+
export default function FileWidget<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(
|
|
15
|
+
props: WidgetProps<T, S, F>,
|
|
16
|
+
) {
|
|
17
|
+
const { id, required, disabled, readonly, schema, onChange, onFocus, onBlur, options = {} } = props;
|
|
18
|
+
|
|
19
|
+
// Ensure isMulti is explicitly a boolean.
|
|
20
|
+
const isMulti: boolean = schema.type === 'array' || Boolean(options.multiple);
|
|
21
|
+
|
|
22
|
+
// Accept attribute for restricting file types (e.g., "image/*"), if defined in options.
|
|
23
|
+
const accept: string | undefined = typeof options.accept === 'string' ? options.accept : undefined;
|
|
24
|
+
|
|
25
|
+
/** Handle file selection changes
|
|
26
|
+
*
|
|
27
|
+
* @param event - The change event from the file input
|
|
28
|
+
*/
|
|
29
|
+
const handleChange = useCallback(
|
|
30
|
+
(event: ChangeEvent<HTMLInputElement>) => {
|
|
31
|
+
if (!event.target.files) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
// Convert FileList to array for multiple upload handling.
|
|
35
|
+
const fileList = Array.from(event.target.files);
|
|
36
|
+
if (isMulti) {
|
|
37
|
+
onChange(fileList);
|
|
38
|
+
} else {
|
|
39
|
+
// For single file, send the first file (or null if none chosen)
|
|
40
|
+
onChange(fileList[0] || null);
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
[onChange, isMulti],
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
/** Handle focus events
|
|
47
|
+
*
|
|
48
|
+
* @param event - The focus event
|
|
49
|
+
*/
|
|
50
|
+
const handleFocus = useCallback(
|
|
51
|
+
(event: FocusEvent<HTMLInputElement>) => {
|
|
52
|
+
if (onFocus) {
|
|
53
|
+
onFocus(id, event.target.files ? Array.from(event.target.files) : null);
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
[onFocus, id],
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
/** Handle blur events
|
|
60
|
+
*
|
|
61
|
+
* @param event - The blur event
|
|
62
|
+
*/
|
|
63
|
+
const handleBlur = useCallback(
|
|
64
|
+
(event: FocusEvent<HTMLInputElement>) => {
|
|
65
|
+
if (onBlur) {
|
|
66
|
+
onBlur(id, event.target.files ? Array.from(event.target.files) : null);
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
[onBlur, id],
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
return (
|
|
73
|
+
<input
|
|
74
|
+
id={id}
|
|
75
|
+
type='file'
|
|
76
|
+
className='file-input w-full'
|
|
77
|
+
required={required}
|
|
78
|
+
disabled={disabled || readonly}
|
|
79
|
+
onChange={handleChange}
|
|
80
|
+
onFocus={handleFocus}
|
|
81
|
+
onBlur={handleBlur}
|
|
82
|
+
multiple={isMulti}
|
|
83
|
+
accept={accept}
|
|
84
|
+
/>
|
|
85
|
+
);
|
|
86
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { FocusEvent, useCallback } from 'react';
|
|
2
|
+
import { WidgetProps, StrictRJSFSchema, FormContextType, RJSFSchema } from '@rjsf/utils';
|
|
3
|
+
|
|
4
|
+
/** The `RadioWidget` component renders a group of radio buttons with DaisyUI styling
|
|
5
|
+
*
|
|
6
|
+
* Features:
|
|
7
|
+
* - Supports both primitive values and objects in enum options
|
|
8
|
+
* - Handles selection state for various data types
|
|
9
|
+
* - Uses DaisyUI radio styling with accessible labels
|
|
10
|
+
* - Supports disabled and readonly states
|
|
11
|
+
* - Provides focus and blur event handling
|
|
12
|
+
* - Renders radio buttons in a vertical layout for better spacing
|
|
13
|
+
*
|
|
14
|
+
* @param props - The `WidgetProps` for this component
|
|
15
|
+
*/
|
|
16
|
+
export default function RadioWidget<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>({
|
|
17
|
+
id,
|
|
18
|
+
options,
|
|
19
|
+
value,
|
|
20
|
+
required,
|
|
21
|
+
disabled,
|
|
22
|
+
readonly,
|
|
23
|
+
onChange,
|
|
24
|
+
onFocus,
|
|
25
|
+
onBlur,
|
|
26
|
+
}: WidgetProps<T, S, F>) {
|
|
27
|
+
const { enumOptions } = options;
|
|
28
|
+
const isEnumeratedObject = enumOptions && enumOptions[0]?.value && typeof enumOptions[0].value === 'object';
|
|
29
|
+
|
|
30
|
+
/** Gets the actual value from an option
|
|
31
|
+
*
|
|
32
|
+
* @param option - The option object to get value from
|
|
33
|
+
* @returns The option's value
|
|
34
|
+
*/
|
|
35
|
+
const getValue = (option: any) => {
|
|
36
|
+
return option.value;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/** Determines if an option is checked based on the current value
|
|
40
|
+
*
|
|
41
|
+
* @param option - The option to check
|
|
42
|
+
* @returns Whether the option should be checked
|
|
43
|
+
*/
|
|
44
|
+
const isChecked = (option: any) => {
|
|
45
|
+
if (isEnumeratedObject) {
|
|
46
|
+
return value && value.name === option.value.name;
|
|
47
|
+
}
|
|
48
|
+
return value === option.value;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
/** Handles focus events for accessibility */
|
|
52
|
+
const handleFocus = useCallback(
|
|
53
|
+
(event: FocusEvent<HTMLInputElement>) => {
|
|
54
|
+
if (onFocus) {
|
|
55
|
+
const index = Number(event.target.dataset.index);
|
|
56
|
+
const optionValue = enumOptions?.[index]?.value;
|
|
57
|
+
onFocus(id, optionValue);
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
[onFocus, id, enumOptions],
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
/** Handles blur events for accessibility */
|
|
64
|
+
const handleBlur = useCallback(
|
|
65
|
+
(event: FocusEvent<HTMLInputElement>) => {
|
|
66
|
+
if (onBlur) {
|
|
67
|
+
const index = Number(event.target.dataset.index);
|
|
68
|
+
const optionValue = enumOptions?.[index]?.value;
|
|
69
|
+
onBlur(id, optionValue);
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
[onBlur, id, enumOptions],
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
/** Handles change events for radio options */
|
|
76
|
+
const handleChange = useCallback(
|
|
77
|
+
(event: React.ChangeEvent<HTMLInputElement>) => {
|
|
78
|
+
const index = Number(event.target.dataset.index);
|
|
79
|
+
const option = enumOptions?.[index];
|
|
80
|
+
if (option) {
|
|
81
|
+
onChange(isEnumeratedObject ? option.value : option.value);
|
|
82
|
+
event.target.blur();
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
[onChange, isEnumeratedObject, enumOptions],
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
return (
|
|
89
|
+
<div className='form-control'>
|
|
90
|
+
{/* Display the options in a vertical flex layout for better spacing */}
|
|
91
|
+
<div className='flex flex-col gap-2 mt-1'>
|
|
92
|
+
{enumOptions?.map((option, index) => (
|
|
93
|
+
<label key={option.value} className='flex items-center cursor-pointer gap-2'>
|
|
94
|
+
<input
|
|
95
|
+
type='radio'
|
|
96
|
+
id={`${id}-${option.value}`}
|
|
97
|
+
className='radio'
|
|
98
|
+
name={id}
|
|
99
|
+
value={getValue(option)}
|
|
100
|
+
checked={isChecked(option)}
|
|
101
|
+
required={required}
|
|
102
|
+
disabled={disabled || readonly}
|
|
103
|
+
data-index={index}
|
|
104
|
+
onChange={handleChange}
|
|
105
|
+
onFocus={handleFocus}
|
|
106
|
+
onBlur={handleBlur}
|
|
107
|
+
/>
|
|
108
|
+
<span className='label-text'>{option.label}</span>
|
|
109
|
+
</label>
|
|
110
|
+
))}
|
|
111
|
+
</div>
|
|
112
|
+
</div>
|
|
113
|
+
);
|
|
114
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { FocusEvent, useCallback } from 'react';
|
|
2
|
+
import { WidgetProps, StrictRJSFSchema, RJSFSchema, FormContextType } from '@rjsf/utils';
|
|
3
|
+
|
|
4
|
+
/** The `RangeWidget` component renders a range slider input with DaisyUI styling
|
|
5
|
+
*
|
|
6
|
+
* Features:
|
|
7
|
+
* - Visual slider control for numerical values
|
|
8
|
+
* - Displays current value next to the slider
|
|
9
|
+
* - Supports min, max, and step attributes from schema
|
|
10
|
+
* - Handles required, disabled, and readonly states
|
|
11
|
+
* - Supports focus and blur events for accessibility
|
|
12
|
+
*
|
|
13
|
+
* @param props - The `WidgetProps` for this component
|
|
14
|
+
*/
|
|
15
|
+
export default function RangeWidget<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>({
|
|
16
|
+
id,
|
|
17
|
+
value,
|
|
18
|
+
required,
|
|
19
|
+
disabled,
|
|
20
|
+
readonly,
|
|
21
|
+
onChange,
|
|
22
|
+
onFocus,
|
|
23
|
+
onBlur,
|
|
24
|
+
schema,
|
|
25
|
+
}: WidgetProps<T, S, F>) {
|
|
26
|
+
/** Handle focus events
|
|
27
|
+
*
|
|
28
|
+
* @param event - The focus event
|
|
29
|
+
*/
|
|
30
|
+
const handleFocus = (event: FocusEvent<HTMLInputElement>) => {
|
|
31
|
+
if (onFocus) {
|
|
32
|
+
onFocus(id, event.target.value);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/** Handle blur events
|
|
37
|
+
*
|
|
38
|
+
* @param event - The blur event
|
|
39
|
+
*/
|
|
40
|
+
const handleBlur = (event: FocusEvent<HTMLInputElement>) => {
|
|
41
|
+
if (onBlur) {
|
|
42
|
+
onBlur(id, event.target.value);
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/** Handle change events
|
|
47
|
+
*
|
|
48
|
+
* @param event - The change event
|
|
49
|
+
*/
|
|
50
|
+
const handleChange = useCallback(
|
|
51
|
+
(event: React.ChangeEvent<HTMLInputElement>) => {
|
|
52
|
+
onChange(event.target.value);
|
|
53
|
+
},
|
|
54
|
+
[onChange],
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
return (
|
|
58
|
+
<div className='form-control'>
|
|
59
|
+
<div className='flex items-center'>
|
|
60
|
+
<input
|
|
61
|
+
type='range'
|
|
62
|
+
id={id}
|
|
63
|
+
className='range'
|
|
64
|
+
value={value || schema.default}
|
|
65
|
+
required={required}
|
|
66
|
+
disabled={disabled || readonly}
|
|
67
|
+
min={schema.minimum}
|
|
68
|
+
max={schema.maximum}
|
|
69
|
+
step={schema.multipleOf || 1}
|
|
70
|
+
onChange={handleChange}
|
|
71
|
+
onFocus={handleFocus}
|
|
72
|
+
onBlur={handleBlur}
|
|
73
|
+
/>
|
|
74
|
+
<span className='label-text ml-4'>{value}</span>
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
|
77
|
+
);
|
|
78
|
+
}
|