@papernote/ui 1.10.16 → 1.10.17
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/Autocomplete.d.ts.map +1 -1
- package/dist/components/Combobox.d.ts.map +1 -1
- package/dist/components/DatePicker.d.ts.map +1 -1
- package/dist/components/DateRangePicker.d.ts.map +1 -1
- package/dist/components/Input.d.ts.map +1 -1
- package/dist/components/Modal.d.ts.map +1 -1
- package/dist/components/Select.d.ts +2 -0
- package/dist/components/Select.d.ts.map +1 -1
- package/dist/components/TimePicker.d.ts.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.esm.js +74 -21
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +73 -20
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/Autocomplete.tsx +1 -0
- package/src/components/Combobox.tsx +1 -0
- package/src/components/DatePicker.tsx +1 -0
- package/src/components/DateRangePicker.tsx +1 -0
- package/src/components/Input.tsx +13 -3
- package/src/components/Modal.tsx +64 -8
- package/src/components/Select.tsx +7 -0
- package/src/components/TimePicker.tsx +1 -0
package/dist/index.js
CHANGED
|
@@ -327,7 +327,9 @@ function ButtonGroup({ options, value, values = [], onChange, onChangeMultiple,
|
|
|
327
327
|
* ```
|
|
328
328
|
*/
|
|
329
329
|
const Input = React.forwardRef(({ label, helperText, validationState, validationMessage, icon, iconPosition = 'left', showCount = false, prefix, suffix, prefixIcon, suffixIcon, showPasswordToggle = false, clearable = false, onClear, loading = false, className = '', id, type = 'text', value, maxLength, inputMode, enterKeyHint, size = 'md', ...props }, ref) => {
|
|
330
|
-
const
|
|
330
|
+
const generatedId = React.useId();
|
|
331
|
+
const inputId = id || generatedId;
|
|
332
|
+
const helperId = `${inputId}-helper`;
|
|
331
333
|
const [showPassword, setShowPassword] = React.useState(false);
|
|
332
334
|
// Handle clear button click
|
|
333
335
|
const handleClear = () => {
|
|
@@ -408,7 +410,7 @@ const Input = React.forwardRef(({ label, helperText, validationState, validation
|
|
|
408
410
|
return 'text-ink-600';
|
|
409
411
|
}
|
|
410
412
|
};
|
|
411
|
-
return (jsxRuntime.jsxs("div", { className: "w-full", children: [label && (jsxRuntime.jsxs("label", { htmlFor: inputId, className: "label", children: [label, props.required && jsxRuntime.jsx("span", { className: "text-error-500 ml-1", children: "*" })] })), jsxRuntime.jsxs("div", { className: "relative", children: [prefix && (jsxRuntime.jsx("div", { className: "absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none text-ink-500 text-sm", children: prefix })), prefixIcon && !prefix && (jsxRuntime.jsx("div", { className: "absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none text-ink-400", children: prefixIcon })), icon && iconPosition === 'left' && !prefix && !prefixIcon && (jsxRuntime.jsx("div", { className: "absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none text-ink-400", children: icon })), jsxRuntime.jsx("input", { ref: ref, id: inputId, type: actualType, value: value, maxLength: maxLength, inputMode: effectiveInputMode, enterKeyHint: enterKeyHint, className: `
|
|
413
|
+
return (jsxRuntime.jsxs("div", { className: "w-full", children: [label && (jsxRuntime.jsxs("label", { htmlFor: inputId, className: "label", children: [label, props.required && jsxRuntime.jsx("span", { className: "text-error-500 ml-1", children: "*" })] })), jsxRuntime.jsxs("div", { className: "relative", children: [prefix && (jsxRuntime.jsx("div", { className: "absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none text-ink-500 text-sm", children: prefix })), prefixIcon && !prefix && (jsxRuntime.jsx("div", { className: "absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none text-ink-400", children: prefixIcon })), icon && iconPosition === 'left' && !prefix && !prefixIcon && (jsxRuntime.jsx("div", { className: "absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none text-ink-400", children: icon })), jsxRuntime.jsx("input", { ref: ref, id: inputId, type: actualType, value: value, maxLength: maxLength, inputMode: effectiveInputMode, enterKeyHint: enterKeyHint, "aria-invalid": validationState === 'error', "aria-describedby": helperText || validationMessage ? helperId : undefined, "aria-required": props.required, className: `
|
|
412
414
|
input
|
|
413
415
|
${sizeClasses[size]}
|
|
414
416
|
${getValidationClasses()}
|
|
@@ -421,7 +423,7 @@ const Input = React.forwardRef(({ label, helperText, validationState, validation
|
|
|
421
423
|
${validationState && !suffix && !suffixIcon && !showPasswordToggle ? 'pr-10' : ''}
|
|
422
424
|
${(showPasswordToggle && type === 'password') || validationState || suffix || suffixIcon ? 'pr-20' : ''}
|
|
423
425
|
${className}
|
|
424
|
-
`, ...props }), suffix && (jsxRuntime.jsx("div", { className: "absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none text-ink-500 text-sm", children: suffix })), jsxRuntime.jsxs("div", { className: "absolute inset-y-0 right-0 pr-3 flex items-center gap-1", children: [loading && (jsxRuntime.jsx("div", { className: "pointer-events-none text-ink-400", children: jsxRuntime.jsx(lucideReact.Loader2, { className: "h-5 w-5 animate-spin" }) })), suffixIcon && !suffix && !validationState && !showPasswordToggle && !showClearButton && !loading && (jsxRuntime.jsx("div", { className: "pointer-events-none text-ink-400", children: suffixIcon })), showClearButton && (jsxRuntime.jsx("button", { type: "button", onClick: handleClear, className: `text-ink-400 hover:text-ink-600 focus:outline-none cursor-pointer pointer-events-auto rounded-full hover:bg-paper-100 flex items-center justify-center ${buttonSizeClasses[size]}`, "aria-label": "Clear input", children: jsxRuntime.jsx(lucideReact.X, { className: "h-4 w-4" }) })), type === 'password' && showPasswordToggle && (jsxRuntime.jsx("button", { type: "button", onClick: () => setShowPassword(!showPassword), className: `text-ink-400 hover:text-ink-600 focus:outline-none cursor-pointer pointer-events-auto rounded-full hover:bg-paper-100 flex items-center justify-center ${buttonSizeClasses[size]}`, "aria-label": showPassword ? 'Hide password' : 'Show password', children: showPassword ? jsxRuntime.jsx(lucideReact.EyeOff, { className: "h-5 w-5" }) : jsxRuntime.jsx(lucideReact.Eye, { className: "h-5 w-5" }) })), validationState && (jsxRuntime.jsx("div", { className: "pointer-events-none", children: getValidationIcon() })), icon && iconPosition === 'right' && !suffix && !suffixIcon && !validationState && (jsxRuntime.jsx("div", { className: "pointer-events-none text-ink-400", children: icon }))] })] }), jsxRuntime.jsxs("div", { className: "flex justify-between items-center mt-2", children: [(helperText || validationMessage) && (jsxRuntime.jsx("p", { className: `text-xs ${validationMessage ? getValidationMessageColor() : 'text-ink-600'}`, children: validationMessage || helperText })), showCounter && (jsxRuntime.jsxs("p", { className: `text-xs ml-auto ${currentLength > maxLength ? 'text-error-600' : 'text-ink-500'}`, children: [currentLength, " / ", maxLength] }))] })] }));
|
|
426
|
+
`, ...props }), suffix && (jsxRuntime.jsx("div", { className: "absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none text-ink-500 text-sm", children: suffix })), jsxRuntime.jsxs("div", { className: "absolute inset-y-0 right-0 pr-3 flex items-center gap-1", children: [loading && (jsxRuntime.jsx("div", { className: "pointer-events-none text-ink-400", children: jsxRuntime.jsx(lucideReact.Loader2, { className: "h-5 w-5 animate-spin" }) })), suffixIcon && !suffix && !validationState && !showPasswordToggle && !showClearButton && !loading && (jsxRuntime.jsx("div", { className: "pointer-events-none text-ink-400", children: suffixIcon })), showClearButton && (jsxRuntime.jsx("button", { type: "button", onClick: handleClear, className: `text-ink-400 hover:text-ink-600 focus:outline-none cursor-pointer pointer-events-auto rounded-full hover:bg-paper-100 flex items-center justify-center ${buttonSizeClasses[size]}`, "aria-label": "Clear input", children: jsxRuntime.jsx(lucideReact.X, { className: "h-4 w-4" }) })), type === 'password' && showPasswordToggle && (jsxRuntime.jsx("button", { type: "button", onClick: () => setShowPassword(!showPassword), className: `text-ink-400 hover:text-ink-600 focus:outline-none cursor-pointer pointer-events-auto rounded-full hover:bg-paper-100 flex items-center justify-center ${buttonSizeClasses[size]}`, "aria-label": showPassword ? 'Hide password' : 'Show password', children: showPassword ? jsxRuntime.jsx(lucideReact.EyeOff, { className: "h-5 w-5" }) : jsxRuntime.jsx(lucideReact.Eye, { className: "h-5 w-5" }) })), validationState && (jsxRuntime.jsx("div", { className: "pointer-events-none", children: getValidationIcon() })), icon && iconPosition === 'right' && !suffix && !suffixIcon && !validationState && (jsxRuntime.jsx("div", { className: "pointer-events-none text-ink-400", children: icon }))] })] }), jsxRuntime.jsxs("div", { className: "flex justify-between items-center mt-2", children: [(helperText || validationMessage) && (jsxRuntime.jsx("p", { id: helperId, className: `text-xs ${validationMessage ? getValidationMessageColor() : 'text-ink-600'}`, role: validationState === 'error' ? 'alert' : undefined, "aria-live": validationState === 'error' ? 'assertive' : undefined, children: validationMessage || helperText })), showCounter && (jsxRuntime.jsxs("p", { className: `text-xs ml-auto ${currentLength > maxLength ? 'text-error-600' : 'text-ink-500'}`, children: [currentLength, " / ", maxLength] }))] })] }));
|
|
425
427
|
});
|
|
426
428
|
Input.displayName = 'Input';
|
|
427
429
|
|
|
@@ -804,7 +806,7 @@ const optionSizeClasses = {
|
|
|
804
806
|
* ```
|
|
805
807
|
*/
|
|
806
808
|
const Select = React.forwardRef((props, ref) => {
|
|
807
|
-
const { options = [], groups = [], value, onChange, placeholder = 'Select an option', searchable = false, disabled = false, label, helperText, error, loading = false, clearable = false, creatable = false, onCreateOption, virtualized = false, virtualHeight = '300px', virtualItemHeight = 42, size = 'md', mobileMode = 'auto', usePortal = true, } = props;
|
|
809
|
+
const { options = [], groups = [], value, onChange, placeholder = 'Select an option', searchable = false, disabled = false, label, helperText, error, loading = false, clearable = false, creatable = false, onCreateOption, virtualized = false, virtualHeight = '300px', virtualItemHeight = 42, size = 'md', mobileMode = 'auto', usePortal = true, required = false, } = props;
|
|
808
810
|
const [isOpen, setIsOpen] = React.useState(false);
|
|
809
811
|
const [searchQuery, setSearchQuery] = React.useState('');
|
|
810
812
|
const [scrollTop, setScrollTop] = React.useState(0);
|
|
@@ -1037,19 +1039,19 @@ const Select = React.forwardRef((props, ref) => {
|
|
|
1037
1039
|
};
|
|
1038
1040
|
// Native select for mobile (optional)
|
|
1039
1041
|
if (useNativeSelect) {
|
|
1040
|
-
return (jsxRuntime.jsxs("div", { className: "w-full", children: [label && (jsxRuntime.
|
|
1042
|
+
return (jsxRuntime.jsxs("div", { className: "w-full", children: [label && (jsxRuntime.jsxs("label", { id: labelId, className: "label", children: [label, required && jsxRuntime.jsx("span", { className: "text-error-500 ml-1", children: "*" })] })), jsxRuntime.jsxs("div", { className: "relative", children: [jsxRuntime.jsxs("select", { ref: nativeSelectRef, value: value || '', onChange: (e) => onChange?.(e.target.value), disabled: disabled, className: `
|
|
1041
1043
|
input w-full appearance-none pr-10
|
|
1042
1044
|
${sizeClasses$b[effectiveSize]}
|
|
1043
1045
|
${error ? 'border-error-400 focus:border-error-400 focus:ring-error-400' : ''}
|
|
1044
1046
|
${disabled ? 'opacity-40 cursor-not-allowed' : 'cursor-pointer'}
|
|
1045
|
-
`, "aria-labelledby": label ? labelId : undefined, "aria-invalid": error ? 'true' : undefined, "aria-describedby": error ? errorId : (helperText ? helperTextId : undefined), children: [jsxRuntime.jsx("option", { value: "", disabled: true, children: placeholder }), options.map((opt) => (jsxRuntime.jsx("option", { value: opt.value, disabled: opt.disabled, children: opt.label }, opt.value))), groups.map((group) => (jsxRuntime.jsx("optgroup", { label: group.label, children: group.options.map((opt) => (jsxRuntime.jsx("option", { value: opt.value, disabled: opt.disabled, children: opt.label }, opt.value))) }, group.label)))] }), jsxRuntime.jsx(lucideReact.ChevronDown, { className: "absolute right-3 top-1/2 -translate-y-1/2 h-5 w-5 text-ink-500 pointer-events-none" })] }), error && (jsxRuntime.jsx("p", { id: errorId, className: "mt-2 text-xs text-error-600", role: "alert", "aria-live": "assertive", children: error })), helperText && !error && (jsxRuntime.jsx("p", { id: helperTextId, className: "mt-2 text-xs text-ink-600", children: helperText }))] }));
|
|
1047
|
+
`, "aria-labelledby": label ? labelId : undefined, "aria-invalid": error ? 'true' : undefined, "aria-describedby": error ? errorId : (helperText ? helperTextId : undefined), "aria-required": required, children: [jsxRuntime.jsx("option", { value: "", disabled: true, children: placeholder }), options.map((opt) => (jsxRuntime.jsx("option", { value: opt.value, disabled: opt.disabled, children: opt.label }, opt.value))), groups.map((group) => (jsxRuntime.jsx("optgroup", { label: group.label, children: group.options.map((opt) => (jsxRuntime.jsx("option", { value: opt.value, disabled: opt.disabled, children: opt.label }, opt.value))) }, group.label)))] }), jsxRuntime.jsx(lucideReact.ChevronDown, { className: "absolute right-3 top-1/2 -translate-y-1/2 h-5 w-5 text-ink-500 pointer-events-none" })] }), error && (jsxRuntime.jsx("p", { id: errorId, className: "mt-2 text-xs text-error-600", role: "alert", "aria-live": "assertive", children: error })), helperText && !error && (jsxRuntime.jsx("p", { id: helperTextId, className: "mt-2 text-xs text-ink-600", children: helperText }))] }));
|
|
1046
1048
|
}
|
|
1047
|
-
return (jsxRuntime.jsxs("div", { className: "w-full", children: [label && (jsxRuntime.
|
|
1049
|
+
return (jsxRuntime.jsxs("div", { className: "w-full", children: [label && (jsxRuntime.jsxs("label", { id: labelId, className: "label", children: [label, required && jsxRuntime.jsx("span", { className: "text-error-500 ml-1", children: "*" })] })), jsxRuntime.jsx("div", { ref: selectRef, className: "relative", children: jsxRuntime.jsxs("button", { ref: buttonRef, type: "button", onClick: () => !disabled && setIsOpen(!isOpen), disabled: disabled, className: `
|
|
1048
1050
|
input w-full flex items-center justify-between px-3
|
|
1049
1051
|
${sizeClasses$b[effectiveSize]}
|
|
1050
1052
|
${error ? 'border-error-400 focus:border-error-400 focus:ring-error-400' : ''}
|
|
1051
1053
|
${disabled ? 'opacity-40 cursor-not-allowed' : 'cursor-pointer'}
|
|
1052
|
-
`, role: "combobox", "aria-haspopup": "listbox", "aria-expanded": isOpen, "aria-controls": listboxId, "aria-labelledby": label ? labelId : undefined, "aria-label": !label ? placeholder : undefined, "aria-activedescendant": activeDescendant, "aria-invalid": error ? 'true' : undefined, "aria-describedby": error ? errorId : (helperText ? helperTextId : undefined), "aria-disabled": disabled, children: [jsxRuntime.jsxs("span", { className: `flex items-center gap-2 ${selectedOption ? 'text-ink-800' : 'text-ink-400'}`, children: [loading && jsxRuntime.jsx(lucideReact.Loader2, { className: "h-4 w-4 animate-spin text-ink-500" }), !loading && selectedOption?.icon && jsxRuntime.jsx("span", { children: selectedOption.icon }), selectedOption ? selectedOption.label : placeholder] }), jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [clearable && value && (jsxRuntime.jsx("button", { type: "button", onClick: (e) => {
|
|
1054
|
+
`, role: "combobox", "aria-haspopup": "listbox", "aria-expanded": isOpen, "aria-controls": listboxId, "aria-labelledby": label ? labelId : undefined, "aria-label": !label ? placeholder : undefined, "aria-activedescendant": activeDescendant, "aria-invalid": error ? 'true' : undefined, "aria-describedby": error ? errorId : (helperText ? helperTextId : undefined), "aria-disabled": disabled, "aria-required": required, children: [jsxRuntime.jsxs("span", { className: `flex items-center gap-2 ${selectedOption ? 'text-ink-800' : 'text-ink-400'}`, children: [loading && jsxRuntime.jsx(lucideReact.Loader2, { className: "h-4 w-4 animate-spin text-ink-500" }), !loading && selectedOption?.icon && jsxRuntime.jsx("span", { children: selectedOption.icon }), selectedOption ? selectedOption.label : placeholder] }), jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [clearable && value && (jsxRuntime.jsx("button", { type: "button", onClick: (e) => {
|
|
1053
1055
|
e.stopPropagation();
|
|
1054
1056
|
onChange?.('');
|
|
1055
1057
|
setIsOpen(false);
|
|
@@ -1654,7 +1656,7 @@ const DatePicker = React.forwardRef(({ value, onChange, label, placeholder = 'Se
|
|
|
1654
1656
|
${disabled ? 'bg-paper-100 text-ink-400 cursor-not-allowed' : 'cursor-pointer'}
|
|
1655
1657
|
focus:outline-none focus:ring-2
|
|
1656
1658
|
pr-10
|
|
1657
|
-
`, "aria-labelledby": label ? labelId : undefined, "aria-label": !label ? 'Date picker' : undefined, "aria-expanded": isOpen, "aria-haspopup": "dialog", "aria-controls": dialogId, "aria-invalid": validationState === 'error' ? 'true' : undefined, "aria-describedby": validationMessage ? descriptionId : undefined, role: "combobox" }), jsxRuntime.jsxs("div", { className: "absolute inset-y-0 right-0 flex items-center pr-3 gap-1", children: [showClearButton && value && !disabled && (jsxRuntime.jsx("button", { type: "button", onClick: (e) => {
|
|
1659
|
+
`, "aria-labelledby": label ? labelId : undefined, "aria-label": !label ? 'Date picker' : undefined, "aria-expanded": isOpen, "aria-haspopup": "dialog", "aria-controls": dialogId, "aria-invalid": validationState === 'error' ? 'true' : undefined, "aria-describedby": validationMessage ? descriptionId : undefined, "aria-required": required, role: "combobox" }), jsxRuntime.jsxs("div", { className: "absolute inset-y-0 right-0 flex items-center pr-3 gap-1", children: [showClearButton && value && !disabled && (jsxRuntime.jsx("button", { type: "button", onClick: (e) => {
|
|
1658
1660
|
e.stopPropagation();
|
|
1659
1661
|
handleClear();
|
|
1660
1662
|
}, className: "text-ink-400 hover:text-ink-600 focus:outline-none", "aria-label": "Clear date", children: jsxRuntime.jsx(lucideReact.X, { className: iconSizeClasses[size] }) })), jsxRuntime.jsx(lucideReact.Calendar, { className: `${iconSizeClasses[size]} text-ink-400` })] })] }), validationMessage && (jsxRuntime.jsx("p", { id: descriptionId, className: `mt-1 text-xs ${validationState ? validationMessageColors[validationState] : 'text-ink-500'}`, role: "alert", "aria-live": "polite", children: validationMessage })), helperText && !validationMessage && (jsxRuntime.jsx("p", { className: "mt-1 text-xs text-ink-500", children: helperText })), isOpen && (jsxRuntime.jsxs("div", { id: dialogId, className: "absolute z-50 mt-1 bg-white rounded-lg shadow-lg border border-paper-200 p-3 w-72", role: "dialog", "aria-modal": "true", "aria-label": "Date picker calendar", children: [jsxRuntime.jsxs("div", { className: "flex items-center justify-between mb-3", children: [jsxRuntime.jsx("button", { type: "button", onClick: goToPrevMonth, className: "p-1 rounded hover:bg-paper-100 text-ink-600 hover:text-ink-900", "aria-label": "Previous month", children: jsxRuntime.jsx(lucideReact.ChevronLeft, { className: "h-5 w-5" }) }), jsxRuntime.jsxs("span", { className: "text-sm font-semibold text-ink-900", children: [MONTHS[viewDate.getMonth()], " ", viewDate.getFullYear()] }), jsxRuntime.jsx("button", { type: "button", onClick: goToNextMonth, className: "p-1 rounded hover:bg-paper-100 text-ink-600 hover:text-ink-900", "aria-label": "Next month", children: jsxRuntime.jsx(lucideReact.ChevronRight, { className: "h-5 w-5" }) })] }), jsxRuntime.jsx("div", { className: "grid grid-cols-7 mb-1", children: DAYS.map(day => (jsxRuntime.jsx("div", { className: "text-center text-xs font-medium text-ink-500 py-1", children: day }, day))) }), jsxRuntime.jsx("div", { className: "grid grid-cols-7", children: generateCalendarDays().map(({ date, isCurrentMonth }, index) => {
|
|
@@ -1845,7 +1847,7 @@ const TimePicker = React.forwardRef(({ value = null, onChange, label, placeholde
|
|
|
1845
1847
|
${disabled ? 'bg-paper-100 text-ink-400 cursor-not-allowed' : ''}
|
|
1846
1848
|
focus:outline-none focus:ring-2
|
|
1847
1849
|
pr-20
|
|
1848
|
-
`, "aria-labelledby": label ? labelId : undefined, "aria-label": !label ? 'Time picker' : undefined, "aria-expanded": isOpen, "aria-haspopup": "dialog", "aria-controls": dropdownId, "aria-invalid": validationState === 'error' ? 'true' : undefined, "aria-describedby": validationMessage ? descriptionId : undefined, role: "combobox" }), jsxRuntime.jsxs("div", { className: "absolute inset-y-0 right-0 flex items-center pr-2 gap-1", children: [value && !disabled && (jsxRuntime.jsx("button", { type: "button", onClick: (e) => {
|
|
1850
|
+
`, "aria-labelledby": label ? labelId : undefined, "aria-label": !label ? 'Time picker' : undefined, "aria-expanded": isOpen, "aria-haspopup": "dialog", "aria-controls": dropdownId, "aria-invalid": validationState === 'error' ? 'true' : undefined, "aria-describedby": validationMessage ? descriptionId : undefined, "aria-required": required, role: "combobox" }), jsxRuntime.jsxs("div", { className: "absolute inset-y-0 right-0 flex items-center pr-2 gap-1", children: [value && !disabled && (jsxRuntime.jsx("button", { type: "button", onClick: (e) => {
|
|
1849
1851
|
e.stopPropagation();
|
|
1850
1852
|
handleClear();
|
|
1851
1853
|
}, className: "p-0.5 text-ink-400 hover:text-ink-600 focus:outline-none", "aria-label": "Clear", tabIndex: -1, children: jsxRuntime.jsx(lucideReact.X, { className: iconSizeClasses[size] }) })), jsxRuntime.jsx(lucideReact.Clock, { className: `${iconSizeClasses[size]} text-ink-400` })] })] }), validationMessage && (jsxRuntime.jsx("p", { id: descriptionId, className: `mt-1 text-xs ${validationState ? validationMessageColors[validationState] : 'text-ink-500'}`, role: "alert", "aria-live": "polite", children: validationMessage })), helperText && !validationMessage && (jsxRuntime.jsx("p", { className: "mt-1 text-xs text-ink-500", children: helperText })), isOpen && (jsxRuntime.jsx("div", { id: dropdownId, className: "absolute z-50 mt-1 bg-white rounded-md shadow-lg border border-paper-200", role: "dialog", "aria-modal": "true", "aria-label": "Time selection", children: jsxRuntime.jsxs("div", { className: "p-4 flex items-center gap-4", children: [jsxRuntime.jsx(TimeSpinner, { value: timeValue.hours, min: use12Hour ? 1 : 0, max: use12Hour ? 12 : 23, onChange: (hours) => updateTime({ hours }), label: "Hour" }), jsxRuntime.jsx("span", { className: "text-2xl font-bold text-ink-600", children: ":" }), jsxRuntime.jsx(TimeSpinner, { value: timeValue.minutes, min: 0, max: 59, step: minuteStep, options: minuteOptions, onChange: (minutes) => updateTime({ minutes }), label: "Min" }), showSeconds && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("span", { className: "text-2xl font-bold text-ink-600", children: ":" }), jsxRuntime.jsx(TimeSpinner, { value: timeValue.seconds, min: 0, max: 59, onChange: (seconds) => updateTime({ seconds }), label: "Sec" })] })), use12Hour && (jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [jsxRuntime.jsx("button", { type: "button", onClick: () => updateTime({ period: 'AM' }), className: `
|
|
@@ -2105,7 +2107,7 @@ const DateRangePicker = React.forwardRef(({ value = { start: null, end: null },
|
|
|
2105
2107
|
${disabled ? 'bg-paper-100 text-ink-400 cursor-not-allowed' : ''}
|
|
2106
2108
|
focus:outline-none focus:ring-2
|
|
2107
2109
|
pr-20
|
|
2108
|
-
`, "aria-labelledby": label ? labelId : undefined, "aria-label": !label ? 'Date range picker' : undefined, "aria-expanded": isOpen, "aria-haspopup": "dialog", "aria-controls": dialogId, "aria-invalid": validationState === 'error' ? 'true' : undefined, "aria-describedby": validationMessage ? descriptionId : (isOpen ? hintId : undefined), role: "combobox" }), jsxRuntime.jsxs("div", { className: "absolute inset-y-0 right-0 flex items-center pr-2 gap-1", children: [(value.start || value.end) && !disabled && (jsxRuntime.jsx("button", { type: "button", onClick: (e) => {
|
|
2110
|
+
`, "aria-labelledby": label ? labelId : undefined, "aria-label": !label ? 'Date range picker' : undefined, "aria-expanded": isOpen, "aria-haspopup": "dialog", "aria-controls": dialogId, "aria-invalid": validationState === 'error' ? 'true' : undefined, "aria-describedby": validationMessage ? descriptionId : (isOpen ? hintId : undefined), "aria-required": required, role: "combobox" }), jsxRuntime.jsxs("div", { className: "absolute inset-y-0 right-0 flex items-center pr-2 gap-1", children: [(value.start || value.end) && !disabled && (jsxRuntime.jsx("button", { type: "button", onClick: (e) => {
|
|
2109
2111
|
e.stopPropagation();
|
|
2110
2112
|
handleClear();
|
|
2111
2113
|
}, className: "p-0.5 text-ink-400 hover:text-ink-600 focus:outline-none", "aria-label": "Clear", tabIndex: -1, children: jsxRuntime.jsx(lucideReact.X, { className: iconSizeClasses[size] }) })), jsxRuntime.jsx(lucideReact.Calendar, { className: `${iconSizeClasses[size]} text-ink-400` })] })] }), validationMessage && (jsxRuntime.jsx("p", { id: descriptionId, className: `mt-1 text-xs ${validationState ? validationMessageColors[validationState] : 'text-ink-500'}`, role: "alert", "aria-live": "polite", children: validationMessage })), helperText && !validationMessage && (jsxRuntime.jsx("p", { className: "mt-1 text-xs text-ink-500", children: helperText })), isOpen && (jsxRuntime.jsx("div", { id: dialogId, className: "absolute z-50 mt-1 bg-white rounded-md shadow-lg border border-paper-200 p-4", role: "dialog", "aria-modal": "true", "aria-label": "Date range selection", children: jsxRuntime.jsxs("div", { className: "flex gap-4", children: [showPresets && (jsxRuntime.jsxs("div", { className: "flex flex-col gap-2 border-r border-paper-200 pr-4", children: [jsxRuntime.jsx("button", { type: "button", onClick: () => handlePreset('today'), className: "text-left px-3 py-2 text-sm text-ink-700 hover:bg-primary-50 rounded whitespace-nowrap", children: "Today" }), jsxRuntime.jsx("button", { type: "button", onClick: () => handlePreset('yesterday'), className: "text-left px-3 py-2 text-sm text-ink-700 hover:bg-primary-50 rounded whitespace-nowrap", children: "Yesterday" }), jsxRuntime.jsx("button", { type: "button", onClick: () => handlePreset('last7days'), className: "text-left px-3 py-2 text-sm text-ink-700 hover:bg-primary-50 rounded whitespace-nowrap", children: "Last 7 days" }), jsxRuntime.jsx("button", { type: "button", onClick: () => handlePreset('last30days'), className: "text-left px-3 py-2 text-sm text-ink-700 hover:bg-primary-50 rounded whitespace-nowrap", children: "Last 30 days" }), jsxRuntime.jsx("button", { type: "button", onClick: () => handlePreset('thisMonth'), className: "text-left px-3 py-2 text-sm text-ink-700 hover:bg-primary-50 rounded whitespace-nowrap", children: "This month" }), jsxRuntime.jsx("button", { type: "button", onClick: () => handlePreset('lastMonth'), className: "text-left px-3 py-2 text-sm text-ink-700 hover:bg-primary-50 rounded whitespace-nowrap", children: "Last month" })] })), jsxRuntime.jsxs("div", { children: [jsxRuntime.jsxs("div", { className: "flex items-center justify-between mb-4", children: [jsxRuntime.jsx("button", { type: "button", onClick: previousMonth, className: "p-2 text-ink-600 hover:bg-paper-100 rounded", "aria-label": "Previous month", children: jsxRuntime.jsx(lucideReact.ChevronLeft, { className: "h-5 w-5" }) }), jsxRuntime.jsx("div", { className: "text-sm font-semibold text-ink-900", children: monthYear }), jsxRuntime.jsx("button", { type: "button", onClick: nextMonth, className: "p-2 text-ink-600 hover:bg-paper-100 rounded", "aria-label": "Next month", children: jsxRuntime.jsx(lucideReact.ChevronRight, { className: "h-5 w-5" }) })] }), jsxRuntime.jsx("div", { className: "grid grid-cols-7 gap-1 mb-2", children: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'].map((day) => (jsxRuntime.jsx("div", { className: "text-center text-xs font-medium text-ink-500 w-8", children: day }, day))) }), jsxRuntime.jsx("div", { className: "grid grid-cols-7 gap-1", children: calendarDays.map((date, index) => {
|
|
@@ -2667,7 +2669,7 @@ const Combobox = React.forwardRef(({ value = '', onChange, options, onSearch, on
|
|
|
2667
2669
|
${disabled ? 'bg-paper-100 text-ink-400 cursor-not-allowed' : ''}
|
|
2668
2670
|
focus:outline-none focus:ring-2
|
|
2669
2671
|
pr-20
|
|
2670
|
-
`, "aria-labelledby": label ? labelId : undefined, "aria-label": !label ? 'Combobox' : undefined, "aria-expanded": isOpen, "aria-autocomplete": "list", "aria-controls": listboxId, "aria-activedescendant": isOpen && filteredOptions.length > 0 ? `option-${highlightedIndex}` : undefined, "aria-invalid": validationState === 'error' ? 'true' : undefined, "aria-describedby": validationMessage ? descriptionId : undefined, role: "combobox" }), jsxRuntime.jsxs("div", { className: "absolute inset-y-0 right-0 flex items-center pr-2 gap-1", children: [loading && (jsxRuntime.jsx("div", { className: "animate-spin", children: jsxRuntime.jsx(lucideReact.Search, { className: `${iconSizeClasses[size]} text-ink-400` }) })), !loading && value && !disabled && (jsxRuntime.jsx("button", { type: "button", onClick: handleClear, className: "p-0.5 text-ink-400 hover:text-ink-600 focus:outline-none", "aria-label": "Clear", tabIndex: -1, children: jsxRuntime.jsx(lucideReact.X, { className: iconSizeClasses[size] }) })), !loading && (jsxRuntime.jsx(lucideReact.ChevronDown, { className: `${iconSizeClasses[size]} text-ink-400 transition-transform ${isOpen ? 'rotate-180' : ''}` }))] })] }) }), validationMessage && (jsxRuntime.jsx("p", { id: descriptionId, className: `mt-1 text-xs ${validationState ? validationMessageColors[validationState] : 'text-ink-500'}`, role: "alert", "aria-live": "polite", children: validationMessage })), helperText && !validationMessage && (jsxRuntime.jsx("p", { className: "mt-1 text-xs text-ink-500", children: helperText })), isOpen && (jsxRuntime.jsx("div", { className: "absolute z-50 mt-1 w-full bg-white rounded-md shadow-lg border border-paper-200 max-h-60 overflow-auto", role: "listbox", id: listboxId, "aria-label": "Available options", children: loading ? (jsxRuntime.jsx("div", { className: "px-4 py-8 text-center text-ink-500 text-sm", role: "status", "aria-live": "polite", children: "Loading..." })) : filteredOptions.length === 0 && !canCreateOption ? (jsxRuntime.jsx("div", { className: "px-4 py-8 text-center text-ink-500 text-sm", role: "status", "aria-live": "polite", children: "No options found" })) : (jsxRuntime.jsxs("ul", { ref: listRef, children: [filteredOptions.map((option, index) => {
|
|
2672
|
+
`, "aria-labelledby": label ? labelId : undefined, "aria-label": !label ? 'Combobox' : undefined, "aria-expanded": isOpen, "aria-autocomplete": "list", "aria-controls": listboxId, "aria-activedescendant": isOpen && filteredOptions.length > 0 ? `option-${highlightedIndex}` : undefined, "aria-invalid": validationState === 'error' ? 'true' : undefined, "aria-describedby": validationMessage ? descriptionId : undefined, "aria-required": required, role: "combobox" }), jsxRuntime.jsxs("div", { className: "absolute inset-y-0 right-0 flex items-center pr-2 gap-1", children: [loading && (jsxRuntime.jsx("div", { className: "animate-spin", children: jsxRuntime.jsx(lucideReact.Search, { className: `${iconSizeClasses[size]} text-ink-400` }) })), !loading && value && !disabled && (jsxRuntime.jsx("button", { type: "button", onClick: handleClear, className: "p-0.5 text-ink-400 hover:text-ink-600 focus:outline-none", "aria-label": "Clear", tabIndex: -1, children: jsxRuntime.jsx(lucideReact.X, { className: iconSizeClasses[size] }) })), !loading && (jsxRuntime.jsx(lucideReact.ChevronDown, { className: `${iconSizeClasses[size]} text-ink-400 transition-transform ${isOpen ? 'rotate-180' : ''}` }))] })] }) }), validationMessage && (jsxRuntime.jsx("p", { id: descriptionId, className: `mt-1 text-xs ${validationState ? validationMessageColors[validationState] : 'text-ink-500'}`, role: "alert", "aria-live": "polite", children: validationMessage })), helperText && !validationMessage && (jsxRuntime.jsx("p", { className: "mt-1 text-xs text-ink-500", children: helperText })), isOpen && (jsxRuntime.jsx("div", { className: "absolute z-50 mt-1 w-full bg-white rounded-md shadow-lg border border-paper-200 max-h-60 overflow-auto", role: "listbox", id: listboxId, "aria-label": "Available options", children: loading ? (jsxRuntime.jsx("div", { className: "px-4 py-8 text-center text-ink-500 text-sm", role: "status", "aria-live": "polite", children: "Loading..." })) : filteredOptions.length === 0 && !canCreateOption ? (jsxRuntime.jsx("div", { className: "px-4 py-8 text-center text-ink-500 text-sm", role: "status", "aria-live": "polite", children: "No options found" })) : (jsxRuntime.jsxs("ul", { ref: listRef, children: [filteredOptions.map((option, index) => {
|
|
2671
2673
|
const Icon = option.icon;
|
|
2672
2674
|
const isSelected = option.value === value;
|
|
2673
2675
|
const isHighlighted = index === highlightedIndex;
|
|
@@ -4606,6 +4608,8 @@ function BottomSheetActions({ children, className = '' }) {
|
|
|
4606
4608
|
return (jsxRuntime.jsx("div", { className: `flex flex-col gap-2 px-4 py-4 border-t border-paper-200 bg-white ${className}`, children: children }));
|
|
4607
4609
|
}
|
|
4608
4610
|
|
|
4611
|
+
// Selector for all focusable elements
|
|
4612
|
+
const FOCUSABLE_SELECTOR = 'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])';
|
|
4609
4613
|
const sizeClasses$9 = {
|
|
4610
4614
|
sm: 'max-w-md',
|
|
4611
4615
|
md: 'max-w-lg',
|
|
@@ -4683,29 +4687,78 @@ const sizeClasses$9 = {
|
|
|
4683
4687
|
function Modal({ isOpen, onClose, title, children, size = 'md', showCloseButton = true, animation = 'scale', scrollable = false, maxHeight, mobileMode = 'auto', mobileHeight = 'lg', mobileShowHandle = true, }) {
|
|
4684
4688
|
const modalRef = React.useRef(null);
|
|
4685
4689
|
const mouseDownOnBackdrop = React.useRef(false);
|
|
4690
|
+
const previousActiveElement = React.useRef(null);
|
|
4686
4691
|
const titleId = React.useId();
|
|
4687
4692
|
const isMobile = useIsMobile();
|
|
4693
|
+
// Get all focusable elements within the modal
|
|
4694
|
+
const getFocusableElements = React.useCallback(() => {
|
|
4695
|
+
if (!modalRef.current)
|
|
4696
|
+
return [];
|
|
4697
|
+
return Array.from(modalRef.current.querySelectorAll(FOCUSABLE_SELECTOR))
|
|
4698
|
+
.filter(el => el.offsetParent !== null); // Filter out hidden elements
|
|
4699
|
+
}, []);
|
|
4688
4700
|
// Determine if we should use BottomSheet
|
|
4689
4701
|
const useBottomSheet = mobileMode === 'sheet' ||
|
|
4690
4702
|
(mobileMode === 'auto' && isMobile);
|
|
4691
|
-
// Handle escape key (only for modal mode, BottomSheet handles its own)
|
|
4703
|
+
// Handle escape key and focus trap (only for modal mode, BottomSheet handles its own)
|
|
4692
4704
|
React.useEffect(() => {
|
|
4693
4705
|
if (useBottomSheet)
|
|
4694
|
-
return; // BottomSheet handles
|
|
4695
|
-
const
|
|
4706
|
+
return; // BottomSheet handles its own focus
|
|
4707
|
+
const handleKeyDown = (e) => {
|
|
4696
4708
|
if (e.key === 'Escape' && isOpen) {
|
|
4697
4709
|
onClose();
|
|
4710
|
+
return;
|
|
4711
|
+
}
|
|
4712
|
+
// Focus trap: keep focus within modal
|
|
4713
|
+
if (e.key === 'Tab' && isOpen) {
|
|
4714
|
+
const focusableElements = getFocusableElements();
|
|
4715
|
+
if (focusableElements.length === 0)
|
|
4716
|
+
return;
|
|
4717
|
+
const firstElement = focusableElements[0];
|
|
4718
|
+
const lastElement = focusableElements[focusableElements.length - 1];
|
|
4719
|
+
if (e.shiftKey) {
|
|
4720
|
+
// Shift+Tab: if on first element, wrap to last
|
|
4721
|
+
if (document.activeElement === firstElement) {
|
|
4722
|
+
e.preventDefault();
|
|
4723
|
+
lastElement.focus();
|
|
4724
|
+
}
|
|
4725
|
+
}
|
|
4726
|
+
else {
|
|
4727
|
+
// Tab: if on last element, wrap to first
|
|
4728
|
+
if (document.activeElement === lastElement) {
|
|
4729
|
+
e.preventDefault();
|
|
4730
|
+
firstElement.focus();
|
|
4731
|
+
}
|
|
4732
|
+
}
|
|
4698
4733
|
}
|
|
4699
4734
|
};
|
|
4700
4735
|
if (isOpen) {
|
|
4701
|
-
|
|
4736
|
+
// Store the currently focused element to restore later
|
|
4737
|
+
previousActiveElement.current = document.activeElement;
|
|
4738
|
+
document.addEventListener('keydown', handleKeyDown);
|
|
4702
4739
|
document.body.style.overflow = 'hidden';
|
|
4740
|
+
// Set initial focus to first focusable element
|
|
4741
|
+
// Use requestAnimationFrame to ensure the modal is rendered
|
|
4742
|
+
requestAnimationFrame(() => {
|
|
4743
|
+
const focusableElements = getFocusableElements();
|
|
4744
|
+
if (focusableElements.length > 0) {
|
|
4745
|
+
focusableElements[0].focus();
|
|
4746
|
+
}
|
|
4747
|
+
else if (modalRef.current) {
|
|
4748
|
+
// If no focusable elements, focus the modal container
|
|
4749
|
+
modalRef.current.focus();
|
|
4750
|
+
}
|
|
4751
|
+
});
|
|
4703
4752
|
}
|
|
4704
4753
|
return () => {
|
|
4705
|
-
document.removeEventListener('keydown',
|
|
4754
|
+
document.removeEventListener('keydown', handleKeyDown);
|
|
4706
4755
|
document.body.style.overflow = 'unset';
|
|
4756
|
+
// Restore focus to the previously focused element
|
|
4757
|
+
if (previousActiveElement.current && typeof previousActiveElement.current.focus === 'function') {
|
|
4758
|
+
previousActiveElement.current.focus();
|
|
4759
|
+
}
|
|
4707
4760
|
};
|
|
4708
|
-
}, [isOpen, onClose, useBottomSheet]);
|
|
4761
|
+
}, [isOpen, onClose, useBottomSheet, getFocusableElements]);
|
|
4709
4762
|
// Track if mousedown originated on the backdrop
|
|
4710
4763
|
const handleBackdropMouseDown = (e) => {
|
|
4711
4764
|
if (e.target === e.currentTarget) {
|
|
@@ -4746,7 +4799,7 @@ function Modal({ isOpen, onClose, title, children, size = 'md', showCloseButton
|
|
|
4746
4799
|
return reactDom.createPortal(jsxRuntime.jsx(BottomSheet, { isOpen: isOpen, onClose: onClose, title: title, height: mobileHeight, showHandle: mobileShowHandle, showCloseButton: showCloseButton, children: children }), document.body);
|
|
4747
4800
|
}
|
|
4748
4801
|
// Render as standard modal on desktop
|
|
4749
|
-
const modalContent = (jsxRuntime.jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center p-4 bg-ink-900 bg-opacity-50 backdrop-blur-sm animate-fade-in", onMouseDown: handleBackdropMouseDown, onClick: handleBackdropClick, children: jsxRuntime.jsxs("div", { ref: modalRef, className: `${sizeClasses$9[size]} w-full bg-white bg-subtle-grain rounded-xl shadow-2xl border border-paper-200 ${getAnimationClass()}`, role: "dialog", "aria-modal": "true", "aria-labelledby": titleId, children: [jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-6 py-4 border-b border-paper-200", children: [jsxRuntime.jsx("h3", { id: titleId, className: "text-lg font-medium text-ink-900", children: title }), showCloseButton && (jsxRuntime.jsx("button", { onClick: onClose, className: "text-ink-400 hover:text-ink-600 transition-colors", "aria-label": "Close modal", children: jsxRuntime.jsx(lucideReact.X, { className: "h-5 w-5" }) }))] }), jsxRuntime.jsx("div", { className: `px-6 py-4 ${scrollable || maxHeight ? 'overflow-y-auto' : ''}`, style: {
|
|
4802
|
+
const modalContent = (jsxRuntime.jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center p-4 bg-ink-900 bg-opacity-50 backdrop-blur-sm animate-fade-in", onMouseDown: handleBackdropMouseDown, onClick: handleBackdropClick, children: jsxRuntime.jsxs("div", { ref: modalRef, className: `${sizeClasses$9[size]} w-full bg-white bg-subtle-grain rounded-xl shadow-2xl border border-paper-200 ${getAnimationClass()}`, role: "dialog", "aria-modal": "true", "aria-labelledby": titleId, tabIndex: -1, children: [jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-6 py-4 border-b border-paper-200", children: [jsxRuntime.jsx("h3", { id: titleId, className: "text-lg font-medium text-ink-900", children: title }), showCloseButton && (jsxRuntime.jsx("button", { onClick: onClose, className: "text-ink-400 hover:text-ink-600 transition-colors", "aria-label": "Close modal", children: jsxRuntime.jsx(lucideReact.X, { className: "h-5 w-5" }) }))] }), jsxRuntime.jsx("div", { className: `px-6 py-4 ${scrollable || maxHeight ? 'overflow-y-auto' : ''}`, style: {
|
|
4750
4803
|
maxHeight: maxHeight || (scrollable ? 'calc(100vh - 200px)' : undefined),
|
|
4751
4804
|
}, children: children })] }) }));
|
|
4752
4805
|
return reactDom.createPortal(modalContent, document.body);
|
|
@@ -7654,7 +7707,7 @@ const Autocomplete = React.forwardRef(({ value, onChange, options = [], onSearch
|
|
|
7654
7707
|
${error
|
|
7655
7708
|
? 'border-error-500 focus:ring-error-400 focus:border-error-400'
|
|
7656
7709
|
: 'border-paper-300'}
|
|
7657
|
-
`, role: "combobox", "aria-labelledby": label ? labelId : undefined, "aria-label": !label ? 'Search' : undefined, "aria-autocomplete": "list", "aria-expanded": isOpen, "aria-controls": listboxId, "aria-activedescendant": highlightedIndex >= 0 ? `autocomplete-option-${highlightedIndex}` : undefined, "aria-invalid": error ? 'true' : undefined, "aria-describedby": error ? errorId : undefined, "aria-busy": loading }), clearable && value && !disabled && (jsxRuntime.jsx("button", { type: "button", onClick: handleClear, className: "absolute right-3 top-1/2 -translate-y-1/2 text-ink-400 hover:text-ink-600 transition-colors", "aria-label": "Clear", children: jsxRuntime.jsx(lucideReact.X, { className: "h-4 w-4" }) }))] }), isOpen && filteredOptions.length > 0 && (jsxRuntime.jsx("div", { ref: dropdownRef, id: listboxId, className: "absolute z-50 w-full mt-1 bg-white border border-paper-200 rounded-lg shadow-lg max-h-60 overflow-y-auto", role: "listbox", "aria-label": "Search results", children: filteredOptions.map((option, index) => (option.isHeader ? (
|
|
7710
|
+
`, role: "combobox", "aria-labelledby": label ? labelId : undefined, "aria-label": !label ? 'Search' : undefined, "aria-autocomplete": "list", "aria-expanded": isOpen, "aria-controls": listboxId, "aria-activedescendant": highlightedIndex >= 0 ? `autocomplete-option-${highlightedIndex}` : undefined, "aria-invalid": error ? 'true' : undefined, "aria-describedby": error ? errorId : undefined, "aria-required": required, "aria-busy": loading }), clearable && value && !disabled && (jsxRuntime.jsx("button", { type: "button", onClick: handleClear, className: "absolute right-3 top-1/2 -translate-y-1/2 text-ink-400 hover:text-ink-600 transition-colors", "aria-label": "Clear", children: jsxRuntime.jsx(lucideReact.X, { className: "h-4 w-4" }) }))] }), isOpen && filteredOptions.length > 0 && (jsxRuntime.jsx("div", { ref: dropdownRef, id: listboxId, className: "absolute z-50 w-full mt-1 bg-white border border-paper-200 rounded-lg shadow-lg max-h-60 overflow-y-auto", role: "listbox", "aria-label": "Search results", children: filteredOptions.map((option, index) => (option.isHeader ? (
|
|
7658
7711
|
// Render section header (non-selectable)
|
|
7659
7712
|
jsxRuntime.jsx("div", { className: "px-3 py-2 text-xs font-semibold text-ink-500 uppercase tracking-wide bg-paper-50 border-t border-paper-200 first:border-t-0 first:rounded-t-lg cursor-default", role: "presentation", children: option.label }, `header-${option.value}`)) : (
|
|
7660
7713
|
// Render selectable option
|