@papernote/ui 1.13.0 → 1.14.1

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/index.js CHANGED
@@ -725,15 +725,15 @@ function usePrefersMobile() {
725
725
 
726
726
  // Size classes for trigger button
727
727
  const sizeClasses$d = {
728
- sm: 'h-8 text-sm py-1',
729
- md: 'h-10 text-base py-2',
730
- lg: 'h-12 text-base py-3 min-h-touch', // 44px touch target
728
+ sm: "h-8 text-sm py-1",
729
+ md: "h-10 text-base py-2",
730
+ lg: "h-12 text-base py-3 min-h-touch", // 44px touch target
731
731
  };
732
732
  // Size classes for options
733
733
  const optionSizeClasses = {
734
- sm: 'py-2 text-sm',
735
- md: 'py-2.5 text-sm',
736
- lg: 'py-3.5 text-base min-h-touch', // 44px touch target for mobile
734
+ sm: "py-2 text-sm",
735
+ md: "py-2.5 text-sm",
736
+ lg: "py-3.5 text-base min-h-touch", // 44px touch target for mobile
737
737
  };
738
738
  /**
739
739
  * Select - Dropdown select component with search, groups, virtual scrolling, and mobile support
@@ -806,9 +806,9 @@ const optionSizeClasses = {
806
806
  * ```
807
807
  */
808
808
  const Select = React.forwardRef((props, ref) => {
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;
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;
810
810
  const [isOpen, setIsOpen] = React.useState(false);
811
- const [searchQuery, setSearchQuery] = React.useState('');
811
+ const [searchQuery, setSearchQuery] = React.useState("");
812
812
  const [scrollTop, setScrollTop] = React.useState(0);
813
813
  const [activeDescendant] = React.useState(undefined);
814
814
  const [dropdownPosition, setDropdownPosition] = React.useState(null);
@@ -821,10 +821,10 @@ const Select = React.forwardRef((props, ref) => {
821
821
  const nativeSelectRef = React.useRef(null);
822
822
  // Detect mobile viewport
823
823
  const isMobile = useIsMobile();
824
- const useMobileSheet = mobileMode === 'auto' && isMobile;
825
- const useNativeSelect = mobileMode === 'native' && isMobile;
824
+ const useMobileSheet = mobileMode === "auto" && isMobile;
825
+ const useNativeSelect = mobileMode === "native" && isMobile;
826
826
  // Auto-size for mobile
827
- const effectiveSize = isMobile && size === 'md' ? 'lg' : size;
827
+ const effectiveSize = isMobile && size === "md" ? "lg" : size;
828
828
  // Generate unique IDs for ARIA
829
829
  const labelId = React.useId();
830
830
  const listboxId = React.useId();
@@ -838,11 +838,8 @@ const Select = React.forwardRef((props, ref) => {
838
838
  close: () => setIsOpen(false),
839
839
  }));
840
840
  // Flatten all options (from both options and groups)
841
- const allOptions = [
842
- ...options,
843
- ...groups.flatMap(group => group.options)
844
- ];
845
- const selectedOption = allOptions.find(opt => opt.value === value);
841
+ const allOptions = [...options, ...groups.flatMap((group) => group.options)];
842
+ const selectedOption = allOptions.find((opt) => opt.value === value);
846
843
  // Filter options/groups based on search
847
844
  const getFilteredData = () => {
848
845
  if (!searchable || !searchQuery) {
@@ -850,41 +847,58 @@ const Select = React.forwardRef((props, ref) => {
850
847
  }
851
848
  const query = searchQuery.toLowerCase();
852
849
  // Filter flat options
853
- const filteredOptions = options.filter(opt => opt.label.toLowerCase().includes(query));
850
+ const filteredOptions = options.filter((opt) => opt.label.toLowerCase().includes(query));
854
851
  // Filter grouped options
855
852
  const filteredGroups = groups
856
- .map(group => ({
853
+ .map((group) => ({
857
854
  ...group,
858
- options: group.options.filter(opt => opt.label.toLowerCase().includes(query))
855
+ options: group.options.filter((opt) => opt.label.toLowerCase().includes(query)),
859
856
  }))
860
- .filter(group => group.options.length > 0);
857
+ .filter((group) => group.options.length > 0);
861
858
  return { options: filteredOptions, groups: filteredGroups };
862
859
  };
863
860
  const { options: filteredOptions, groups: filteredGroups } = getFilteredData();
864
861
  // Virtual scrolling calculations
865
- const totalItems = filteredOptions.length + filteredGroups.flatMap(g => g.options).length;
862
+ const totalItems = filteredOptions.length + filteredGroups.flatMap((g) => g.options).length;
866
863
  const useVirtualScrolling = virtualized && totalItems > 50;
867
864
  const visibleRangeStart = useVirtualScrolling
868
865
  ? Math.floor(scrollTop / virtualItemHeight)
869
866
  : 0;
870
867
  const visibleRangeEnd = useVirtualScrolling
871
- ? Math.min(visibleRangeStart + Math.ceil(parseInt(virtualHeight) / virtualItemHeight) + 5, totalItems)
868
+ ? Math.min(visibleRangeStart +
869
+ Math.ceil(parseInt(virtualHeight) / virtualItemHeight) +
870
+ 5, totalItems)
872
871
  : totalItems;
873
872
  // Flatten all filtered items for virtualization
874
873
  const allFilteredItems = [
875
- ...filteredOptions.map((opt, idx) => ({ type: 'option', option: opt, groupIndex: -1, optionIndex: idx })),
876
- ...filteredGroups.flatMap((group, groupIdx) => group.options.map((opt, optIdx) => ({ type: 'grouped', option: opt, groupIndex: groupIdx, optionIndex: optIdx, groupLabel: group.label })))
874
+ ...filteredOptions.map((opt, idx) => ({
875
+ type: "option",
876
+ option: opt,
877
+ groupIndex: -1,
878
+ optionIndex: idx,
879
+ })),
880
+ ...filteredGroups.flatMap((group, groupIdx) => group.options.map((opt, optIdx) => ({
881
+ type: "grouped",
882
+ option: opt,
883
+ groupIndex: groupIdx,
884
+ optionIndex: optIdx,
885
+ groupLabel: group.label,
886
+ }))),
877
887
  ];
878
888
  const visibleItems = useVirtualScrolling
879
889
  ? allFilteredItems.slice(visibleRangeStart, visibleRangeEnd)
880
890
  : allFilteredItems;
881
- const offsetY = useVirtualScrolling ? visibleRangeStart * virtualItemHeight : 0;
882
- const totalHeight = useVirtualScrolling ? totalItems * virtualItemHeight : 'auto';
891
+ const offsetY = useVirtualScrolling
892
+ ? visibleRangeStart * virtualItemHeight
893
+ : 0;
894
+ const totalHeight = useVirtualScrolling
895
+ ? totalItems * virtualItemHeight
896
+ : "auto";
883
897
  // Check if we should show "Create" option
884
898
  const showCreateOption = creatable &&
885
- searchQuery.trim() !== '' &&
886
- !filteredOptions.some(opt => opt.label.toLowerCase() === searchQuery.toLowerCase()) &&
887
- !filteredGroups.some(group => group.options.some(opt => opt.label.toLowerCase() === searchQuery.toLowerCase()));
899
+ searchQuery.trim() !== "" &&
900
+ !filteredOptions.some((opt) => opt.label.toLowerCase() === searchQuery.toLowerCase()) &&
901
+ !filteredGroups.some((group) => group.options.some((opt) => opt.label.toLowerCase() === searchQuery.toLowerCase()));
888
902
  // Handle creating new option
889
903
  const handleCreateOption = () => {
890
904
  if (onCreateOption) {
@@ -894,7 +908,7 @@ const Select = React.forwardRef((props, ref) => {
894
908
  // If no callback, just select the typed value
895
909
  onChange?.(searchQuery.trim());
896
910
  }
897
- setSearchQuery('');
911
+ setSearchQuery("");
898
912
  setIsOpen(false);
899
913
  };
900
914
  // Handle click outside (desktop dropdown only)
@@ -908,14 +922,14 @@ const Select = React.forwardRef((props, ref) => {
908
922
  const isOutsideDropdown = dropdownRef.current && !dropdownRef.current.contains(target);
909
923
  if (isOutsideSelect && isOutsideDropdown) {
910
924
  setIsOpen(false);
911
- setSearchQuery('');
925
+ setSearchQuery("");
912
926
  }
913
927
  };
914
928
  if (isOpen) {
915
- document.addEventListener('mousedown', handleClickOutside);
929
+ document.addEventListener("mousedown", handleClickOutside);
916
930
  }
917
931
  return () => {
918
- document.removeEventListener('mousedown', handleClickOutside);
932
+ document.removeEventListener("mousedown", handleClickOutside);
919
933
  };
920
934
  }, [isOpen, useMobileSheet]);
921
935
  // Focus search input when opened
@@ -949,8 +963,8 @@ const Select = React.forwardRef((props, ref) => {
949
963
  const hasSpaceBelow = spaceBelow >= dropdownHeight + gap;
950
964
  const hasSpaceAbove = spaceAbove >= dropdownHeight + gap;
951
965
  // Prefer bottom placement, flip to top if not enough space below but enough above
952
- const placement = hasSpaceBelow || !hasSpaceAbove ? 'bottom' : 'top';
953
- const top = placement === 'bottom'
966
+ const placement = hasSpaceBelow || !hasSpaceAbove ? "bottom" : "top";
967
+ const top = placement === "bottom"
954
968
  ? rect.bottom + gap
955
969
  : rect.top - dropdownHeight - gap;
956
970
  setDropdownPosition({
@@ -963,23 +977,23 @@ const Select = React.forwardRef((props, ref) => {
963
977
  // Initial position calculation
964
978
  updatePosition();
965
979
  // Listen for scroll events on all scrollable ancestors
966
- window.addEventListener('scroll', updatePosition, true);
967
- window.addEventListener('resize', updatePosition);
980
+ window.addEventListener("scroll", updatePosition, true);
981
+ window.addEventListener("resize", updatePosition);
968
982
  return () => {
969
- window.removeEventListener('scroll', updatePosition, true);
970
- window.removeEventListener('resize', updatePosition);
983
+ window.removeEventListener("scroll", updatePosition, true);
984
+ window.removeEventListener("resize", updatePosition);
971
985
  };
972
986
  }, [isOpen, useMobileSheet, usePortal]);
973
987
  // Lock body scroll when mobile sheet is open
974
988
  React.useEffect(() => {
975
989
  if (useMobileSheet && isOpen) {
976
- document.body.style.overflow = 'hidden';
990
+ document.body.style.overflow = "hidden";
977
991
  }
978
992
  else {
979
- document.body.style.overflow = '';
993
+ document.body.style.overflow = "";
980
994
  }
981
995
  return () => {
982
- document.body.style.overflow = '';
996
+ document.body.style.overflow = "";
983
997
  };
984
998
  }, [isOpen, useMobileSheet]);
985
999
  // Handle escape key for mobile sheet
@@ -987,83 +1001,105 @@ const Select = React.forwardRef((props, ref) => {
987
1001
  if (!useMobileSheet || !isOpen)
988
1002
  return;
989
1003
  const handleEscape = (e) => {
990
- if (e.key === 'Escape') {
1004
+ if (e.key === "Escape") {
991
1005
  setIsOpen(false);
992
- setSearchQuery('');
1006
+ setSearchQuery("");
993
1007
  }
994
1008
  };
995
- document.addEventListener('keydown', handleEscape);
996
- return () => document.removeEventListener('keydown', handleEscape);
1009
+ document.addEventListener("keydown", handleEscape);
1010
+ return () => document.removeEventListener("keydown", handleEscape);
997
1011
  }, [isOpen, useMobileSheet]);
998
1012
  const handleSelect = (optionValue) => {
999
1013
  onChange?.(optionValue);
1000
1014
  setIsOpen(false);
1001
- setSearchQuery('');
1015
+ setSearchQuery("");
1002
1016
  };
1003
1017
  const handleClose = () => {
1004
1018
  setIsOpen(false);
1005
- setSearchQuery('');
1019
+ setSearchQuery("");
1006
1020
  };
1007
1021
  // Render option button (shared between desktop and mobile)
1008
1022
  const renderOption = (option, isSelected, mobile = false) => (jsxRuntime.jsxs("button", { type: "button", onClick: () => !option.disabled && handleSelect(option.value), disabled: option.disabled, className: `
1009
1023
  w-full flex items-center justify-between px-4 transition-colors
1010
1024
  ${mobile ? optionSizeClasses.lg : optionSizeClasses[effectiveSize]}
1011
- ${isSelected ? 'bg-accent-50 text-accent-900' : 'text-ink-700'}
1012
- ${option.disabled ? 'opacity-40 cursor-not-allowed' : 'hover:bg-paper-50 active:bg-paper-100 cursor-pointer'}
1013
- `, role: "option", "aria-selected": isSelected, children: [jsxRuntime.jsxs("span", { className: "flex items-center gap-2", children: [option.icon && jsxRuntime.jsx("span", { children: option.icon }), option.label] }), isSelected && jsxRuntime.jsx(lucideReact.Check, { className: `${mobile ? 'h-5 w-5' : 'h-4 w-4'} text-accent-600` })] }, option.value));
1025
+ ${isSelected ? "bg-accent-50 text-accent-900" : "text-ink-700"}
1026
+ ${option.disabled ? "opacity-40 cursor-not-allowed" : "hover:bg-paper-50 active:bg-paper-100 cursor-pointer"}
1027
+ `, role: "option", "aria-selected": isSelected, children: [jsxRuntime.jsxs("span", { className: "flex items-center gap-2", children: [option.icon && jsxRuntime.jsx("span", { children: option.icon }), option.label] }), isSelected && (jsxRuntime.jsx(lucideReact.Check, { className: `${mobile ? "h-5 w-5" : "h-4 w-4"} text-accent-600` }))] }, option.value));
1014
1028
  // Render options list content (shared between desktop and mobile)
1015
1029
  const renderOptionsContent = (mobile = false) => {
1016
1030
  if (loading) {
1017
1031
  return (jsxRuntime.jsxs("div", { className: "px-4 py-8 flex items-center justify-center", role: "status", "aria-live": "polite", children: [jsxRuntime.jsx(lucideReact.Loader2, { className: "h-5 w-5 animate-spin text-ink-500" }), jsxRuntime.jsx("span", { className: "ml-2 text-sm text-ink-500", children: "Loading..." })] }));
1018
1032
  }
1019
- if (filteredOptions.length === 0 && filteredGroups.length === 0 && !showCreateOption) {
1033
+ if (filteredOptions.length === 0 &&
1034
+ filteredGroups.length === 0 &&
1035
+ !showCreateOption) {
1020
1036
  return (jsxRuntime.jsx("div", { className: "px-4 py-3 text-sm text-ink-500 text-center", role: "status", "aria-live": "polite", children: "No options found" }));
1021
1037
  }
1022
1038
  return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [showCreateOption && (jsxRuntime.jsx("button", { type: "button", onClick: handleCreateOption, className: `
1023
1039
  w-full flex items-center px-4 text-accent-700 hover:bg-accent-50 transition-colors border-b border-paper-200
1024
- ${mobile ? 'py-3.5 text-base' : 'py-2.5 text-sm'}
1025
- `, children: jsxRuntime.jsxs("span", { className: "font-medium", children: ["Create \"", searchQuery, "\""] }) })), useVirtualScrolling ? (jsxRuntime.jsx("div", { style: { height: totalHeight, position: 'relative' }, children: jsxRuntime.jsx("div", { style: { transform: `translateY(${offsetY}px)` }, children: visibleItems.map((item) => {
1040
+ ${mobile ? "py-3.5 text-base" : "py-2.5 text-sm"}
1041
+ `, children: jsxRuntime.jsxs("span", { className: "font-medium", children: ["Create \"", searchQuery, "\""] }) })), useVirtualScrolling ? (jsxRuntime.jsx("div", { style: { height: totalHeight, position: "relative" }, children: jsxRuntime.jsx("div", { style: { transform: `translateY(${offsetY}px)` }, children: visibleItems.map((item) => {
1026
1042
  const option = item.option;
1027
1043
  const isSelected = option.value === value;
1028
1044
  const key = `${item.type}-${item.groupIndex}-${item.optionIndex}-${option.value}`;
1029
- return (jsxRuntime.jsxs("button", { type: "button", onClick: () => !option.disabled && handleSelect(option.value), disabled: option.disabled, style: { height: mobile ? '56px' : `${virtualItemHeight}px` }, className: `
1045
+ return (jsxRuntime.jsxs("button", { type: "button", onClick: () => !option.disabled && handleSelect(option.value), disabled: option.disabled, style: {
1046
+ height: mobile ? "56px" : `${virtualItemHeight}px`,
1047
+ }, className: `
1030
1048
  w-full flex items-center justify-between px-4 transition-colors
1031
- ${mobile ? 'text-base' : 'text-sm'}
1032
- ${isSelected ? 'bg-accent-50 text-accent-900' : 'text-ink-700'}
1033
- ${option.disabled ? 'opacity-40 cursor-not-allowed' : 'hover:bg-paper-50 cursor-pointer'}
1034
- `, role: "option", "aria-selected": isSelected, children: [jsxRuntime.jsxs("span", { className: "flex items-center gap-2", children: [option.icon && jsxRuntime.jsx("span", { children: option.icon }), option.label] }), isSelected && jsxRuntime.jsx(lucideReact.Check, { className: `${mobile ? 'h-5 w-5' : 'h-4 w-4'} text-accent-600` })] }, key));
1049
+ ${mobile ? "text-base" : "text-sm"}
1050
+ ${isSelected ? "bg-accent-50 text-accent-900" : "text-ink-700"}
1051
+ ${option.disabled ? "opacity-40 cursor-not-allowed" : "hover:bg-paper-50 cursor-pointer"}
1052
+ `, role: "option", "aria-selected": isSelected, children: [jsxRuntime.jsxs("span", { className: "flex items-center gap-2", children: [option.icon && jsxRuntime.jsx("span", { children: option.icon }), option.label] }), isSelected && (jsxRuntime.jsx(lucideReact.Check, { className: `${mobile ? "h-5 w-5" : "h-4 w-4"} text-accent-600` }))] }, key));
1035
1053
  }) }) })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [filteredOptions.map((option) => renderOption(option, option.value === value, mobile)), filteredGroups.map((group) => (jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("div", { className: `
1036
1054
  px-4 font-semibold text-ink-500 uppercase tracking-wider bg-paper-50 border-t border-b border-paper-200
1037
- ${mobile ? 'py-2.5 text-xs' : 'py-2 text-xs'}
1055
+ ${mobile ? "py-2.5 text-xs" : "py-2 text-xs"}
1038
1056
  `, children: group.label }), group.options.map((option) => renderOption(option, option.value === value, mobile))] }, group.label)))] }))] }));
1039
1057
  };
1040
1058
  // Native select for mobile (optional)
1041
1059
  if (useNativeSelect) {
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: `
1060
+ 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: `
1043
1061
  input w-full appearance-none pr-10
1044
1062
  ${sizeClasses$d[effectiveSize]}
1045
- ${error ? 'border-error-400 focus:border-error-400 focus:ring-error-400' : ''}
1046
- ${disabled ? 'opacity-40 cursor-not-allowed' : 'cursor-pointer'}
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 }))] }));
1063
+ ${error ? "border-error-400 focus:border-error-400 focus:ring-error-400" : ""}
1064
+ ${disabled ? "opacity-40 cursor-not-allowed" : "cursor-pointer"}
1065
+ `, "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 }))] }));
1048
1066
  }
1049
1067
  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: `
