pixelize-design-library 2.4.2-beta.34 → 2.4.2-beta.35

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.
Files changed (84) hide show
  1. package/dist/Components/AppSwitcher/AppSwitcher.js +10 -4
  2. package/dist/Components/ButtonGroupIcon/ButtonGroupIcon.js +1 -3
  3. package/dist/Components/Common/fieldStyles.js +1 -1
  4. package/dist/Components/CopyButton/CopyButton.js +7 -6
  5. package/dist/Components/DatePicker/CalendarPanel.js +8 -5
  6. package/dist/Components/DatePicker/RangeDatePicker.js +27 -3
  7. package/dist/Components/DatePicker/SingleDatePicker.js +48 -14
  8. package/dist/Components/DatePicker/TimeOnlyPicker.js +7 -1
  9. package/dist/Components/DatePicker/TimePicker.js +3 -3
  10. package/dist/Components/DatePicker/TimePickerInput.js +1 -1
  11. package/dist/Components/Dropdown/DropDown.js +19 -6
  12. package/dist/Components/EmptyState/EmptyState.js +1 -1
  13. package/dist/Components/FileUpload/FileUpload.js +2 -2
  14. package/dist/Components/FilterSidebar/FilterSidebar.js +5 -5
  15. package/dist/Components/FilterSidebar/filterHoverStyle.d.ts +2 -1
  16. package/dist/Components/FilterSidebar/filterHoverStyle.js +3 -4
  17. package/dist/Components/Input/TextInput.styles.js +2 -2
  18. package/dist/Components/InputTextArea/InputTextArea.style.js +2 -2
  19. package/dist/Components/MoreItems/MoreItems.js +3 -1
  20. package/dist/Components/MultiSelect/MultiSelect.js +2 -2
  21. package/dist/Components/NavigationBar/NavigationBar.js +6 -7
  22. package/dist/Components/NumberInput/NumberInput.styles.js +2 -2
  23. package/dist/Components/OrgSwitcher/OrgSwitcher.js +14 -10
  24. package/dist/Components/PdfViewer/PdfViewer.js +4 -4
  25. package/dist/Components/RolesPermission/RolesPermission.js +4 -3
  26. package/dist/Components/SearchSelect/SearchSelect.js +46 -41
  27. package/dist/Components/Select/Select.styles.js +1 -1
  28. package/dist/Components/SelectSearch/SelectSearch.js +35 -17
  29. package/dist/Components/SelectV2/SelectV2.js +3 -0
  30. package/dist/Components/Table/TableProps.d.ts +2 -0
  31. package/dist/Components/Table/components/TableBody.js +7 -10
  32. package/dist/Components/Table/filters/CompactSelect.js +29 -8
  33. package/dist/Components/Table/settings/ManageColumns.js +5 -3
  34. package/dist/Components/Table/settings/TableSettings.js +4 -2
  35. package/dist/Components/Trail/EditableCell.js +1 -1
  36. package/dist/Components/UserDetails/DeactivateConfirmModal.js +1 -1
  37. package/dist/Components/UserDetails/UserDetails.js +5 -3
  38. package/dist/Hooks/exclusiveOverlay.d.ts +4 -0
  39. package/dist/Hooks/exclusiveOverlay.js +22 -0
  40. package/dist/Hooks/useExclusiveOverlay.d.ts +5 -0
  41. package/dist/Hooks/useExclusiveOverlay.js +24 -0
  42. package/dist/Theme/chakra/Menu.styles.js +4 -2
  43. package/dist/Utils/table.d.ts +3 -0
  44. package/dist/Utils/table.js +44 -1
  45. package/dist/esm/Components/AppSwitcher/AppSwitcher.js +11 -5
  46. package/dist/esm/Components/ButtonGroupIcon/ButtonGroupIcon.js +1 -3
  47. package/dist/esm/Components/Common/fieldStyles.js +1 -1
  48. package/dist/esm/Components/CopyButton/CopyButton.js +7 -6
  49. package/dist/esm/Components/DatePicker/CalendarPanel.js +9 -6
  50. package/dist/esm/Components/DatePicker/RangeDatePicker.js +28 -4
  51. package/dist/esm/Components/DatePicker/SingleDatePicker.js +49 -15
  52. package/dist/esm/Components/DatePicker/TimeOnlyPicker.js +7 -1
  53. package/dist/esm/Components/DatePicker/TimePicker.js +3 -3
  54. package/dist/esm/Components/DatePicker/TimePickerInput.js +1 -1
  55. package/dist/esm/Components/Dropdown/DropDown.js +20 -7
  56. package/dist/esm/Components/EmptyState/EmptyState.js +1 -1
  57. package/dist/esm/Components/FileUpload/FileUpload.js +2 -2
  58. package/dist/esm/Components/FilterSidebar/FilterSidebar.js +5 -5
  59. package/dist/esm/Components/FilterSidebar/filterHoverStyle.js +3 -4
  60. package/dist/esm/Components/Input/TextInput.styles.js +2 -2
  61. package/dist/esm/Components/InputTextArea/InputTextArea.style.js +2 -2
  62. package/dist/esm/Components/MoreItems/MoreItems.js +3 -1
  63. package/dist/esm/Components/MultiSelect/MultiSelect.js +2 -2
  64. package/dist/esm/Components/NavigationBar/NavigationBar.js +6 -7
  65. package/dist/esm/Components/NumberInput/NumberInput.styles.js +2 -2
  66. package/dist/esm/Components/OrgSwitcher/OrgSwitcher.js +14 -10
  67. package/dist/esm/Components/PdfViewer/PdfViewer.js +4 -4
  68. package/dist/esm/Components/RolesPermission/RolesPermission.js +4 -3
  69. package/dist/esm/Components/SearchSelect/SearchSelect.js +46 -41
  70. package/dist/esm/Components/Select/Select.styles.js +1 -1
  71. package/dist/esm/Components/SelectSearch/SelectSearch.js +35 -17
  72. package/dist/esm/Components/SelectV2/SelectV2.js +3 -0
  73. package/dist/esm/Components/Table/components/TableBody.js +7 -10
  74. package/dist/esm/Components/Table/filters/CompactSelect.js +29 -8
  75. package/dist/esm/Components/Table/settings/ManageColumns.js +5 -3
  76. package/dist/esm/Components/Table/settings/TableSettings.js +5 -3
  77. package/dist/esm/Components/Trail/EditableCell.js +1 -1
  78. package/dist/esm/Components/UserDetails/DeactivateConfirmModal.js +1 -1
  79. package/dist/esm/Components/UserDetails/UserDetails.js +5 -3
  80. package/dist/esm/Hooks/exclusiveOverlay.js +16 -0
  81. package/dist/esm/Hooks/useExclusiveOverlay.js +20 -0
  82. package/dist/esm/Theme/chakra/Menu.styles.js +4 -2
  83. package/dist/esm/Utils/table.js +45 -3
  84. package/package.json +1 -1
@@ -36,6 +36,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.TableCellCalculation = exports.pageSizeCalculation = exports.searchAndSortData = exports.calculateLeftOffset = void 0;
37
37
  exports.compareValues = compareValues;
38
38
  exports.SortMultiColumnData = SortMultiColumnData;
39
+ exports.buildSemanticStatusPalette = buildSemanticStatusPalette;
39
40
  exports.buildTablePalette = buildTablePalette;
40
41
  exports.pickTableColor = pickTableColor;
41
42
  exports.groupRows = groupRows;
@@ -45,6 +46,8 @@ exports.useDebounce = useDebounce;
45
46
  exports.globalSearchFilter = globalSearchFilter;
46
47
  const react_1 = __importStar(require("react"));
47
48
  const accentText_1 = require("../Theme/tokens/builders/accentText");
49
+ const color_1 = require("../Theme/tokens/builders/color");
50
+ const outlineRung_1 = require("../Theme/tokens/builders/outlineRung");
48
51
  const filledSurface_1 = require("../Theme/tokens/builders/filledSurface");
