@flikk/ui 1.0.0-beta.22 → 1.0.0-beta.24
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/dist/components/ai/PromptInput/PromptInput.js +139 -7
- package/dist/components/ai/PromptInput/VoiceRecorder.js +84 -0
- package/dist/components/charts/AreaChart/AreaChart.types.d.ts +2 -2
- package/dist/components/charts/BarChart/BarChart.types.d.ts +2 -2
- package/dist/components/charts/ComboChart/ComboChart.types.d.ts +2 -2
- package/dist/components/charts/LineChart/LineChart.types.d.ts +2 -2
- package/dist/components/charts/ScatterPlot/ScatterPlot.types.d.ts +2 -2
- package/dist/components/charts/StackedBarChart/StackedBarChart.types.d.ts +2 -2
- package/dist/components/core/Accordion/AccordionTrigger.js +28 -1
- package/dist/components/core/Alert/Alert.js +2 -1
- package/dist/components/core/AlertDialog/AlertDialog.js +1 -1
- package/dist/components/core/Badge/Badge.js +6 -17
- package/dist/components/core/Breadcrumbs/Breadcrumbs.js +17 -28
- package/dist/components/core/Calendar/Calendar.js +1 -1
- package/dist/components/core/Carousel/CarouselBody.js +6 -1
- package/dist/components/core/ContextMenu/ContextMenuBody.js +6 -3
- package/dist/components/core/ContextMenu/ContextMenuSubBody.js +6 -3
- package/dist/components/core/DragDrop/DragDrop.js +170 -17
- package/dist/components/core/DragDrop/DragDrop.types.d.ts +6 -0
- package/dist/components/core/Drawer/Drawer.d.ts +1 -1
- package/dist/components/core/Drawer/Drawer.js +2 -0
- package/dist/components/core/Drawer/Drawer.types.d.ts +2 -0
- package/dist/components/core/Drawer/DrawerTitle.d.ts +1 -1
- package/dist/components/core/Drawer/DrawerTitle.js +2 -2
- package/dist/components/core/Dropdown/DropdownMenu.js +2 -30
- package/dist/components/core/Masonry/Masonry.js +7 -2
- package/dist/components/core/Modal/Modal.d.ts +1 -1
- package/dist/components/core/Modal/Modal.js +3 -3
- package/dist/components/core/Modal/Modal.types.d.ts +2 -0
- package/dist/components/core/Modal/ModalTitle.d.ts +1 -1
- package/dist/components/core/Modal/ModalTitle.js +2 -2
- package/dist/components/core/NavItem/NavItem.js +21 -6
- package/dist/components/core/NavItem/NavItem.theme.js +2 -2
- package/dist/components/core/Pagination/Pagination.js +1 -1
- package/dist/components/core/Popover/PopoverBody.js +3 -27
- package/dist/components/core/Popover/PopoverTrigger.js +1 -1
- package/dist/components/core/Progress/Progress.js +4 -4
- package/dist/components/core/Rating/Rating.js +1 -1
- package/dist/components/core/ScrollArea/ScrollArea.js +3 -2
- package/dist/components/core/Segmented/Segmented.js +6 -6
- package/dist/components/core/Segmented/SegmentedItem.js +28 -2
- package/dist/components/core/Sidebar/Sidebar.d.ts +1 -0
- package/dist/components/core/Sidebar/Sidebar.js +5 -3
- package/dist/components/core/Sidebar/Sidebar.types.d.ts +7 -1
- package/dist/components/core/Sidebar/SidebarContent.js +1 -1
- package/dist/components/core/Sidebar/SidebarFooter.js +1 -1
- package/dist/components/core/Sidebar/SidebarHeader.js +5 -2
- package/dist/components/core/Sidebar/SidebarLogo.d.ts +3 -0
- package/dist/components/core/Sidebar/SidebarLogo.js +17 -0
- package/dist/components/core/Sidebar/SidebarNavGroup.js +2 -1
- package/dist/components/core/Sidebar/SidebarToggle.js +2 -4
- package/dist/components/core/Sidebar/SidebarUserProfile.js +3 -6
- package/dist/components/core/Sidebar/index.d.ts +2 -1
- package/dist/components/core/Skeleton/Skeleton.js +2 -2
- package/dist/components/core/Sortable/Sortable.d.ts +1 -1
- package/dist/components/core/Sortable/Sortable.js +20 -4
- package/dist/components/core/Sortable/Sortable.types.d.ts +2 -0
- package/dist/components/core/Tabs/Tabs.js +6 -4
- package/dist/components/core/Tabs/Tabs.types.d.ts +2 -0
- package/dist/components/core/Tabs/TabsBody.js +3 -3
- package/dist/components/core/Tabs/TabsList.js +19 -21
- package/dist/components/core/Tabs/TabsTrigger.js +47 -28
- package/dist/components/core/Toast/Toast.js +1 -1
- package/dist/components/core/Toast/ToastProvider.js +3 -2
- package/dist/components/core/Tooltip/Tooltip.d.ts +1 -1
- package/dist/components/core/Tooltip/Tooltip.js +26 -35
- package/dist/components/core/Tooltip/Tooltip.types.d.ts +5 -1
- package/dist/components/core/Tree/Tree.js +178 -8
- package/dist/components/core/index.js +1 -0
- package/dist/components/data-display/GanttChart/GanttSplitter.js +15 -1
- package/dist/components/data-display/Table/Table.js +2 -2
- package/dist/components/data-display/Table/Table.types.d.ts +2 -0
- package/dist/components/data-display/Table/TableFilter.js +21 -18
- package/dist/components/effects/CustomCursor/CustomCursor.js +3 -7
- package/dist/components/effects/Overlay/Overlay.js +4 -9
- package/dist/components/effects/PageTransition/PageTransition.js +17 -6
- package/dist/components/effects/SpotlightBorder/SpotlightBorder.js +3 -4
- package/dist/components/forms/Checkbox/Checkbox.js +11 -1
- package/dist/components/forms/Checkbox/Checkbox.theme.js +4 -1
- package/dist/components/forms/Checkbox/Checkbox.types.d.ts +7 -5
- package/dist/components/forms/Checkbox/CheckboxGroup.js +9 -4
- package/dist/components/forms/ColorPicker/ColorPicker.js +7 -4
- package/dist/components/forms/ColorPicker/ColorPicker.types.d.ts +2 -2
- package/dist/components/forms/ColorPicker/ColorPickerBody.js +5 -3
- package/dist/components/forms/ColorPicker/ColorPickerEyeDropper.js +3 -3
- package/dist/components/forms/ColorPicker/ColorPickerGradient.js +8 -8
- package/dist/components/forms/ColorPicker/ColorPickerInput.js +1 -1
- package/dist/components/forms/ColorPicker/index.d.ts +1 -1
- package/dist/components/forms/Combobox/Combobox.js +20 -45
- package/dist/components/forms/Combobox/Combobox.types.d.ts +4 -2
- package/dist/components/forms/CronInput/CronInput.js +10 -4
- package/dist/components/forms/CronInput/CronInput.types.d.ts +12 -1
- package/dist/components/forms/DatePicker/DatePicker.js +2 -2
- package/dist/components/forms/DatePicker/DatePicker.types.d.ts +4 -2
- package/dist/components/forms/DatePicker/DatePickerBody.js +1 -29
- package/dist/components/forms/DatePicker/DatePickerTrigger.js +2 -2
- package/dist/components/forms/DateRangePicker/DateRangePicker.js +3 -3
- package/dist/components/forms/DateRangePicker/DateRangePicker.types.d.ts +4 -2
- package/dist/components/forms/DateRangePicker/DateRangePickerBody.js +1 -28
- package/dist/components/forms/DateRangePicker/DateRangePickerTrigger.js +2 -2
- package/dist/components/forms/FileUpload/FileUpload.js +99 -10
- package/dist/components/forms/FileUpload/FileUpload.types.d.ts +15 -0
- package/dist/components/forms/FormLabel/FormLabel.js +2 -2
- package/dist/components/forms/Input/Input.js +5 -3
- package/dist/components/forms/Input/Input.types.d.ts +2 -0
- package/dist/components/forms/InputAddress/InputAddress.d.ts +1 -1
- package/dist/components/forms/InputAddress/InputAddress.js +8 -9
- package/dist/components/forms/InputCounter/InputCounter.js +25 -18
- package/dist/components/forms/InputCounter/InputCounter.types.d.ts +8 -1
- package/dist/components/forms/InputCreditCard/InputCreditCard.d.ts +1 -1
- package/dist/components/forms/InputCreditCard/InputCreditCard.js +27 -9
- package/dist/components/forms/InputCreditCard/InputCreditCard.types.d.ts +5 -0
- package/dist/components/forms/InputOTP/InputOTP.js +26 -17
- package/dist/components/forms/InputOTP/InputOTP.types.d.ts +21 -4
- package/dist/components/forms/InputTag/InputTag.js +12 -39
- package/dist/components/forms/InputTag/InputTag.types.d.ts +2 -2
- package/dist/components/forms/Mention/Mention.js +13 -6
- package/dist/components/forms/Mention/Mention.types.d.ts +9 -0
- package/dist/components/forms/Radio/Radio.js +12 -2
- package/dist/components/forms/Radio/Radio.theme.js +5 -1
- package/dist/components/forms/Radio/Radio.types.d.ts +7 -5
- package/dist/components/forms/Radio/RadioGroup.js +8 -3
- package/dist/components/forms/RichTextEditor/RichTextEditor.js +21 -21
- package/dist/components/forms/RichTextEditor/RichTextEditor.types.d.ts +2 -2
- package/dist/components/forms/Select/Select.d.ts +2 -0
- package/dist/components/forms/Select/Select.js +26 -56
- package/dist/components/forms/Select/Select.types.d.ts +14 -0
- package/dist/components/forms/Signature/Signature.js +1 -1
- package/dist/components/forms/Signature/Signature.types.d.ts +3 -3
- package/dist/components/forms/Slider/Slider.js +18 -4
- package/dist/components/forms/Slider/Slider.types.d.ts +2 -2
- package/dist/components/forms/Switch/Switch.js +12 -19
- package/dist/components/forms/Switch/Switch.theme.js +12 -2
- package/dist/components/forms/Switch/Switch.types.d.ts +20 -2
- package/dist/components/forms/Textarea/Textarea.js +8 -3
- package/dist/components/forms/Textarea/Textarea.types.d.ts +2 -0
- package/dist/components/forms/TimePicker/TimePicker.js +1 -1
- package/dist/components/forms/TimePicker/TimePicker.types.d.ts +6 -2
- package/dist/components/forms/TimePicker/TimePickerContent.js +88 -30
- package/dist/components/forms/TimePicker/TimePickerTrigger.js +2 -2
- package/dist/components/forms/index.js +86 -1
- package/dist/components/layout/FormLayout/FormLayout.js +87 -1
- package/dist/components/layout/index.js +4 -0
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/useClickOutside.d.ts +10 -4
- package/dist/hooks/useClickOutside.js +44 -24
- package/dist/hooks/useDarkMode.d.ts +1 -0
- package/dist/hooks/useDarkMode.js +42 -0
- package/dist/hooks/useFormStateMachine.d.ts +1 -1
- package/dist/hooks/useNetworkStatus.js +1 -1
- package/dist/hooks/useOverlay.js +4 -0
- package/dist/hooks/useScaleBackground.js +5 -2
- package/dist/hooks/useSelectPortal.js +14 -5
- package/dist/index.d.ts +0 -2
- package/dist/index.js +174 -223
- package/dist/shadcn-compat.css +80 -0
- package/dist/styles.css +1 -1
- package/dist/utils/stateMachine.d.ts +4 -4
- package/package.json +12 -10
- package/tailwind.preset.cjs +32 -3
- /package/{src/theme-plugin.css → dist/theme.css} +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
1
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
2
2
|
import React__default, { useId, useRef, useEffect, useCallback } from 'react';
|
|
3
3
|
import { AnimatePresence, motion } from 'motion/react';
|
|
4
4
|
import { createPortal } from 'react-dom';
|
|
@@ -11,43 +11,16 @@ import { cn } from '../../../utils/cn.js';
|
|
|
11
11
|
import { SelectProvider, useSelectContext } from '../Select/Select.js';
|
|
12
12
|
import '../Select/Select.theme.js';
|
|
13
13
|
import { useIsClient } from '../../../hooks/useIsClient.js';
|
|
14
|
+
import { useDarkMode } from '../../../hooks/useDarkMode.js';
|
|
14
15
|
import { useSelectState } from '../Select/useSelectState.js';
|
|
15
16
|
|
|
16
|
-
/**
|
|
17
|
-
* Hook to detect dark mode state from document
|
|
18
|
-
*/
|
|
19
|
-
const useDarkMode = () => {
|
|
20
|
-
const [isDark, setIsDark] = React__default.useState(false);
|
|
21
|
-
useEffect(() => {
|
|
22
|
-
if (typeof document === "undefined")
|
|
23
|
-
return;
|
|
24
|
-
const checkDarkMode = () => {
|
|
25
|
-
const hasDarkClass = document.documentElement.classList.contains("dark") ||
|
|
26
|
-
document.body.classList.contains("dark") ||
|
|
27
|
-
document.querySelector(".dark") !== null;
|
|
28
|
-
setIsDark(hasDarkClass);
|
|
29
|
-
};
|
|
30
|
-
checkDarkMode();
|
|
31
|
-
const observer = new MutationObserver(checkDarkMode);
|
|
32
|
-
observer.observe(document.documentElement, {
|
|
33
|
-
attributes: true,
|
|
34
|
-
attributeFilter: ["class"],
|
|
35
|
-
});
|
|
36
|
-
observer.observe(document.body, {
|
|
37
|
-
attributes: true,
|
|
38
|
-
attributeFilter: ["class"],
|
|
39
|
-
});
|
|
40
|
-
return () => observer.disconnect();
|
|
41
|
-
}, []);
|
|
42
|
-
return isDark;
|
|
43
|
-
};
|
|
44
17
|
/**
|
|
45
18
|
* Default filter function for options
|
|
46
19
|
*/
|
|
47
20
|
const defaultFilterFn = (option, inputValue) => {
|
|
48
21
|
return option.label.toLowerCase().includes(inputValue.toLowerCase());
|
|
49
22
|
};
|
|
50
|
-
const ComboboxOptions = ({ isOpen, onClose, triggerRef, portal, placement, offset, emptyMessage, className, }) => {
|
|
23
|
+
const ComboboxOptions = ({ isOpen, onClose, triggerRef, portal, placement, offset, emptyMessage, className, listboxId, comboboxId, }) => {
|
|
51
24
|
var _a;
|
|
52
25
|
const { filteredOptions, highlightedIndex, setHighlightedIndex, selectedValue, selectOption, optionsRef, theme, } = useSelectContext();
|
|
53
26
|
const isDarkMode = useDarkMode();
|
|
@@ -105,7 +78,7 @@ const ComboboxOptions = ({ isOpen, onClose, triggerRef, portal, placement, offse
|
|
|
105
78
|
const disabledOptionStyle = theme.disabledOptionStyle || comboboxTheme.disabledOptionStyle;
|
|
106
79
|
const createOptionStyleTheme = theme.createOptionStyle || comboboxTheme.createOptionStyle;
|
|
107
80
|
const emptyMessageStyle = comboboxTheme.emptyMessageStyle;
|
|
108
|
-
const optionsContent = (jsx(AnimatePresence, { mode: "wait", children: isOpen && (jsx(motion.div, { ref: combinedRef, role: "listbox", className: cn(dropdownStyle, className), style: {
|
|
81
|
+
const optionsContent = (jsx(AnimatePresence, { mode: "wait", children: isOpen && (jsx(motion.div, { ref: combinedRef, id: listboxId, role: "listbox", className: cn(dropdownStyle, className), style: {
|
|
109
82
|
...cssVariables,
|
|
110
83
|
overscrollBehavior: "contain",
|
|
111
84
|
...(portal
|
|
@@ -115,17 +88,17 @@ const ComboboxOptions = ({ isOpen, onClose, triggerRef, portal, placement, offse
|
|
|
115
88
|
width: `${position.width}px`,
|
|
116
89
|
}
|
|
117
90
|
: {}),
|
|
118
|
-
}, tabIndex: -1, variants: animations, initial: "initial", animate: "visible", exit: "exit", children: filteredOptions.length === 0 ? (jsx("div", { className: emptyMessageStyle, children: emptyMessage })) : (filteredOptions.map((option, index) => {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
91
|
+
}, tabIndex: -1, variants: animations, initial: "initial", animate: "visible", exit: "exit", children: filteredOptions.length === 0 ? (jsx("div", { className: emptyMessageStyle, children: emptyMessage })) : (jsxs(Fragment, { children: [(filteredOptions.length > 100 ? filteredOptions.slice(0, 100) : filteredOptions).map((option, index) => {
|
|
92
|
+
const isSelected = option.value === selectedValue;
|
|
93
|
+
const isHighlighted = index === highlightedIndex;
|
|
94
|
+
const isCreateOption = option.id === "__create__";
|
|
95
|
+
return (jsx("div", { id: comboboxId ? `${comboboxId}-option-${option.id}` : undefined, role: "option", "aria-selected": isSelected, "aria-disabled": option.disabled, "data-option-index": index, className: cn(optionStyle, isSelected && selectedOptionStyle, isHighlighted && highlightedOptionStyle, option.disabled && disabledOptionStyle, isCreateOption && createOptionStyleTheme), onMouseEnter: () => !option.disabled && setHighlightedIndex(index), onClick: () => {
|
|
96
|
+
if (!option.disabled) {
|
|
97
|
+
selectOption(option.value, isCreateOption);
|
|
98
|
+
onClose();
|
|
99
|
+
}
|
|
100
|
+
}, children: isCreateOption ? (jsxs("span", { className: "flex items-center gap-1", children: [jsx(PlusIcon, { className: "size-4 shrink-0", strokeWidth: 2 }), jsx("span", { className: "block truncate", children: option.label })] })) : (jsx("span", { className: "block truncate", children: option.label })) }, option.id));
|
|
101
|
+
}), filteredOptions.length > 100 && (jsxs("div", { className: "px-3 py-2 text-xs text-[var(--color-text-muted)] text-center border-t border-[var(--color-border)]", children: ["Showing 100 of ", filteredOptions.length, " results \u2014 refine your search"] }))] })) })) }));
|
|
129
102
|
if (portal && isClient) {
|
|
130
103
|
return createPortal(jsx("div", { className: isDarkMode ? "dark" : undefined, children: optionsContent }), document.body);
|
|
131
104
|
}
|
|
@@ -153,7 +126,7 @@ const ComboboxOptions = ({ isOpen, onClose, triggerRef, portal, placement, offse
|
|
|
153
126
|
* />
|
|
154
127
|
* ```
|
|
155
128
|
*/
|
|
156
|
-
const ComboboxInner = ({ options, value, onChange, placeholder = "Search...", label, helperText, size = "md", state = "default", clearable = true, iconStart, openOnFocus = true, filterFn = defaultFilterFn, emptyMessage = "No results found", creatable = false, onCreateOption, createLabel, required = false, name, className, wrapperClassName, inputClassName, dropdownClassName, theme: themeOverrides, portal = true, placement = "bottom-start", offset = 8, id, ...props }, ref) => {
|
|
129
|
+
const ComboboxInner = ({ options, value, onChange, placeholder = "Search...", label, helperText, size = "md", state = "default", clearable = true, iconStart, iconEnd, openOnFocus = true, filterFn = defaultFilterFn, emptyMessage = "No results found", creatable = false, onCreateOption, createLabel, required = false, name, className, wrapperClassName, inputClassName, dropdownClassName, theme: themeOverrides, portal = true, placement = "bottom-start", offset = 8, id, ...props }, ref) => {
|
|
157
130
|
var _a;
|
|
158
131
|
// Merge theme
|
|
159
132
|
const theme = React__default.useMemo(() => ({ ...comboboxTheme, ...(themeOverrides || {}) }), [themeOverrides]);
|
|
@@ -385,17 +358,19 @@ const ComboboxInner = ({ options, value, onChange, placeholder = "Search...", la
|
|
|
385
358
|
const showClearButton = clearable && value !== undefined && !isDisabled;
|
|
386
359
|
// Icon padding
|
|
387
360
|
const hasLeftIcon = Boolean(iconStart);
|
|
361
|
+
const hasRightIcon = Boolean(iconEnd);
|
|
388
362
|
const iconStartPadding = hasLeftIcon ? theme.iconPadding.left : "";
|
|
363
|
+
const iconEndPadding = hasRightIcon ? theme.iconPadding.right : "";
|
|
389
364
|
// Create a modified selectState for the provider with our custom filtered options
|
|
390
365
|
const modifiedSelectState = React__default.useMemo(() => ({
|
|
391
366
|
...selectState,
|
|
392
367
|
filteredOptions: customFilteredOptions,
|
|
393
368
|
selectOption: handleSelectOption,
|
|
394
369
|
}), [selectState, customFilteredOptions, handleSelectOption]);
|
|
395
|
-
return (jsx(SelectProvider, { selectState: modifiedSelectState, size: size, componentState: state, theme: theme, searchable: true, enableTypeahead: false, children: jsxs("div", { ref: ref, className: cn("relative", containerClasses, stateClass, wrapperClassName), ...props, children: [label && (jsx(FormLabel, { htmlFor: comboboxId, required: required, children: label })), jsxs("div", { ref: wrapperRef, className: theme.wrapperStyle, children: [jsx("div", { className: cn(inputGroupClasses, stateClasses, className), children: jsxs("div", { className: "relative flex-1", children: [jsx("input", { ref: inputRef, id: comboboxId, type: "text", role: "combobox", "aria-expanded": isOpen, "aria-haspopup": "listbox", "aria-controls": listboxId, "aria-activedescendant": highlightedIndex >= 0 &&
|
|
370
|
+
return (jsx(SelectProvider, { selectState: modifiedSelectState, size: size, componentState: state, theme: theme, searchable: true, enableTypeahead: false, children: jsxs("div", { ref: ref, className: cn("relative", containerClasses, stateClass, wrapperClassName), ...props, children: [label && (typeof label === 'string' ? (jsx(FormLabel, { htmlFor: comboboxId, state: state, required: required, children: label })) : (label)), jsxs("div", { ref: wrapperRef, className: theme.wrapperStyle, children: [jsx("div", { className: cn(inputGroupClasses, stateClasses, className), children: jsxs("div", { className: "relative flex-1", children: [jsx("input", { ref: inputRef, id: comboboxId, type: "text", role: "combobox", "aria-expanded": isOpen, "aria-haspopup": "listbox", "aria-controls": listboxId, "aria-activedescendant": highlightedIndex >= 0 &&
|
|
396
371
|
customFilteredOptions[highlightedIndex]
|
|
397
372
|
? `${comboboxId}-option-${customFilteredOptions[highlightedIndex].id}`
|
|
398
|
-
: undefined, "aria-autocomplete": "list", "aria-invalid": isInvalid, autoComplete: "off", className: cn(inputClasses, sizeClasses, iconStartPadding, showClearButton && "pr-8", inputClassName), value: searchValue, onChange: handleInputChange, onFocus: handleInputFocus, onKeyDown: handleKeyDown, placeholder: placeholder, disabled: isDisabled, required: required }), hasLeftIcon && (jsx("div", { className: theme.iconStartStyle, children: iconStart })), showClearButton && (jsx("button", { type: "button", className: theme.clearButtonStyle, onClick: handleClear, "aria-label": "Clear selection", tabIndex: -1, children: jsx(XMarkIcon, { className: theme.clearIconStyle }) }))] }) }), jsx(ComboboxOptions, { isOpen: isOpen, onClose: () => setIsOpen(false), triggerRef: wrapperRef, portal: portal, placement: placement, offset: offset, emptyMessage: emptyMessage, className: dropdownClassName })] }), helperText && (jsx("p", { className: cn(helperTextClasses, helperTextStateClasses), children: helperText })), name && (jsx("input", { type: "hidden", name: name, value: value !== undefined ? String(value) : "" }))] }) }));
|
|
373
|
+
: undefined, "aria-autocomplete": "list", "aria-invalid": isInvalid, "aria-describedby": helperText ? `${comboboxId}-helper` : undefined, autoComplete: "off", className: cn(inputClasses, sizeClasses, iconStartPadding, (hasRightIcon || showClearButton) && iconEndPadding, showClearButton && "pr-8", inputClassName), value: searchValue, onChange: handleInputChange, onFocus: handleInputFocus, onKeyDown: handleKeyDown, placeholder: placeholder, disabled: isDisabled, required: required }), hasLeftIcon && (jsx("div", { className: theme.iconStartStyle, children: iconStart })), showClearButton && (jsx("button", { type: "button", className: theme.clearButtonStyle, onClick: handleClear, "aria-label": "Clear selection", tabIndex: -1, children: jsx(XMarkIcon, { className: theme.clearIconStyle }) })), hasRightIcon && !showClearButton && (jsx("div", { className: theme.iconEndStyle, children: iconEnd }))] }) }), jsx(ComboboxOptions, { isOpen: isOpen, onClose: () => setIsOpen(false), triggerRef: wrapperRef, portal: portal, placement: placement, offset: offset, emptyMessage: emptyMessage, className: dropdownClassName, listboxId: listboxId, comboboxId: comboboxId })] }), helperText && (typeof helperText === 'string' ? (jsx("p", { id: `${comboboxId}-helper`, className: cn(helperTextClasses, helperTextStateClasses), children: helperText })) : (helperText)), name && (jsx("input", { type: "hidden", name: name, value: value !== undefined ? String(value) : "" }))] }) }));
|
|
399
374
|
};
|
|
400
375
|
const ComboboxForwarded = React__default.forwardRef(ComboboxInner);
|
|
401
376
|
ComboboxForwarded.displayName = "Combobox";
|
|
@@ -33,9 +33,9 @@ export interface ComboboxProps<T = string> extends Omit<React.HTMLAttributes<HTM
|
|
|
33
33
|
/** Placeholder text when no value is selected */
|
|
34
34
|
placeholder?: string;
|
|
35
35
|
/** Label text displayed above the input */
|
|
36
|
-
label?:
|
|
36
|
+
label?: React.ReactNode;
|
|
37
37
|
/** Helper text displayed below the input */
|
|
38
|
-
helperText?:
|
|
38
|
+
helperText?: React.ReactNode;
|
|
39
39
|
/** Size variant */
|
|
40
40
|
size?: ComboboxSize;
|
|
41
41
|
/** State variant (controls styling) */
|
|
@@ -44,6 +44,8 @@ export interface ComboboxProps<T = string> extends Omit<React.HTMLAttributes<HTM
|
|
|
44
44
|
clearable?: boolean;
|
|
45
45
|
/** Icon displayed at the start of the input */
|
|
46
46
|
iconStart?: React.ReactNode;
|
|
47
|
+
/** Icon displayed at the end of the input */
|
|
48
|
+
iconEnd?: React.ReactNode;
|
|
47
49
|
/** Whether to open dropdown on focus (default: true) */
|
|
48
50
|
openOnFocus?: boolean;
|
|
49
51
|
/** Custom filter function */
|
|
@@ -13,9 +13,12 @@ const FieldSelect = ({ label, value, options, onChange, disabled, size = "md", c
|
|
|
13
13
|
// ============================================================================
|
|
14
14
|
// Main CronInput Component
|
|
15
15
|
// ============================================================================
|
|
16
|
-
const CronInput = React__default.forwardRef(({ value: controlledValue, defaultValue = "* * * * *", onChange, showPresets = true, showDescription = true, showNextRuns = true, nextRunCount = 3, size = "md", mode = "both", className, theme: themeOverrides, disabled = false, label, ...props }, ref) => {
|
|
16
|
+
const CronInput = React__default.forwardRef(({ value: controlledValue, defaultValue = "* * * * *", onChange, showPresets = true, showDescription = true, showNextRuns = true, nextRunCount = 3, size = "md", mode = "both", className, theme: themeOverrides, state = "default", disabled = false, label, helperText, ...props }, ref) => {
|
|
17
17
|
// Unique ID for accessibility
|
|
18
18
|
const generatedId = useId();
|
|
19
|
+
// Derive disabled/invalid from `state` prop (with deprecated `disabled` as fallback)
|
|
20
|
+
const isDisabled = state === "disabled" || disabled;
|
|
21
|
+
const isInvalid = state === "invalid";
|
|
19
22
|
// Controlled vs uncontrolled state
|
|
20
23
|
const isControlled = controlledValue !== undefined;
|
|
21
24
|
const [internalValue, setInternalValue] = useState(defaultValue);
|
|
@@ -88,10 +91,10 @@ const CronInput = React__default.forwardRef(({ value: controlledValue, defaultVa
|
|
|
88
91
|
}, [rawInput, cronValue, updateValue]);
|
|
89
92
|
// Handle preset click
|
|
90
93
|
const handlePresetClick = useCallback((presetValue) => {
|
|
91
|
-
if (
|
|
94
|
+
if (isDisabled)
|
|
92
95
|
return;
|
|
93
96
|
updateValue(presetValue);
|
|
94
|
-
}, [
|
|
97
|
+
}, [isDisabled, updateValue]);
|
|
95
98
|
// Handle Enter key in raw input
|
|
96
99
|
const handleRawKeyDown = useCallback((e) => {
|
|
97
100
|
if (e.key === "Enter") {
|
|
@@ -100,7 +103,10 @@ const CronInput = React__default.forwardRef(({ value: controlledValue, defaultVa
|
|
|
100
103
|
}, [handleRawInputBlur]);
|
|
101
104
|
const showVisual = mode === "visual" || (mode === "both" && activeMode === "visual");
|
|
102
105
|
const showRaw = mode === "raw" || (mode === "both" && activeMode === "raw");
|
|
103
|
-
|
|
106
|
+
const helperId = `${generatedId}-helper`;
|
|
107
|
+
return (jsxs("div", { ref: ref, className: cn(theme.baseStyle, className), role: "group", "aria-label": label || "Cron expression builder", ...props, children: [label && (jsx(FormLabel, { htmlFor: `${generatedId}-cron`, state: isDisabled ? "disabled" : isInvalid ? "invalid" : "default", children: label })), mode === "both" && (jsxs("div", { className: "flex items-center gap-1.5", children: [jsxs("button", { type: "button", onClick: () => setActiveMode("visual"), disabled: isDisabled, className: cn(theme.modeToggleStyle, activeMode === "visual" && theme.modeToggleActiveStyle), "aria-pressed": activeMode === "visual", children: [jsx(CalendarIcon, { className: "w-3.5 h-3.5 mr-1", "aria-hidden": "true" }), "Visual"] }), jsxs("button", { type: "button", onClick: () => setActiveMode("raw"), disabled: isDisabled, className: cn(theme.modeToggleStyle, activeMode === "raw" && theme.modeToggleActiveStyle), "aria-pressed": activeMode === "raw", children: [jsx(ClockIcon, { className: "w-3.5 h-3.5 mr-1", "aria-hidden": "true" }), "Cron"] })] })), showPresets && (jsxs("div", { children: [jsx("div", { className: cn(theme.sectionHeadingStyle, "mb-1.5"), children: "Presets" }), jsx("div", { className: "flex flex-wrap gap-1.5", children: CRON_PRESETS.map((preset) => (jsx("button", { type: "button", onClick: () => handlePresetClick(preset.value), disabled: isDisabled, className: cn(theme.presetButtonStyle, activePreset === preset.value && theme.presetButtonActiveStyle), title: preset.description, "aria-pressed": activePreset === preset.value, children: preset.label }, preset.value))) })] })), showVisual && (jsxs("div", { className: cn(theme.builderStyle, isInvalid && "ring-1 ring-[var(--color-danger)] rounded-[var(--form-radius)]"), children: [jsx(FieldSelect, { label: "Minute", value: fields.minute, options: minuteOptions, onChange: (v) => handleFieldChange("minute", v), disabled: isDisabled, size: size }), jsx(FieldSelect, { label: "Hour", value: fields.hour, options: hourOptions, onChange: (v) => handleFieldChange("hour", v), disabled: isDisabled, size: size }), jsx(FieldSelect, { label: "Day of Month", value: fields.dayOfMonth, options: dayOfMonthOptions, onChange: (v) => handleFieldChange("dayOfMonth", v), disabled: isDisabled, size: size }), jsx(FieldSelect, { label: "Month", value: fields.month, options: monthOptions, onChange: (v) => handleFieldChange("month", v), disabled: isDisabled, size: size }), jsx(FieldSelect, { label: "Day of Week", value: fields.dayOfWeek, options: dayOfWeekOptions, onChange: (v) => handleFieldChange("dayOfWeek", v), disabled: isDisabled, size: size })] })), showRaw && (jsxs("div", { children: [jsx("div", { className: cn(theme.sectionHeadingStyle, "mb-1.5"), children: "Cron Expression" }), jsx("input", { type: "text", value: rawInput, onChange: handleRawInputChange, onBlur: handleRawInputBlur, onKeyDown: handleRawKeyDown, disabled: isDisabled, placeholder: "* * * * *", className: cn(theme.rawInputStyle, theme.rawInputSizes[size], isInvalid && "border-[var(--color-danger)] focus:border-[var(--color-danger)] focus:ring-[var(--color-danger)]"), id: `${generatedId}-cron`, "aria-label": "Cron expression", "aria-invalid": isInvalid || !validateCron(rawInput), "aria-describedby": helperText ? helperId : undefined, spellCheck: false, autoComplete: "off" }), !validateCron(rawInput) && rawInput !== cronValue && (jsx("div", { className: cn(theme.errorStyle, "mt-1"), children: "Invalid cron expression. Format: minute hour day month weekday" }))] })), showVisual && !showRaw && (jsxs("div", { className: "flex items-center gap-2", children: [jsx("span", { className: cn(theme.fieldLabelStyle, "shrink-0"), children: "Expression:" }), jsx("code", { className: cn("font-mono text-sm px-2 py-1 rounded-[var(--form-radius)]", "bg-[var(--color-background-secondary)] text-[var(--color-text-primary)]", "dark:bg-[var(--color-neutral-800)] dark:text-[var(--color-neutral-200)]"), children: cronValue })] })), showDescription && isValid && (jsxs("div", { className: theme.descriptionStyle, children: [jsx(ClockIcon, { className: "w-4 h-4 shrink-0", "aria-hidden": "true" }), jsx("span", { children: description })] })), showNextRuns && isValid && nextRuns.length > 0 && (jsxs("div", { className: theme.nextRunsStyle, children: [jsx("div", { className: cn(theme.sectionHeadingStyle, "mb-1"), children: "Next runs" }), nextRuns.map((date, index) => (jsxs("div", { className: theme.nextRunItemStyle, children: [jsx(CalendarIcon, { className: "w-3.5 h-3.5 shrink-0", "aria-hidden": "true" }), jsx("span", { children: formatNextRun(date) })] }, index)))] })), helperText && (jsx("div", { id: helperId, className: cn("text-sm mt-1", isInvalid
|
|
108
|
+
? "text-[var(--color-danger)]"
|
|
109
|
+
: "text-[var(--color-text-muted)]"), children: helperText }))] }));
|
|
104
110
|
});
|
|
105
111
|
CronInput.displayName = "CronInput";
|
|
106
112
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
+
export type CronInputState = "default" | "disabled" | "invalid";
|
|
2
3
|
/**
|
|
3
4
|
* CronInput component props
|
|
4
5
|
* A visual cron expression builder that outputs standard cron syntax
|
|
@@ -26,10 +27,20 @@ export interface CronInputProps extends Omit<React.HTMLAttributes<HTMLDivElement
|
|
|
26
27
|
className?: string;
|
|
27
28
|
/** Theme overrides for sub-component styling */
|
|
28
29
|
theme?: CronInputThemeOverrides;
|
|
29
|
-
/**
|
|
30
|
+
/**
|
|
31
|
+
* Validation/interaction state
|
|
32
|
+
* @default "default"
|
|
33
|
+
*/
|
|
34
|
+
state?: CronInputState;
|
|
35
|
+
/**
|
|
36
|
+
* Whether the input is disabled
|
|
37
|
+
* @deprecated Use `state="disabled"` instead
|
|
38
|
+
*/
|
|
30
39
|
disabled?: boolean;
|
|
31
40
|
/** Label text for the component */
|
|
32
41
|
label?: string;
|
|
42
|
+
/** Helper text displayed below the component */
|
|
43
|
+
helperText?: React.ReactNode;
|
|
33
44
|
}
|
|
34
45
|
export type CronInputSize = "sm" | "md" | "lg";
|
|
35
46
|
export type CronInputMode = "visual" | "raw" | "both";
|
|
@@ -23,7 +23,7 @@ const formatDate = (date, formatString, options = {
|
|
|
23
23
|
return formatDateCustom(date, formatString);
|
|
24
24
|
}
|
|
25
25
|
// Otherwise use the Intl API
|
|
26
|
-
return date.toLocaleDateString(
|
|
26
|
+
return date.toLocaleDateString('en-US', options);
|
|
27
27
|
};
|
|
28
28
|
/**
|
|
29
29
|
* Format date range for display
|
|
@@ -120,7 +120,7 @@ const DatePickerComponent = React__default.forwardRef(({ id, name, mode = "singl
|
|
|
120
120
|
theme,
|
|
121
121
|
triggerRef,
|
|
122
122
|
contentRef,
|
|
123
|
-
}), [mode, isOpen, selectedValue, minDate, maxDate, isDateDisabled, showToday, selectTodayByDefault, weekdays, months, yearRange, handleDateSelect, handleRangeSelect, size, state, theme]), children: jsxs("div", { ref: ref, className: cn(containerStyle, wrapperClassName), children: [label && (jsx(FormLabel, { htmlFor: datePickerId, required: required, children: label })), jsx(DatePickerTrigger, { id: datePickerId, disabled: disabled, isOpen: isOpen, state: state, size: size, onClick: handleButtonClick, onFocus: onFocus, onBlur: onBlur, className: className, children: jsx("span", { className: cn("block truncate", selectedValue ? theme.valueTextStyle : theme.placeholderTextStyle), children: displayValue }) }), jsx(DatePickerBody, { isOpen: isOpen, onClose: () => setIsOpen(false), portal: portal, placement: placement, offset: offset, presets: presets, children: children }), helperText && (jsx("p", { className: cn(helperTextStyle, helperTextStateStyle), children: helperText })), name && mode === "single" && selectedValue instanceof Date && (jsx("input", { type: "hidden", name: name, value: selectedValue.toISOString() })), name &&
|
|
123
|
+
}), [mode, isOpen, selectedValue, minDate, maxDate, isDateDisabled, showToday, selectTodayByDefault, weekdays, months, yearRange, handleDateSelect, handleRangeSelect, size, state, theme]), children: jsxs("div", { ref: ref, className: cn(containerStyle, wrapperClassName), children: [label && (typeof label === 'string' ? (jsx(FormLabel, { htmlFor: datePickerId, state: state, required: required, children: label })) : (label)), jsx(DatePickerTrigger, { id: datePickerId, disabled: disabled, isOpen: isOpen, state: state, size: size, onClick: handleButtonClick, onFocus: onFocus, onBlur: onBlur, className: className, "aria-invalid": state === 'invalid' || undefined, "aria-describedby": helperText ? `${datePickerId}-helper` : undefined, children: jsx("span", { className: cn("block truncate", selectedValue ? theme.valueTextStyle : theme.placeholderTextStyle), children: displayValue }) }), jsx(DatePickerBody, { isOpen: isOpen, onClose: () => setIsOpen(false), portal: portal, placement: placement, offset: offset, presets: presets, children: children }), helperText && (typeof helperText === 'string' ? (jsx("p", { id: `${datePickerId}-helper`, className: cn(helperTextStyle, helperTextStateStyle), children: helperText })) : (helperText)), name && mode === "single" && selectedValue instanceof Date && (jsx("input", { type: "hidden", name: name, value: selectedValue.toISOString() })), name &&
|
|
124
124
|
mode === "range" &&
|
|
125
125
|
selectedValue &&
|
|
126
126
|
typeof selectedValue === "object" &&
|
|
@@ -49,9 +49,9 @@ export interface DatePickerProps extends Omit<React.HTMLAttributes<HTMLDivElemen
|
|
|
49
49
|
/** Whether the field is required */
|
|
50
50
|
required?: boolean;
|
|
51
51
|
/** Label for the field */
|
|
52
|
-
label?:
|
|
52
|
+
label?: React.ReactNode;
|
|
53
53
|
/** Helper text below the field */
|
|
54
|
-
helperText?:
|
|
54
|
+
helperText?: React.ReactNode;
|
|
55
55
|
/** Callback fired when a date is selected - type depends on mode */
|
|
56
56
|
onChange?: (value: Date | DateRange | null) => void;
|
|
57
57
|
/** Array of preset date ranges (only for range mode) */
|
|
@@ -110,6 +110,8 @@ export interface DatePickerTriggerProps {
|
|
|
110
110
|
onFocus?: (event: React.FocusEvent<HTMLButtonElement>) => void;
|
|
111
111
|
onBlur?: (event: React.FocusEvent<HTMLButtonElement>) => void;
|
|
112
112
|
children?: React.ReactNode;
|
|
113
|
+
"aria-invalid"?: boolean;
|
|
114
|
+
"aria-describedby"?: string;
|
|
113
115
|
}
|
|
114
116
|
/**
|
|
115
117
|
* DatePicker body props (calendar dropdown content)
|
|
@@ -12,36 +12,8 @@ import { useSelectPortal } from '../../../hooks/useSelectPortal.js';
|
|
|
12
12
|
import { formatDateCustom } from '../../../utils/dateUtils.js';
|
|
13
13
|
import { createDropdownAnimations } from './DatePicker.animations.js';
|
|
14
14
|
import { useIsClient } from '../../../hooks/useIsClient.js';
|
|
15
|
+
import { useDarkMode } from '../../../hooks/useDarkMode.js';
|
|
15
16
|
|
|
16
|
-
/**
|
|
17
|
-
* Hook to detect dark mode state from document
|
|
18
|
-
*/
|
|
19
|
-
const useDarkMode = () => {
|
|
20
|
-
const [isDark, setIsDark] = useState(false);
|
|
21
|
-
useEffect(() => {
|
|
22
|
-
if (typeof document === "undefined")
|
|
23
|
-
return;
|
|
24
|
-
const checkDarkMode = () => {
|
|
25
|
-
const hasDarkClass = document.documentElement.classList.contains("dark") ||
|
|
26
|
-
document.body.classList.contains("dark") ||
|
|
27
|
-
document.querySelector(".dark") !== null;
|
|
28
|
-
setIsDark(hasDarkClass);
|
|
29
|
-
};
|
|
30
|
-
checkDarkMode();
|
|
31
|
-
// Observe class changes on html and body
|
|
32
|
-
const observer = new MutationObserver(checkDarkMode);
|
|
33
|
-
observer.observe(document.documentElement, {
|
|
34
|
-
attributes: true,
|
|
35
|
-
attributeFilter: ["class"],
|
|
36
|
-
});
|
|
37
|
-
observer.observe(document.body, {
|
|
38
|
-
attributes: true,
|
|
39
|
-
attributeFilter: ["class"],
|
|
40
|
-
});
|
|
41
|
-
return () => observer.disconnect();
|
|
42
|
-
}, []);
|
|
43
|
-
return isDark;
|
|
44
|
-
};
|
|
45
17
|
/**
|
|
46
18
|
* DatePicker Body Component - contains the Calendar and optional Presets
|
|
47
19
|
* Uses portal positioning like Select component
|
|
@@ -8,7 +8,7 @@ import { DatePickerContext } from './DatePicker.js';
|
|
|
8
8
|
* DatePicker Trigger Component - styled like form input
|
|
9
9
|
* Displays selected date or placeholder text
|
|
10
10
|
*/
|
|
11
|
-
const DatePickerTrigger = ({ id, disabled, isOpen, state = "default", size = "md", className, onClick, onFocus, onBlur, children, }) => {
|
|
11
|
+
const DatePickerTrigger = ({ id, disabled, isOpen, state = "default", size = "md", className, onClick, onFocus, onBlur, children, 'aria-invalid': ariaInvalid, 'aria-describedby': ariaDescribedby, }) => {
|
|
12
12
|
var _a, _b, _c, _d;
|
|
13
13
|
const context = useContext(DatePickerContext);
|
|
14
14
|
if (!context) {
|
|
@@ -28,7 +28,7 @@ const DatePickerTrigger = ({ id, disabled, isOpen, state = "default", size = "md
|
|
|
28
28
|
? focusStateStyle ||
|
|
29
29
|
"outline-2 outline-primary-600 -outline-offset-1 ring-4 ring-[var(--color-primary)]/10"
|
|
30
30
|
: "";
|
|
31
|
-
return (jsx("div", { className: "relative w-full", children: jsxs("button", { ref: triggerRef, id: id, type: "button", className: cn(baseStyle, sizeStyle, stateStyle, hoverStateStyle, activeFocusState, iconEndPadding, triggerButtonStyle, "text-left", className), onClick: onClick, onFocus: onFocus, onBlur: onBlur, disabled: disabled, "aria-expanded": isOpen, "aria-haspopup": "dialog", "aria-label": "Open calendar", children: [children, jsx("span", { className: iconEndStyle, children: jsx(CalendarIcon, { className: "h-4 w-4" }) })] }) }));
|
|
31
|
+
return (jsx("div", { className: "relative w-full", children: jsxs("button", { ref: triggerRef, id: id, type: "button", className: cn(baseStyle, sizeStyle, stateStyle, hoverStateStyle, activeFocusState, iconEndPadding, triggerButtonStyle, "text-left", className), onClick: onClick, onFocus: onFocus, onBlur: onBlur, disabled: disabled, "aria-expanded": isOpen, "aria-haspopup": "dialog", "aria-label": "Open calendar", "aria-invalid": ariaInvalid, "aria-describedby": ariaDescribedby, children: [children, jsx("span", { className: iconEndStyle, children: jsx(CalendarIcon, { className: "h-4 w-4" }) })] }) }));
|
|
32
32
|
};
|
|
33
33
|
|
|
34
34
|
export { DatePickerTrigger };
|
|
@@ -21,7 +21,7 @@ const formatDate = (date, formatString, options = {
|
|
|
21
21
|
if (formatString) {
|
|
22
22
|
return formatDateCustom(date, formatString);
|
|
23
23
|
}
|
|
24
|
-
return date.toLocaleDateString(
|
|
24
|
+
return date.toLocaleDateString('en-US', options);
|
|
25
25
|
};
|
|
26
26
|
/**
|
|
27
27
|
* Format date range for display
|
|
@@ -96,9 +96,9 @@ const DateRangePickerComponent = React__default.forwardRef(({ id, name, size = "
|
|
|
96
96
|
size,
|
|
97
97
|
state,
|
|
98
98
|
theme,
|
|
99
|
-
]), children: jsxs("div", { ref: ref, className: cn(theme.containerStyle, wrapperClassName), ...rest, children: [label && (jsx(FormLabel, { htmlFor: pickerId, required: required, children: label })), jsx(DateRangePickerTrigger, { id: pickerId, disabled: disabled, isOpen: isOpen, state: state, size: size, onClick: handleButtonClick, onFocus: onFocus, onBlur: onBlur, className: className, children: jsx("span", { className: cn("block truncate", selectedRange && (selectedRange.start || selectedRange.end)
|
|
99
|
+
]), children: jsxs("div", { ref: ref, className: cn(theme.containerStyle, wrapperClassName), ...rest, children: [label && (typeof label === 'string' ? (jsx(FormLabel, { htmlFor: pickerId, state: state, required: required, children: label })) : (label)), jsx(DateRangePickerTrigger, { id: pickerId, disabled: disabled, isOpen: isOpen, state: state, size: size, onClick: handleButtonClick, onFocus: onFocus, onBlur: onBlur, className: className, "aria-invalid": state === 'invalid' || undefined, "aria-describedby": helperText ? `${pickerId}-helper` : undefined, children: jsx("span", { className: cn("block truncate", selectedRange && (selectedRange.start || selectedRange.end)
|
|
100
100
|
? theme.valueTextStyle
|
|
101
|
-
: theme.placeholderTextStyle), children: displayValue }) }), jsx(DateRangePickerBody, { isOpen: isOpen, onClose: () => setIsOpen(false), portal: portal, placement: placement, offset: offset, presets: presets, showPresets: showPresets }), helperText && (jsx("p", { className: cn(theme.helperTextStyle, theme.helperTextStates[state]), children: helperText })), name && selectedRange && (jsxs(Fragment, { children: [jsx("input", { type: "hidden", name: `${name}[start]`, value: ((_a = selectedRange.start) === null || _a === void 0 ? void 0 : _a.toISOString()) || "" }), jsx("input", { type: "hidden", name: `${name}[end]`, value: ((_b = selectedRange.end) === null || _b === void 0 ? void 0 : _b.toISOString()) || "" })] }))] }) }));
|
|
101
|
+
: theme.placeholderTextStyle), children: displayValue }) }), jsx(DateRangePickerBody, { isOpen: isOpen, onClose: () => setIsOpen(false), portal: portal, placement: placement, offset: offset, presets: presets, showPresets: showPresets }), helperText && (typeof helperText === 'string' ? (jsx("p", { id: `${pickerId}-helper`, className: cn(theme.helperTextStyle, theme.helperTextStates[state]), children: helperText })) : (helperText)), name && selectedRange && (jsxs(Fragment, { children: [jsx("input", { type: "hidden", name: `${name}[start]`, value: ((_a = selectedRange.start) === null || _a === void 0 ? void 0 : _a.toISOString()) || "" }), jsx("input", { type: "hidden", name: `${name}[end]`, value: ((_b = selectedRange.end) === null || _b === void 0 ? void 0 : _b.toISOString()) || "" })] }))] }) }));
|
|
102
102
|
});
|
|
103
103
|
DateRangePickerComponent.displayName = "DateRangePicker";
|
|
104
104
|
// Compound component
|
|
@@ -44,9 +44,9 @@ export interface DateRangePickerProps extends Omit<React.HTMLAttributes<HTMLDivE
|
|
|
44
44
|
/** Whether the field is required */
|
|
45
45
|
required?: boolean;
|
|
46
46
|
/** Label for the field */
|
|
47
|
-
label?:
|
|
47
|
+
label?: React.ReactNode;
|
|
48
48
|
/** Helper text below the field */
|
|
49
|
-
helperText?:
|
|
49
|
+
helperText?: React.ReactNode;
|
|
50
50
|
/** Callback fired when a range is applied */
|
|
51
51
|
onChange?: (range: DateRange | null) => void;
|
|
52
52
|
/** Array of preset date ranges */
|
|
@@ -103,6 +103,8 @@ export interface DateRangePickerTriggerProps {
|
|
|
103
103
|
onFocus?: (event: React.FocusEvent<HTMLButtonElement>) => void;
|
|
104
104
|
onBlur?: (event: React.FocusEvent<HTMLButtonElement>) => void;
|
|
105
105
|
children?: React.ReactNode;
|
|
106
|
+
"aria-invalid"?: boolean;
|
|
107
|
+
"aria-describedby"?: string;
|
|
106
108
|
}
|
|
107
109
|
/**
|
|
108
110
|
* DateRangePicker body props
|
|
@@ -13,35 +13,8 @@ import { formatDateCustom } from '../../../utils/dateUtils.js';
|
|
|
13
13
|
import { createDropdownAnimations } from '../DatePicker/DatePicker.animations.js';
|
|
14
14
|
import { useIsClient } from '../../../hooks/useIsClient.js';
|
|
15
15
|
import { useMediaQuery } from '../../../hooks/useMediaQuery.js';
|
|
16
|
+
import { useDarkMode } from '../../../hooks/useDarkMode.js';
|
|
16
17
|
|
|
17
|
-
/**
|
|
18
|
-
* Hook to detect dark mode state from document
|
|
19
|
-
*/
|
|
20
|
-
const useDarkMode = () => {
|
|
21
|
-
const [isDark, setIsDark] = useState(false);
|
|
22
|
-
useEffect(() => {
|
|
23
|
-
if (typeof document === "undefined")
|
|
24
|
-
return;
|
|
25
|
-
const checkDarkMode = () => {
|
|
26
|
-
const hasDarkClass = document.documentElement.classList.contains("dark") ||
|
|
27
|
-
document.body.classList.contains("dark") ||
|
|
28
|
-
document.querySelector(".dark") !== null;
|
|
29
|
-
setIsDark(hasDarkClass);
|
|
30
|
-
};
|
|
31
|
-
checkDarkMode();
|
|
32
|
-
const observer = new MutationObserver(checkDarkMode);
|
|
33
|
-
observer.observe(document.documentElement, {
|
|
34
|
-
attributes: true,
|
|
35
|
-
attributeFilter: ["class"],
|
|
36
|
-
});
|
|
37
|
-
observer.observe(document.body, {
|
|
38
|
-
attributes: true,
|
|
39
|
-
attributeFilter: ["class"],
|
|
40
|
-
});
|
|
41
|
-
return () => observer.disconnect();
|
|
42
|
-
}, []);
|
|
43
|
-
return isDark;
|
|
44
|
-
};
|
|
45
18
|
/**
|
|
46
19
|
* DateRangePicker Body Component — dual calendars + presets sidebar + footer
|
|
47
20
|
*/
|
|
@@ -7,7 +7,7 @@ import { DateRangePickerContext } from './DateRangePicker.js';
|
|
|
7
7
|
/**
|
|
8
8
|
* DateRangePicker Trigger Component — styled like form input
|
|
9
9
|
*/
|
|
10
|
-
const DateRangePickerTrigger = ({ id, disabled, isOpen, state = "default", size = "md", className, onClick, onFocus, onBlur, children, }) => {
|
|
10
|
+
const DateRangePickerTrigger = ({ id, disabled, isOpen, state = "default", size = "md", className, onClick, onFocus, onBlur, children, 'aria-invalid': ariaInvalid, 'aria-describedby': ariaDescribedby, }) => {
|
|
11
11
|
var _a, _b, _c, _d;
|
|
12
12
|
const context = useContext(DateRangePickerContext);
|
|
13
13
|
if (!context) {
|
|
@@ -26,7 +26,7 @@ const DateRangePickerTrigger = ({ id, disabled, isOpen, state = "default", size
|
|
|
26
26
|
? focusStateStyle ||
|
|
27
27
|
"outline-2 outline-primary-600 -outline-offset-1 ring-4 ring-[var(--color-primary)]/10"
|
|
28
28
|
: "";
|
|
29
|
-
return (jsx("div", { className: "relative w-full", children: jsxs("button", { ref: triggerRef, id: id, type: "button", className: cn(baseStyle, sizeStyle, stateStyle, hoverStateStyle, activeFocusState, iconEndPadding, triggerButtonStyle, "text-left", className), onClick: onClick, onFocus: onFocus, onBlur: onBlur, disabled: disabled, "aria-expanded": isOpen, "aria-haspopup": "dialog", "aria-label": "Open date range picker", children: [children, jsx("span", { className: iconEndStyle, children: jsx(CalendarIcon, { className: "h-4 w-4" }) })] }) }));
|
|
29
|
+
return (jsx("div", { className: "relative w-full", children: jsxs("button", { ref: triggerRef, id: id, type: "button", className: cn(baseStyle, sizeStyle, stateStyle, hoverStateStyle, activeFocusState, iconEndPadding, triggerButtonStyle, "text-left", className), onClick: onClick, onFocus: onFocus, onBlur: onBlur, disabled: disabled, "aria-expanded": isOpen, "aria-haspopup": "dialog", "aria-label": "Open date range picker", "aria-invalid": ariaInvalid, "aria-describedby": ariaDescribedby, children: [children, jsx("span", { className: iconEndStyle, children: jsx(CalendarIcon, { className: "h-4 w-4" }) })] }) }));
|
|
30
30
|
};
|
|
31
31
|
|
|
32
32
|
export { DateRangePickerTrigger };
|
|
@@ -1,9 +1,95 @@
|
|
|
1
1
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
-
import React__default, { useMemo, useRef, useState, useCallback } from 'react';
|
|
2
|
+
import React__default, { useMemo, useId, useRef, useState, useCallback } from 'react';
|
|
3
3
|
import { fileUploadTheme } from './FileUpload.theme.js';
|
|
4
4
|
import { cn } from '../../../utils/cn.js';
|
|
5
5
|
import { CloudArrowUpIcon, DocumentIcon, XMarkIcon } from '@heroicons/react/24/outline';
|
|
6
|
+
import '../../core/Accordion/Accordion.js';
|
|
7
|
+
import '../../core/AlertDialog/AlertDialog.js';
|
|
8
|
+
import '../../core/AspectRatio/AspectRatio.js';
|
|
9
|
+
import '../../core/Avatar/Avatar.js';
|
|
10
|
+
import '../../core/AvatarGroup/AvatarGroup.js';
|
|
11
|
+
import '../../core/Badge/Badge.js';
|
|
12
|
+
import '../../core/Breadcrumbs/Breadcrumbs.js';
|
|
13
|
+
import '../../core/Breadcrumbs/Breadcrumbs.theme.js';
|
|
6
14
|
import { Button } from '../../core/Button/Button.js';
|
|
15
|
+
import '../../core/ButtonGroup/ButtonGroup.js';
|
|
16
|
+
import '../../core/ButtonGroup/ButtonGroupSeparator.js';
|
|
17
|
+
import '../../core/ButtonGroup/ButtonGroupText.js';
|
|
18
|
+
import '../../core/Calendar/Calendar.js';
|
|
19
|
+
import '../../core/Calendar/CalendarMini/CalendarMini.js';
|
|
20
|
+
import '../../core/Card/Card.js';
|
|
21
|
+
import '../../core/CardStack/CardStack.js';
|
|
22
|
+
import '../../core/Carousel/Carousel.js';
|
|
23
|
+
import '../../core/Carousel/CarouselBody.js';
|
|
24
|
+
import '../../core/CommandPalette/CommandPalette.js';
|
|
25
|
+
import '../../core/Separator/Separator.js';
|
|
26
|
+
import '../../core/Drawer/Drawer.js';
|
|
27
|
+
import '../../core/Drawer/DrawerBody.js';
|
|
28
|
+
import '../../core/Dropdown/Dropdown.js';
|
|
29
|
+
import '../../core/Dropdown/DropdownTrigger.js';
|
|
30
|
+
import '../../core/Dropdown/DropdownMenu.js';
|
|
31
|
+
import '../../core/Dropdown/DropdownItem.js';
|
|
32
|
+
import '../../core/Dropdown/DropdownSection.js';
|
|
33
|
+
import '../../core/Dropdown/DropdownSeparator.js';
|
|
34
|
+
import '../../core/Dropdown/Dropdown.theme.js';
|
|
35
|
+
import '../../core/Kbd/Kbd.js';
|
|
36
|
+
import '../../core/MenuItem/MenuItem.js';
|
|
37
|
+
import '../../core/Link/Link.js';
|
|
38
|
+
import '../../core/Modal/Modal.js';
|
|
39
|
+
import '../../core/ModalStack/ModalStack.js';
|
|
40
|
+
import '../../core/PageHeading/PageHeading.js';
|
|
41
|
+
import '../../core/Pagination/Pagination.js';
|
|
42
|
+
import '../../core/Popover/Popover.js';
|
|
43
|
+
import '../../core/Popover/PopoverContext.js';
|
|
44
|
+
import '../../core/Popover/PopoverBody.js';
|
|
45
|
+
import '../../core/Progress/Progress.js';
|
|
46
|
+
import '../../core/Rating/Rating.js';
|
|
47
|
+
import '../../core/ScrollArea/ScrollArea.js';
|
|
48
|
+
import '../../core/ScrollArea/smooth/SmoothScrollEngine.js';
|
|
49
|
+
import '../../core/Segmented/Segmented.js';
|
|
50
|
+
import '../../core/Skeleton/Skeleton.js';
|
|
51
|
+
import '../../core/SlidingNumber/SlidingNumber.js';
|
|
52
|
+
import '../../core/Tabs/Tabs.js';
|
|
53
|
+
import '../../core/Tabs/TabsList.js';
|
|
54
|
+
import '../../core/Tabs/TabsTrigger.js';
|
|
55
|
+
import '../../core/Tabs/TabsBody.js';
|
|
56
|
+
import '../../core/Tabs/TabsContext.js';
|
|
57
|
+
import '../../core/Tooltip/Tooltip.js';
|
|
58
|
+
import '../../core/Tooltip/Tooltip.animations.js';
|
|
59
|
+
import '../../core/Tree/Tree.js';
|
|
60
|
+
import '../../core/Tag/Tag.js';
|
|
61
|
+
import '../../core/Alert/Alert.js';
|
|
62
|
+
import '../../core/Toast/Toast.js';
|
|
63
|
+
import '../../core/Toast/ToastProvider.js';
|
|
64
|
+
import '@heroicons/react/24/solid';
|
|
65
|
+
import '../../core/Spinner/Spinner.js';
|
|
66
|
+
import '../../core/Message/Message.js';
|
|
67
|
+
import '../../core/Message/TypeWriter.js';
|
|
68
|
+
import '../../core/Message/MessageBody.js';
|
|
69
|
+
import '@heroicons/react/16/solid';
|
|
70
|
+
import '../../core/Empty/Empty.js';
|
|
71
|
+
import '../../core/Masonry/Masonry.js';
|
|
72
|
+
import '../../core/DragDrop/DragDrop.js';
|
|
73
|
+
import '../../core/Sortable/Sortable.js';
|
|
74
|
+
import '../../core/NavItem/NavItem.js';
|
|
75
|
+
import '../../core/Sidebar/Sidebar.js';
|
|
76
|
+
import '../../core/Sidebar/SidebarHeader.js';
|
|
77
|
+
import '../../core/Sidebar/SidebarContent.js';
|
|
78
|
+
import '../../core/Sidebar/SidebarFooter.js';
|
|
79
|
+
import '../../core/Sidebar/SidebarNav.js';
|
|
80
|
+
import '../../core/Sidebar/SidebarNavGroup.js';
|
|
81
|
+
import 'motion/react';
|
|
82
|
+
import '../../core/Sidebar/SidebarToggle.js';
|
|
83
|
+
import '../../core/Sidebar/SidebarMobileTrigger.js';
|
|
84
|
+
import '../../core/Sidebar/SidebarUserProfile.js';
|
|
85
|
+
import '../../core/Sidebar/SidebarLogo.js';
|
|
86
|
+
import '../../core/Sidebar/SidebarContext.js';
|
|
87
|
+
import '../../core/OfflineIndicator/OfflineIndicator.js';
|
|
88
|
+
import '../../core/ContextMenu/ContextMenu.js';
|
|
89
|
+
import '../../core/ContextMenu/ContextMenu.theme.js';
|
|
90
|
+
import '../../core/Pill/Pill.js';
|
|
91
|
+
import '../../core/Icon/Icon.js';
|
|
92
|
+
import { FormLabel } from '../FormLabel/FormLabel.js';
|
|
7
93
|
|
|
8
94
|
/**
|
|
9
95
|
* FileUpload component - Drag-and-drop file uploader with previews
|
|
@@ -25,11 +111,14 @@ import { Button } from '../../core/Button/Button.js';
|
|
|
25
111
|
* onUpload={(files) => console.log(files)}
|
|
26
112
|
* />
|
|
27
113
|
*/
|
|
28
|
-
const FileUpload = React__default.forwardRef(({ accept, multiple = false, maxSize, maxFiles, disabled = false, onUpload, onRemove, onError, showPreview = true, buttonText = "Choose files", helperText = "or drag and drop", description, className, theme: customTheme, ...props }, ref) => {
|
|
114
|
+
const FileUpload = React__default.forwardRef(({ accept, multiple = false, maxSize, maxFiles, state = "default", disabled = false, label, errorMessage, onUpload, onRemove, onError, showPreview = true, buttonText = "Choose files", helperText = "or drag and drop", description, className, theme: customTheme, ...props }, ref) => {
|
|
29
115
|
// Merge custom theme with default theme
|
|
30
116
|
const theme = useMemo(() => customTheme
|
|
31
117
|
? { ...fileUploadTheme, ...customTheme }
|
|
32
118
|
: fileUploadTheme, [customTheme]);
|
|
119
|
+
const generatedId = useId();
|
|
120
|
+
const isDisabled = state === "disabled" || disabled;
|
|
121
|
+
const isInvalid = state === "invalid";
|
|
33
122
|
const inputRef = useRef(null);
|
|
34
123
|
const [isDragActive, setIsDragActive] = useState(false);
|
|
35
124
|
const [selectedFiles, setSelectedFiles] = useState([]);
|
|
@@ -144,9 +233,9 @@ const FileUpload = React__default.forwardRef(({ accept, multiple = false, maxSiz
|
|
|
144
233
|
const handleDragEnter = useCallback((e) => {
|
|
145
234
|
e.preventDefault();
|
|
146
235
|
e.stopPropagation();
|
|
147
|
-
if (!
|
|
236
|
+
if (!isDisabled)
|
|
148
237
|
setIsDragActive(true);
|
|
149
|
-
}, [
|
|
238
|
+
}, [isDisabled]);
|
|
150
239
|
const handleDragLeave = useCallback((e) => {
|
|
151
240
|
e.preventDefault();
|
|
152
241
|
e.stopPropagation();
|
|
@@ -160,18 +249,18 @@ const FileUpload = React__default.forwardRef(({ accept, multiple = false, maxSiz
|
|
|
160
249
|
e.preventDefault();
|
|
161
250
|
e.stopPropagation();
|
|
162
251
|
setIsDragActive(false);
|
|
163
|
-
if (
|
|
252
|
+
if (isDisabled)
|
|
164
253
|
return;
|
|
165
254
|
const files = e.dataTransfer.files;
|
|
166
255
|
handleFiles(files);
|
|
167
|
-
}, [
|
|
256
|
+
}, [isDisabled, handleFiles]);
|
|
168
257
|
// Handle click to select files
|
|
169
258
|
const handleClick = useCallback(() => {
|
|
170
259
|
var _a;
|
|
171
|
-
if (!
|
|
260
|
+
if (!isDisabled) {
|
|
172
261
|
(_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.click();
|
|
173
262
|
}
|
|
174
|
-
}, [
|
|
263
|
+
}, [isDisabled]);
|
|
175
264
|
// Handle input change
|
|
176
265
|
const handleInputChange = useCallback((e) => {
|
|
177
266
|
handleFiles(e.target.files);
|
|
@@ -193,12 +282,12 @@ const FileUpload = React__default.forwardRef(({ accept, multiple = false, maxSiz
|
|
|
193
282
|
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
194
283
|
return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + " " + sizes[i];
|
|
195
284
|
};
|
|
196
|
-
return (jsxs("div", { ref: ref, className: cn(theme.containerStyle, className), ...props, children: [jsx("input", { ref: inputRef, type: "file", accept: acceptString, multiple: multiple, onChange: handleInputChange, disabled:
|
|
285
|
+
return (jsxs("div", { ref: ref, className: cn(theme.containerStyle, className), ...props, children: [label && (typeof label === "string" ? (jsx(FormLabel, { htmlFor: `${generatedId}-input`, state: isDisabled ? "disabled" : isInvalid ? "invalid" : "default", children: label })) : (label)), jsx("input", { ref: inputRef, id: `${generatedId}-input`, type: "file", accept: acceptString, multiple: multiple, onChange: handleInputChange, disabled: isDisabled, className: "sr-only", "aria-label": "File upload" }), jsx("div", { className: cn(theme.dropzoneStyle, isDragActive && theme.dropzoneActiveStyle, isDisabled && theme.dropzoneDisabledStyle, isInvalid && "border-[var(--color-danger)]"), onDragEnter: handleDragEnter, onDragLeave: handleDragLeave, onDragOver: handleDragOver, onDrop: handleDrop, onClick: handleClick, role: "button", tabIndex: isDisabled ? -1 : 0, "aria-disabled": isDisabled, "aria-invalid": isInvalid || undefined, onKeyDown: (e) => {
|
|
197
286
|
if (e.key === "Enter" || e.key === " ") {
|
|
198
287
|
e.preventDefault();
|
|
199
288
|
handleClick();
|
|
200
289
|
}
|
|
201
|
-
}, children: jsxs("div", { className: theme.contentStyle, children: [jsx("div", { className: "size-11 border border-border bg-white rounded-full flex items-center justify-center mb-4", children: jsx(CloudArrowUpIcon, { className: `size-6 text-[var(--color-text-secondary)]` }) }), jsxs("p", { className: "text-base font-medium text-[var(--color-text-primary)]", children: [jsx("span", { className: "text-[var(--color-primary)] font-semibold", children: buttonText }), " ", helperText] }), description && (jsx("p", { className: "text-sm text-[var(--color-text-muted)] mt-1", children: description }))] }) }), showPreview && selectedFiles.length > 0 && (jsx("div", { className: theme.previewContainerStyle, children: selectedFiles.map((file, index) => {
|
|
290
|
+
}, children: jsxs("div", { className: theme.contentStyle, children: [jsx("div", { className: "size-11 border border-border bg-white rounded-full flex items-center justify-center mb-4", children: jsx(CloudArrowUpIcon, { className: `size-6 text-[var(--color-text-secondary)]` }) }), jsxs("p", { className: "text-base font-medium text-[var(--color-text-primary)]", children: [jsx("span", { className: "text-[var(--color-primary)] font-semibold", children: buttonText }), " ", helperText] }), description && (jsx("p", { className: "text-sm text-[var(--color-text-muted)] mt-1", children: description }))] }) }), errorMessage && (jsx("div", { className: "text-sm mt-1 text-[var(--color-danger)]", children: errorMessage })), showPreview && selectedFiles.length > 0 && (jsx("div", { className: theme.previewContainerStyle, children: selectedFiles.map((file, index) => {
|
|
202
291
|
const previewUrl = previewUrls.get(file);
|
|
203
292
|
const isImage = file.type.startsWith("image/");
|
|
204
293
|
return (jsxs("div", { className: theme.previewItemStyle, children: [isImage && previewUrl ? (jsx("img", { src: previewUrl, alt: file.name, className: theme.previewImageStyle })) : (jsxs("div", { className: theme.previewFileStyle, children: [jsx(DocumentIcon, { className: "w-8 h-8 mx-auto mb-2 text-[var(--color-text-muted)]" }), jsx("span", { className: "text-sm", children: file.name }), jsx("span", { className: "text-sm text-[var(--color-text-placeholder)] mt-1", children: formatFileSize(file.size) })] })), jsx(Button, { variant: "filled", color: "danger", size: "sm", iconOnly: true, onClick: (e) => {
|