1050
1068
  input w-full flex items-center justify-between px-3
1051
1069
  ${sizeClasses$d[effectiveSize]}
1052
- ${error ? 'border-error-400 focus:border-error-400 focus:ring-error-400' : ''}
1053
- ${disabled ? 'opacity-40 cursor-not-allowed' : 'cursor-pointer'}
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) => {
1070
+ ${error ? "border-error-400 focus:border-error-400 focus:ring-error-400" : ""}
1071
+ ${disabled ? "opacity-40 cursor-not-allowed" : "cursor-pointer"}
1072
+ `, 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("span", { role: "button", tabIndex: -1, onClick: (e) => {
1055
1073
  e.stopPropagation();
1056
- onChange?.('');
1074
+ onChange?.("");
1057
1075
  setIsOpen(false);
1058
- }, className: "text-ink-400 hover:text-ink-600 transition-colors p-0.5", "aria-label": "Clear selection", children: jsxRuntime.jsx(lucideReact.X, { className: `${effectiveSize === 'lg' ? 'h-5 w-5' : 'h-4 w-4'}` }) })), jsxRuntime.jsx(lucideReact.ChevronDown, { className: `${effectiveSize === 'lg' ? 'h-5 w-5' : 'h-4 w-4'} text-ink-500 transition-transform ${isOpen ? 'rotate-180' : ''}` })] })] }) }), isOpen && !useMobileSheet && (usePortal ? dropdownPosition : true) && (usePortal ? reactDom.createPortal(jsxRuntime.jsxs("div", { ref: dropdownRef, className: `fixed z-[9999] bg-white bg-subtle-grain rounded-lg shadow-lg border border-paper-200 max-h-60 overflow-hidden animate-fade-in ${dropdownPosition?.placement === 'top' ? 'origin-bottom' : 'origin-top'}`, style: {
1059
- top: dropdownPosition.top,
1060
- left: dropdownPosition.left,
1061
- width: dropdownPosition.width,
1062
- }, children: [searchable && (jsxRuntime.jsx("div", { className: "p-2 border-b border-paper-200", children: jsxRuntime.jsxs("div", { className: "relative", children: [jsxRuntime.jsx(lucideReact.Search, { className: "absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-ink-400" }), jsxRuntime.jsx("input", { ref: searchInputRef, type: "text", value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), placeholder: "Search...", className: "w-full pl-9 pr-3 py-2 text-sm border border-paper-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-accent-400 focus:border-accent-400", role: "searchbox", "aria-label": "Search options", "aria-autocomplete": "list", "aria-controls": listboxId })] }) })), jsxRuntime.jsx("div", { ref: listRef, id: listboxId, className: "overflow-y-auto", style: { maxHeight: useVirtualScrolling ? virtualHeight : '12rem' }, onScroll: (e) => useVirtualScrolling && setScrollTop(e.currentTarget.scrollTop), role: "listbox", "aria-label": "Available options", "aria-multiselectable": "false", children: renderOptionsContent(false) })] }), document.body) : (
1063
- // Non-portal dropdown (inline, relative positioning)
1064
- jsxRuntime.jsxs("div", { ref: dropdownRef, className: "absolute z-50 mt-1 w-full bg-white bg-subtle-grain rounded-lg shadow-lg border border-paper-200 max-h-60 overflow-hidden animate-fade-in", children: [searchable && (jsxRuntime.jsx("div", { className: "p-2 border-b border-paper-200", children: jsxRuntime.jsxs("div", { className: "relative", children: [jsxRuntime.jsx(lucideReact.Search, { className: "absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-ink-400" }), jsxRuntime.jsx("input", { ref: searchInputRef, type: "text", value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), placeholder: "Search...", className: "w-full pl-9 pr-3 py-2 text-sm border border-paper-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-accent-400 focus:border-accent-400", role: "searchbox", "aria-label": "Search options", "aria-autocomplete": "list", "aria-controls": listboxId })] }) })), jsxRuntime.jsx("div", { ref: listRef, id: listboxId, className: "overflow-y-auto", style: { maxHeight: useVirtualScrolling ? virtualHeight : '12rem' }, onScroll: (e) => useVirtualScrolling && setScrollTop(e.currentTarget.scrollTop), role: "listbox", "aria-label": "Available options", "aria-multiselectable": "false", children: renderOptionsContent(false) })] }))), isOpen && useMobileSheet && reactDom.createPortal(jsxRuntime.jsxs("div", { className: "fixed inset-0 z-50 flex items-end", onClick: (e) => e.target === e.currentTarget && handleClose(), role: "dialog", "aria-modal": "true", "aria-labelledby": label ? `mobile-${labelId}` : undefined, children: [jsxRuntime.jsx("div", { className: "absolute inset-0 bg-black/50 animate-fade-in" }), jsxRuntime.jsxs("div", { className: "relative w-full bg-white rounded-t-2xl shadow-2xl animate-slide-up max-h-[85vh] flex flex-col", style: { paddingBottom: 'env(safe-area-inset-bottom)' }, children: [jsxRuntime.jsx("div", { className: "py-3 cursor-grab", children: jsxRuntime.jsx("div", { className: "w-12 h-1.5 bg-ink-300 rounded-full mx-auto" }) }), jsxRuntime.jsxs("div", { className: "px-4 pb-3 border-b border-paper-200 flex items-center justify-between", children: [label && (jsxRuntime.jsx("h2", { id: `mobile-${labelId}`, className: "text-lg font-semibold text-ink-900", children: label })), !label && (jsxRuntime.jsx("h2", { className: "text-lg font-semibold text-ink-900", children: placeholder })), jsxRuntime.jsx("button", { onClick: handleClose, className: "text-ink-400 hover:text-ink-600 transition-colors p-2 -mr-2", "aria-label": "Close", children: jsxRuntime.jsx(lucideReact.X, { className: "h-5 w-5" }) })] }), searchable && (jsxRuntime.jsx("div", { className: "p-3 border-b border-paper-200", children: jsxRuntime.jsxs("div", { className: "relative", children: [jsxRuntime.jsx(lucideReact.Search, { className: "absolute left-4 top-1/2 -translate-y-1/2 h-5 w-5 text-ink-400" }), jsxRuntime.jsx("input", { ref: mobileSearchInputRef, type: "text", value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), placeholder: "Search...", inputMode: "search", enterKeyHint: "search", className: "w-full pl-12 pr-4 py-3 text-base border border-paper-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-accent-400 focus:border-accent-400", role: "searchbox", "aria-label": "Search options" })] }) })), jsxRuntime.jsx("div", { id: listboxId, className: "overflow-y-auto flex-1", role: "listbox", "aria-label": "Available options", "aria-multiselectable": "false", children: renderOptionsContent(true) })] })] }), document.body), 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 }))] }));
1076
+ }, onKeyDown: (e) => {
1077
+ if (e.key === "Enter" || e.key === " ") {
1078
+ e.preventDefault();
1079
+ e.stopPropagation();
1080
+ onChange?.("");
1081
+ setIsOpen(false);
1082
+ }
1083
+ }, className: "text-ink-400 hover:text-ink-600 transition-colors p-0.5 cursor-pointer inline-flex", "aria-label": "Clear selection", children: jsxRuntime.jsx(lucideReact.X, { className: `${effectiveSize === "lg" ? "h-5 w-5" : "h-4 w-4"}` }) })), jsxRuntime.jsx(lucideReact.ChevronDown, { className: `${effectiveSize === "lg" ? "h-5 w-5" : "h-4 w-4"} text-ink-500 transition-transform ${isOpen ? "rotate-180" : ""}` })] })] }) }), isOpen &&
1084
+ !useMobileSheet &&
1085
+ (usePortal ? dropdownPosition : true) &&
1086
+ (usePortal ? (reactDom.createPortal(jsxRuntime.jsxs("div", { ref: dropdownRef, className: `fixed z-[9999] bg-white bg-subtle-grain rounded-lg shadow-lg border border-paper-200 max-h-60 overflow-hidden animate-fade-in ${dropdownPosition?.placement === "top"
1087
+ ? "origin-bottom"
1088
+ : "origin-top"}`, style: {
1089
+ top: dropdownPosition.top,
1090
+ left: dropdownPosition.left,
1091
+ width: dropdownPosition.width,
1092
+ }, children: [searchable && (jsxRuntime.jsx("div", { className: "p-2 border-b border-paper-200", children: jsxRuntime.jsxs("div", { className: "relative", children: [jsxRuntime.jsx(lucideReact.Search, { className: "absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-ink-400" }), jsxRuntime.jsx("input", { ref: searchInputRef, type: "text", value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), placeholder: "Search...", className: "w-full pl-9 pr-3 py-2 text-sm border border-paper-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-accent-400 focus:border-accent-400", role: "searchbox", "aria-label": "Search options", "aria-autocomplete": "list", "aria-controls": listboxId })] }) })), jsxRuntime.jsx("div", { ref: listRef, id: listboxId, className: "overflow-y-auto", style: {
1093
+ maxHeight: useVirtualScrolling ? virtualHeight : "12rem",
1094
+ }, onScroll: (e) => useVirtualScrolling && setScrollTop(e.currentTarget.scrollTop), role: "listbox", "aria-label": "Available options", "aria-multiselectable": "false", children: renderOptionsContent(false) })] }), document.body)) : (
1095
+ // Non-portal dropdown (inline, relative positioning)
1096
+ jsxRuntime.jsxs("div", { ref: dropdownRef, className: "absolute z-50 mt-1 w-full bg-white bg-subtle-grain rounded-lg shadow-lg border border-paper-200 max-h-60 overflow-hidden animate-fade-in", children: [searchable && (jsxRuntime.jsx("div", { className: "p-2 border-b border-paper-200", children: jsxRuntime.jsxs("div", { className: "relative", children: [jsxRuntime.jsx(lucideReact.Search, { className: "absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-ink-400" }), jsxRuntime.jsx("input", { ref: searchInputRef, type: "text", value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), placeholder: "Search...", className: "w-full pl-9 pr-3 py-2 text-sm border border-paper-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-accent-400 focus:border-accent-400", role: "searchbox", "aria-label": "Search options", "aria-autocomplete": "list", "aria-controls": listboxId })] }) })), jsxRuntime.jsx("div", { ref: listRef, id: listboxId, className: "overflow-y-auto", style: {
1097
+ maxHeight: useVirtualScrolling ? virtualHeight : "12rem",
1098
+ }, onScroll: (e) => useVirtualScrolling && setScrollTop(e.currentTarget.scrollTop), role: "listbox", "aria-label": "Available options", "aria-multiselectable": "false", children: renderOptionsContent(false) })] }))), isOpen &&
1099
+ useMobileSheet &&
1100
+ reactDom.createPortal(jsxRuntime.jsxs("div", { className: "fixed inset-0 z-50 flex items-end", onClick: (e) => e.target === e.currentTarget && handleClose(), role: "dialog", "aria-modal": "true", "aria-labelledby": label ? `mobile-${labelId}` : undefined, children: [jsxRuntime.jsx("div", { className: "absolute inset-0 bg-black/50 animate-fade-in" }), jsxRuntime.jsxs("div", { className: "relative w-full bg-white rounded-t-2xl shadow-2xl animate-slide-up max-h-[85vh] flex flex-col", style: { paddingBottom: "env(safe-area-inset-bottom)" }, children: [jsxRuntime.jsx("div", { className: "py-3 cursor-grab", children: jsxRuntime.jsx("div", { className: "w-12 h-1.5 bg-ink-300 rounded-full mx-auto" }) }), jsxRuntime.jsxs("div", { className: "px-4 pb-3 border-b border-paper-200 flex items-center justify-between", children: [label && (jsxRuntime.jsx("h2", { id: `mobile-${labelId}`, className: "text-lg font-semibold text-ink-900", children: label })), !label && (jsxRuntime.jsx("h2", { className: "text-lg font-semibold text-ink-900", children: placeholder })), jsxRuntime.jsx("button", { onClick: handleClose, className: "text-ink-400 hover:text-ink-600 transition-colors p-2 -mr-2", "aria-label": "Close", children: jsxRuntime.jsx(lucideReact.X, { className: "h-5 w-5" }) })] }), searchable && (jsxRuntime.jsx("div", { className: "p-3 border-b border-paper-200", children: jsxRuntime.jsxs("div", { className: "relative", children: [jsxRuntime.jsx(lucideReact.Search, { className: "absolute left-4 top-1/2 -translate-y-1/2 h-5 w-5 text-ink-400" }), jsxRuntime.jsx("input", { ref: mobileSearchInputRef, type: "text", value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), placeholder: "Search...", inputMode: "search", enterKeyHint: "search", className: "w-full pl-12 pr-4 py-3 text-base border border-paper-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-accent-400 focus:border-accent-400", role: "searchbox", "aria-label": "Search options" })] }) })), jsxRuntime.jsx("div", { id: listboxId, className: "overflow-y-auto flex-1", role: "listbox", "aria-label": "Available options", "aria-multiselectable": "false", children: renderOptionsContent(true) })] })] }), document.body), 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 }))] }));
1065
1101
  });
1066
- Select.displayName = 'Select';
1102
+ Select.displayName = "Select";
1067
1103
 
1068
1104
  const MultiSelect = React.forwardRef(({ options, value = [], onChange, placeholder = 'Select options', searchable = false, disabled = false, label, helperText, error, maxHeight = 240, maxSelections, loading = false, 'aria-label': ariaLabel, }, ref) => {
1069
1105
  const [isOpen, setIsOpen] = React.useState(false);
@@ -2719,7 +2755,18 @@ function FilterBar({ filters, values, onChange, className = '', onClear, showCle
2719
2755
  // Default clear: set all values to null/empty
2720
2756
  const clearedValues = {};
2721
2757
  filters.forEach(filter => {
2722
- clearedValues[filter.key] = filter.type === 'text' ? '' : null;
2758
+ if (filter.type === 'text' || filter.type === 'search') {
2759
+ clearedValues[filter.key] = '';
2760
+ }
2761
+ else if (filter.type === 'dateRange') {
2762
+ clearedValues[filter.key] = { from: undefined, to: undefined };
2763
+ }
2764
+ else if (filter.type === 'multiSelect') {
2765
+ clearedValues[filter.key] = [];
2766
+ }
2767
+ else {
2768
+ clearedValues[filter.key] = null;
2769
+ }
2723
2770
  });
2724
2771
  onChange(clearedValues);
2725
2772
  }
@@ -2751,6 +2798,38 @@ function FilterBar({ filters, values, onChange, className = '', onClear, showCle
2751
2798
  ];
2752
2799
  return (jsxRuntime.jsx(Select, { options: boolOptions, value: value === null || value === undefined ? '' : String(value), onChange: (newValue) => handleFilterChange(filter.key, newValue === '' ? null : newValue === 'true') }));
2753
2800
  }
2801
+ case 'search':
2802
+ return (jsxRuntime.jsxs("div", { className: "relative", children: [jsxRuntime.jsx("div", { className: "absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none", children: jsxRuntime.jsx(lucideReact.Search, { className: "h-4 w-4 text-ink-400" }) }), jsxRuntime.jsx("input", { type: "text", placeholder: filter.placeholder || `Search ${filter.label}...`, value: value || '', onChange: (e) => handleFilterChange(filter.key, e.target.value), className: "input pl-9" })] }));
2803
+ case 'dateRange': {
2804
+ const rangeValue = value || {};
2805
+ return (jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [jsxRuntime.jsx("input", { type: "date", value: rangeValue.from || '', onChange: (e) => handleFilterChange(filter.key, { ...rangeValue, from: e.target.value || undefined }), className: "input text-sm", "aria-label": `${filter.label} from` }), jsxRuntime.jsx("span", { className: "text-ink-400 text-xs", children: "to" }), jsxRuntime.jsx("input", { type: "date", value: rangeValue.to || '', onChange: (e) => handleFilterChange(filter.key, { ...rangeValue, to: e.target.value || undefined }), className: "input text-sm", "aria-label": `${filter.label} to` })] }));
2806
+ }
2807
+ case 'toggle': {
2808
+ const toggleOptions = [
2809
+ { value: '', label: 'All' },
2810
+ { value: 'true', label: 'Yes' },
2811
+ { value: 'false', label: 'No' },
2812
+ ];
2813
+ const currentVal = value === null || value === undefined ? '' : String(value);
2814
+ return (jsxRuntime.jsx("div", { className: "flex rounded-lg border border-paper-300 overflow-hidden", role: "group", children: toggleOptions.map((opt) => (jsxRuntime.jsx("button", { type: "button", onClick: () => handleFilterChange(filter.key, opt.value === '' ? null : opt.value === 'true'), className: `px-3 py-1.5 text-xs font-medium transition-colors ${currentVal === opt.value
2815
+ ? 'bg-accent-500 text-white'
2816
+ : 'bg-white text-ink-600 hover:bg-paper-50'} ${opt.value !== '' ? 'border-l border-paper-300' : ''}`, children: opt.label }, opt.value))) }));
2817
+ }
2818
+ case 'multiSelect': {
2819
+ const selectedValues = Array.isArray(value) ? value : [];
2820
+ const msOptions = filter.options || [];
2821
+ return (jsxRuntime.jsxs("div", { className: "relative", children: [jsxRuntime.jsx(Select, { options: [{ value: '', label: `All ${filter.label}` }, ...msOptions.map(o => ({ value: String(o.value), label: o.label }))], value: "", onChange: (newValue) => {
2822
+ if (!newValue) {
2823
+ handleFilterChange(filter.key, []);
2824
+ }
2825
+ else if (!selectedValues.includes(newValue)) {
2826
+ handleFilterChange(filter.key, [...selectedValues, newValue]);
2827
+ }
2828
+ } }), selectedValues.length > 0 && (jsxRuntime.jsx("div", { className: "flex flex-wrap gap-1 mt-1", children: selectedValues.map((sv) => {
2829
+ const opt = msOptions.find(o => String(o.value) === sv);
2830
+ return (jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1 px-2 py-0.5 text-xs bg-accent-100 text-accent-700 rounded-full", children: [opt?.label || sv, jsxRuntime.jsx("button", { type: "button", onClick: () => handleFilterChange(filter.key, selectedValues.filter(v => v !== sv)), className: "hover:text-accent-900", children: jsxRuntime.jsx(lucideReact.X, { className: "h-3 w-3" }) })] }, sv));
2831
+ }) }))] }));
2832
+ }
2754
2833
  default:
2755
2834
  return null;
2756
2835
  }
@@ -2760,6 +2839,237 @@ function FilterBar({ filters, values, onChange, className = '', onClear, showCle
2760
2839
  return (jsxRuntime.jsx("div", { className: `bg-white bg-subtle-grain border border-paper-200 rounded-lg shadow-sm p-4 ${className}`, children: jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-4 flex-wrap", children: [jsxRuntime.jsx("div", { className: "flex-1 flex flex-wrap gap-4", children: filters.map((filter) => (jsxRuntime.jsxs("div", { className: "flex flex-col space-y-1 min-w-[200px]", children: [jsxRuntime.jsx("label", { className: "label", children: filter.label }), renderFilter(filter)] }, filter.key))) }), showClearButton && (jsxRuntime.jsx("div", { className: "flex items-end", children: jsxRuntime.jsx(Button, { variant: "ghost", size: "md", onClick: handleClear, icon: jsxRuntime.jsx(lucideReact.X, { className: "h-4 w-4" }), iconPosition: "left", children: "Clear" }) }))] }) }));
2761
2840
  }
2762
2841
 
2842
+ const variantClasses$4 = {
2843
+ primary: {
2844
+ default: 'bg-primary-100 text-primary-700 border-primary-200',
2845
+ hover: 'hover:bg-primary-200',
2846
+ close: 'hover:bg-primary-300 text-primary-600',
2847
+ selected: 'bg-primary-200 border-primary-400 ring-2 ring-primary-300',
2848
+ },
2849
+ secondary: {
2850
+ default: 'bg-ink-100 text-ink-700 border-ink-200',
2851
+ hover: 'hover:bg-ink-200',
2852
+ close: 'hover:bg-ink-300 text-ink-600',
2853
+ selected: 'bg-ink-200 border-ink-400 ring-2 ring-ink-300',
2854
+ },
2855
+ success: {
2856
+ default: 'bg-success-100 text-success-700 border-success-200',
2857
+ hover: 'hover:bg-success-200',
2858
+ close: 'hover:bg-success-300 text-success-600',
2859
+ selected: 'bg-success-200 border-success-400 ring-2 ring-success-300',
2860
+ },
2861
+ warning: {
2862
+ default: 'bg-warning-100 text-warning-700 border-warning-200',
2863
+ hover: 'hover:bg-warning-200',
2864
+ close: 'hover:bg-warning-300 text-warning-600',
2865
+ selected: 'bg-warning-200 border-warning-400 ring-2 ring-warning-300',
2866
+ },
2867
+ error: {
2868
+ default: 'bg-error-100 text-error-700 border-error-200',
2869
+ hover: 'hover:bg-error-200',
2870
+ close: 'hover:bg-error-300 text-error-600',
2871
+ selected: 'bg-error-200 border-error-400 ring-2 ring-error-300',
2872
+ },
2873
+ info: {
2874
+ default: 'bg-accent-100 text-accent-700 border-accent-200',
2875
+ hover: 'hover:bg-accent-200',
2876
+ close: 'hover:bg-accent-300 text-accent-600',
2877
+ selected: 'bg-accent-200 border-accent-400 ring-2 ring-accent-300',
2878
+ },
2879
+ };
2880
+ const sizeClasses$b = {
2881
+ sm: {
2882
+ container: 'h-6 px-2 text-xs gap-1',
2883
+ icon: 'h-3 w-3',
2884
+ close: 'h-3 w-3 ml-1',
2885
+ },
2886
+ md: {
2887
+ container: 'h-7 px-2.5 text-sm gap-1.5',
2888
+ icon: 'h-3.5 w-3.5',
2889
+ close: 'h-3.5 w-3.5 ml-1.5',
2890
+ },
2891
+ lg: {
2892
+ container: 'h-8 px-3 text-base gap-2',
2893
+ icon: 'h-4 w-4',
2894
+ close: 'h-4 w-4 ml-2',
2895
+ },
2896
+ };
2897
+ const gapClasses = {
2898
+ xs: 'gap-1',
2899
+ sm: 'gap-1.5',
2900
+ md: 'gap-2',
2901
+ lg: 'gap-3',
2902
+ };
2903
+ /**
2904
+ * Chip - Compact element for displaying values with optional remove functionality
2905
+ *
2906
+ * @example Basic chip
2907
+ * ```tsx
2908
+ * <Chip>Tag Name</Chip>
2909
+ * ```
2910
+ *
2911
+ * @example Removable chip
2912
+ * ```tsx
2913
+ * <Chip onClose={() => removeTag(tag)}>
2914
+ * {tag.name}
2915
+ * </Chip>
2916
+ * ```
2917
+ *
2918
+ * @example With icon and selected state
2919
+ * ```tsx
2920
+ * <Chip
2921
+ * icon={<Star className="h-3 w-3" />}
2922
+ * selected={isSelected}
2923
+ * onClick={() => toggleSelection()}
2924
+ * >
2925
+ * Favorite
2926
+ * </Chip>
2927
+ * ```
2928
+ */
2929
+ function Chip({ children, variant = 'secondary', size = 'md', onClose, icon, disabled = false, className = '', onClick, selected = false, maxWidth, chipKey, }) {
2930
+ const variantStyle = variantClasses$4[variant];
2931
+ const sizeStyle = sizeClasses$b[size];
2932
+ const isClickable = !disabled && (onClick || onClose);
2933
+ return (jsxRuntime.jsxs("div", { className: `
2934
+ inline-flex items-center rounded-full border font-medium
2935
+ transition-colors
2936
+ ${selected ? variantStyle.selected : variantStyle.default}
2937
+ ${isClickable && !disabled && !selected ? variantStyle.hover : ''}
2938
+ ${sizeStyle.container}
2939
+ ${disabled ? 'opacity-50 cursor-not-allowed' : ''}
2940
+ ${onClick && !disabled ? 'cursor-pointer' : ''}
2941
+ ${className}
2942
+ `, onClick: onClick && !disabled ? onClick : undefined, role: onClick ? 'button' : undefined, "aria-disabled": disabled, "aria-pressed": onClick ? selected : undefined, "data-chip-key": chipKey, style: { maxWidth: maxWidth || undefined }, children: [icon && (jsxRuntime.jsx("span", { className: `flex-shrink-0 ${sizeStyle.icon}`, children: icon })), jsxRuntime.jsx("span", { className: "truncate", children: children }), onClose && (jsxRuntime.jsx("button", { type: "button", onClick: (e) => {
2943
+ e.stopPropagation();
2944
+ if (!disabled)
2945
+ onClose();
2946
+ }, disabled: disabled, className: `
2947
+ flex-shrink-0 rounded-full transition-colors
2948
+ ${variantStyle.close}
2949
+ ${disabled ? 'cursor-not-allowed' : 'cursor-pointer'}
2950
+ ${sizeStyle.close}
2951
+ `, "aria-label": "Remove", children: jsxRuntime.jsx(lucideReact.X, { className: "w-full h-full" }) }))] }));
2952
+ }
2953
+ /**
2954
+ * ChipGroup - Container for multiple chips with layout and selection support
2955
+ *
2956
+ * @example Basic group
2957
+ * ```tsx
2958
+ * <ChipGroup wrap gap="sm">
2959
+ * {tags.map(tag => (
2960
+ * <Chip key={tag.id} onClose={() => removeTag(tag)}>
2961
+ * {tag.name}
2962
+ * </Chip>
2963
+ * ))}
2964
+ * </ChipGroup>
2965
+ * ```
2966
+ *
2967
+ * @example Selectable group (single)
2968
+ * ```tsx
2969
+ * <ChipGroup
2970
+ * selectionMode="single"
2971
+ * selectedKeys={[selectedCategory]}
2972
+ * onSelectionChange={(keys) => setSelectedCategory(keys[0])}
2973
+ * >
2974
+ * <Chip chipKey="all">All</Chip>
2975
+ * <Chip chipKey="active">Active</Chip>
2976
+ * <Chip chipKey="archived">Archived</Chip>
2977
+ * </ChipGroup>
2978
+ * ```
2979
+ *
2980
+ * @example Multi-select group
2981
+ * ```tsx
2982
+ * <ChipGroup
2983
+ * selectionMode="multiple"
2984
+ * selectedKeys={selectedTags}
2985
+ * onSelectionChange={setSelectedTags}
2986
+ * wrap
2987
+ * >
2988
+ * {availableTags.map(tag => (
2989
+ * <Chip key={tag} chipKey={tag}>{tag}</Chip>
2990
+ * ))}
2991
+ * </ChipGroup>
2992
+ * ```
2993
+ */
2994
+ function ChipGroup({ children, direction = 'horizontal', wrap = false, gap = 'sm', selectionMode = 'none', selectedKeys = [], onSelectionChange, className = '', }) {
2995
+ const handleChipClick = (chipKey) => {
2996
+ if (selectionMode === 'none' || !onSelectionChange)
2997
+ return;
2998
+ if (selectionMode === 'single') {
2999
+ // Toggle single selection
3000
+ if (selectedKeys.includes(chipKey)) {
3001
+ onSelectionChange([]);
3002
+ }
3003
+ else {
3004
+ onSelectionChange([chipKey]);
3005
+ }
3006
+ }
3007
+ else if (selectionMode === 'multiple') {
3008
+ // Toggle in array
3009
+ if (selectedKeys.includes(chipKey)) {
3010
+ onSelectionChange(selectedKeys.filter(k => k !== chipKey));
3011
+ }
3012
+ else {
3013
+ onSelectionChange([...selectedKeys, chipKey]);
3014
+ }
3015
+ }
3016
+ };
3017
+ // Clone children to inject selection props
3018
+ const enhancedChildren = React.Children.map(children, (child) => {
3019
+ if (!React.isValidElement(child))
3020
+ return child;
3021
+ const chipKey = child.props.chipKey;
3022
+ if (!chipKey || selectionMode === 'none')
3023
+ return child;
3024
+ const isSelected = selectedKeys.includes(chipKey);
3025
+ return React.cloneElement(child, {
3026
+ ...child.props,
3027
+ selected: isSelected,
3028
+ onClick: () => {
3029
+ // Call original onClick if exists
3030
+ if (child.props.onClick) {
3031
+ child.props.onClick();
3032
+ }
3033
+ handleChipClick(chipKey);
3034
+ },
3035
+ });
3036
+ });
3037
+ return (jsxRuntime.jsx("div", { className: `
3038
+ flex
3039
+ ${direction === 'vertical' ? 'flex-col' : 'flex-row'}
3040
+ ${wrap ? 'flex-wrap' : ''}
3041
+ ${gapClasses[gap]}
3042
+ ${className}
3043
+ `, role: selectionMode !== 'none' ? 'group' : undefined, "aria-label": selectionMode !== 'none' ? 'Chip selection group' : undefined, children: enhancedChildren }));
3044
+ }
3045
+
3046
+ function FilterPills({ pills, onRemove, onClearAll, totalCount, className = '', }) {
3047
+ if (pills.length === 0)
3048
+ return null;
3049
+ return (jsxRuntime.jsxs("div", { className: `flex items-center gap-2 px-4 py-2 border-b border-paper-200 bg-paper-50 ${className}`, children: [jsxRuntime.jsx(lucideReact.Filter, { className: "h-3.5 w-3.5 text-ink-400 shrink-0" }), jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5 flex-wrap flex-1", children: [pills.map((pill) => (jsxRuntime.jsxs(Chip, { size: "sm", variant: "primary", onClose: () => onRemove(pill.key), children: [pill.label, ": ", pill.displayValue] }, pill.key))), pills.length >= 2 && (jsxRuntime.jsx("button", { type: "button", onClick: onClearAll, className: "text-xs text-ink-500 hover:text-ink-700 underline underline-offset-2 ml-1", children: "Clear all" }))] }), totalCount !== undefined && (jsxRuntime.jsxs("span", { className: "text-xs text-ink-500 shrink-0 tabular-nums", children: [totalCount.toLocaleString(), " ", totalCount === 1 ? 'record' : 'records'] }))] }));
3050
+ }
3051
+
3052
+ const LETTERS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('');
3053
+ function LetterNav({ activeLetter, onChange, availableLetters, className = '', }) {
3054
+ const hasAvailability = availableLetters && availableLetters.length > 0;
3055
+ const availableSet = hasAvailability
3056
+ ? new Set(availableLetters.map((l) => l.toUpperCase()))
3057
+ : null;
3058
+ return (jsxRuntime.jsxs("div", { className: `flex items-center gap-0.5 px-4 py-1.5 border-b border-paper-200 bg-white overflow-x-auto ${className}`, children: [jsxRuntime.jsx("button", { type: "button", onClick: () => onChange(null), className: `px-2 py-1 text-xs font-medium rounded transition-colors ${activeLetter === null
3059
+ ? 'bg-accent-500 text-white'
3060
+ : 'text-ink-600 hover:bg-paper-100'}`, children: "All" }), LETTERS.map((letter) => {
3061
+ const isActive = activeLetter === letter;
3062
+ const isAvailable = !availableSet || availableSet.has(letter);
3063
+ return (jsxRuntime.jsx("button", { type: "button", onClick: () => onChange(isActive ? null : letter), className: `w-7 h-7 text-xs font-medium rounded transition-colors ${isActive
3064
+ ? 'bg-accent-500 text-white'
3065
+ : isAvailable
3066
+ ? 'text-ink-600 hover:bg-paper-100'
3067
+ : 'text-ink-300'}`, children: letter }, letter));
3068
+ }), jsxRuntime.jsx("button", { type: "button", onClick: () => onChange(activeLetter === '#' ? null : '#'), className: `px-2 py-1 text-xs font-medium rounded transition-colors ${activeLetter === '#'
3069
+ ? 'bg-accent-500 text-white'
3070
+ : 'text-ink-600 hover:bg-paper-100'}`, children: "#" })] }));
3071
+ }
3072
+
2763
3073
  function Loading({ variant = 'spinner', size = 'md', text }) {
2764
3074
  const sizeClasses = {
2765
3075
  sm: 'h-4 w-4',
@@ -4610,7 +4920,7 @@ function BottomSheetActions({ children, className = '' }) {
4610
4920
 
4611
4921
  // Selector for all focusable elements
4612
4922
  const FOCUSABLE_SELECTOR = 'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])';
4613
- const sizeClasses$b = {
4923
+ const sizeClasses$a = {
4614
4924
  sm: 'max-w-md',
4615
4925
  md: 'max-w-lg',
4616
4926
  lg: 'max-w-2xl',
@@ -4799,7 +5109,7 @@ function Modal({ isOpen, onClose, title, children, size = 'md', showCloseButton
4799
5109
  return reactDom.createPortal(jsxRuntime.jsx(BottomSheet, { isOpen: isOpen, onClose: onClose, title: title, height: mobileHeight, showHandle: mobileShowHandle, showCloseButton: showCloseButton, children: children }), document.body);
4800
5110
  }
4801
5111
  // Render as standard modal on desktop
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$b[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: {
5112
+ 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$a[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: {
4803
5113
  maxHeight: maxHeight || (scrollable ? 'calc(100vh - 200px)' : undefined),
4804
5114
  }, children: children })] }) }));
4805
5115
  return reactDom.createPortal(modalContent, document.body);
@@ -5870,7 +6180,7 @@ function Alert({ variant = 'info', title, children, onClose, className = '', act
5870
6180
  return (jsxRuntime.jsx("div", { className: `rounded-lg border p-4 ${styles.container} ${className}`, role: "alert", children: jsxRuntime.jsxs("div", { className: "flex items-start gap-3", children: [jsxRuntime.jsx("div", { className: "flex-shrink-0 mt-0.5", children: styles.icon }), jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [title && jsxRuntime.jsx("h4", { className: "text-sm font-medium mb-1", children: title }), jsxRuntime.jsx("div", { className: "text-sm", children: children }), actions.length > 0 && (jsxRuntime.jsx("div", { className: "flex gap-2 mt-3", children: actions.map((action, index) => (jsxRuntime.jsx("button", { onClick: action.onClick, className: getButtonStyles(action.variant), children: action.label }, index))) }))] }), onClose && (jsxRuntime.jsx("button", { onClick: onClose, className: "flex-shrink-0 text-current opacity-70 hover:opacity-100 transition-opacity", "aria-label": "Close alert", children: jsxRuntime.jsx(lucideReact.X, { className: "h-4 w-4" }) }))] }) }));
5871
6181
  }
5872
6182
 
5873
- const sizeClasses$a = {
6183
+ const sizeClasses$9 = {
5874
6184
  left: {
5875
6185
  sm: 'w-64',
5876
6186
  md: 'w-96',
@@ -5949,7 +6259,7 @@ function Drawer({ isOpen, onClose, title, children, placement = 'right', size =
5949
6259
  const isHorizontal = placement === 'left' || placement === 'right';
5950
6260
  return (jsxRuntime.jsxs("div", { className: "fixed inset-0 z-50 flex", children: [showOverlay && (jsxRuntime.jsx("div", { className: "fixed inset-0 bg-ink-900 bg-opacity-50 backdrop-blur-sm animate-fade-in", onClick: handleOverlayClick, "aria-hidden": "true" })), jsxRuntime.jsxs("div", { className: `
5951
6261
  fixed ${placementClasses[placement]}
5952
- ${sizeClasses$a[placement][size]}
6262
+ ${sizeClasses$9[placement][size]}
5953
6263
  bg-white border-paper-200 shadow-2xl
5954
6264
  ${isHorizontal ? 'border-r' : 'border-b'}
5955
6265
  ${animationClasses[placement].enter}
@@ -6091,14 +6401,14 @@ function useConfirmDialog() {
6091
6401
  };
6092
6402
  }