49
52
  const statusTone_1 = require("../Theme/tokens/builders/statusTone");
50
53
  // Built once, not per comparison: sorting 10k rows runs this ~130k times, and constructing a
@@ -99,7 +102,45 @@ function SortMultiColumnData(data, sortConfig) {
99
102
  return 0;
100
103
  });
101
104
  }
105
+ /** Theme-aware status palette per value — soft fill + semantic ink from `resolveStatusTone`. */
106
+ function buildSemanticStatusPalette(toneByValue, theme) {
107
+ const c = theme.colors;
108
+ const mode = (0, accentText_1.isDarkCanvas)(c.backgroundColor.main) ? "dark" : "light";
109
+ const scales = {
110
+ info: c.semantic.info,
111
+ warning: c.semantic.warning,
112
+ success: c.semantic.success,
113
+ error: c.semantic.error,
114
+ neutral: c.gray,
115
+ };
116
+ return Object.fromEntries(Object.entries(toneByValue).map(([value, key]) => {
117
+ const scale = scales[key];
118
+ const tone = (0, statusTone_1.resolveStatusTone)(scale, mode);
119
+ const solidRung = (0, filledSurface_1.solidFillRung)(scale, c.white);
120
+ const solid = scale[solidRung];
121
+ return [
122
+ value,
123
+ {
124
+ solid,
125
+ soft: tone.bg,
126
+ text: tone.fg,
127
+ onSolid: (0, filledSurface_1.labelOnFill)(solid, c.white, c.black),
128
+ },
129
+ ];
130
+ }));
131
+ }
102
132
  const withAlpha = (hex, aa) => /^#([0-9a-fA-F]{6})$/.test(hex) ? `${hex}${aa}` : hex;
133
+ /** Consumer `statusColors` values are fill seeds — derive bleed + pill ink from them. */
134
+ const resolveStatusOverride = (fill, white, black, surface) => {
135
+ const soft = withAlpha(fill, "22");
136
+ const solid = (0, statusTone_1.resolveAvatarFill)(fill);
137
+ return {
138
+ solid,
139
+ soft,
140
+ text: (0, outlineRung_1.liftToContrast)(fill, (0, color_1.compositeOver)(soft, surface), accentText_1.AA_NORMAL_TEXT),
141
+ onSolid: (0, accentText_1.onFilled)(solid, white, black),
142
+ };
143
+ };
103
144
  /** Deterministic, theme-aware color palette for groups + status pills. */
104
145
  function buildTablePalette(theme) {
105
146
  const c = theme.colors;
@@ -137,10 +178,12 @@ const hashKey = (value) => {
137
178
  };
138
179
  /** Pick a deterministic palette color for a value, with optional consumer override hex. */
139
180
  function pickTableColor(value, palette, override, theme) {
181
+ var _a;
140
182
  const white = theme ? theme.colors.white : accentText_1.DEFAULT_WHITE;
141
183
  const black = theme ? theme.colors.black : accentText_1.DEFAULT_BLACK;
142
184
  if (override) {
143
- return { solid: override, soft: withAlpha(override, "22"), text: override, onSolid: (0, accentText_1.onFilled)(override, white, black) };
185
+ const surface = (_a = theme === null || theme === void 0 ? void 0 : theme.colors.background[50]) !== null && _a !== void 0 ? _a : accentText_1.DEFAULT_WHITE;
186
+ return resolveStatusOverride(override, white, black, surface);
144
187
  }
145
188
  if (!palette.length) {
146
189
  throw new Error("pickTableColor: empty palette — build it with buildTablePalette(theme).");
@@ -5,7 +5,9 @@ import { Grip, Lock, Search } from "lucide-react";
5
5
  import { useCustomTheme } from "../../Theme/useCustomTheme.js";
6
6
  import { chartMode } from "../../Theme/tokens/builders/chartColorsFromTheme.js";
7
7
  import { AA_NORMAL_TEXT } from "../../Theme/tokens/builders/accentText.js";
8
- import { liftToContrast, neutralStep, resolveHoverTint, ruleInk, AA_NON_TEXT, CHROME_RULE, MIN_PERCEPTIBLE_STEP, } from "../../Theme/tokens/builders/outlineRung.js";
8
+ import { liftToContrast, neutralStep, ruleInk, AA_NON_TEXT, CHROME_RULE, MIN_PERCEPTIBLE_STEP, } from "../../Theme/tokens/builders/outlineRung.js";
9
+ import { listRowBackgrounds } from "../../Theme/tokens/builders/rowStates.js";
10
+ import { useExclusiveOverlay } from "../../Hooks/useExclusiveOverlay.js";
9
11
  import AppMark, { assignAppHues } from "./AppMark.js";
10
12
  import { filterApps, formatShortcut, } from "./AppSwitcherProps.js";
11
13
  import { useAppSwitcherShortcut } from "./useAppSwitcherShortcut.js";
@@ -43,6 +45,7 @@ const SEARCH_OPENS_BY_COUNT = false;
43
45
  const AppSwitcher = ({ apps, currentAppId, onSelect, onOpenChange, isSearchable, searchThreshold = 8, shortcut = null, showShortcutHint = true, hideWhenAlone = true, emptyMessage = "No app matches that name.", footer, ariaLabel = "Switch app", panelWidth, maxHeight = "24rem", isLoading = false, }) => {
44
46
  const theme = useCustomTheme();
45
47
  const { isOpen, onOpen, onClose } = useDisclosure();
48
+ const requestExclusiveOpen = useExclusiveOverlay(isOpen, onClose);
46
49
  const [query, setQuery] = useState("");
47
50
  const [activeIndex, setActiveIndex] = useState(0);
48
51
  const searchRef = useRef(null);
@@ -59,12 +62,15 @@ const AppSwitcher = ({ apps, currentAppId, onSelect, onOpenChange, isSearchable,
59
62
  const hues = useMemo(() => assignAppHues(apps, chartMode(theme.colors)), [apps, theme.colors]);
60
63
  const visible = useMemo(() => filterApps(apps, query), [apps, query]);
61
64
  const setOpen = useCallback((next) => {
62
- if (next)
65
+ if (next) {
66
+ requestExclusiveOpen();
63
67
  onOpen();
64
- else
68
+ }
69
+ else {
65
70
  onClose();
71
+ }
66
72
  onOpenChange === null || onOpenChange === void 0 ? void 0 : onOpenChange(next);
67
- }, [onOpen, onClose, onOpenChange]);
73
+ }, [onOpen, onClose, onOpenChange, requestExclusiveOpen]);
68
74
  const cancelHoverTimer = useCallback(() => {
69
75
  if (hoverTimer.current)
70
76
  clearTimeout(hoverTimer.current);
@@ -208,7 +214,7 @@ const AppSwitcher = ({ apps, currentAppId, onSelect, onOpenChange, isSearchable,
208
214
  const nothingToSwitchTo = apps.length === 0 || (hideWhenAlone && apps.every((a) => a.id === currentAppId));
209
215
  if (nothingToSwitchTo)
210
216
  return null;
211
- const rowHoverBg = resolveHoverTint(theme.colors.gray[50], theme.colors.backgroundColor.light);
217
+ const rowHoverBg = listRowBackgrounds(theme.colors).active;
212
218
  const footerRule = ruleInk(theme.colors.boxborder[200], [theme.colors.backgroundColor.subtle], CHROME_RULE);
213
219
  // Neutral, not `backgroundColor.tertiary`: that rung is brand-tinted in the hand-authored light
214
220
  // palettes, so lifting it painted the row a saturated block. The rail identifies the row, so the
@@ -79,7 +79,7 @@ rightIconDropdown, menulistStyle, menuItemStyle, isLoading, divider = true, left
79
79
  borderRadius: "lg",
80
80
  fontSize: "13px",
81
81
  fontWeight: 500,
82
- color: theme.colors.gray[700],
82
+ color: theme.colors.text[700],
83
83
  transition: "background 0.12s",
84
84
  "& svg": { width: "16px", height: "16px" },
85
85
  ...(divider
@@ -90,8 +90,6 @@ rightIconDropdown, menulistStyle, menuItemStyle, isLoading, divider = true, left
90
90
  borderColor: theme.colors.boxborder[200],
91
91
  }
92
92
  : {}),
93
- _hover: { bg: theme.colors.primary[50], color: theme.colors.primary[700] },
94
- _focus: { bg: theme.colors.primary[50], color: theme.colors.primary[700] },
95
93
  "&:last-child": { borderBottom: "none" },
96
94
  }, children: [option === null || option === void 0 ? void 0 : option.image, option.label] }, index))) }) })] })) : (rightIcon && (_jsx(IconButton, { "aria-label": "Right icon button", icon: rightIcon, isLoading: RightIconLoading, onClick: onRightIconClick, onBlur: onBlurRightIcon, style: buttonGroupRightIconStyle, sx: gradientItemSx })))] }));