6093
6403
 
6094
- const sizeClasses$9 = {
6404
+ const sizeClasses$8 = {
6095
6405
  sm: 'h-3.5 w-3.5',
6096
6406
  md: 'h-4 w-4',
6097
6407
  lg: 'h-5 w-5',
6098
6408
  };
6099
6409
  function HelpTooltip({ content, icon = 'help', size = 'md', position = 'top', className = '', }) {
6100
6410
  const IconComponent = icon === 'info' ? lucideReact.Info : lucideReact.HelpCircle;
6101
- return (jsxRuntime.jsx(Tooltip, { content: content, position: position, children: jsxRuntime.jsx("span", { className: `inline-flex items-center justify-center text-ink-400 hover:text-ink-600 cursor-help transition-colors ${className}`, role: "button", "aria-label": "Help", tabIndex: 0, children: jsxRuntime.jsx(IconComponent, { className: sizeClasses$9[size] }) }) }));
6411
+ return (jsxRuntime.jsx(Tooltip, { content: content, position: position, children: jsxRuntime.jsx("span", { className: `inline-flex items-center justify-center text-ink-400 hover:text-ink-600 cursor-help transition-colors ${className}`, role: "button", "aria-label": "Help", tabIndex: 0, children: jsxRuntime.jsx(IconComponent, { className: sizeClasses$8[size] }) }) }));
6102
6412
  }
6103
6413
 
6104
6414
  /**
@@ -8476,210 +8786,6 @@ function MenuDivider() {
8476
8786
  return { divider: true, id: `divider-${Date.now()}`, label: '' };
8477
8787
  }
8478
8788
 
8479
- const variantClasses$4 = {
8480
- primary: {
8481
- default: 'bg-primary-100 text-primary-700 border-primary-200',
8482
- hover: 'hover:bg-primary-200',
8483
- close: 'hover:bg-primary-300 text-primary-600',
8484
- selected: 'bg-primary-200 border-primary-400 ring-2 ring-primary-300',
8485
- },
8486
- secondary: {
8487
- default: 'bg-ink-100 text-ink-700 border-ink-200',
8488
- hover: 'hover:bg-ink-200',
8489
- close: 'hover:bg-ink-300 text-ink-600',
8490
- selected: 'bg-ink-200 border-ink-400 ring-2 ring-ink-300',
8491
- },
8492
- success: {
8493
- default: 'bg-success-100 text-success-700 border-success-200',
8494
- hover: 'hover:bg-success-200',
8495
- close: 'hover:bg-success-300 text-success-600',
8496
- selected: 'bg-success-200 border-success-400 ring-2 ring-success-300',
8497
- },
8498
- warning: {
8499
- default: 'bg-warning-100 text-warning-700 border-warning-200',
8500
- hover: 'hover:bg-warning-200',
8501
- close: 'hover:bg-warning-300 text-warning-600',
8502
- selected: 'bg-warning-200 border-warning-400 ring-2 ring-warning-300',
8503
- },
8504
- error: {
8505
- default: 'bg-error-100 text-error-700 border-error-200',
8506
- hover: 'hover:bg-error-200',
8507
- close: 'hover:bg-error-300 text-error-600',
8508
- selected: 'bg-error-200 border-error-400 ring-2 ring-error-300',
8509
- },
8510
- info: {
8511
- default: 'bg-accent-100 text-accent-700 border-accent-200',
8512
- hover: 'hover:bg-accent-200',
8513
- close: 'hover:bg-accent-300 text-accent-600',
8514
- selected: 'bg-accent-200 border-accent-400 ring-2 ring-accent-300',
8515
- },
8516
- };
8517
- const sizeClasses$8 = {
8518
- sm: {
8519
- container: 'h-6 px-2 text-xs gap-1',
8520
- icon: 'h-3 w-3',
8521
- close: 'h-3 w-3 ml-1',
8522
- },
8523
- md: {
8524
- container: 'h-7 px-2.5 text-sm gap-1.5',
8525
- icon: 'h-3.5 w-3.5',
8526
- close: 'h-3.5 w-3.5 ml-1.5',
8527
- },
8528
- lg: {
8529
- container: 'h-8 px-3 text-base gap-2',
8530
- icon: 'h-4 w-4',
8531
- close: 'h-4 w-4 ml-2',
8532
- },
8533
- };
8534
- const gapClasses = {
8535
- xs: 'gap-1',
8536
- sm: 'gap-1.5',
8537
- md: 'gap-2',
8538
- lg: 'gap-3',
8539
- };
8540
- /**
8541
- * Chip - Compact element for displaying values with optional remove functionality
8542
- *
8543
- * @example Basic chip
8544
- * ```tsx
8545
- * <Chip>Tag Name</Chip>
8546
- * ```
8547
- *
8548
- * @example Removable chip
8549
- * ```tsx
8550
- * <Chip onClose={() => removeTag(tag)}>
8551
- * {tag.name}
8552
- * </Chip>
8553
- * ```
8554
- *
8555
- * @example With icon and selected state
8556
- * ```tsx
8557
- * <Chip
8558
- * icon={<Star className="h-3 w-3" />}
8559
- * selected={isSelected}
8560
- * onClick={() => toggleSelection()}
8561
- * >
8562
- * Favorite
8563
- * </Chip>
8564
- * ```
8565
- */
8566
- function Chip({ children, variant = 'secondary', size = 'md', onClose, icon, disabled = false, className = '', onClick, selected = false, maxWidth, chipKey, }) {
8567
- const variantStyle = variantClasses$4[variant];
8568
- const sizeStyle = sizeClasses$8[size];
8569
- const isClickable = !disabled && (onClick || onClose);
8570
- return (jsxRuntime.jsxs("div", { className: `
8571
- inline-flex items-center rounded-full border font-medium
8572
- transition-colors
8573
- ${selected ? variantStyle.selected : variantStyle.default}
8574
- ${isClickable && !disabled && !selected ? variantStyle.hover : ''}
8575
- ${sizeStyle.container}
8576
- ${disabled ? 'opacity-50 cursor-not-allowed' : ''}
8577
- ${onClick && !disabled ? 'cursor-pointer' : ''}
8578
- ${className}
8579
- `, onClick: onClick && !disabled ? onClick : undefined, role: onClick ? 'button' : undefined, "aria-disabled": disabled, "aria-pressed": onClick ? selected : undefined, "data-chip-key": chipKey, style: { maxWidth: maxWidth || undefined }, children: [icon && (jsxRuntime.jsx("span", { className: `flex-shrink-0 ${sizeStyle.icon}`, children: icon })), jsxRuntime.jsx("span", { className: "truncate", children: children }), onClose && (jsxRuntime.jsx("button", { type: "button", onClick: (e) => {
8580
- e.stopPropagation();
8581
- if (!disabled)
8582
- onClose();
8583
- }, disabled: disabled, className: `
8584
- flex-shrink-0 rounded-full transition-colors
8585
- ${variantStyle.close}
8586
- ${disabled ? 'cursor-not-allowed' : 'cursor-pointer'}
8587
- ${sizeStyle.close}
8588
- `, "aria-label": "Remove", children: jsxRuntime.jsx(lucideReact.X, { className: "w-full h-full" }) }))] }));
8589
- }
8590
- /**
8591
- * ChipGroup - Container for multiple chips with layout and selection support
8592
- *
8593
- * @example Basic group
8594
- * ```tsx
8595
- * <ChipGroup wrap gap="sm">
8596
- * {tags.map(tag => (
8597
- * <Chip key={tag.id} onClose={() => removeTag(tag)}>
8598
- * {tag.name}
8599
- * </Chip>
8600
- * ))}
8601
- * </ChipGroup>
8602
- * ```
8603
- *
8604
- * @example Selectable group (single)
8605
- * ```tsx
8606
- * <ChipGroup
8607
- * selectionMode="single"
8608
- * selectedKeys={[selectedCategory]}
8609
- * onSelectionChange={(keys) => setSelectedCategory(keys[0])}
8610
- * >
8611
- * <Chip chipKey="all">All</Chip>
8612
- * <Chip chipKey="active">Active</Chip>
8613
- * <Chip chipKey="archived">Archived</Chip>
8614
- * </ChipGroup>
8615
- * ```
8616
- *
8617
- * @example Multi-select group
8618
- * ```tsx
8619
- * <ChipGroup
8620
- * selectionMode="multiple"
8621
- * selectedKeys={selectedTags}
8622
- * onSelectionChange={setSelectedTags}
8623
- * wrap
8624
- * >
8625
- * {availableTags.map(tag => (
8626
- * <Chip key={tag} chipKey={tag}>{tag}</Chip>
8627
- * ))}
8628
- * </ChipGroup>
8629
- * ```
8630
- */
8631
- function ChipGroup({ children, direction = 'horizontal', wrap = false, gap = 'sm', selectionMode = 'none', selectedKeys = [], onSelectionChange, className = '', }) {
8632
- const handleChipClick = (chipKey) => {
8633
- if (selectionMode === 'none' || !onSelectionChange)
8634
- return;
8635
- if (selectionMode === 'single') {
8636
- // Toggle single selection
8637
- if (selectedKeys.includes(chipKey)) {
8638
- onSelectionChange([]);
8639
- }
8640
- else {
8641
- onSelectionChange([chipKey]);
8642
- }
8643
- }
8644
- else if (selectionMode === 'multiple') {
8645
- // Toggle in array
8646
- if (selectedKeys.includes(chipKey)) {
8647
- onSelectionChange(selectedKeys.filter(k => k !== chipKey));
8648
- }
8649
- else {
8650
- onSelectionChange([...selectedKeys, chipKey]);
8651
- }
8652
- }
8653
- };
8654
- // Clone children to inject selection props
8655
- const enhancedChildren = React.Children.map(children, (child) => {
8656
- if (!React.isValidElement(child))
8657
- return child;
8658
- const chipKey = child.props.chipKey;
8659
- if (!chipKey || selectionMode === 'none')
8660
- return child;
8661
- const isSelected = selectedKeys.includes(chipKey);
8662
- return React.cloneElement(child, {
8663
- ...child.props,
8664
- selected: isSelected,
8665
- onClick: () => {
8666
- // Call original onClick if exists
8667
- if (child.props.onClick) {
8668
- child.props.onClick();
8669
- }
8670
- handleChipClick(chipKey);
8671
- },
8672
- });
8673
- });
8674
- return (jsxRuntime.jsx("div", { className: `
8675
- flex
8676
- ${direction === 'vertical' ? 'flex-col' : 'flex-row'}
8677
- ${wrap ? 'flex-wrap' : ''}
8678
- ${gapClasses[gap]}
8679
- ${className}
8680
- `, role: selectionMode !== 'none' ? 'group' : undefined, "aria-label": selectionMode !== 'none' ? 'Chip selection group' : undefined, children: enhancedChildren }));
8681
- }
8682
-
8683
8789
  const sizeClasses$7 = {
8684
8790
  sm: {
8685
8791
  item: 'py-1.5 px-2',
@@ -11799,7 +11905,7 @@ function Tabs({ tabs, activeTab: controlledActiveTab, defaultTab, variant = 'und
11799
11905
  }) })] }));
11800
11906
  }
11801
11907
 
11802
- function Pagination({ currentPage, totalPages, onPageChange, showPageNumbers = true, maxPageNumbers = 5, showPageJump = false, }) {
11908
+ function Pagination({ currentPage, totalPages, onPageChange, showPageNumbers = true, maxPageNumbers = 5, showPageJump = false, totalItems, pageSize, pageSizeOptions, onPageSizeChange, showRecordCount = false, }) {
11803
11909
  const [jumpValue, setJumpValue] = React.useState('');
11804
11910
  const getPageNumbers = () => {
11805
11911
  const pages = [];
@@ -11840,16 +11946,20 @@ function Pagination({ currentPage, totalPages, onPageChange, showPageNumbers = t
11840
11946
  setJumpValue('');
11841
11947
  }
11842
11948
  };
11843
- return (jsxRuntime.jsxs("nav", { className: "flex items-center justify-center gap-2", "aria-label": "Pagination", children: [jsxRuntime.jsxs("button", { onClick: () => onPageChange(currentPage - 1), disabled: currentPage === 1, className: "inline-flex items-center gap-2 px-3 py-2 text-sm font-medium text-ink-700 bg-white border border-paper-300 rounded-lg hover:bg-paper-50 hover:border-paper-400 disabled:opacity-40 disabled:cursor-not-allowed transition-all shadow-xs hover:shadow-sm", "aria-label": "Previous page", children: [jsxRuntime.jsx(lucideReact.ChevronLeft, { className: "h-4 w-4" }), jsxRuntime.jsx("span", { className: "hidden sm:inline", children: "Previous" })] }), showPageNumbers && (jsxRuntime.jsx("div", { className: "flex items-center gap-1", children: pageNumbers.map((page, index) => {
11844
- if (page === '...') {
11845
- return (jsxRuntime.jsx("span", { className: "px-3 py-2 text-ink-500", children: "..." }, `ellipsis-${index}`));
11846
- }
11847
- const pageNum = page;
11848
- const isActive = pageNum === currentPage;
11849
- return (jsxRuntime.jsx("button", { onClick: () => onPageChange(pageNum), className: `px-3 py-2 text-sm font-medium rounded-lg transition-all ${isActive
11850
- ? 'bg-accent-500 text-white shadow-sm'
11851
- : 'text-ink-700 bg-white border border-paper-300 hover:bg-paper-50 hover:border-paper-400'}`, "aria-label": `Page ${pageNum}`, "aria-current": isActive ? 'page' : undefined, children: pageNum }, pageNum));
11852
- }) })), jsxRuntime.jsxs("button", { onClick: () => onPageChange(currentPage + 1), disabled: currentPage === totalPages, className: "inline-flex items-center gap-2 px-3 py-2 text-sm font-medium text-ink-700 bg-white border border-paper-300 rounded-lg hover:bg-paper-50 hover:border-paper-400 disabled:opacity-40 disabled:cursor-not-allowed transition-all shadow-xs hover:shadow-sm", "aria-label": "Next page", children: [jsxRuntime.jsx("span", { className: "hidden sm:inline", children: "Next" }), jsxRuntime.jsx(lucideReact.ChevronRight, { className: "h-4 w-4" })] }), showPageJump && (jsxRuntime.jsxs("form", { onSubmit: handlePageJump, className: "flex items-center gap-2 ml-2", children: [jsxRuntime.jsx("span", { className: "text-sm text-ink-600 hidden sm:inline", children: "Go to:" }), jsxRuntime.jsx("input", { type: "number", min: "1", max: totalPages, value: jumpValue, onChange: (e) => setJumpValue(e.target.value), placeholder: "#", className: "w-16 px-2 py-1.5 text-sm text-center border border-paper-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-accent-400 focus:border-accent-400", "aria-label": "Jump to page" }), jsxRuntime.jsx("button", { type: "submit", disabled: !jumpValue, className: "px-3 py-1.5 text-sm font-medium text-white bg-accent-500 rounded-lg hover:bg-accent-600 disabled:opacity-40 disabled:cursor-not-allowed transition-all", children: "Go" })] }))] }));
11949
+ const showLeftSection = showRecordCount && totalItems !== undefined && pageSize;
11950
+ const showRightSection = onPageSizeChange && pageSizeOptions && pageSizeOptions.length > 0;
11951
+ const rangeStart = totalItems ? (currentPage - 1) * (pageSize || 0) + 1 : 0;
11952
+ const rangeEnd = totalItems ? Math.min(currentPage * (pageSize || 0), totalItems) : 0;
11953
+ return (jsxRuntime.jsxs("nav", { className: `flex items-center gap-2 ${showLeftSection || showRightSection ? 'justify-between' : 'justify-center'}`, "aria-label": "Pagination", children: [showLeftSection ? (jsxRuntime.jsxs("span", { className: "text-sm text-ink-500 tabular-nums shrink-0", children: ["Showing ", rangeStart.toLocaleString(), "\u2013", rangeEnd.toLocaleString(), " of ", totalItems.toLocaleString()] })) : showRightSection ? jsxRuntime.jsx("div", {}) : null, jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [jsxRuntime.jsxs("button", { onClick: () => onPageChange(currentPage - 1), disabled: currentPage === 1, className: "inline-flex items-center gap-2 px-3 py-2 text-sm font-medium text-ink-700 bg-white border border-paper-300 rounded-lg hover:bg-paper-50 hover:border-paper-400 disabled:opacity-40 disabled:cursor-not-allowed transition-all shadow-xs hover:shadow-sm", "aria-label": "Previous page", children: [jsxRuntime.jsx(lucideReact.ChevronLeft, { className: "h-4 w-4" }), jsxRuntime.jsx("span", { className: "hidden sm:inline", children: "Previous" })] }), showPageNumbers && (jsxRuntime.jsx("div", { className: "flex items-center gap-1", children: pageNumbers.map((page, index) => {
11954
+ if (page === '...') {
11955
+ return (jsxRuntime.jsx("span", { className: "px-3 py-2 text-ink-500", children: "..." }, `ellipsis-${index}`));
11956
+ }
11957
+ const pageNum = page;
11958
+ const isActive = pageNum === currentPage;
11959
+ return (jsxRuntime.jsx("button", { onClick: () => onPageChange(pageNum), className: `px-3 py-2 text-sm font-medium rounded-lg transition-all ${isActive
11960
+ ? 'bg-accent-500 text-white shadow-sm'
11961
+ : 'text-ink-700 bg-white border border-paper-300 hover:bg-paper-50 hover:border-paper-400'}`, "aria-label": `Page ${pageNum}`, "aria-current": isActive ? 'page' : undefined, children: pageNum }, pageNum));
11962
+ }) })), jsxRuntime.jsxs("button", { onClick: () => onPageChange(currentPage + 1), disabled: currentPage === totalPages, className: "inline-flex items-center gap-2 px-3 py-2 text-sm font-medium text-ink-700 bg-white border border-paper-300 rounded-lg hover:bg-paper-50 hover:border-paper-400 disabled:opacity-40 disabled:cursor-not-allowed transition-all shadow-xs hover:shadow-sm", "aria-label": "Next page", children: [jsxRuntime.jsx("span", { className: "hidden sm:inline", children: "Next" }), jsxRuntime.jsx(lucideReact.ChevronRight, { className: "h-4 w-4" })] }), showPageJump && (jsxRuntime.jsxs("form", { onSubmit: handlePageJump, className: "flex items-center gap-2 ml-2", children: [jsxRuntime.jsx("span", { className: "text-sm text-ink-600 hidden sm:inline", children: "Go to:" }), jsxRuntime.jsx("input", { type: "number", min: "1", max: totalPages, value: jumpValue, onChange: (e) => setJumpValue(e.target.value), placeholder: "#", className: "w-16 px-2 py-1.5 text-sm text-center border border-paper-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-accent-400 focus:border-accent-400", "aria-label": "Jump to page" }), jsxRuntime.jsx("button", { type: "submit", disabled: !jumpValue, className: "px-3 py-1.5 text-sm font-medium text-white bg-accent-500 rounded-lg hover:bg-accent-600 disabled:opacity-40 disabled:cursor-not-allowed transition-all", children: "Go" })] }))] }), showRightSection ? (jsxRuntime.jsxs("div", { className: "flex items-center gap-2 shrink-0", children: [jsxRuntime.jsx("span", { className: "text-sm text-ink-500 hidden sm:inline", children: "Per page:" }), jsxRuntime.jsx("select", { value: pageSize || pageSizeOptions[0], onChange: (e) => onPageSizeChange(Number(e.target.value)), className: "px-2 py-1.5 text-sm border border-paper-300 rounded-lg bg-white text-ink-700 focus:outline-none focus:ring-2 focus:ring-accent-400 focus:border-accent-400 cursor-pointer", "aria-label": "Items per page", children: pageSizeOptions.map((size) => (jsxRuntime.jsx("option", { value: size, children: size }, size))) })] })) : showLeftSection ? jsxRuntime.jsx("div", {}) : null] }));
11853
11963
  }