97
95
  }
@@ -17,7 +17,7 @@ export const getFieldStateStyles = (theme, { error = false, focused = false } =
17
17
  const ringPrimary = theme.colors.boxShadow.primary;
18
18
  const ringError = theme.colors.boxShadow.error;
19
19
  return {
20
- bg: theme.colors.gray[50],
20
+ bg: theme.colors.background[50],
21
21
  borderRadius: FIELD_RADIUS,
22
22
  border: `${FIELD_BORDER_WIDTH} solid`,
23
23
  borderColor: error ? errorColor : focused ? primary : idleBorder,
@@ -4,6 +4,7 @@ import { Box, Flex, IconButton, Text } from "@chakra-ui/react";
4
4
  import { Check, Copy } from "lucide-react";
5
5
  import { useCustomTheme } from "../../Theme/useCustomTheme.js";
6
6
  import { accentTextOnCanvas, AA_NORMAL_TEXT } from "../../Theme/tokens/builders/accentText.js";
7
+ import { listRowBackgrounds } from "../../Theme/tokens/builders/rowStates.js";
7
8
  import { liftToContrast } from "../../Theme/tokens/builders/outlineRung.js";
8
9
  import ToolTip from "../ToolTip/ToolTip.js";
9
10
  const CopyButton = ({ value, variant = "icon", label = "Copy", copiedLabel = "Copied!", tooltip = "Copy", feedbackDuration = 1500, iconSize = 16, size = "sm", onCopy, }) => {
@@ -29,17 +30,17 @@ const CopyButton = ({ value, variant = "icon", label = "Copy", copiedLabel = "Co
29
30
  // nearest rung that clears AA against the canvas this button actually sits on.
30
31
  const accent = accentTextOnCanvas(theme.colors.primary, theme.colors.backgroundColor.main);
31
32
  const success = theme.colors.semanticText.success;
33
+ const rowBackgrounds = listRowBackgrounds(theme.colors);
32
34
  if (variant === "menu-item") {
33
- // Ink also sits on the row's own hover fill (gray[100]), not just the canvas — resolve against it.
34
- const hoverAccent = accentTextOnCanvas(theme.colors.primary, theme.colors.gray[100]);
35
- const hoverSuccess = liftToContrast(theme.colors.semantic.success[500], theme.colors.gray[100], AA_NORMAL_TEXT);
36
- return (_jsxs(Flex, { align: "center", gap: "0.5rem", px: "12px", py: "8px", width: "100%", cursor: "pointer", borderRadius: "md", transition: "background 0.15s ease", _hover: { bg: theme.colors.gray[100] }, onClick: handleCopy, children: [copied ? (_jsx(Check, { size: iconSize, color: hoverSuccess })) : (_jsx(Copy, { size: iconSize, color: hoverAccent })), _jsx(Box, { fontSize: "14px", fontWeight: 500, color: copied ? hoverSuccess : hoverAccent, children: copied ? copiedLabel : label })] }));
35
+ const hoverAccent = accentTextOnCanvas(theme.colors.primary, rowBackgrounds.active);
36
+ const hoverSuccess = liftToContrast(theme.colors.semantic.success[500], rowBackgrounds.active, AA_NORMAL_TEXT);
37
+ return (_jsxs(Flex, { align: "center", gap: "0.5rem", px: "12px", py: "8px", width: "100%", cursor: "pointer", borderRadius: "md", transition: "background 0.15s ease", _hover: { bg: rowBackgrounds.active }, onClick: handleCopy, children: [copied ? (_jsx(Check, { size: iconSize, color: hoverSuccess })) : (_jsx(Copy, { size: iconSize, color: hoverAccent })), _jsx(Box, { fontSize: "14px", fontWeight: 500, color: copied ? hoverSuccess : hoverAccent, children: copied ? copiedLabel : label })] }));
37
38
  }
38
39
  if (variant === "button") {
39
40
  return (_jsxs(Flex, { as: "button", align: "center", gap: "6px", px: "12px", py: "6px", borderRadius: "lg", border: "1px solid",
40
41
  // [800], not [500]: a real control boundary needs >=3:1 on every surface up to `muted`.
41
- borderColor: copied ? success : theme.colors.boxborder[500], bg: "transparent", color: copied ? success : theme.colors.gray[700], fontSize: "14px", fontWeight: 500, transition: "all 0.15s ease", _hover: { borderColor: accent, color: accent }, onClick: handleCopy, children: [copied ? _jsx(Check, { size: iconSize }) : _jsx(Copy, { size: iconSize }), _jsx(Text, { children: copied ? copiedLabel : label })] }));
42
+ borderColor: copied ? success : theme.colors.boxborder[500], bg: "transparent", color: copied ? success : theme.colors.text[700], fontSize: "14px", fontWeight: 500, transition: "all 0.15s ease", _hover: { borderColor: accent, color: accent }, onClick: handleCopy, children: [copied ? _jsx(Check, { size: iconSize }) : _jsx(Copy, { size: iconSize }), _jsx(Text, { children: copied ? copiedLabel : label })] }));
42
43
  }
43
- return (_jsx(ToolTip, { label: copied ? copiedLabel : tooltip, placement: "top", children: _jsx(IconButton, { "aria-label": tooltip, size: size, variant: "ghost", onClick: handleCopy, color: copied ? success : theme.colors.gray[500], _hover: { color: accent, bg: theme.colors.primary[50] }, icon: copied ? _jsx(Check, { size: iconSize }) : _jsx(Copy, { size: iconSize }) }) }));
44
+ return (_jsx(ToolTip, { label: copied ? copiedLabel : tooltip, placement: "top", children: _jsx(IconButton, { "aria-label": tooltip, size: size, variant: "ghost", onClick: handleCopy, color: copied ? success : theme.colors.textMuted, _hover: { color: accent, bg: rowBackgrounds.active }, icon: copied ? _jsx(Check, { size: iconSize }) : _jsx(Copy, { size: iconSize }) }) }));
44
45
  };
45
46
  export default CopyButton;
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Box, Grid } from "@chakra-ui/react";
2
+ import { Box } from "@chakra-ui/react";
3
3
  import { format, startOfMonth, endOfMonth, startOfWeek, endOfWeek, addDays } from "date-fns";
4
4
  import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react";
5
5
  import Button from "../Button/Button.js";
@@ -118,7 +118,10 @@ export const CalendarPanel = ({ currentMonth, setCurrentMonth, today, isRange, t
118
118
  const customContent = renderDayContent ? renderDayContent(thisDay, states) : null;
119
119
  const markerColor = customContent == null && (stateVisual === null || stateVisual === void 0 ? void 0 : stateVisual.markerColor) ? stateVisual.markerColor : null;
120
120
  const hasDayOverlay = customContent != null || markerColor != null;
121
- days.push(_jsx(Box, { h: "2rem", display: "flex", alignItems: "center", justifyContent: "center", style: cellBg ? { background: cellBg } : undefined, children: _jsxs(Box, { as: "button", type: "button", disabled: disabled, className: dayStates ? dayStateClassName(states) : undefined, "aria-label": dayStates ? dayAccessibleName(format(thisDay, "d MMMM yyyy"), states) : undefined, onClick: () => !disabled && onDaySelect(new Date(thisDay)), onMouseEnter: () => !disabled && (onDayHover === null || onDayHover === void 0 ? void 0 : onDayHover(new Date(thisDay))), position: hasDayOverlay ? "relative" : undefined, w: "1.75rem", h: "1.75rem", borderRadius: "full", display: "flex", alignItems: "center", justifyContent: "center", fontSize: "0.8125rem", fontWeight: circleSolid ? 600 : 500, lineHeight: "1", cursor: disabled ? "not-allowed" : "pointer", bg: circleSolid ? solidFill : "transparent", color: dayColor, textDecoration: (stateVisual === null || stateVisual === void 0 ? void 0 : stateVisual.strikethrough) ? "line-through" : undefined, border: dayBorder, transition: "background 0.12s, color 0.12s", _hover: disabled
121
+ const dayLabel = dayStates
122
+ ? dayAccessibleName(format(thisDay, "d MMMM yyyy"), states)
123
+ : format(thisDay, "d MMMM yyyy");
124
+ days.push(_jsx(Box, { role: "gridcell", h: "2rem", display: "flex", alignItems: "center", justifyContent: "center", style: cellBg ? { background: cellBg } : undefined, children: _jsxs(Box, { as: "button", type: "button", disabled: disabled, className: dayStates ? dayStateClassName(states) : undefined, "aria-label": dayLabel, "aria-selected": isSelected || isStart || isEnd || undefined, "aria-current": isToday ? "date" : undefined, onClick: () => !disabled && onDaySelect(new Date(thisDay)), onMouseEnter: () => !disabled && (onDayHover === null || onDayHover === void 0 ? void 0 : onDayHover(new Date(thisDay))), position: hasDayOverlay ? "relative" : undefined, w: "1.75rem", h: "1.75rem", borderRadius: "full", display: "flex", alignItems: "center", justifyContent: "center", fontSize: "0.8125rem", fontWeight: circleSolid ? 600 : 500, lineHeight: "1", cursor: disabled ? "not-allowed" : "pointer", bg: circleSolid ? solidFill : "transparent", color: dayColor, textDecoration: (stateVisual === null || stateVisual === void 0 ? void 0 : stateVisual.strikethrough) ? "line-through" : undefined, border: dayBorder, transition: "background 0.12s, color 0.12s", _hover: disabled
122
125
  ? {}
123
126
  : circleSolid
124
127
  ? { bg: solidHoverFill, color: circleHoverColor }
@@ -127,14 +130,14 @@ export const CalendarPanel = ({ currentMonth, setCurrentMonth, today, isRange, t
127
130
  pointerEvents: "none", children: customContent !== null && customContent !== void 0 ? customContent : (_jsx(Box, { as: "span", className: `${DAY_CLASS}__marker`, w: "0.25rem", h: "0.25rem", borderRadius: "full", bg: markerColor })) }))] }) }, thisDay.toString()));
128
131
  day = addDays(day, 1);
129
132
  }
130
- rows.push(_jsx(Grid, { templateColumns: "repeat(7, 1fr)", gap: 0, children: days }, day.toString()));
133
+ rows.push(_jsx(Box, { role: "row", display: "grid", gridTemplateColumns: "repeat(7, 1fr)", gap: 0, children: days }, day.toString()));
131
134
  }
132
135
  return rows;
133
136
  };
134
- return (_jsxs(Box, { children: [_jsxs(Box, { display: "flex", alignItems: "center", justifyContent: "space-between", mb: 2, gap: 1, children: [_jsx(Button, { variant: "ghost", size: "xs", sx: { flexShrink: 0 }, onClick: () => setCurrentMonth((prev) => new Date(prev.getFullYear(), prev.getMonth() - 1, 1)), "aria-label": "Previous month", children: _jsx(ChevronLeftIcon, { size: 16 }) }), _jsxs(Box, { display: "flex", alignItems: "center", gap: 1.5, minW: 0, children: [_jsx(Box, { as: "select", value: currentMonth.getMonth(), onChange: (e) => {
137
+ return (_jsxs(Box, { children: [_jsxs(Box, { display: "flex", alignItems: "center", justifyContent: "space-between", mb: 2, gap: 1, children: [_jsx(Button, { variant: "ghost", size: "xs", sx: { flexShrink: 0 }, onClick: () => setCurrentMonth((prev) => new Date(prev.getFullYear(), prev.getMonth() - 1, 1)), "aria-label": "Previous month", children: _jsx(ChevronLeftIcon, { size: 16 }) }), _jsxs(Box, { display: "flex", alignItems: "center", gap: 1.5, minW: 0, children: [_jsx(Box, { as: "select", "aria-label": "Month", value: currentMonth.getMonth(), onChange: (e) => {
135
138
  const newMonth = parseInt(e.target.value, 10);
136
139
  setCurrentMonth((prev) => new Date(prev.getFullYear(), newMonth, 1));
137
- }, ...selectStyles, children: Array.from({ length: 12 }).map((_, i) => (_jsx("option", { value: i, children: format(new Date(2000, i, 1), "MMMM") }, i))) }), _jsx(Box, { as: "select", value: currentMonth.getFullYear(), onChange: (e) => {
140
+ }, ...selectStyles, children: Array.from({ length: 12 }).map((_, i) => (_jsx("option", { value: i, children: format(new Date(2000, i, 1), "MMMM") }, i))) }), _jsx(Box, { as: "select", "aria-label": "Year", value: currentMonth.getFullYear(), onChange: (e) => {
138
141
  const newYear = parseInt(e.target.value, 10);
139
142
  setCurrentMonth((prev) => new Date(newYear, prev.getMonth(), 1));
140
143
  }, ...selectStyles, children: Array.from({ length: 100 }).map((_, i) => {
@@ -145,6 +148,6 @@ export const CalendarPanel = ({ currentMonth, setCurrentMonth, today, isRange, t
145
148
  if (!disableToday) {
146
149
  onDaySelect(new Date(today));
147
150
  }
148
- }, "aria-label": "Today", sx: { fontSize: "0.7rem", px: 2, flexShrink: 0 }, isDisabled: disableToday, children: "Today" })), _jsx(Button, { variant: "ghost", size: "xs", sx: { flexShrink: 0 }, onClick: () => setCurrentMonth((prev) => new Date(prev.getFullYear(), prev.getMonth() + 1, 1)), "aria-label": "Next month", children: _jsx(ChevronRightIcon, { size: 16 }) })] })] }), renderWeekdays !== null && renderWeekdays !== void 0 ? renderWeekdays : (_jsx(Grid, { templateColumns: "repeat(7, 1fr)", gap: 0, mb: 1, children: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"].map((day) => (_jsx(Box, { textAlign: "center", fontSize: "0.7rem", fontWeight: 600, textTransform: "uppercase", letterSpacing: "0.02em", color: theme.colors.textMuted, py: 1, children: day }, day))) })), _jsx(Box, { onMouseLeave: () => onDayHover === null || onDayHover === void 0 ? void 0 : onDayHover(null), children: renderDays() })] }));
151
+ }, "aria-label": "Today", sx: { fontSize: "0.7rem", px: 2, flexShrink: 0 }, isDisabled: disableToday, children: "Today" })), _jsx(Button, { variant: "ghost", size: "xs", sx: { flexShrink: 0 }, onClick: () => setCurrentMonth((prev) => new Date(prev.getFullYear(), prev.getMonth() + 1, 1)), "aria-label": "Next month", children: _jsx(ChevronRightIcon, { size: 16 }) })] })] }), _jsxs(Box, { role: "grid", "aria-label": "Calendar", children: [renderWeekdays !== null && renderWeekdays !== void 0 ? renderWeekdays : (_jsx(Box, { role: "row", display: "grid", gridTemplateColumns: "repeat(7, 1fr)", gap: 0, mb: 1, children: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"].map((day) => (_jsx(Box, { role: "columnheader", textAlign: "center", fontSize: "0.7rem", fontWeight: 600, textTransform: "uppercase", letterSpacing: "0.02em", color: theme.colors.textMuted, py: 1, children: day }, day))) })), _jsx(Box, { onMouseLeave: () => onDayHover === null || onDayHover === void 0 ? void 0 : onDayHover(null), children: renderDays() })] })] }));
149
152
  };
150
153
  export default CalendarPanel;
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useEffect, useMemo, useRef, useState } from "react";
3
- import { Box, FormControl, Input, InputGroup, InputRightElement, Popover, PopoverBody, PopoverContent, PopoverTrigger, useDisclosure, } from "@chakra-ui/react";
3
+ import { Box, FormControl, IconButton, Input, InputGroup, InputRightElement, Popover, PopoverAnchor, PopoverBody, PopoverContent, useDisclosure, } from "@chakra-ui/react";
4
4
  import { format, isAfter, isBefore, isSameDay, subDays, startOfMonth, endOfMonth } from "date-fns";
5
5
  import { CalendarIcon } from "lucide-react";
6
6
  import Button from "../Button/Button.js";
@@ -12,6 +12,7 @@ import { useCustomTheme } from "../../Theme/useCustomTheme.js";
12
12
  import { getTextInputIconSizePx, getTextInputIconWrapperSx, } from "../Input/textInputIconSize.js";
13
13
  import TimePicker from "./TimePicker.js";
14
14
  import { TimePickerInput } from "./TimePickerInput.js";
15
+ import { useExclusiveOverlay } from "../../Hooks/useExclusiveOverlay.js";
15
16
  // helper to set time on date while preserving date part
16
17
  const setTimeFrom = (base, time) => {
17
18
  const b = base ? new Date(base) : new Date();
@@ -25,6 +26,9 @@ export const RangeDatePicker = (props) => {
25
26
  const calendarIconPx = getTextInputIconSizePx(sizeKey);
26
27
  const calendarIconWrapperSx = getTextInputIconWrapperSx(sizeKey);
27
28
  const { isOpen, onOpen, onClose } = useDisclosure();
29
+ const requestExclusiveOpen = useExclusiveOverlay(isOpen, onClose);
30
+ const errorFieldId = id ? `${id}-error` : undefined;
31
+ const calendarLabel = label !== null && label !== void 0 ? label : "Choose date range";
28
32
  const inputRef = useRef(null);
29
33
  const triggerRef = useRef(null);
30
34
  const popoverRef = useRef(null);
@@ -91,8 +95,20 @@ export const RangeDatePicker = (props) => {
91
95
  onClose();
92
96
  };
93
97
  const handleOpen = () => {
98
+ requestExclusiveOpen();
94
99
  onOpen();
95
100
  };
101
+ const handleInputKeyDown = (e) => {
102
+ if (e.key === "Escape" && isOpen) {
103
+ e.preventDefault();
104
+ onClose();
105
+ return;
106
+ }
107
+ if (!isOpen && (e.key === "ArrowDown" || e.key === "Enter")) {
108
+ e.preventDefault();
109
+ handleOpen();
110
+ }
111
+ };
96
112
  const handleDaySelect = (day) => {
97
113
  const updated = new Date(day);
98
114
  if (selectingStart) {
@@ -159,7 +175,7 @@ export const RangeDatePicker = (props) => {
159
175
  return `${format(tempRangeStart, dateFormat)} -`;
160
176
  return "";
161
177
  }, [inputValue, tempRangeStart, tempRangeEnd, dateFormat]);
162
- return (_jsxs(FormControl, { id: id, isInvalid: error, isRequired: isRequired, isDisabled: disabled, width: width, children: [label && (_jsx(TextLabel, { label: label, isRequired: isRequired, isInformation: isInformation, informationMessage: informationMessage })), _jsxs(Popover, { isLazy: true, isOpen: isOpen, onClose: finalize, placement: "bottom-start", closeOnBlur: false, returnFocusOnClose: false, children: [_jsx(PopoverTrigger, { children: _jsx(Box, { ref: triggerRef, as: "div", children: _jsxs(InputGroup, { size: sizeKey, children: [_jsx(Input, { ref: inputRef, id: id, name: name, size: sizeKey, placeholder: placeholder, value: displayValue, readOnly: true, isDisabled: disabled, autoComplete: autoComplete, onFocus: () => {
178
+ return (_jsxs(FormControl, { id: id, isInvalid: error, isRequired: isRequired, isDisabled: disabled, width: width, children: [label && (_jsx(TextLabel, { label: label, id: id, isRequired: isRequired, isInformation: isInformation, informationMessage: informationMessage })), _jsxs(Popover, { isLazy: true, isOpen: isOpen, onClose: finalize, placement: "bottom-start", closeOnBlur: false, returnFocusOnClose: true, children: [_jsx(PopoverAnchor, { children: _jsx(Box, { ref: triggerRef, as: "div", children: _jsxs(InputGroup, { size: sizeKey, children: [_jsx(Input, { ref: inputRef, id: id, name: name, size: sizeKey, placeholder: placeholder, value: displayValue, readOnly: true, isDisabled: disabled, isInvalid: error, isRequired: isRequired, "aria-describedby": error && errorMessage ? errorFieldId : undefined, autoComplete: autoComplete, onKeyDown: handleInputKeyDown, onFocus: () => {
163
179
  if (!isOpen)
164
180
  handleOpen();
165
181
  }, onClick: () => {
@@ -172,7 +188,15 @@ export const RangeDatePicker = (props) => {
172
188
  : `0 0 0 1px ${theme.colors.primary[500]}`,
173
189
  }, _hover: {
174
190
  borderColor: error ? theme.colors.semanticText.error : undefined,
175
- }, cursor: "pointer" }), _jsx(InputRightElement, { height: "100%", display: "flex", alignItems: "center", pointerEvents: "auto", style: { cursor: "pointer" }, onClick: () => (isOpen ? onClose() : handleOpen()), children: _jsx(Box, { as: "span", sx: calendarIconWrapperSx, children: _jsx(CalendarIcon, { size: calendarIconPx, color: theme.colors.textMuted }) }) })] }) }) }), _jsx(PopoverContent, { w: "18rem", maxW: "18rem", p: 3, ref: popoverRef, boxShadow: "lg", borderRadius: "xl", bg: theme.colors.background[50], children: _jsxs(PopoverBody, { p: 0, children: [_jsx(Box, { display: "flex", flexWrap: "wrap", gap: 2, mb: 3, children: presets.map((p) => (_jsx(Button, { size: "xs", variant: "outline", colorScheme: "gray", sx: { flex: "1 1 auto" }, onClick: () => {
191
+ }, cursor: "pointer" }), _jsx(InputRightElement, { height: "100%", display: "flex", alignItems: "center", pointerEvents: "auto", children: _jsx(IconButton, { "aria-label": "Open calendar", variant: "ghost", size: "sm", minW: "auto", h: "auto", isDisabled: disabled, onClick: (e) => {
192
+ e.stopPropagation();
193
+ if (!isOpen) {
194
+ handleOpen();
195
+ }
196
+ else {
197
+ onClose();
198
+ }
199
+ }, icon: _jsx(Box, { as: "span", sx: calendarIconWrapperSx, children: _jsx(CalendarIcon, { size: calendarIconPx, color: theme.colors.textMuted }) }) }) })] }) }) }), _jsx(PopoverContent, { w: "18rem", maxW: "18rem", p: 3, ref: popoverRef, boxShadow: "lg", borderRadius: "xl", bg: theme.colors.background[50], role: "dialog", "aria-label": calendarLabel, children: _jsxs(PopoverBody, { p: 0, children: [_jsx(Box, { display: "flex", flexWrap: "wrap", gap: 2, mb: 3, children: presets.map((p) => (_jsx(Button, { size: "xs", variant: "outline", colorScheme: "gray", sx: { flex: "1 1 auto" }, onClick: () => {
176
200
  const [from, to] = p.getRange();
177
201
  applyPreset(from, to);
178
202
  }, children: p.label }, p.label))) }), _jsx(CalendarPanel, { currentMonth: currentMonth, setCurrentMonth: (updater) => setCurrentMonthState((prev) => updater(prev)), today: today, isRange: true, tempDate: null, tempRangeStart: tempRangeStart, tempRangeEnd: tempRangeEnd, hoveredDate: hoveredDate, onDayHover: setHoveredDate, isSameDay: isSameDay, isBefore: isBefore, isAfter: isAfter, minDate: minDate, maxDate: maxDate, disablePastDates: disablePastDates, disableFutureDates: disableFutureDates, disableToday: disableToday, onDaySelect: handleDaySelect, dayStates: dayStates, renderDayContent: renderDayContent }), formatHasTime && (_jsxs(Box, { mt: 3, display: "grid", gridTemplateColumns: "1fr", gap: 2, children: [_jsxs(Box, { children: [_jsx(Box, { fontSize: "xs", color: theme.colors.textMuted, mb: 1, children: "Start time" }), shouldUseSelectTimePicker ? (_jsx(TimePicker, { date: startTimeBaseDate, onChange: (updated) => {
@@ -201,6 +225,6 @@ export const RangeDatePicker = (props) => {
201
225
  return;
202
226
  }
203
227
  setTempRangeEnd(setTimeFrom(endTimeBaseDate, t));
204
- }, dateFormat: timePickerFormat, disabled: disabled || !tempRangeEnd, size: sizeKey }))] })] })), _jsxs(Box, { display: "flex", justifyContent: "space-between", alignItems: "center", mt: 3, pt: 3, borderTop: "1px solid", borderColor: theme.colors.boxborder[200], children: [_jsx(Button, { size: "sm", variant: "ghost", colorScheme: "gray", onClick: handleClear, children: "Clear" }), _jsx(Button, { size: "sm", variant: "gradient", onClick: requestClose, children: "Apply" })] })] }) })] }), error && errorMessage && _jsx(ErrorMessage, { errorMessage: errorMessage }), helperText && !error && _jsx(HelperText, { helperText: helperText })] }));
228
+ }, dateFormat: timePickerFormat, disabled: disabled || !tempRangeEnd, size: sizeKey }))] })] })), _jsxs(Box, { display: "flex", justifyContent: "space-between", alignItems: "center", mt: 3, pt: 3, borderTop: "1px solid", borderColor: theme.colors.boxborder[200], children: [_jsx(Button, { size: "sm", variant: "ghost", colorScheme: "gray", onClick: handleClear, children: "Clear" }), _jsx(Button, { size: "sm", variant: "gradient", onClick: requestClose, children: "Apply" })] })] }) })] }), error && errorMessage && (_jsx(ErrorMessage, { id: errorFieldId, errorMessage: errorMessage })), helperText && !error && _jsx(HelperText, { helperText: helperText })] }));
205
229
  };
206
230
  export default RangeDatePicker;
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useState, useRef, useEffect, useLayoutEffect, useMemo, useCallback } from "react";
3
- import { Box, Popover, PopoverTrigger, PopoverContent, PopoverBody, useDisclosure, Input, InputGroup, InputRightElement, FormControl, } from "@chakra-ui/react";
3
+ import { Box, Popover, PopoverAnchor, PopoverContent, PopoverBody, useDisclosure, Input, InputGroup, InputRightElement, IconButton, FormControl, } from "@chakra-ui/react";
4
4
  import { format, parse, isValid, isSameDay, isBefore, isAfter, addDays, } from "date-fns";
5
5
  import { CalendarIcon } from "lucide-react";
6
6
  import Button from "../Button/Button.js";
@@ -12,6 +12,7 @@ import ErrorMessage from "../Common/ErrorMessage.js";
12
12
  import TimePicker from "./TimePicker.js";
13
13
  import { TimePickerInput } from "./TimePickerInput.js";
14
14
  import CalendarPanel from "./CalendarPanel.js";
15
+ import { useExclusiveOverlay } from "../../Hooks/useExclusiveOverlay.js";
15
16
  const TOKEN_CHARS = /[dMyHhmsaAm]/;
16
17
  const getPlaceholderForToken = (token, length) => {
17
18
  const lower = token.toLowerCase();
@@ -226,6 +227,9 @@ export const SingleDatePicker = (props) => {
226
227
  const calendarIconPx = getTextInputIconSizePx(sizeKey);
227
228
  const calendarIconWrapperSx = getTextInputIconWrapperSx(sizeKey);
228
229
  const { isOpen, onOpen, onClose } = useDisclosure();
230
+ const requestExclusiveOpen = useExclusiveOverlay(isOpen, onClose);
231
+ const errorFieldId = id ? `${id}-error` : undefined;
232
+ const calendarLabel = label !== null && label !== void 0 ? label : "Choose date";
229
233
  const inputRef = useRef(null);
230
234
  const popoverRef = useRef(null);
231
235
  const triggerRef = useRef(null);
@@ -387,6 +391,7 @@ export const SingleDatePicker = (props) => {
387
391
  }
388
392
  }, [inputValue, tokenSegments, enableMask, isMaskActive]);
389
393
  const handleOpen = () => {
394
+ requestExclusiveOpen();
390
395
  if (!selectedDate && showDate) {
391
396
  setTempDate(disableToday ? tomorrow : new Date());
392
397
  }
@@ -777,14 +782,28 @@ export const SingleDatePicker = (props) => {
777
782
  }
778
783
  if (key === "Escape") {
779
784
  e.preventDefault();
780
- (_d = inputRef.current) === null || _d === void 0 ? void 0 : _d.blur();
785
+ if (isOpen) {
786
+ onClose();
787
+ }
788
+ else {
789
+ (_d = inputRef.current) === null || _d === void 0 ? void 0 : _d.blur();
790
+ }
781
791
  return;
782
792
  }
783
793
  if (key === "Enter") {
784
794
  e.preventDefault();
795
+ if (!isOpen && showDate) {
796
+ handleOpen();
797
+ return;
798
+ }
785
799
  handleInputBlur();
786
800
  return;
787
801
  }
802
+ if (key === "ArrowDown" && !isOpen && showDate) {
803
+ e.preventDefault();
804
+ handleOpen();
805
+ return;
806
+ }
788
807
  if (key === "Backspace" || key === "Delete") {
789
808
  e.preventDefault();
790
809
  const baseValue = ensureMaskValue();
@@ -926,20 +945,35 @@ export const SingleDatePicker = (props) => {
926
945
  };
927
946
  const handleInputFocusMask = enableMask ? handleInputFocus : undefined;
928
947
  const handleInputClickMask = enableMask ? handleInputClick : undefined;
929
- const handleKeyDownMask = enableMask ? handleKeyDown : undefined;
930
948
  const handlePasteMask = enableMask ? handlePaste : undefined;
949
+ const handleInputKeyDown = (e) => {
950
+ if (!enableMask) {
951
+ if (e.key === "Escape" && isOpen) {
952
+ e.preventDefault();
953
+ onClose();
954
+ return;
955
+ }
956
+ if (!isOpen && showDate && (e.key === "ArrowDown" || e.key === "Enter")) {
957
+ e.preventDefault();
958
+ handleOpen();
959
+ return;
960
+ }
961
+ return;
962
+ }
963
+ handleKeyDown(e);
964
+ };
931
965
  const displayValue = isTyping
932
966
  ? inputValue
933
967
  : tempDate
934
968
  ? format(tempDate, resolvedDateFormat)
935
969
  : "";
936
- return (_jsxs(FormControl, { id: id, isDisabled: disabled, width: width, children: [label && (_jsx(TextLabel, { label: label, isRequired: isRequired, isInformation: isInformation, informationMessage: informationMessage })), _jsxs(Popover, { isLazy: true, isOpen: isOpen, onClose: () => {
970
+ return (_jsxs(FormControl, { id: id, isDisabled: disabled, isInvalid: error, isRequired: isRequired, width: width, children: [label && (_jsx(TextLabel, { label: label, id: id, isRequired: isRequired, isInformation: isInformation, informationMessage: informationMessage })), _jsxs(Popover, { isLazy: true, isOpen: isOpen, onClose: () => {
937
971
  if (suppressOnCloseFinalizeRef.current) {
938
972
  suppressOnCloseFinalizeRef.current = false;
939
973
  return;
940
974
  }
941
975
  finalize();
942
- }, placement: "bottom-start", closeOnBlur: false, returnFocusOnClose: false, children: [_jsx(PopoverTrigger, { children: _jsx(Box, { ref: triggerRef, as: "div", children: _jsxs(InputGroup, { size: sizeKey, children: [_jsx(Input, { ref: inputRef, id: id, name: name, size: sizeKey, placeholder: displayPlaceholder, value: displayValue, onChange: handleInputChange, onKeyDown: handleKeyDownMask, onPaste: handlePasteMask, onBlur: handleInputBlur, onClick: handleInputClickMask, onFocus: handleInputFocusMask, isDisabled: disabled, autoComplete: autoComplete, bg: theme.colors.gray[50], borderColor: error
976
+ }, placement: "bottom-start", closeOnBlur: false, returnFocusOnClose: true, children: [_jsx(PopoverAnchor, { children: _jsx(Box, { ref: triggerRef, as: "div", children: _jsxs(InputGroup, { size: sizeKey, children: [_jsx(Input, { ref: inputRef, id: id, name: name, size: sizeKey, placeholder: displayPlaceholder, value: displayValue, onChange: handleInputChange, onKeyDown: handleInputKeyDown, onPaste: handlePasteMask, onBlur: handleInputBlur, onClick: handleInputClickMask, onFocus: handleInputFocusMask, isDisabled: disabled, isInvalid: error, isRequired: isRequired, "aria-describedby": error && errorMessage ? errorFieldId : undefined, autoComplete: autoComplete, bg: theme.colors.background[50], borderColor: error
943
977
  ? theme.colors.semanticText.error
944
978
  : theme.colors.boxborder[500], _focus: {
945
979
  borderColor: error ? theme.colors.semanticText.error : theme.colors.primary[500],
@@ -948,15 +982,15 @@ export const SingleDatePicker = (props) => {
948
982
  : `0 0 0 0.125rem ${theme.colors.boxShadow.primary}`,
949
983
  }, _hover: {
950
984
  borderColor: error ? theme.colors.semanticText.error : theme.colors.primary[500],
951
- }, cursor: "text", boxShadow: error ? `0 0 0 0.125rem ${theme.colors.boxShadow.error}` : `` }), _jsx(InputRightElement, { height: "100%", display: "flex", alignItems: "center", pointerEvents: "auto", onClick: (e) => {
952
- e.stopPropagation();
953
- if (!isOpen) {
954
- handleOpen();
955
- }
956
- else {
957
- onClose();
958
- }
959
- }, style: { cursor: "pointer" }, children: _jsx(Box, { as: "span", sx: calendarIconWrapperSx, children: _jsx(CalendarIcon, { size: calendarIconPx, color: theme.colors.textMuted }) }) })] }) }) }), _jsx(PopoverContent, { width: "auto", p: 3, ref: popoverRef, boxShadow: "lg", borderRadius: "xl", bg: theme.colors.background[50], children: _jsxs(PopoverBody, { p: 0, children: [showDate && (_jsx(CalendarPanel, { currentMonth: currentMonth, setCurrentMonth: (updater) => setCurrentMonth((prev) => updater(prev)), today: today, isRange: false, tempDate: tempDate, tempRangeStart: null, tempRangeEnd: null, isSameDay: isSameDay, isBefore: isBefore, isAfter: isAfter, minDate: minDate, maxDate: maxDate, disablePastDates: disablePastDates, disableFutureDates: disableFutureDates, disableToday: disableToday, onDaySelect: handleDaySelect })), showTime && (_jsx(Box, { mt: showDate ? 3 : 0, children: resolvedPickerType === "time" ? (_jsx(TimePickerInput, { value: tempDate, onChange: (updatedDate) => {
985
+ }, cursor: "text", boxShadow: error ? `0 0 0 0.125rem ${theme.colors.boxShadow.error}` : `` }), _jsx(InputRightElement, { height: "100%", display: "flex", alignItems: "center", pointerEvents: "auto", children: _jsx(IconButton, { "aria-label": "Open calendar", variant: "ghost", size: "sm", minW: "auto", h: "auto", isDisabled: disabled, onClick: (e) => {
986
+ e.stopPropagation();
987
+ if (!isOpen) {
988
+ handleOpen();
989
+ }
990
+ else {
991
+ onClose();
992
+ }
993
+ }, icon: _jsx(Box, { as: "span", sx: calendarIconWrapperSx, children: _jsx(CalendarIcon, { size: calendarIconPx, color: theme.colors.textMuted }) }) }) })] }) }) }), _jsx(PopoverContent, { width: "auto", p: 3, ref: popoverRef, boxShadow: "lg", borderRadius: "xl", bg: theme.colors.background[50], role: "dialog", "aria-label": calendarLabel, children: _jsxs(PopoverBody, { p: 0, children: [showDate && (_jsx(CalendarPanel, { currentMonth: currentMonth, setCurrentMonth: (updater) => setCurrentMonth((prev) => updater(prev)), today: today, isRange: false, tempDate: tempDate, tempRangeStart: null, tempRangeEnd: null, isSameDay: isSameDay, isBefore: isBefore, isAfter: isAfter, minDate: minDate, maxDate: maxDate, disablePastDates: disablePastDates, disableFutureDates: disableFutureDates, disableToday: disableToday, onDaySelect: handleDaySelect })), showTime && (_jsx(Box, { mt: showDate ? 3 : 0, children: resolvedPickerType === "time" ? (_jsx(TimePickerInput, { value: tempDate, onChange: (updatedDate) => {
960
994
  setTempDate(updatedDate);
961
995
  setInputValue(updatedDate ? format(updatedDate, resolvedDateFormat) : "");
962
996
  setIsTyping(false);
@@ -967,6 +1001,6 @@ export const SingleDatePicker = (props) => {
967
1001
  }, disabled: disabled })) })), _jsxs(Box, { display: "flex", justifyContent: "space-between", alignItems: "center", mt: 3, pt: 3, borderTop: "1px solid", borderColor: theme.colors.boxborder[200], children: [_jsx(Button, { size: "sm", variant: "ghost", colorScheme: "gray", onClick: () => {
968
1002
  setTempDate(null);
969
1003
  requestClose(null);
970
- }, children: "Clear" }), _jsx(Button, { size: "sm", variant: "gradient", onClick: () => requestClose(), children: "Apply" })] })] }) })] }), error && errorMessage && _jsx(ErrorMessage, { errorMessage: errorMessage }), helperText && !error && _jsx(HelperText, { helperText: helperText })] }));
1004
+ }, children: "Clear" }), _jsx(Button, { size: "sm", variant: "gradient", onClick: () => requestClose(), children: "Apply" })] })] }) })] }), error && errorMessage && (_jsx(ErrorMessage, { id: errorFieldId, errorMessage: errorMessage })), helperText && !error && _jsx(HelperText, { helperText: helperText })] }));
971
1005
  };
972
1006
  export default SingleDatePicker;
@@ -7,6 +7,7 @@ import HelperText from "../Common/HelperText.js";
7
7
  import ErrorMessage from "../Common/ErrorMessage.js";
8
8
  import TimePicker from "./TimePicker.js";
9
9
  import { TimePickerInput } from "./TimePickerInput.js";
10
+ import { useExclusiveOverlay } from "../../Hooks/useExclusiveOverlay.js";
10
11
  import { useCustomTheme } from "../../Theme/useCustomTheme.js";
11
12
  const formatToPlaceholder = (formatStr) => formatStr.replace(/h{1,2}/gi, "HH").replace(/m{1,2}/gi, "mm").replace(/a{1,2}/gi, "AM");
12
13
  export const TimeOnlyPicker = (props) => {
@@ -14,6 +15,11 @@ export const TimeOnlyPicker = (props) => {
14
15
  const { id, label, isRequired, isInformation, informationMessage, error, errorMessage, helperText, placeholderText, dateFormat = "HH:mm", disabled, width = "100%", disableToday = false, size = "md", } = props;
15
16
  const theme = useCustomTheme();
16
17
  const { isOpen, onOpen, onClose } = useDisclosure();
18
+ const requestExclusiveOpen = useExclusiveOverlay(isOpen, onClose);
19
+ const openPicker = () => {
20
+ requestExclusiveOpen();
21
+ onOpen();
22
+ };
17
23
  const popoverRef = useRef(null);
18
24
  const triggerRef = useRef(null);
19
25
  const inputWrapperRef = useRef(null);
@@ -70,7 +76,7 @@ export const TimeOnlyPicker = (props) => {
70
76
  handleTimeInputChange(date);
71
77
  }, dateFormat: dateFormat, disabled: disabled, placeholder: placeholder, onFocus: () => {
72
78
  if (!disabled && !isOpen)
73
- onOpen();
79
+ openPicker();
74
80
  }, error: error, size: sizeKey }) }) }) }), _jsx(PopoverContent, { width: "auto", p: 4, ref: popoverRef, boxShadow: "lg", bg: theme.colors.background[50], children: _jsxs(PopoverBody, { children: [_jsx(TimePicker, { date: resolvedTempDate, dateFormat: dateFormat, onChange: (updatedDate) => {
75
81
  handleTimePickerChange(updatedDate);
76
82
  } }), _jsxs(Box, { display: "flex", justifyContent: "space-between", mt: 4, pt: 4, borderTop: "1px solid", borderColor: theme.colors.boxborder[200], children: [_jsx(ChakraButton, { size: "sm", variant: "ghost", onClick: handleClear, children: "Clear" }), _jsx(ChakraButton, { size: "sm", colorScheme: "primary", onClick: handleOk, children: "OK" })] })] }) })] }), error && errorMessage && _jsx(ErrorMessage, { errorMessage: errorMessage }), helperText && !error && _jsx(HelperText, { helperText: helperText })] }));
@@ -52,16 +52,16 @@ const TimePicker = ({ date, onChange, dateFormat, disabled = false }) => {
52
52
  updateTime(hour, minute, newMeridiem);
53
53
  };
54
54
  const displayHour = is12HourFormat ? (hour % 12 === 0 ? 12 : hour % 12) : hour;
55
- return (_jsxs(HStack, { spacing: 1.5, w: "100%", children: [_jsx(Select, { value: displayHour, onChange: handleHourChange, flex: "1", minW: 0, size: "sm", isDisabled: disabled, bg: theme.colors.gray[50], borderColor: theme.colors.boxborder[500], _focus: {
55
+ return (_jsxs(HStack, { spacing: 1.5, w: "100%", children: [_jsx(Select, { "aria-label": "Hour", value: displayHour, onChange: handleHourChange, flex: "1", minW: 0, size: "sm", isDisabled: disabled, bg: theme.colors.background[50], borderColor: theme.colors.boxborder[500], _focus: {
56
56
  borderColor: theme.colors.primary[500],
57
57
  boxShadow: `0 0 0 0.125rem ${theme.colors.boxShadow.primary}`,
58
58
  }, _hover: { borderColor: theme.colors.primary[500] }, children: Array.from({ length: is12HourFormat ? 12 : 24 }, (_, i) => {
59
59
  const value = is12HourFormat ? i + 1 : i;
60
60
  return (_jsx("option", { value: value, children: value.toString().padStart(2, "0") }, value));
61
- }) }), _jsx(Select, { value: minute, onChange: handleMinuteChange, flex: "1", minW: 0, size: "sm", isDisabled: disabled, bg: theme.colors.gray[50], borderColor: theme.colors.boxborder[500], _focus: {
61
+ }) }), _jsx(Select, { "aria-label": "Minute", value: minute, onChange: handleMinuteChange, flex: "1", minW: 0, size: "sm", isDisabled: disabled, bg: theme.colors.background[50], borderColor: theme.colors.boxborder[500], _focus: {
62
62
  borderColor: theme.colors.primary[500],
63
63
  boxShadow: `0 0 0 0.125rem ${theme.colors.boxShadow.primary}`,
64
- }, _hover: { borderColor: theme.colors.primary[500] }, children: Array.from({ length: 60 }, (_, i) => (_jsx("option", { value: i, children: i.toString().padStart(2, "0") }, i))) }), is12HourFormat && (_jsxs(Select, { value: ampm, onChange: handleAmpmChange, flex: "1", minW: 0, size: "sm", isDisabled: disabled, bg: theme.colors.gray[50], borderColor: theme.colors.boxborder[500], _focus: {
64
+ }, _hover: { borderColor: theme.colors.primary[500] }, children: Array.from({ length: 60 }, (_, i) => (_jsx("option", { value: i, children: i.toString().padStart(2, "0") }, i))) }), is12HourFormat && (_jsxs(Select, { "aria-label": "AM or PM", value: ampm, onChange: handleAmpmChange, flex: "1", minW: 0, size: "sm", isDisabled: disabled, bg: theme.colors.background[50], borderColor: theme.colors.boxborder[500], _focus: {
65
65
  borderColor: theme.colors.primary[500],
66
66
  boxShadow: `0 0 0 0.125rem ${theme.colors.boxShadow.primary}`,
67
67
  }, _hover: { borderColor: theme.colors.primary[500] }, children: [_jsx("option", { value: "AM", children: "AM" }), _jsx("option", { value: "PM", children: "PM" })] }))] }));
@@ -400,7 +400,7 @@ export const TimePickerInput = ({ value, onChange, dateFormat, disabled = false,
400
400
  inputRef.current.value = is12Hour && formatted ? `${formatted} ${ampm}` : formatted;
401
401
  }
402
402
  }, [digits, ampm, is12Hour]);
403
- return (_jsxs(InputGroup, { size: sizeKey, children: [_jsx(Input, { ref: inputRef, defaultValue: "", onChange: handleInput, onKeyDown: handleKeyDown, onFocus: handleFocus, onBlur: handleBlur, onClick: handleClick, onPaste: handlePaste, placeholder: placeholderText, isDisabled: disabled, autoComplete: "off", isInvalid: error, size: sizeKey, bg: theme.colors.gray[50], borderColor: error ? theme.colors.semanticText.error : theme.colors.boxborder[500], _focus: {
403
+ return (_jsxs(InputGroup, { size: sizeKey, children: [_jsx(Input, { ref: inputRef, defaultValue: "", onChange: handleInput, onKeyDown: handleKeyDown, onFocus: handleFocus, onBlur: handleBlur, onClick: handleClick, onPaste: handlePaste, placeholder: placeholderText, isDisabled: disabled, autoComplete: "off", isInvalid: error, size: sizeKey, bg: theme.colors.background[50], borderColor: error ? theme.colors.semanticText.error : theme.colors.boxborder[500], _focus: {
404
404
  borderColor: error ? theme.colors.semanticText.error : theme.colors.primary[500],
405
405
  boxShadow: error
406
406
  ? `0 0 0 0.125rem ${theme.colors.boxShadow.error}`