11854
11964
 
11855
11965
  function StepIndicator({ steps, currentStep, variant = 'horizontal', onStepClick, }) {
@@ -15706,52 +15816,44 @@ function getAugmentedNamespace(n) {
15706
15816
  * (A1, A1:C5, ...)
15707
15817
  */
15708
15818
 
15709
- var collection;
15710
- var hasRequiredCollection;
15711
-
15712
- function requireCollection () {
15713
- if (hasRequiredCollection) return collection;
15714
- hasRequiredCollection = 1;
15715
- class Collection {
15819
+ let Collection$3 = class Collection {
15716
15820
 
15717
- constructor(data, refs) {
15718
- if (data == null && refs == null) {
15719
- this._data = [];
15720
- this._refs = [];
15721
- } else {
15722
- if (data.length !== refs.length)
15723
- throw Error('Collection: data length should match references length.');
15724
- this._data = data;
15725
- this._refs = refs;
15726
- }
15727
- }
15821
+ constructor(data, refs) {
15822
+ if (data == null && refs == null) {
15823
+ this._data = [];
15824
+ this._refs = [];
15825
+ } else {
15826
+ if (data.length !== refs.length)
15827
+ throw Error('Collection: data length should match references length.');
15828
+ this._data = data;
15829
+ this._refs = refs;
15830
+ }
15831
+ }
15728
15832
 
15729
- get data() {
15730
- return this._data;
15731
- }
15833
+ get data() {
15834
+ return this._data;
15835
+ }
15732
15836
 
15733
- get refs() {
15734
- return this._refs;
15735
- }
15837
+ get refs() {
15838
+ return this._refs;
15839
+ }
15736
15840
 
15737
- get length() {
15738
- return this._data.length;
15739
- }
15841
+ get length() {
15842
+ return this._data.length;
15843
+ }
15740
15844
 
15741
- /**
15742
- * Add data and references to this collection.
15743
- * @param {{}} obj - data
15744
- * @param {{}} ref - reference
15745
- */
15746
- add(obj, ref) {
15747
- this._data.push(obj);
15748
- this._refs.push(ref);
15749
- }
15750
- }
15845
+ /**
15846
+ * Add data and references to this collection.
15847
+ * @param {{}} obj - data
15848
+ * @param {{}} ref - reference
15849
+ */
15850
+ add(obj, ref) {
15851
+ this._data.push(obj);
15852
+ this._refs.push(ref);
15853
+ }
15854
+ };
15751
15855
 
15752
- collection = Collection;
15753
- return collection;
15754
- }
15856
+ var collection = Collection$3;
15755
15857
 
15756
15858
  var helpers;
15757
15859
  var hasRequiredHelpers;
@@ -15760,7 +15862,7 @@ function requireHelpers () {
15760
15862
  if (hasRequiredHelpers) return helpers;
15761
15863
  hasRequiredHelpers = 1;
15762
15864
  const FormulaError = requireError();
15763
- const Collection = requireCollection();
15865
+ const Collection = collection;
15764
15866
 
15765
15867
  const Types = {
15766
15868
  NUMBER: 0,
@@ -25414,7 +25516,7 @@ var engineering = EngineeringFunctions;
25414
25516
 
25415
25517
  const FormulaError$b = requireError();
25416
25518
  const {FormulaHelpers: FormulaHelpers$8, Types: Types$6, WildCard, Address: Address$3} = requireHelpers();
25417
- const Collection$2 = requireCollection();
25519
+ const Collection$2 = collection;
25418
25520
  const H$5 = FormulaHelpers$8;
25419
25521
 
25420
25522
  const ReferenceFunctions$1 = {
@@ -37042,7 +37144,7 @@ var parsing = {
37042
37144
  const FormulaError$4 = requireError();
37043
37145
  const {Address: Address$1} = requireHelpers();
37044
37146
  const {Prefix: Prefix$1, Postfix: Postfix$1, Infix: Infix$1, Operators: Operators$1} = operators;
37045
- const Collection$1 = requireCollection();
37147
+ const Collection$1 = collection;
37046
37148
  const MAX_ROW$1 = 1048576, MAX_COLUMN$1 = 16384;
37047
37149
  const {NotAllInputParsedException} = require$$4;
37048
37150
 
@@ -37804,7 +37906,7 @@ var hooks$1 = {
37804
37906
  const FormulaError$2 = requireError();
37805
37907
  const {FormulaHelpers: FormulaHelpers$1, Types, Address} = requireHelpers();
37806
37908
  const {Prefix, Postfix, Infix, Operators} = operators;
37807
- const Collection = requireCollection();
37909
+ const Collection = collection;
37808
37910
  const MAX_ROW = 1048576, MAX_COLUMN = 16384;
37809
37911
 
37810
37912
  let Utils$1 = class Utils {
@@ -62855,6 +62957,7 @@ exports.FieldArray = FieldArray;
62855
62957
  exports.FileUpload = FileUpload;
62856
62958
  exports.FilterBar = FilterBar;
62857
62959
  exports.FilterControls = FilterControls;
62960
+ exports.FilterPills = FilterPills;
62858
62961
  exports.FilterStatusBanner = FilterStatusBanner;
62859
62962
  exports.FloatingActionButton = FloatingActionButton;
62860
62963
  exports.Form = Form;
@@ -62874,6 +62977,7 @@ exports.InsightsPanelUI = InsightsPanelUI;
62874
62977
  exports.InviteCard = InviteCard;
62875
62978
  exports.KanbanBoard = KanbanBoard;
62876
62979
  exports.Layout = Layout;
62980
+ exports.LetterNav = LetterNav;
62877
62981
  exports.Loading = Loading;
62878
62982
  exports.LoadingOverlay = LoadingOverlay;
62879
62983
  exports.Logo = Logo;