@spear-ai/spectral 1.22.5 → 1.22.7
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/Accordion.d.ts.map +1 -1
- package/dist/Accordion.js +1 -1
- package/dist/Accordion.js.map +1 -1
- package/dist/Alert/AlertBase.d.ts.map +1 -1
- package/dist/Alert/AlertBase.js +2 -3
- package/dist/Alert/AlertBase.js.map +1 -1
- package/dist/Alert.js.map +1 -1
- package/dist/AlertDialog.d.ts.map +1 -1
- package/dist/AlertDialog.js +8 -8
- package/dist/AlertDialog.js.map +1 -1
- package/dist/Avatar.js +4 -4
- package/dist/Avatar.js.map +1 -1
- package/dist/Badge.d.ts.map +1 -1
- package/dist/Badge.js.map +1 -1
- package/dist/Button.d.ts +2 -2
- package/dist/Button.d.ts.map +1 -1
- package/dist/Button.js +6 -9
- package/dist/Button.js.map +1 -1
- package/dist/ButtonGroup/ButtonGroupButton.d.ts.map +1 -1
- package/dist/ButtonGroup/ButtonGroupButton.js +1 -2
- package/dist/ButtonGroup/ButtonGroupButton.js.map +1 -1
- package/dist/ButtonGroup.d.ts.map +1 -1
- package/dist/ButtonGroup.js +1 -1
- package/dist/ButtonGroup.js.map +1 -1
- package/dist/ButtonIcon.d.ts +4 -0
- package/dist/ButtonIcon.d.ts.map +1 -1
- package/dist/ButtonIcon.js +7 -2
- package/dist/ButtonIcon.js.map +1 -1
- package/dist/Card.d.ts +43 -0
- package/dist/Card.d.ts.map +1 -0
- package/dist/{DataCard/Card.js → Card.js} +20 -20
- package/dist/Card.js.map +1 -0
- package/dist/Checkbox/CheckboxBase.js +1 -1
- package/dist/Checkbox/CheckboxBase.js.map +1 -1
- package/dist/Checkbox.js +1 -1
- package/dist/Checkbox.js.map +1 -1
- package/dist/Combobox.js.map +1 -1
- package/dist/ControlGroup/ControlGroupSelect.js.map +1 -1
- package/dist/ControlGroup.d.ts.map +1 -1
- package/dist/ControlGroup.js +1 -1
- package/dist/ControlGroup.js.map +1 -1
- package/dist/DateTimePicker/Calendar.js.map +1 -1
- package/dist/DateTimePicker/DateTimeDisplayInput.js.map +1 -1
- package/dist/DateTimePicker/TimePeriodSelect.js +1 -1
- package/dist/DateTimePicker/TimePeriodSelect.js.map +1 -1
- package/dist/DateTimePicker/TimePicker.js.map +1 -1
- package/dist/DateTimePicker.d.ts +1 -1
- package/dist/DateTimePicker.js.map +1 -1
- package/dist/Dialog.d.ts.map +1 -1
- package/dist/Dialog.js +3 -3
- package/dist/Dialog.js.map +1 -1
- package/dist/DirectionalColorWheel/DirectionalColorWheelDisclosure.js +1 -1
- package/dist/DirectionalColorWheel/DirectionalColorWheelDisclosure.js.map +1 -1
- package/dist/Drawer.d.ts.map +1 -1
- package/dist/Drawer.js.map +1 -1
- package/dist/DropdownMenu.js.map +1 -1
- package/dist/HoverCard.d.ts.map +1 -1
- package/dist/HoverCard.js.map +1 -1
- package/dist/Icons/AdjustmentsIcon.d.ts.map +1 -1
- package/dist/Icons/AdjustmentsIcon.js +11 -20
- package/dist/Icons/AdjustmentsIcon.js.map +1 -1
- package/dist/Icons/AnalyzeIcon.d.ts.map +1 -1
- package/dist/Icons/AnalyzeIcon.js +4 -9
- package/dist/Icons/AnalyzeIcon.js.map +1 -1
- package/dist/Icons/AnnotationsIcon.d.ts +2 -17
- package/dist/Icons/AnnotationsIcon.js +2 -25
- package/dist/Icons/ApprovedIcon.d.ts.map +1 -1
- package/dist/Icons/ApprovedIcon.js +4 -6
- package/dist/Icons/ApprovedIcon.js.map +1 -1
- package/dist/Icons/ArrowDownIcon.d.ts.map +1 -1
- package/dist/Icons/ArrowDownIcon.js +24 -11
- package/dist/Icons/ArrowDownIcon.js.map +1 -1
- package/dist/Icons/ArrowUpIcon.d.ts.map +1 -1
- package/dist/Icons/ArrowUpIcon.js +24 -11
- package/dist/Icons/ArrowUpIcon.js.map +1 -1
- package/dist/Icons/BoxToolIcon.d.ts.map +1 -1
- package/dist/Icons/BoxToolIcon.js +2 -2
- package/dist/Icons/BoxToolIcon.js.map +1 -1
- package/dist/Icons/{PanelIconOpen.d.ts → BuoyIcon.d.ts} +4 -4
- package/dist/Icons/BuoyIcon.d.ts.map +1 -0
- package/dist/Icons/BuoyIcon.js +40 -0
- package/dist/Icons/BuoyIcon.js.map +1 -0
- package/dist/Icons/CalendarIcon.d.ts.map +1 -1
- package/dist/Icons/CalendarIcon.js +5 -9
- package/dist/Icons/CalendarIcon.js.map +1 -1
- package/dist/Icons/CheckCircleIcon.d.ts.map +1 -1
- package/dist/Icons/CheckCircleIcon.js +13 -15
- package/dist/Icons/CheckCircleIcon.js.map +1 -1
- package/dist/Icons/CheckSquareIcon.d.ts.map +1 -1
- package/dist/Icons/CheckSquareIcon.js +25 -17
- package/dist/Icons/CheckSquareIcon.js.map +1 -1
- package/dist/Icons/CheckmarkIcon.d.ts +0 -1
- package/dist/Icons/CheckmarkIcon.d.ts.map +1 -1
- package/dist/Icons/CheckmarkIcon.js +11 -14
- package/dist/Icons/CheckmarkIcon.js.map +1 -1
- package/dist/Icons/ChevronDownIcon.d.ts.map +1 -1
- package/dist/Icons/ChevronDownIcon.js +10 -13
- package/dist/Icons/ChevronDownIcon.js.map +1 -1
- package/dist/Icons/ChevronUpIcon.d.ts.map +1 -1
- package/dist/Icons/ChevronUpIcon.js +10 -13
- package/dist/Icons/ChevronUpIcon.js.map +1 -1
- package/dist/Icons/{PanelIconClose.d.ts → CircleDotIcon.d.ts} +4 -4
- package/dist/Icons/CircleDotIcon.d.ts.map +1 -0
- package/dist/Icons/CircleDotIcon.js +31 -0
- package/dist/Icons/CircleDotIcon.js.map +1 -0
- package/dist/Icons/ClockIcon.d.ts.map +1 -1
- package/dist/Icons/ClockIcon.js +2 -4
- package/dist/Icons/ClockIcon.js.map +1 -1
- package/dist/Icons/CloseCircleIcon.d.ts.map +1 -1
- package/dist/Icons/CloseCircleIcon.js +19 -12
- package/dist/Icons/CloseCircleIcon.js.map +1 -1
- package/dist/Icons/CloseIcon.d.ts.map +1 -1
- package/dist/Icons/CloseIcon.js +13 -7
- package/dist/Icons/CloseIcon.js.map +1 -1
- package/dist/Icons/Crosshairs2Icon.d.ts.map +1 -1
- package/dist/Icons/Crosshairs2Icon.js +2 -7
- package/dist/Icons/Crosshairs2Icon.js.map +1 -1
- package/dist/Icons/CrosshairsIcon.d.ts.map +1 -1
- package/dist/Icons/CrosshairsIcon.js +2 -3
- package/dist/Icons/CrosshairsIcon.js.map +1 -1
- package/dist/Icons/DashboardIcon.d.ts.map +1 -1
- package/dist/Icons/DashboardIcon.js +2 -5
- package/dist/Icons/DashboardIcon.js.map +1 -1
- package/dist/Icons/DatabaseIcon.d.ts.map +1 -1
- package/dist/Icons/DatabaseIcon.js +2 -5
- package/dist/Icons/DatabaseIcon.js.map +1 -1
- package/dist/Icons/DeleteIcon.d.ts.map +1 -1
- package/dist/Icons/DeleteIcon.js +5 -8
- package/dist/Icons/DeleteIcon.js.map +1 -1
- package/dist/Icons/DeploymentsIcon.d.ts +18 -0
- package/dist/Icons/DeploymentsIcon.d.ts.map +1 -0
- package/dist/Icons/DeploymentsIcon.js +46 -0
- package/dist/Icons/DeploymentsIcon.js.map +1 -0
- package/dist/Icons/DotIcon.d.ts +18 -0
- package/dist/Icons/DotIcon.d.ts.map +1 -0
- package/dist/Icons/DotIcon.js +24 -0
- package/dist/Icons/DotIcon.js.map +1 -0
- package/dist/Icons/DurationIcon.d.ts.map +1 -1
- package/dist/Icons/DurationIcon.js +2 -8
- package/dist/Icons/DurationIcon.js.map +1 -1
- package/dist/Icons/EditIcon.d.ts.map +1 -1
- package/dist/Icons/EditIcon.js +7 -14
- package/dist/Icons/EditIcon.js.map +1 -1
- package/dist/Icons/EmailIcon.d.ts.map +1 -1
- package/dist/Icons/EmailIcon.js +13 -15
- package/dist/Icons/EmailIcon.js.map +1 -1
- package/dist/Icons/EraserIcon.d.ts.map +1 -1
- package/dist/Icons/EraserIcon.js +7 -11
- package/dist/Icons/EraserIcon.js.map +1 -1
- package/dist/Icons/ErrorIcon.d.ts.map +1 -1
- package/dist/Icons/ErrorIcon.js +18 -12
- package/dist/Icons/ErrorIcon.js.map +1 -1
- package/dist/Icons/EyeClosedIcon.d.ts.map +1 -1
- package/dist/Icons/EyeClosedIcon.js +30 -14
- package/dist/Icons/EyeClosedIcon.js.map +1 -1
- package/dist/Icons/EyeClosedIcon2.d.ts.map +1 -1
- package/dist/Icons/EyeClosedIcon2.js +24 -12
- package/dist/Icons/EyeClosedIcon2.js.map +1 -1
- package/dist/Icons/EyeOpenIcon.d.ts.map +1 -1
- package/dist/Icons/EyeOpenIcon.js +12 -10
- package/dist/Icons/EyeOpenIcon.js.map +1 -1
- package/dist/Icons/FileDownloadIcon.d.ts.map +1 -1
- package/dist/Icons/FileDownloadIcon.js +2 -6
- package/dist/Icons/FileDownloadIcon.js.map +1 -1
- package/dist/Icons/GeofenceIcon.d.ts +18 -0
- package/dist/Icons/GeofenceIcon.d.ts.map +1 -0
- package/dist/Icons/GeofenceIcon.js +38 -0
- package/dist/Icons/GeofenceIcon.js.map +1 -0
- package/dist/Icons/GeospatialIcon.d.ts +18 -0
- package/dist/Icons/GeospatialIcon.d.ts.map +1 -0
- package/dist/Icons/GeospatialIcon.js +53 -0
- package/dist/Icons/GeospatialIcon.js.map +1 -0
- package/dist/Icons/GlobeIcon.d.ts +18 -0
- package/dist/Icons/GlobeIcon.d.ts.map +1 -0
- package/dist/Icons/GlobeIcon.js +52 -0
- package/dist/Icons/GlobeIcon.js.map +1 -0
- package/dist/Icons/GoToFirstIcon.d.ts.map +1 -1
- package/dist/Icons/GoToFirstIcon.js +2 -4
- package/dist/Icons/GoToFirstIcon.js.map +1 -1
- package/dist/Icons/GoToLastIcon.d.ts.map +1 -1
- package/dist/Icons/GoToLastIcon.js +2 -4
- package/dist/Icons/GoToLastIcon.js.map +1 -1
- package/dist/Icons/GpsIcon.d.ts +18 -0
- package/dist/Icons/GpsIcon.d.ts.map +1 -0
- package/dist/Icons/GpsIcon.js +31 -0
- package/dist/Icons/GpsIcon.js.map +1 -0
- package/dist/Icons/HarmonicCursorsIcon.d.ts.map +1 -1
- package/dist/Icons/HarmonicCursorsIcon.js +2 -2
- package/dist/Icons/HarmonicCursorsIcon.js.map +1 -1
- package/dist/Icons/IconBase.d.ts +11 -8
- package/dist/Icons/IconBase.d.ts.map +1 -1
- package/dist/Icons/IconBase.js +3 -2
- package/dist/Icons/IconBase.js.map +1 -1
- package/dist/Icons/InfoIcon.d.ts.map +1 -1
- package/dist/Icons/InfoIcon.js +33 -26
- package/dist/Icons/InfoIcon.js.map +1 -1
- package/dist/Icons/IterationArrowIcon.d.ts.map +1 -1
- package/dist/Icons/IterationArrowIcon.js +2 -5
- package/dist/Icons/IterationArrowIcon.js.map +1 -1
- package/dist/Icons/KeyIcon.d.ts +18 -0
- package/dist/Icons/KeyIcon.d.ts.map +1 -0
- package/dist/Icons/KeyIcon.js +46 -0
- package/dist/Icons/KeyIcon.js.map +1 -0
- package/dist/Icons/KeyboardIcon.d.ts.map +1 -1
- package/dist/Icons/KeyboardIcon.js +4 -4
- package/dist/Icons/KeyboardIcon.js.map +1 -1
- package/dist/Icons/LabelIcon.d.ts.map +1 -1
- package/dist/Icons/LabelIcon.js +2 -3
- package/dist/Icons/LabelIcon.js.map +1 -1
- package/dist/Icons/LassoIcon.d.ts.map +1 -1
- package/dist/Icons/LassoIcon.js +5 -8
- package/dist/Icons/LassoIcon.js.map +1 -1
- package/dist/Icons/LineDashedIcon.d.ts +18 -0
- package/dist/Icons/LineDashedIcon.d.ts.map +1 -0
- package/dist/Icons/LineDashedIcon.js +50 -0
- package/dist/Icons/LineDashedIcon.js.map +1 -0
- package/dist/Icons/LineToolIcon.d.ts.map +1 -1
- package/dist/Icons/LineToolIcon.js +11 -14
- package/dist/Icons/LineToolIcon.js.map +1 -1
- package/dist/Icons/LinkIcon.d.ts.map +1 -1
- package/dist/Icons/LinkIcon.js +5 -8
- package/dist/Icons/LinkIcon.js.map +1 -1
- package/dist/Icons/LiveViewIcon.d.ts.map +1 -1
- package/dist/Icons/LiveViewIcon.js +14 -12
- package/dist/Icons/LiveViewIcon.js.map +1 -1
- package/dist/Icons/LoaderIcon.d.ts.map +1 -1
- package/dist/Icons/LoaderIcon.js +5 -7
- package/dist/Icons/LoaderIcon.js.map +1 -1
- package/dist/Icons/LocationIcon.d.ts.map +1 -1
- package/dist/Icons/LocationIcon.js +2 -4
- package/dist/Icons/LocationIcon.js.map +1 -1
- package/dist/Icons/LockIcon.d.ts.map +1 -1
- package/dist/Icons/LockIcon.js +2 -3
- package/dist/Icons/LockIcon.js.map +1 -1
- package/dist/Icons/LogoutIcon.d.ts.map +1 -1
- package/dist/Icons/LogoutIcon.js +3 -4
- package/dist/Icons/LogoutIcon.js.map +1 -1
- package/dist/Icons/MapIcon.d.ts +18 -0
- package/dist/Icons/MapIcon.d.ts.map +1 -0
- package/dist/Icons/MapIcon.js +40 -0
- package/dist/Icons/MapIcon.js.map +1 -0
- package/dist/Icons/MapPinIcon.d.ts +18 -0
- package/dist/Icons/MapPinIcon.d.ts.map +1 -0
- package/dist/Icons/MapPinIcon.js +31 -0
- package/dist/Icons/MapPinIcon.js.map +1 -0
- package/dist/Icons/MaximizeIcon.d.ts.map +1 -1
- package/dist/Icons/MaximizeIcon.js +2 -6
- package/dist/Icons/MaximizeIcon.js.map +1 -1
- package/dist/Icons/MeasureIcon.d.ts.map +1 -1
- package/dist/Icons/MeasureIcon.js +2 -11
- package/dist/Icons/MeasureIcon.js.map +1 -1
- package/dist/Icons/MenuDotsIcon.d.ts.map +1 -1
- package/dist/Icons/MenuDotsIcon.js +2 -5
- package/dist/Icons/MenuDotsIcon.js.map +1 -1
- package/dist/Icons/MenuIcon.d.ts.map +1 -1
- package/dist/Icons/MenuIcon.js +5 -8
- package/dist/Icons/MenuIcon.js.map +1 -1
- package/dist/Icons/MessagesIcon.d.ts.map +1 -1
- package/dist/Icons/MessagesIcon.js +5 -8
- package/dist/Icons/MessagesIcon.js.map +1 -1
- package/dist/Icons/MetadataIcon.d.ts.map +1 -1
- package/dist/Icons/MetadataIcon.js +2 -3
- package/dist/Icons/MetadataIcon.js.map +1 -1
- package/dist/Icons/MinimizeIcon.d.ts.map +1 -1
- package/dist/Icons/MinimizeIcon.js +2 -6
- package/dist/Icons/MinimizeIcon.js.map +1 -1
- package/dist/Icons/MinusIcon.d.ts.map +1 -1
- package/dist/Icons/MinusIcon.js +8 -7
- package/dist/Icons/MinusIcon.js.map +1 -1
- package/dist/Icons/OntologyIcon.d.ts.map +1 -1
- package/dist/Icons/OntologyIcon.js +8 -19
- package/dist/Icons/OntologyIcon.js.map +1 -1
- package/dist/Icons/PanelCloseIcon.d.ts +18 -0
- package/dist/Icons/PanelCloseIcon.d.ts.map +1 -0
- package/dist/Icons/PanelCloseIcon.js +40 -0
- package/dist/Icons/PanelCloseIcon.js.map +1 -0
- package/dist/Icons/PanelOpenIcon.d.ts +18 -0
- package/dist/Icons/PanelOpenIcon.d.ts.map +1 -0
- package/dist/Icons/PanelOpenIcon.js +40 -0
- package/dist/Icons/PanelOpenIcon.js.map +1 -0
- package/dist/Icons/PaperclipIcon.d.ts +18 -0
- package/dist/Icons/PaperclipIcon.d.ts.map +1 -0
- package/dist/Icons/PaperclipIcon.js +26 -0
- package/dist/Icons/PaperclipIcon.js.map +1 -0
- package/dist/Icons/PathIcon.d.ts +18 -0
- package/dist/Icons/PathIcon.d.ts.map +1 -0
- package/dist/Icons/PathIcon.js +86 -0
- package/dist/Icons/PathIcon.js.map +1 -0
- package/dist/Icons/PauseIcon.d.ts.map +1 -1
- package/dist/Icons/PauseIcon.js +4 -6
- package/dist/Icons/PauseIcon.js.map +1 -1
- package/dist/Icons/PdfIcon.d.ts.map +1 -1
- package/dist/Icons/PdfIcon.js +3 -8
- package/dist/Icons/PdfIcon.js.map +1 -1
- package/dist/Icons/PinIcon.d.ts +18 -0
- package/dist/Icons/PinIcon.d.ts.map +1 -0
- package/dist/Icons/PinIcon.js +40 -0
- package/dist/Icons/PinIcon.js.map +1 -0
- package/dist/Icons/PlayIcon.d.ts.map +1 -1
- package/dist/Icons/PlayIcon.js +3 -5
- package/dist/Icons/PlayIcon.js.map +1 -1
- package/dist/Icons/PlusIcon.d.ts.map +1 -1
- package/dist/Icons/PlusIcon.js +5 -6
- package/dist/Icons/PlusIcon.js.map +1 -1
- package/dist/Icons/PolygonIcon.d.ts +0 -1
- package/dist/Icons/PolygonIcon.d.ts.map +1 -1
- package/dist/Icons/PolygonIcon.js +28 -15
- package/dist/Icons/PolygonIcon.js.map +1 -1
- package/dist/Icons/PrinterIcon.d.ts.map +1 -1
- package/dist/Icons/PrinterIcon.js +3 -4
- package/dist/Icons/PrinterIcon.js.map +1 -1
- package/dist/Icons/ProgressArrowIcon.d.ts.map +1 -1
- package/dist/Icons/ProgressArrowIcon.js +20 -14
- package/dist/Icons/ProgressArrowIcon.js.map +1 -1
- package/dist/Icons/ProgressCheckIcon.d.ts.map +1 -1
- package/dist/Icons/ProgressCheckIcon.js +5 -11
- package/dist/Icons/ProgressCheckIcon.js.map +1 -1
- package/dist/Icons/ResetIcon.d.ts.map +1 -1
- package/dist/Icons/ResetIcon.js +2 -2
- package/dist/Icons/ResetIcon.js.map +1 -1
- package/dist/Icons/ReviewedIcon.d.ts.map +1 -1
- package/dist/Icons/ReviewedIcon.js +2 -5
- package/dist/Icons/ReviewedIcon.js.map +1 -1
- package/dist/Icons/Route2Icon.d.ts +18 -0
- package/dist/Icons/Route2Icon.d.ts.map +1 -0
- package/dist/Icons/Route2Icon.js +40 -0
- package/dist/Icons/Route2Icon.js.map +1 -0
- package/dist/Icons/Route3Icon.d.ts +18 -0
- package/dist/Icons/Route3Icon.d.ts.map +1 -0
- package/dist/Icons/Route3Icon.js +40 -0
- package/dist/Icons/Route3Icon.js.map +1 -0
- package/dist/Icons/RouteIcon.d.ts +18 -0
- package/dist/Icons/RouteIcon.d.ts.map +1 -0
- package/dist/Icons/RouteIcon.js +40 -0
- package/dist/Icons/RouteIcon.js.map +1 -0
- package/dist/Icons/SaveIcon.d.ts +18 -0
- package/dist/Icons/SaveIcon.d.ts.map +1 -0
- package/dist/Icons/SaveIcon.js +26 -0
- package/dist/Icons/SaveIcon.js.map +1 -0
- package/dist/Icons/ScissorsIcon.d.ts.map +1 -1
- package/dist/Icons/ScissorsIcon.js +6 -6
- package/dist/Icons/ScissorsIcon.js.map +1 -1
- package/dist/Icons/SearchIcon.d.ts.map +1 -1
- package/dist/Icons/SearchIcon.js +4 -6
- package/dist/Icons/SearchIcon.js.map +1 -1
- package/dist/Icons/SettingsIcon.d.ts.map +1 -1
- package/dist/Icons/SettingsIcon.js +3 -5
- package/dist/Icons/SettingsIcon.js.map +1 -1
- package/dist/Icons/SortAscendingIcon.d.ts.map +1 -1
- package/dist/Icons/SortAscendingIcon.js +7 -12
- package/dist/Icons/SortAscendingIcon.js.map +1 -1
- package/dist/Icons/SortAtoZIcon.d.ts.map +1 -1
- package/dist/Icons/SortAtoZIcon.js +2 -7
- package/dist/Icons/SortAtoZIcon.js.map +1 -1
- package/dist/Icons/SortDescendingIcon.d.ts.map +1 -1
- package/dist/Icons/SortDescendingIcon.js +6 -11
- package/dist/Icons/SortDescendingIcon.js.map +1 -1
- package/dist/Icons/SortZtoAIcon.d.ts.map +1 -1
- package/dist/Icons/SortZtoAIcon.js +2 -7
- package/dist/Icons/SortZtoAIcon.js.map +1 -1
- package/dist/Icons/SparklesAIIcon.d.ts +18 -0
- package/dist/Icons/SparklesAIIcon.d.ts.map +1 -0
- package/dist/Icons/SparklesAIIcon.js +33 -0
- package/dist/Icons/SparklesAIIcon.js.map +1 -0
- package/dist/Icons/SparklesIcon.d.ts.map +1 -1
- package/dist/Icons/SparklesIcon.js +2 -3
- package/dist/Icons/SparklesIcon.js.map +1 -1
- package/dist/Icons/StackIcon.d.ts.map +1 -1
- package/dist/Icons/StackIcon.js +3 -5
- package/dist/Icons/StackIcon.js.map +1 -1
- package/dist/Icons/StarIcon.d.ts.map +1 -1
- package/dist/Icons/StarIcon.js +2 -3
- package/dist/Icons/StarIcon.js.map +1 -1
- package/dist/Icons/SyncIcon.d.ts.map +1 -1
- package/dist/Icons/SyncIcon.js +2 -4
- package/dist/Icons/SyncIcon.js.map +1 -1
- package/dist/Icons/SyncOffIcon.d.ts.map +1 -1
- package/dist/Icons/SyncOffIcon.js +3 -6
- package/dist/Icons/SyncOffIcon.js.map +1 -1
- package/dist/Icons/ToolsIcon.d.ts +18 -0
- package/dist/Icons/ToolsIcon.d.ts.map +1 -0
- package/dist/Icons/ToolsIcon.js +58 -0
- package/dist/Icons/ToolsIcon.js.map +1 -0
- package/dist/Icons/TrashIcon.d.ts.map +1 -1
- package/dist/Icons/TrashIcon.js +2 -2
- package/dist/Icons/TrashIcon.js.map +1 -1
- package/dist/Icons/UndoIcon.d.ts.map +1 -1
- package/dist/Icons/UndoIcon.js +3 -4
- package/dist/Icons/UndoIcon.js.map +1 -1
- package/dist/Icons/UnlinkIcon.d.ts.map +1 -1
- package/dist/Icons/UnlinkIcon.js +46 -65
- package/dist/Icons/UnlinkIcon.js.map +1 -1
- package/dist/Icons/UnlockIcon.d.ts.map +1 -1
- package/dist/Icons/UnlockIcon.js +3 -4
- package/dist/Icons/UnlockIcon.js.map +1 -1
- package/dist/Icons/UploadIcon.d.ts.map +1 -1
- package/dist/Icons/UploadIcon.js +3 -6
- package/dist/Icons/UploadIcon.js.map +1 -1
- package/dist/Icons/User2Icon.d.ts.map +1 -1
- package/dist/Icons/User2Icon.js +13 -15
- package/dist/Icons/User2Icon.js.map +1 -1
- package/dist/Icons/UserAddIcon.d.ts +18 -0
- package/dist/Icons/UserAddIcon.d.ts.map +1 -0
- package/dist/Icons/UserAddIcon.js +46 -0
- package/dist/Icons/UserAddIcon.js.map +1 -0
- package/dist/Icons/UserDeleteIcon.d.ts +18 -0
- package/dist/Icons/UserDeleteIcon.d.ts.map +1 -0
- package/dist/Icons/UserDeleteIcon.js +46 -0
- package/dist/Icons/UserDeleteIcon.js.map +1 -0
- package/dist/Icons/UserEditIcon.d.ts +18 -0
- package/dist/Icons/UserEditIcon.d.ts.map +1 -0
- package/dist/Icons/UserEditIcon.js +40 -0
- package/dist/Icons/UserEditIcon.js.map +1 -0
- package/dist/Icons/UserIcon.d.ts.map +1 -1
- package/dist/Icons/UserIcon.js +3 -4
- package/dist/Icons/UserIcon.js.map +1 -1
- package/dist/Icons/UserSearchIcon.d.ts +18 -0
- package/dist/Icons/UserSearchIcon.d.ts.map +1 -0
- package/dist/Icons/UserSearchIcon.js +56 -0
- package/dist/Icons/UserSearchIcon.js.map +1 -0
- package/dist/Icons/UserSquareIcon.d.ts +18 -0
- package/dist/Icons/UserSquareIcon.d.ts.map +1 -0
- package/dist/Icons/UserSquareIcon.js +40 -0
- package/dist/Icons/UserSquareIcon.js.map +1 -0
- package/dist/Icons/UsersIcon.d.ts +18 -0
- package/dist/Icons/UsersIcon.d.ts.map +1 -0
- package/dist/Icons/UsersIcon.js +46 -0
- package/dist/Icons/UsersIcon.js.map +1 -0
- package/dist/Icons/WarningIcon.d.ts.map +1 -1
- package/dist/Icons/WarningIcon.js +2 -3
- package/dist/Icons/WarningIcon.js.map +1 -1
- package/dist/Icons/ZoomAllIcon.d.ts.map +1 -1
- package/dist/Icons/ZoomAllIcon.js +6 -38
- package/dist/Icons/ZoomAllIcon.js.map +1 -1
- package/dist/Icons/ZoomXIcon.d.ts.map +1 -1
- package/dist/Icons/ZoomXIcon.js +5 -8
- package/dist/Icons/ZoomXIcon.js.map +1 -1
- package/dist/Icons/ZoomYIcon.d.ts.map +1 -1
- package/dist/Icons/ZoomYIcon.js +5 -8
- package/dist/Icons/ZoomYIcon.js.map +1 -1
- package/dist/Icons.d.ts +31 -5
- package/dist/Icons.js +31 -5
- package/dist/IconsAnimated/PanelLeftCloseIcon.js.map +1 -1
- package/dist/IconsAnimated/PanelLeftOpenIcon.js.map +1 -1
- package/dist/Input.d.ts.map +1 -1
- package/dist/Input.js +2 -2
- package/dist/Input.js.map +1 -1
- package/dist/InputNumeric.d.ts.map +1 -1
- package/dist/InputNumeric.js.map +1 -1
- package/dist/InputOTP.d.ts.map +1 -1
- package/dist/InputOTP.js +1 -1
- package/dist/InputOTP.js.map +1 -1
- package/dist/InputSearch.d.ts.map +1 -1
- package/dist/InputSearch.js +7 -7
- package/dist/InputSearch.js.map +1 -1
- package/dist/Kbd.d.ts.map +1 -1
- package/dist/Kbd.js.map +1 -1
- package/dist/Label.js.map +1 -1
- package/dist/Meter.js +4 -4
- package/dist/Meter.js.map +1 -1
- package/dist/MultiSelect/MultiSelectBase.js.map +1 -1
- package/dist/MultiSelect.js.map +1 -1
- package/dist/Popover.d.ts.map +1 -1
- package/dist/Popover.js.map +1 -1
- package/dist/RadialMenu.d.ts.map +1 -1
- package/dist/RadialMenu.js.map +1 -1
- package/dist/RadioButton.js +1 -4
- package/dist/RadioButton.js.map +1 -1
- package/dist/RadioButtonGroup/RadioButtonGroupBase.d.ts.map +1 -1
- package/dist/RadioButtonGroup/RadioButtonGroupBase.js +2 -5
- package/dist/RadioButtonGroup/RadioButtonGroupBase.js.map +1 -1
- package/dist/RadioButtonGroup.d.ts.map +1 -1
- package/dist/RadioButtonGroup.js.map +1 -1
- package/dist/RadioGroup.d.ts.map +1 -1
- package/dist/RadioGroup.js +4 -4
- package/dist/RadioGroup.js.map +1 -1
- package/dist/Select.js +2 -2
- package/dist/Select.js.map +1 -1
- package/dist/Skeleton.js.map +1 -1
- package/dist/Slider.js.map +1 -1
- package/dist/Switch/SwitchBase.d.ts.map +1 -1
- package/dist/Switch/SwitchBase.js.map +1 -1
- package/dist/Switch.js.map +1 -1
- package/dist/Tabs/TabsBase.d.ts.map +1 -1
- package/dist/Tabs/TabsBase.js.map +1 -1
- package/dist/Tabs.d.ts.map +1 -1
- package/dist/Tabs.js.map +1 -1
- package/dist/Textarea.js.map +1 -1
- package/dist/Toast.d.ts.map +1 -1
- package/dist/Toast.js +2 -2
- package/dist/Toast.js.map +1 -1
- package/dist/Toggle/ToggleBase.js.map +1 -1
- package/dist/Toggle.d.ts.map +1 -1
- package/dist/Toggle.js +2 -6
- package/dist/Toggle.js.map +1 -1
- package/dist/ToggleGroup/ToggleGroupBase.d.ts.map +1 -1
- package/dist/ToggleGroup/ToggleGroupBase.js.map +1 -1
- package/dist/ToggleGroup/ToggleGroupItem.js +1 -1
- package/dist/ToggleGroup/ToggleGroupItem.js.map +1 -1
- package/dist/ToggleGroup/ToggleGroupSplitMenuItem.js +1 -1
- package/dist/ToggleGroup/ToggleGroupSplitMenuItem.js.map +1 -1
- package/dist/ToggleGroup.js +1 -9
- package/dist/ToggleGroup.js.map +1 -1
- package/dist/Tooltip.d.ts.map +1 -1
- package/dist/Tooltip.js +1 -4
- package/dist/Tooltip.js.map +1 -1
- package/dist/Tray.d.ts.map +1 -1
- package/dist/Tray.js +2 -2
- package/dist/Tray.js.map +1 -1
- package/dist/index.d.ts +32 -6
- package/dist/index.js +32 -6
- package/dist/primitives/button.js +1 -1
- package/dist/primitives/button.js.map +1 -1
- package/dist/styles/horizon/base-colors.css +11 -11
- package/dist/styles/horizon/utilities.css +7 -8
- package/dist/styles/spectral.css +1 -1
- package/package.json +2 -2
- package/dist/DataCard/Card.d.ts +0 -38
- package/dist/DataCard/Card.d.ts.map +0 -1
- package/dist/DataCard/Card.js.map +0 -1
- package/dist/DataCard.d.ts +0 -25
- package/dist/DataCard.d.ts.map +0 -1
- package/dist/DataCard.js +0 -37
- package/dist/DataCard.js.map +0 -1
- package/dist/Icons/AnnotationsIcon.d.ts.map +0 -1
- package/dist/Icons/AnnotationsIcon.js.map +0 -1
- package/dist/Icons/PanelIconClose.d.ts.map +0 -1
- package/dist/Icons/PanelIconClose.js +0 -35
- package/dist/Icons/PanelIconClose.js.map +0 -1
- package/dist/Icons/PanelIconOpen.d.ts.map +0 -1
- package/dist/Icons/PanelIconOpen.js +0 -35
- package/dist/Icons/PanelIconOpen.js.map +0 -1
package/dist/InputSearch.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InputSearch.js","names":[],"sources":["../src/components/InputSearch/InputSearch.tsx"],"sourcesContent":["import { CloseCircleIcon, ErrorIcon, LoaderIcon, PlusIcon, SearchIcon, WarningIcon } from '@components/Icons'\nimport { Label } from '@components/Label/Label'\nimport { useUncontrolledState } from '@hooks/useUncontrolledState'\nimport { useAutoDropdownHorizontalShift } from '@utils/dropdownPositioning'\nimport {\n EmptyState,\n ErrorMessage,\n WarningMessage,\n getAriaProps,\n getDropdownSurfaceClasses,\n getDropdownWidthStyles,\n getErrorMessageId,\n getFormFieldCSSProperties,\n getInputClasses,\n getOptionClasses,\n getPasswordManagerIgnoreProps,\n getWarningMessageId,\n useFormFieldId,\n useFormFieldState,\n type BaseFormFieldProps,\n type BaseOption,\n type DropdownWidth,\n type FormFieldState,\n} from '@utils/formFieldUtils'\nimport { cn } from '@utils/twUtils'\nimport { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, type CSSProperties, type KeyboardEvent, type ReactNode, type Ref } from 'react'\nimport { createPortal } from 'react-dom'\n\nexport type InputSearchOption = BaseOption\n\nexport interface InputSearchProps extends Omit<BaseFormFieldProps, 'onChange' | 'state'> {\n className?: string\n createOptionLabel?: (query: string) => ReactNode\n creatingLabel?: string\n defaultValue?: string\n dropdownWidth?: DropdownWidth\n emptyMessage?: string\n hideSearchIcon?: boolean\n isCreating?: boolean\n labelClassName?: string\n onChange?: (value: string) => void\n onCreate?: (query: string) => void\n onValueChange?: (value: string) => void\n openOnFocus?: boolean\n options: InputSearchOption[]\n placeholder?: string\n renderOption?: (option: InputSearchOption) => ReactNode\n state?: Exclude<FormFieldState, 'disabled'>\n value?: string\n warningMessage?: BaseFormFieldProps['errorMessage']\n}\n\nconst defaultCreateOptionLabel = (query: string): ReactNode => (\n <>\n <PlusIcon size={16} className='shrink-0' />\n <span className='truncate'>\n Create <span className='font-semibold'>“{query}”</span>\n </span>\n </>\n)\n\nexport const InputSearch = ({\n className,\n createOptionLabel = defaultCreateOptionLabel,\n creatingLabel = 'Creating…',\n defaultValue = '',\n disabled,\n dropdownWidth = 'trigger',\n emptyMessage = 'No options found',\n errorMessage,\n hideSearchIcon = false,\n id,\n isCreating = false,\n label,\n labelClassName,\n messageReserveLines = 1,\n messageReserveSpace = false,\n name,\n onChange,\n onCreate,\n onValueChange,\n openOnFocus = false,\n options,\n placeholder = 'Search…',\n ref,\n renderOption,\n required,\n state = 'default',\n value: valueProp,\n warningMessage,\n 'aria-describedby': ariaDescribedBy,\n 'aria-label': ariaLabel,\n}: InputSearchProps & { ref?: Ref<HTMLDivElement> }) => {\n if (process.env.NODE_ENV !== 'production' && !label && !ariaLabel) {\n // eslint-disable-next-line no-console\n console.warn('InputSearch: provide either `label` or `aria-label` for an accessible name.')\n }\n\n const fieldId = useFormFieldId(id, name)\n const listboxId = `${fieldId}-listbox`\n const errorMessageId = getErrorMessageId(fieldId)\n const warningMessageId = getWarningMessageId(fieldId)\n const messageId = state === 'error' ? errorMessageId : state === 'warning' && warningMessage ? warningMessageId : undefined\n const { isDisabled, isLoading, isInvalid } = useFormFieldState(disabled, state)\n const ariaProps = getAriaProps(state, ariaDescribedBy, required, messageId)\n\n const [value, setValue] = useUncontrolledState<string>({\n value: valueProp,\n defaultValue,\n onChange: (nextValue) => {\n if (onChange) {\n onChange(nextValue)\n } else {\n onValueChange?.(nextValue)\n }\n },\n })\n\n const [query, setQuery] = useState('')\n const [isFocused, setIsFocused] = useState(false)\n const [highlightedIndex, setHighlightedIndex] = useState(-1)\n const [dropdownPosition, setDropdownPosition] = useState<{ top: number; left: number; width: number } | null>(null)\n const inputRef = useRef<HTMLInputElement>(null)\n const listRef = useRef<HTMLDivElement>(null)\n const blurTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null)\n const triggerRef = useRef<HTMLDivElement>(null)\n\n const selectedOption = useMemo(() => options.find((option) => option.value === value) ?? null, [options, value])\n\n useEffect(() => {\n if (!isFocused) {\n setQuery(selectedOption?.label ?? '')\n }\n }, [selectedOption, isFocused])\n\n const trimmedQuery = query.trim()\n\n const filteredOptions = useMemo(() => {\n if (trimmedQuery.length === 0) return options\n const lower = trimmedQuery.toLowerCase()\n return options.filter((option) => option.label.toLowerCase().includes(lower))\n }, [trimmedQuery, options])\n\n const exactMatch = useMemo(() => options.some((option) => option.label.toLowerCase() === trimmedQuery.toLowerCase()), [options, trimmedQuery])\n\n const showCreateOption = onCreate !== undefined && trimmedQuery.length > 0 && !exactMatch\n const createOptionIndex = showCreateOption ? filteredOptions.length : -1\n const totalItems = filteredOptions.length + (showCreateOption ? 1 : 0)\n const showDropdown = isFocused && !isDisabled && !isLoading && (openOnFocus || trimmedQuery.length > 0)\n const showEmptyState = showDropdown && totalItems === 0\n\n const { dropdownShiftStyle, recalculateDropdownPosition, setDropdownElement } = useAutoDropdownHorizontalShift(showDropdown)\n\n const updateDropdownPosition = useCallback(() => {\n const trigger = triggerRef.current\n if (!trigger) return\n const rect = trigger.getBoundingClientRect()\n setDropdownPosition({\n top: rect.bottom + 4,\n left: rect.left,\n width: rect.width,\n })\n recalculateDropdownPosition()\n }, [recalculateDropdownPosition])\n\n useLayoutEffect(() => {\n if (!showDropdown) {\n setDropdownPosition(null)\n return\n }\n updateDropdownPosition()\n const handle = () => updateDropdownPosition()\n window.addEventListener('scroll', handle, true)\n window.addEventListener('resize', handle)\n return () => {\n window.removeEventListener('scroll', handle, true)\n window.removeEventListener('resize', handle)\n }\n }, [showDropdown, updateDropdownPosition])\n\n useLayoutEffect(() => {\n if (!showDropdown) return\n updateDropdownPosition()\n }, [showDropdown, updateDropdownPosition, filteredOptions.length, showCreateOption])\n\n useEffect(() => {\n setHighlightedIndex(-1)\n }, [filteredOptions.length, showCreateOption])\n\n useEffect(() => {\n if (highlightedIndex < 0 || !listRef.current) return\n const element = listRef.current.querySelector<HTMLElement>(`[data-index='${highlightedIndex}']`)\n element?.scrollIntoView({ block: 'nearest' })\n }, [highlightedIndex])\n\n useEffect(\n () => () => {\n if (blurTimeoutRef.current !== null) {\n clearTimeout(blurTimeoutRef.current)\n }\n },\n [],\n )\n\n const commitSelection = useCallback(\n (option: InputSearchOption) => {\n if (option.disabled) return\n setValue(option.value)\n setQuery(option.label)\n setIsFocused(false)\n setHighlightedIndex(-1)\n inputRef.current?.blur()\n },\n [setValue],\n )\n\n const commitCreate = useCallback(() => {\n if (!onCreate || isCreating || trimmedQuery.length === 0) return\n onCreate(trimmedQuery)\n }, [onCreate, isCreating, trimmedQuery])\n\n const handleKeyDown = useCallback(\n (event: KeyboardEvent<HTMLInputElement>) => {\n switch (event.key) {\n case 'ArrowDown': {\n if (totalItems === 0) return\n event.preventDefault()\n setHighlightedIndex((prev) => (prev < totalItems - 1 ? prev + 1 : prev))\n break\n }\n case 'ArrowUp': {\n if (totalItems === 0) return\n event.preventDefault()\n setHighlightedIndex((prev) => (prev > 0 ? prev - 1 : 0))\n break\n }\n case 'Enter': {\n if (totalItems === 0) return\n event.preventDefault()\n if (highlightedIndex >= 0 && highlightedIndex < filteredOptions.length) {\n const option = filteredOptions[highlightedIndex]\n if (option) commitSelection(option)\n } else if (highlightedIndex === createOptionIndex && !isCreating) {\n commitCreate()\n }\n break\n }\n case 'Escape': {\n event.preventDefault()\n setIsFocused(false)\n setHighlightedIndex(-1)\n if (selectedOption) setQuery(selectedOption.label)\n inputRef.current?.blur()\n break\n }\n default:\n break\n }\n },\n [totalItems, filteredOptions, highlightedIndex, createOptionIndex, commitSelection, commitCreate, isCreating, selectedOption],\n )\n\n const handleFocus = useCallback(() => {\n if (blurTimeoutRef.current !== null) {\n clearTimeout(blurTimeoutRef.current)\n blurTimeoutRef.current = null\n }\n setIsFocused(true)\n setQuery('')\n }, [])\n\n const handleBlur = useCallback(() => {\n if (blurTimeoutRef.current !== null) {\n clearTimeout(blurTimeoutRef.current)\n }\n blurTimeoutRef.current = setTimeout(() => {\n blurTimeoutRef.current = null\n // Don't write to `query` here — the useEffect tied to selectedOption + isFocused\n // is the single source of truth for the visible query when the field isn't focused.\n // Setting it from this stale closure causes off-by-one display after selection.\n setIsFocused(false)\n setHighlightedIndex(-1)\n }, 150)\n }, [])\n\n const handleClear = useCallback(() => {\n setValue('')\n setQuery('')\n inputRef.current?.focus()\n }, [setValue])\n\n const showClearButton = !isLoading && !isDisabled && (value.length > 0 || (isFocused && query.length > 0))\n\n const { dropdownWidthMode, dropdownWidthStyle } = getDropdownWidthStyles({\n dropdownWidth,\n triggerWidth: '100%',\n })\n\n const inputClasses = cn(getInputClasses(state, className), 'pe-4', !hideSearchIcon && 'ps-11', showClearButton && 'pe-10!', state === 'loading' && 'cursor-wait')\n\n return (\n <div className='flex w-full flex-col' data-testid='spectral-input-search-container' ref={ref}>\n {label && (\n <Label className={cn('mb-2 block', labelClassName, isDisabled && 'cursor-not-allowed text-input-text--disabled')} data-testid='spectral-input-search-label' htmlFor={fieldId}>\n {label}\n </Label>\n )}\n\n <div className='relative' data-testid='spectral-input-search-wrapper' ref={triggerRef}>\n <div className='relative'>\n {!hideSearchIcon && (\n <span aria-hidden='true' className={cn('left-4 text-input-icon absolute top-1/2 -translate-y-1/2', isDisabled && 'text-input-text--disabled')} data-testid='spectral-input-search-icon'>\n <SearchIcon size={20} />\n </span>\n )}\n\n <input\n aria-activedescendant={highlightedIndex >= 0 ? `${fieldId}-option-${highlightedIndex}` : undefined}\n aria-autocomplete='list'\n aria-controls={listboxId}\n aria-expanded={showDropdown}\n // oxlint-disable-next-line jsx-a11y/role-supports-aria-props -- Valid per WAI-ARIA 1.2 Combobox pattern\n aria-haspopup='listbox'\n aria-label={ariaLabel ?? label}\n autoComplete='off'\n className={inputClasses}\n data-state={state}\n data-testid='spectral-input-search'\n disabled={isDisabled || isLoading}\n id={fieldId}\n name={name}\n onBlur={handleBlur}\n onChange={(event) => {\n setQuery(event.target.value)\n if (!isFocused) setIsFocused(true)\n }}\n onFocus={handleFocus}\n onKeyDown={handleKeyDown}\n placeholder={placeholder}\n ref={inputRef}\n required={required}\n role='combobox'\n style={getFormFieldCSSProperties() as CSSProperties}\n type='text'\n value={query}\n {...getPasswordManagerIgnoreProps('text')}\n {...ariaProps}\n />\n\n {showClearButton ? (\n <button\n aria-label={`Clear ${label ?? 'search'}`}\n className='right-4 text-input-icon hover:text-input-icon--hover focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-input-border--focus rounded-sm absolute top-1/2 -translate-y-1/2 cursor-pointer'\n data-testid='spectral-input-search-clear-button'\n onClick={handleClear}\n // Prevent input blur from firing before click resolves\n onMouseDown={(event) => event.preventDefault()}\n type='button'\n >\n <CloseCircleIcon size={20} />\n </button>\n ) : state === 'loading' ? (\n <div className='right-4 text-input-icon absolute top-1/2 -translate-y-1/2' data-testid='spectral-input-search-loading-icon'>\n <LoaderIcon className='motion-safe:animate-spin' size={20} />\n </div>\n ) : state === 'error' ? (\n <div className='right-4 text-danger-400 absolute top-1/2 -translate-y-1/2' data-testid='spectral-input-search-error-icon'>\n <ErrorIcon size={20} />\n </div>\n ) : state === 'warning' ? (\n <div className='right-4 text-warning-400 absolute top-1/2 -translate-y-1/2' data-testid='spectral-input-search-warning-icon'>\n <WarningIcon size={20} />\n </div>\n ) : null}\n </div>\n\n </div>\n\n {showDropdown && dropdownPosition && typeof document !== 'undefined'\n ? createPortal(\n <div\n className={cn('origin-top p-1 z-50 fixed', getDropdownSurfaceClasses(), 'max-h-72 overflow-hidden', 'motion-safe:animate-in motion-safe:fade-in-0 motion-safe:zoom-in-95 motion-safe:slide-in-from-top-2')}\n data-dropdown-width-mode={dropdownWidthMode}\n data-dropdown-width-value={dropdownWidthMode === 'custom' ? dropdownWidth : undefined}\n data-testid='spectral-input-search-content'\n ref={setDropdownElement}\n style={{\n top: `${dropdownPosition.top}px`,\n left: `${dropdownPosition.left}px`,\n ...(dropdownWidthMode === 'trigger' ? { width: `${dropdownPosition.width}px` } : (dropdownWidthStyle as CSSProperties)),\n ...dropdownShiftStyle,\n }}\n >\n <div\n className='max-h-[17.5rem] overflow-y-auto'\n id={listboxId}\n ref={listRef}\n // oxlint-disable-next-line jsx-a11y/prefer-tag-over-role -- WAI-ARIA combobox pattern requires role='listbox' on the popup\n role='listbox'\n >\n {showEmptyState ? (\n <EmptyState message={emptyMessage} />\n ) : (\n <>\n {filteredOptions.map((option, index) => {\n const isHighlighted = index === highlightedIndex\n const isSelected = option.value === value\n return (\n <button\n aria-selected={isSelected}\n className={cn(getOptionClasses(!!option.disabled, isHighlighted, isSelected), 'text-left')}\n data-highlighted={isHighlighted ? '' : undefined}\n data-index={index}\n data-testid='spectral-input-search-item'\n disabled={option.disabled}\n id={`${fieldId}-option-${index}`}\n key={option.value}\n onMouseDown={(event) => {\n event.preventDefault()\n commitSelection(option)\n }}\n onMouseEnter={() => setHighlightedIndex(index)}\n role='option'\n tabIndex={-1}\n type='button'\n >\n <span className='min-w-0 flex-1 truncate'>{renderOption ? renderOption(option) : option.label}</span>\n </button>\n )\n })}\n\n {showCreateOption && (\n <button\n aria-selected={createOptionIndex === highlightedIndex}\n className={cn(getOptionClasses(isCreating, createOptionIndex === highlightedIndex, false), 'gap-2 text-left text-accent font-medium')}\n data-highlighted={createOptionIndex === highlightedIndex ? '' : undefined}\n data-index={createOptionIndex}\n data-testid='spectral-input-search-create-option'\n disabled={isCreating}\n id={`${fieldId}-option-${createOptionIndex}`}\n onMouseDown={(event) => {\n event.preventDefault()\n commitCreate()\n }}\n onMouseEnter={() => setHighlightedIndex(createOptionIndex)}\n role='option'\n tabIndex={-1}\n type='button'\n >\n {isCreating ? (\n <span className='gap-2 flex items-center'>\n <LoaderIcon className='shrink-0 motion-safe:animate-spin' size={16} />\n <span>{creatingLabel}</span>\n </span>\n ) : (\n <span className='gap-2 flex items-center min-w-0'>{createOptionLabel(trimmedQuery)}</span>\n )}\n </button>\n )}\n </>\n )}\n </div>\n </div>,\n document.body,\n )\n : null}\n\n <ErrorMessage\n dataTestId='spectral-input-search-error-message'\n id={errorMessageId}\n message={isInvalid ? (errorMessage ?? null) : null}\n messageReserveLines={messageReserveLines}\n messageReserveSpace={messageReserveSpace && state === 'error'}\n />\n <WarningMessage\n dataTestId='spectral-input-search-warning-message'\n id={warningMessageId}\n message={state === 'warning' ? (warningMessage ?? null) : null}\n messageReserveLines={messageReserveLines}\n messageReserveSpace={messageReserveSpace && state === 'warning'}\n />\n </div>\n )\n}\nInputSearch.displayName = 'InputSearch'\n"],"mappings":";;;;;;;;;;;;;;;;;;AAoDA,MAAM,4BAA4B,UAChC,8CACE,oBAAC,UAAD;CAAU,MAAM;CAAI,WAAU;CAAY,GAC1C,qBAAC,QAAD;CAAM,WAAU;WAAhB,CAA0B,WACjB,qBAAC,QAAD;EAAM,WAAU;YAAhB;GAAgC;GAAQ;GAAM;GAAa;IAC9D;GACN;AAGJ,MAAa,eAAe,EAC1B,WACA,oBAAoB,0BACpB,gBAAgB,aAChB,eAAe,IACf,UACA,gBAAgB,WAChB,eAAe,oBACf,cACA,iBAAiB,OACjB,IACA,aAAa,OACb,OACA,gBACA,sBAAsB,GACtB,sBAAsB,OACtB,MACA,UACA,UACA,eACA,cAAc,OACd,SACA,cAAc,WACd,KACA,cACA,UACA,QAAQ,WACR,OAAO,WACP,gBACA,oBAAoB,iBACpB,cAAc,gBACwC;AACtD,KAA6C,CAAC,SAAS,CAAC,UAEtD,SAAQ,KAAK,8EAA6E;CAG5F,MAAM,UAAU,eAAe,IAAI,KAAI;CACvC,MAAM,YAAY,GAAG,QAAQ;CAC7B,MAAM,iBAAiB,kBAAkB,QAAO;CAChD,MAAM,mBAAmB,oBAAoB,QAAO;CACpD,MAAM,YAAY,UAAU,UAAU,iBAAiB,UAAU,aAAa,iBAAiB,mBAAmB;CAClH,MAAM,EAAE,YAAY,WAAW,cAAc,kBAAkB,UAAU,MAAK;CAC9E,MAAM,YAAY,aAAa,OAAO,iBAAiB,UAAU,UAAS;CAE1E,MAAM,CAAC,OAAO,YAAY,qBAA6B;EACrD,OAAO;EACP;EACA,WAAW,cAAc;AACvB,OAAI,SACF,UAAS,UAAS;OAElB,iBAAgB,UAAS;;EAG9B,CAAA;CAED,MAAM,CAAC,OAAO,YAAY,SAAS,GAAE;CACrC,MAAM,CAAC,WAAW,gBAAgB,SAAS,MAAK;CAChD,MAAM,CAAC,kBAAkB,uBAAuB,SAAS,GAAE;CAC3D,MAAM,CAAC,kBAAkB,uBAAuB,SAA8D,KAAI;CAClH,MAAM,WAAW,OAAyB,KAAI;CAC9C,MAAM,UAAU,OAAuB,KAAI;CAC3C,MAAM,iBAAiB,OAA6C,KAAI;CACxE,MAAM,aAAa,OAAuB,KAAI;CAE9C,MAAM,iBAAiB,cAAc,QAAQ,MAAM,WAAW,OAAO,UAAU,MAAM,IAAI,MAAM,CAAC,SAAS,MAAM,CAAA;AAE/G,iBAAgB;AACd,MAAI,CAAC,UACH,UAAS,gBAAgB,SAAS,GAAE;IAErC,CAAC,gBAAgB,UAAU,CAAA;CAE9B,MAAM,eAAe,MAAM,MAAK;CAEhC,MAAM,kBAAkB,cAAc;AACpC,MAAI,aAAa,WAAW,EAAG,QAAO;EACtC,MAAM,QAAQ,aAAa,aAAY;AACvC,SAAO,QAAQ,QAAQ,WAAW,OAAO,MAAM,aAAa,CAAC,SAAS,MAAM,CAAA;IAC3E,CAAC,cAAc,QAAQ,CAAA;CAE1B,MAAM,aAAa,cAAc,QAAQ,MAAM,WAAW,OAAO,MAAM,aAAa,KAAK,aAAa,aAAa,CAAC,EAAE,CAAC,SAAS,aAAa,CAAA;CAE7I,MAAM,mBAAmB,aAAa,UAAa,aAAa,SAAS,KAAK,CAAC;CAC/E,MAAM,oBAAoB,mBAAmB,gBAAgB,SAAS;CACtE,MAAM,aAAa,gBAAgB,UAAU,mBAAmB,IAAI;CACpE,MAAM,eAAe,aAAa,CAAC,cAAc,CAAC,cAAc,eAAe,aAAa,SAAS;CACrG,MAAM,iBAAiB,gBAAgB,eAAe;CAEtD,MAAM,EAAE,oBAAoB,6BAA6B,uBAAuB,+BAA+B,aAAY;CAE3H,MAAM,yBAAyB,kBAAkB;EAC/C,MAAM,UAAU,WAAW;AAC3B,MAAI,CAAC,QAAS;EACd,MAAM,OAAO,QAAQ,uBAAsB;AAC3C,sBAAoB;GAClB,KAAK,KAAK,SAAS;GACnB,MAAM,KAAK;GACX,OAAO,KAAK;GACb,CAAA;AACD,+BAA4B;IAC3B,CAAC,4BAA4B,CAAA;AAEhC,uBAAsB;AACpB,MAAI,CAAC,cAAc;AACjB,uBAAoB,KAAI;AACxB;;AAEF,0BAAuB;EACvB,MAAM,eAAe,wBAAuB;AAC5C,SAAO,iBAAiB,UAAU,QAAQ,KAAI;AAC9C,SAAO,iBAAiB,UAAU,OAAM;AACxC,eAAa;AACX,UAAO,oBAAoB,UAAU,QAAQ,KAAI;AACjD,UAAO,oBAAoB,UAAU,OAAM;;IAE5C,CAAC,cAAc,uBAAuB,CAAA;AAEzC,uBAAsB;AACpB,MAAI,CAAC,aAAc;AACnB,0BAAuB;IACtB;EAAC;EAAc;EAAwB,gBAAgB;EAAQ;EAAiB,CAAA;AAEnF,iBAAgB;AACd,sBAAoB,GAAE;IACrB,CAAC,gBAAgB,QAAQ,iBAAiB,CAAA;AAE7C,iBAAgB;AACd,MAAI,mBAAmB,KAAK,CAAC,QAAQ,QAAS;AAE9C,EADgB,QAAQ,QAAQ,cAA2B,gBAAgB,iBAAiB,IACrF,EAAE,eAAe,EAAE,OAAO,WAAW,CAAA;IAC3C,CAAC,iBAAiB,CAAA;AAErB,uBACc;AACV,MAAI,eAAe,YAAY,KAC7B,cAAa,eAAe,QAAO;IAGvC,EAAE,CACJ;CAEA,MAAM,kBAAkB,aACrB,WAA8B;AAC7B,MAAI,OAAO,SAAU;AACrB,WAAS,OAAO,MAAK;AACrB,WAAS,OAAO,MAAK;AACrB,eAAa,MAAK;AAClB,sBAAoB,GAAE;AACtB,WAAS,SAAS,MAAK;IAEzB,CAAC,SAAS,CACZ;CAEA,MAAM,eAAe,kBAAkB;AACrC,MAAI,CAAC,YAAY,cAAc,aAAa,WAAW,EAAG;AAC1D,WAAS,aAAY;IACpB;EAAC;EAAU;EAAY;EAAa,CAAA;CAEvC,MAAM,gBAAgB,aACnB,UAA2C;AAC1C,UAAQ,MAAM,KAAd;GACE,KAAK;AACH,QAAI,eAAe,EAAG;AACtB,UAAM,gBAAe;AACrB,yBAAqB,SAAU,OAAO,aAAa,IAAI,OAAO,IAAI,KAAK;AACvE;GAEF,KAAK;AACH,QAAI,eAAe,EAAG;AACtB,UAAM,gBAAe;AACrB,yBAAqB,SAAU,OAAO,IAAI,OAAO,IAAI,EAAE;AACvD;GAEF,KAAK;AACH,QAAI,eAAe,EAAG;AACtB,UAAM,gBAAe;AACrB,QAAI,oBAAoB,KAAK,mBAAmB,gBAAgB,QAAQ;KACtE,MAAM,SAAS,gBAAgB;AAC/B,SAAI,OAAQ,iBAAgB,OAAM;eACzB,qBAAqB,qBAAqB,CAAC,WACpD,eAAa;AAEf;GAEF,KAAK;AACH,UAAM,gBAAe;AACrB,iBAAa,MAAK;AAClB,wBAAoB,GAAE;AACtB,QAAI,eAAgB,UAAS,eAAe,MAAK;AACjD,aAAS,SAAS,MAAK;AACvB;GAEF,QACE;;IAGN;EAAC;EAAY;EAAiB;EAAkB;EAAmB;EAAiB;EAAc;EAAY;EAAe,CAC/H;CAEA,MAAM,cAAc,kBAAkB;AACpC,MAAI,eAAe,YAAY,MAAM;AACnC,gBAAa,eAAe,QAAO;AACnC,kBAAe,UAAU;;AAE3B,eAAa,KAAI;AACjB,WAAS,GAAE;IACV,EAAE,CAAA;CAEL,MAAM,aAAa,kBAAkB;AACnC,MAAI,eAAe,YAAY,KAC7B,cAAa,eAAe,QAAO;AAErC,iBAAe,UAAU,iBAAiB;AACxC,kBAAe,UAAU;AAIzB,gBAAa,MAAK;AAClB,uBAAoB,GAAE;KACrB,IAAG;IACL,EAAE,CAAA;CAEL,MAAM,cAAc,kBAAkB;AACpC,WAAS,GAAE;AACX,WAAS,GAAE;AACX,WAAS,SAAS,OAAM;IACvB,CAAC,SAAS,CAAA;CAEb,MAAM,kBAAkB,CAAC,aAAa,CAAC,eAAe,MAAM,SAAS,KAAM,aAAa,MAAM,SAAS;CAEvG,MAAM,EAAE,mBAAmB,uBAAuB,uBAAuB;EACvE;EACA,cAAc;EACf,CAAA;CAED,MAAM,eAAe,GAAG,gBAAgB,OAAO,UAAU,EAAE,QAAQ,CAAC,kBAAkB,SAAS,mBAAmB,UAAU,UAAU,aAAa,cAAa;AAEhK,QACE,qBAAC,OAAD;EAAK,WAAU;EAA0E;YAAzF;GACG,SACC,oBAAC,OAAD;IAAO,WAAW,GAAG,cAAc,gBAAgB,cAAc,+CAA+C;IAA4C,SAAS;cAClK;IACI;GAGT,oBAAC,OAAD;IAAK,WAAU;IAAuD,KAAK;cACzE,qBAAC,OAAD;KAAK,WAAU;eAAf;MACG,CAAC,kBACA,oBAAC,QAAD;OAAM,eAAY;OAAO,WAAW,GAAG,4DAA4D,cAAc,4BAA4B;iBAC3I,oBAAC,YAAD,EAAY,MAAM,IAAK;OACnB;MAGR,oBAAC,SAAD;OACE,yBAAuB,oBAAoB,IAAI,GAAG,QAAQ,UAAU,qBAAqB;OACzF,qBAAkB;OAClB,iBAAe;OACf,iBAAe;OAEf,iBAAc;OACd,cAAY,aAAa;OACzB,cAAa;OACb,WAAW;OACX,cAAY;OAEZ,UAAU,cAAc;OACxB,IAAI;OACE;OACN,QAAQ;OACR,WAAW,UAAU;AACnB,iBAAS,MAAM,OAAO,MAAK;AAC3B,YAAI,CAAC,UAAW,cAAa,KAAI;;OAEnC,SAAS;OACT,WAAW;OACE;OACb,KAAK;OACK;OACV,MAAK;OACL,OAAO,2BAA2B;OAClC,MAAK;OACL,OAAO;OACP,GAAI,8BAA8B,OAAO;OACzC,GAAI;OACL;MAEA,kBACC,oBAAC,UAAD;OACE,cAAY,SAAS,SAAS;OAC9B,WAAU;OAEV,SAAS;OAET,cAAc,UAAU,MAAM,gBAAgB;OAC9C,MAAK;iBAEL,oBAAC,iBAAD,EAAiB,MAAM,IAAK;OACtB,IACN,UAAU,YACZ,oBAAC,OAAD;OAAK,WAAU;iBACb,oBAAC,YAAD;QAAY,WAAU;QAA2B,MAAM;QAAK;OACzD,IACH,UAAU,UACZ,oBAAC,OAAD;OAAK,WAAU;iBACb,oBAAC,WAAD,EAAW,MAAM,IAAK;OACnB,IACH,UAAU,YACZ,oBAAC,OAAD;OAAK,WAAU;iBACb,oBAAC,aAAD,EAAa,MAAM,IAAK;OACrB,IACH;MACD;;IAEF;GAEJ,gBAAgB,oBAAoB,OAAO,aAAa,cACrD,aACE,oBAAC,OAAD;IACE,WAAW,GAAG,6BAA6B,2BAA2B,EAAE,4BAA4B,sGAAsG;IAC1M,4BAA0B;IAC1B,6BAA2B,sBAAsB,WAAW,gBAAgB;IAE5E,KAAK;IACL,OAAO;KACL,KAAK,GAAG,iBAAiB,IAAI;KAC7B,MAAM,GAAG,iBAAiB,KAAK;KAC/B,GAAI,sBAAsB,YAAY,EAAE,OAAO,GAAG,iBAAiB,MAAM,KAAK,GAAI;KAClF,GAAG;KACJ;cAED,oBAAC,OAAD;KACE,WAAU;KACV,IAAI;KACJ,KAAK;KAEL,MAAK;eAEJ,iBACC,oBAAC,YAAD,EAAY,SAAS,cAAe,IAEpC,8CACG,gBAAgB,KAAK,QAAQ,UAAU;MACxC,MAAM,gBAAgB,UAAU;MAChC,MAAM,aAAa,OAAO,UAAU;AACpC,aACE,oBAAC,UAAD;OACE,iBAAe;OACf,WAAW,GAAG,iBAAiB,CAAC,CAAC,OAAO,UAAU,eAAe,WAAW,EAAE,YAAY;OAC1F,oBAAkB,gBAAgB,KAAK;OACvC,cAAY;OAEZ,UAAU,OAAO;OACjB,IAAI,GAAG,QAAQ,UAAU;OAEzB,cAAc,UAAU;AACtB,cAAM,gBAAe;AACrB,wBAAgB,OAAM;;OAExB,oBAAoB,oBAAoB,MAAM;OAC9C,MAAK;OACL,UAAU;OACV,MAAK;iBAEL,oBAAC,QAAD;QAAM,WAAU;kBAA2B,eAAe,aAAa,OAAO,GAAG,OAAO;QAAY;OAC9F,EAXD,OAAO,MAWN;OAEV,EAEC,oBACC,oBAAC,UAAD;MACE,iBAAe,sBAAsB;MACrC,WAAW,GAAG,iBAAiB,YAAY,sBAAsB,kBAAkB,MAAM,EAAE,0CAA0C;MACrI,oBAAkB,sBAAsB,mBAAmB,KAAK;MAChE,cAAY;MAEZ,UAAU;MACV,IAAI,GAAG,QAAQ,UAAU;MACzB,cAAc,UAAU;AACtB,aAAM,gBAAe;AACrB,qBAAa;;MAEf,oBAAoB,oBAAoB,kBAAkB;MAC1D,MAAK;MACL,UAAU;MACV,MAAK;gBAEJ,aACC,qBAAC,QAAD;OAAM,WAAU;iBAAhB,CACE,oBAAC,YAAD;QAAY,WAAU;QAAoC,MAAM;QAAK,GACrE,oBAAC,QAAD,YAAO,eAAoB,EACvB;WAEN,oBAAC,QAAD;OAAM,WAAU;iBAAmC,kBAAkB,aAAa;OAAO;MAErF,EAEV;KAED;IACD,GACN,SAAS,KACX,GACA;GAEJ,oBAAC,cAAD;IACE,YAAW;IACX,IAAI;IACJ,SAAS,YAAa,gBAAgB,OAAQ;IACzB;IACrB,qBAAqB,uBAAuB,UAAU;IACvD;GACD,oBAAC,gBAAD;IACE,YAAW;IACX,IAAI;IACJ,SAAS,UAAU,YAAa,kBAAkB,OAAQ;IACrC;IACrB,qBAAqB,uBAAuB,UAAU;IACvD;GACE;;;AAGT,YAAY,cAAc"}
|
|
1
|
+
{"version":3,"file":"InputSearch.js","names":[],"sources":["../src/components/InputSearch/InputSearch.tsx"],"sourcesContent":["import { CloseCircleIcon, ErrorIcon, LoaderIcon, PlusIcon, SearchIcon, WarningIcon } from '@components/Icons'\nimport { Label } from '@components/Label/Label'\nimport { useUncontrolledState } from '@hooks/useUncontrolledState'\nimport { useAutoDropdownHorizontalShift } from '@utils/dropdownPositioning'\nimport {\n EmptyState,\n ErrorMessage,\n WarningMessage,\n getAriaProps,\n getDropdownSurfaceClasses,\n getDropdownWidthStyles,\n getErrorMessageId,\n getFormFieldCSSProperties,\n getInputClasses,\n getOptionClasses,\n getPasswordManagerIgnoreProps,\n getWarningMessageId,\n useFormFieldId,\n useFormFieldState,\n type BaseFormFieldProps,\n type BaseOption,\n type DropdownWidth,\n type FormFieldState,\n} from '@utils/formFieldUtils'\nimport { cn } from '@utils/twUtils'\nimport { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, type CSSProperties, type KeyboardEvent, type ReactNode, type Ref } from 'react'\nimport { createPortal } from 'react-dom'\n\nexport type InputSearchOption = BaseOption\n\nexport interface InputSearchProps extends Omit<BaseFormFieldProps, 'onChange' | 'state'> {\n className?: string\n createOptionLabel?: (query: string) => ReactNode\n creatingLabel?: string\n defaultValue?: string\n dropdownWidth?: DropdownWidth\n emptyMessage?: string\n hideSearchIcon?: boolean\n isCreating?: boolean\n labelClassName?: string\n onChange?: (value: string) => void\n onCreate?: (query: string) => void\n onValueChange?: (value: string) => void\n openOnFocus?: boolean\n options: InputSearchOption[]\n placeholder?: string\n renderOption?: (option: InputSearchOption) => ReactNode\n state?: Exclude<FormFieldState, 'disabled'>\n value?: string\n warningMessage?: BaseFormFieldProps['errorMessage']\n}\n\nconst defaultCreateOptionLabel = (query: string): ReactNode => (\n <>\n <PlusIcon\n size={16}\n className='shrink-0'\n />\n <span className='truncate'>\n Create <span className='font-semibold'>“{query}”</span>\n </span>\n </>\n)\n\nexport const InputSearch = ({\n className,\n createOptionLabel = defaultCreateOptionLabel,\n creatingLabel = 'Creating…',\n defaultValue = '',\n disabled,\n dropdownWidth = 'trigger',\n emptyMessage = 'No options found',\n errorMessage,\n hideSearchIcon = false,\n id,\n isCreating = false,\n label,\n labelClassName,\n messageReserveLines = 1,\n messageReserveSpace = false,\n name,\n onChange,\n onCreate,\n onValueChange,\n openOnFocus = false,\n options,\n placeholder = 'Search…',\n ref,\n renderOption,\n required,\n state = 'default',\n value: valueProp,\n warningMessage,\n 'aria-describedby': ariaDescribedBy,\n 'aria-label': ariaLabel,\n}: InputSearchProps & { ref?: Ref<HTMLDivElement> }) => {\n if (process.env.NODE_ENV !== 'production' && !label && !ariaLabel) {\n // eslint-disable-next-line no-console\n console.warn('InputSearch: provide either `label` or `aria-label` for an accessible name.')\n }\n\n const fieldId = useFormFieldId(id, name)\n const listboxId = `${fieldId}-listbox`\n const errorMessageId = getErrorMessageId(fieldId)\n const warningMessageId = getWarningMessageId(fieldId)\n const messageId = state === 'error' ? errorMessageId : state === 'warning' && warningMessage ? warningMessageId : undefined\n const { isDisabled, isLoading, isInvalid } = useFormFieldState(disabled, state)\n const ariaProps = getAriaProps(state, ariaDescribedBy, required, messageId)\n\n const [value, setValue] = useUncontrolledState<string>({\n value: valueProp,\n defaultValue,\n onChange: (nextValue) => {\n if (onChange) {\n onChange(nextValue)\n } else {\n onValueChange?.(nextValue)\n }\n },\n })\n\n const [query, setQuery] = useState('')\n const [isFocused, setIsFocused] = useState(false)\n const [highlightedIndex, setHighlightedIndex] = useState(-1)\n const [dropdownPosition, setDropdownPosition] = useState<{ top: number; left: number; width: number } | null>(null)\n const inputRef = useRef<HTMLInputElement>(null)\n const listRef = useRef<HTMLDivElement>(null)\n const blurTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null)\n const triggerRef = useRef<HTMLDivElement>(null)\n\n const selectedOption = useMemo(() => options.find((option) => option.value === value) ?? null, [options, value])\n\n useEffect(() => {\n if (!isFocused) {\n setQuery(selectedOption?.label ?? '')\n }\n }, [selectedOption, isFocused])\n\n const trimmedQuery = query.trim()\n\n const filteredOptions = useMemo(() => {\n if (trimmedQuery.length === 0) return options\n const lower = trimmedQuery.toLowerCase()\n return options.filter((option) => option.label.toLowerCase().includes(lower))\n }, [trimmedQuery, options])\n\n const exactMatch = useMemo(() => options.some((option) => option.label.toLowerCase() === trimmedQuery.toLowerCase()), [options, trimmedQuery])\n\n const showCreateOption = onCreate !== undefined && trimmedQuery.length > 0 && !exactMatch\n const createOptionIndex = showCreateOption ? filteredOptions.length : -1\n const totalItems = filteredOptions.length + (showCreateOption ? 1 : 0)\n const showDropdown = isFocused && !isDisabled && !isLoading && (openOnFocus || trimmedQuery.length > 0)\n const showEmptyState = showDropdown && totalItems === 0\n\n const { dropdownShiftStyle, recalculateDropdownPosition, setDropdownElement } = useAutoDropdownHorizontalShift(showDropdown)\n\n const updateDropdownPosition = useCallback(() => {\n const trigger = triggerRef.current\n if (!trigger) return\n const rect = trigger.getBoundingClientRect()\n setDropdownPosition({\n top: rect.bottom + 4,\n left: rect.left,\n width: rect.width,\n })\n recalculateDropdownPosition()\n }, [recalculateDropdownPosition])\n\n useLayoutEffect(() => {\n if (!showDropdown) {\n setDropdownPosition(null)\n return\n }\n updateDropdownPosition()\n const handle = () => updateDropdownPosition()\n window.addEventListener('scroll', handle, true)\n window.addEventListener('resize', handle)\n return () => {\n window.removeEventListener('scroll', handle, true)\n window.removeEventListener('resize', handle)\n }\n }, [showDropdown, updateDropdownPosition])\n\n useLayoutEffect(() => {\n if (!showDropdown) return\n updateDropdownPosition()\n }, [showDropdown, updateDropdownPosition, filteredOptions.length, showCreateOption])\n\n useEffect(() => {\n setHighlightedIndex(-1)\n }, [filteredOptions.length, showCreateOption])\n\n useEffect(() => {\n if (highlightedIndex < 0 || !listRef.current) return\n const element = listRef.current.querySelector<HTMLElement>(`[data-index='${highlightedIndex}']`)\n element?.scrollIntoView({ block: 'nearest' })\n }, [highlightedIndex])\n\n useEffect(\n () => () => {\n if (blurTimeoutRef.current !== null) {\n clearTimeout(blurTimeoutRef.current)\n }\n },\n [],\n )\n\n const commitSelection = useCallback(\n (option: InputSearchOption) => {\n if (option.disabled) return\n setValue(option.value)\n setQuery(option.label)\n setIsFocused(false)\n setHighlightedIndex(-1)\n inputRef.current?.blur()\n },\n [setValue],\n )\n\n const commitCreate = useCallback(() => {\n if (!onCreate || isCreating || trimmedQuery.length === 0) return\n onCreate(trimmedQuery)\n }, [onCreate, isCreating, trimmedQuery])\n\n const handleKeyDown = useCallback(\n (event: KeyboardEvent<HTMLInputElement>) => {\n switch (event.key) {\n case 'ArrowDown': {\n if (totalItems === 0) return\n event.preventDefault()\n setHighlightedIndex((prev) => (prev < totalItems - 1 ? prev + 1 : prev))\n break\n }\n case 'ArrowUp': {\n if (totalItems === 0) return\n event.preventDefault()\n setHighlightedIndex((prev) => (prev > 0 ? prev - 1 : 0))\n break\n }\n case 'Enter': {\n if (totalItems === 0) return\n event.preventDefault()\n if (highlightedIndex >= 0 && highlightedIndex < filteredOptions.length) {\n const option = filteredOptions[highlightedIndex]\n if (option) commitSelection(option)\n } else if (highlightedIndex === createOptionIndex && !isCreating) {\n commitCreate()\n }\n break\n }\n case 'Escape': {\n event.preventDefault()\n setIsFocused(false)\n setHighlightedIndex(-1)\n if (selectedOption) setQuery(selectedOption.label)\n inputRef.current?.blur()\n break\n }\n default:\n break\n }\n },\n [totalItems, filteredOptions, highlightedIndex, createOptionIndex, commitSelection, commitCreate, isCreating, selectedOption],\n )\n\n const handleFocus = useCallback(() => {\n if (blurTimeoutRef.current !== null) {\n clearTimeout(blurTimeoutRef.current)\n blurTimeoutRef.current = null\n }\n setIsFocused(true)\n setQuery('')\n }, [])\n\n const handleBlur = useCallback(() => {\n if (blurTimeoutRef.current !== null) {\n clearTimeout(blurTimeoutRef.current)\n }\n blurTimeoutRef.current = setTimeout(() => {\n blurTimeoutRef.current = null\n // Don't write to `query` here — the useEffect tied to selectedOption + isFocused\n // is the single source of truth for the visible query when the field isn't focused.\n // Setting it from this stale closure causes off-by-one display after selection.\n setIsFocused(false)\n setHighlightedIndex(-1)\n }, 150)\n }, [])\n\n const handleClear = useCallback(() => {\n setValue('')\n setQuery('')\n inputRef.current?.focus()\n }, [setValue])\n\n const showClearButton = !isLoading && !isDisabled && (value.length > 0 || (isFocused && query.length > 0))\n\n const { dropdownWidthMode, dropdownWidthStyle } = getDropdownWidthStyles({\n dropdownWidth,\n triggerWidth: '100%',\n })\n\n const inputClasses = cn(getInputClasses(state, className), 'pe-4', !hideSearchIcon && 'ps-11', showClearButton && 'pe-10!', state === 'loading' && 'cursor-wait')\n\n return (\n <div\n className='flex w-full flex-col'\n data-testid='spectral-input-search-container'\n ref={ref}\n >\n {label && (\n <Label\n className={cn('mb-2 block', labelClassName, isDisabled && 'cursor-not-allowed text-input-text--disabled')}\n data-testid='spectral-input-search-label'\n htmlFor={fieldId}\n >\n {label}\n </Label>\n )}\n\n <div\n className='relative'\n data-testid='spectral-input-search-wrapper'\n ref={triggerRef}\n >\n <div className='relative'>\n {!hideSearchIcon && (\n <span\n aria-hidden='true'\n className={cn('left-4 text-input-icon absolute top-1/2 -translate-y-1/2', isDisabled && 'text-input-text--disabled')}\n data-testid='spectral-input-search-icon'\n >\n <SearchIcon size={20} />\n </span>\n )}\n\n <input\n aria-activedescendant={highlightedIndex >= 0 ? `${fieldId}-option-${highlightedIndex}` : undefined}\n aria-autocomplete='list'\n aria-controls={listboxId}\n aria-expanded={showDropdown}\n // oxlint-disable-next-line jsx-a11y/role-supports-aria-props -- Valid per WAI-ARIA 1.2 Combobox pattern\n aria-haspopup='listbox'\n aria-label={ariaLabel ?? label}\n autoComplete='off'\n className={inputClasses}\n data-state={state}\n data-testid='spectral-input-search'\n disabled={isDisabled || isLoading}\n id={fieldId}\n name={name}\n onBlur={handleBlur}\n onChange={(event) => {\n setQuery(event.target.value)\n if (!isFocused) setIsFocused(true)\n }}\n onFocus={handleFocus}\n onKeyDown={handleKeyDown}\n placeholder={placeholder}\n ref={inputRef}\n required={required}\n role='combobox'\n style={getFormFieldCSSProperties() as CSSProperties}\n type='text'\n value={query}\n {...getPasswordManagerIgnoreProps('text')}\n {...ariaProps}\n />\n\n {showClearButton ? (\n <button\n aria-label={`Clear ${label ?? 'search'}`}\n className='right-4 text-input-icon hover:text-input-icon--hover rounded-sm absolute top-1/2 -translate-y-1/2 cursor-pointer focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-input-border--focus'\n data-testid='spectral-input-search-clear-button'\n onClick={handleClear}\n // Prevent input blur from firing before click resolves\n onMouseDown={(event) => event.preventDefault()}\n type='button'\n >\n <CloseCircleIcon size={20} />\n </button>\n ) : state === 'loading' ? (\n <div\n className='right-4 text-input-icon absolute top-1/2 -translate-y-1/2'\n data-testid='spectral-input-search-loading-icon'\n >\n <LoaderIcon\n className='motion-safe:animate-spin'\n size={20}\n />\n </div>\n ) : state === 'error' ? (\n <div\n className='right-4 absolute top-1/2 -translate-y-1/2 text-danger-400'\n data-testid='spectral-input-search-error-icon'\n >\n <ErrorIcon size={20} />\n </div>\n ) : state === 'warning' ? (\n <div\n className='right-4 absolute top-1/2 -translate-y-1/2 text-warning-400'\n data-testid='spectral-input-search-warning-icon'\n >\n <WarningIcon size={20} />\n </div>\n ) : null}\n </div>\n </div>\n\n {showDropdown && dropdownPosition && typeof document !== 'undefined'\n ? createPortal(\n <div\n className={cn('p-1 fixed z-50 origin-top', getDropdownSurfaceClasses(), 'max-h-72 overflow-hidden', 'motion-safe:animate-in motion-safe:fade-in-0 motion-safe:slide-in-from-top-2 motion-safe:zoom-in-95')}\n data-dropdown-width-mode={dropdownWidthMode}\n data-dropdown-width-value={dropdownWidthMode === 'custom' ? dropdownWidth : undefined}\n data-testid='spectral-input-search-content'\n ref={setDropdownElement}\n style={{\n top: `${dropdownPosition.top}px`,\n left: `${dropdownPosition.left}px`,\n ...(dropdownWidthMode === 'trigger' ? { width: `${dropdownPosition.width}px` } : (dropdownWidthStyle as CSSProperties)),\n ...dropdownShiftStyle,\n }}\n >\n <div\n className='max-h-[17.5rem] overflow-y-auto'\n id={listboxId}\n ref={listRef}\n // oxlint-disable-next-line jsx-a11y/prefer-tag-over-role -- WAI-ARIA combobox pattern requires role='listbox' on the popup\n role='listbox'\n >\n {showEmptyState ? (\n <EmptyState message={emptyMessage} />\n ) : (\n <>\n {filteredOptions.map((option, index) => {\n const isHighlighted = index === highlightedIndex\n const isSelected = option.value === value\n return (\n <button\n aria-selected={isSelected}\n className={cn(getOptionClasses(!!option.disabled, isHighlighted, isSelected), 'text-left')}\n data-highlighted={isHighlighted ? '' : undefined}\n data-index={index}\n data-testid='spectral-input-search-item'\n disabled={option.disabled}\n id={`${fieldId}-option-${index}`}\n key={option.value}\n onMouseDown={(event) => {\n event.preventDefault()\n commitSelection(option)\n }}\n onMouseEnter={() => setHighlightedIndex(index)}\n role='option'\n tabIndex={-1}\n type='button'\n >\n <span className='min-w-0 flex-1 truncate'>{renderOption ? renderOption(option) : option.label}</span>\n </button>\n )\n })}\n\n {showCreateOption && (\n <button\n aria-selected={createOptionIndex === highlightedIndex}\n className={cn(getOptionClasses(isCreating, createOptionIndex === highlightedIndex, false), 'gap-2 font-medium text-left text-accent')}\n data-highlighted={createOptionIndex === highlightedIndex ? '' : undefined}\n data-index={createOptionIndex}\n data-testid='spectral-input-search-create-option'\n disabled={isCreating}\n id={`${fieldId}-option-${createOptionIndex}`}\n onMouseDown={(event) => {\n event.preventDefault()\n commitCreate()\n }}\n onMouseEnter={() => setHighlightedIndex(createOptionIndex)}\n role='option'\n tabIndex={-1}\n type='button'\n >\n {isCreating ? (\n <span className='gap-2 flex items-center'>\n <LoaderIcon\n className='motion-safe:animate-spin shrink-0'\n size={16}\n />\n <span>{creatingLabel}</span>\n </span>\n ) : (\n <span className='gap-2 min-w-0 flex items-center'>{createOptionLabel(trimmedQuery)}</span>\n )}\n </button>\n )}\n </>\n )}\n </div>\n </div>,\n document.body,\n )\n : null}\n\n <ErrorMessage\n dataTestId='spectral-input-search-error-message'\n id={errorMessageId}\n message={isInvalid ? (errorMessage ?? null) : null}\n messageReserveLines={messageReserveLines}\n messageReserveSpace={messageReserveSpace && state === 'error'}\n />\n <WarningMessage\n dataTestId='spectral-input-search-warning-message'\n id={warningMessageId}\n message={state === 'warning' ? (warningMessage ?? null) : null}\n messageReserveLines={messageReserveLines}\n messageReserveSpace={messageReserveSpace && state === 'warning'}\n />\n </div>\n )\n}\nInputSearch.displayName = 'InputSearch'\n"],"mappings":"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"}
|
package/dist/Kbd.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Kbd.d.ts","names":[],"sources":["../src/components/Kbd/Kbd.tsx"],"mappings":";;;;;KAGY,SAAA;AAAA,UAEK,QAAA;EACf,SAAA;EACA,MAAA,GAAS,SAAA;EACT,cAAA;AAAA;AAAA,UAGe,aAAA;EACf,SAAA;AAAA;AAAA;
|
|
1
|
+
{"version":3,"file":"Kbd.d.ts","names":[],"sources":["../src/components/Kbd/Kbd.tsx"],"mappings":";;;;;KAGY,SAAA;AAAA,UAEK,QAAA;EACf,SAAA;EACA,MAAA,GAAS,SAAA;EACT,cAAA;AAAA;AAAA,UAGe,aAAA;EACf,SAAA;AAAA;AAAA;EA4LoB,SAAA;EAAW,MAAA;EAAQ,cAAA;EAA0B,QAAA;EAAA,GAAa;AAAA,GAAS,QAAA,GAAW,cAAA,UAAqB,oBAAA,CAAA,GAAA,CAAA,OAAA;AAAA;;;;EA6B9F,SAAA;EAAA,GAAc;AAAA,GAAS,aAAA,GAAgB,cAAA,UAAqB,oBAAA,CAAA,GAAA,CAAA,OAAA;AAAA"}
|
package/dist/Kbd.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Kbd.js","names":[],"sources":["../src/components/Kbd/Kbd.tsx"],"sourcesContent":["import { cn } from '@utils/twUtils'\nimport { type ComponentProps } from 'react'\n\nexport type KbdSymbol = 'arrowLeft' | 'arrowRight' | 'arrowUp' | 'arrowDown' | 'command' | 'option' | 'shift' | 'control' | 'return' | 'delete'\n\nexport interface KbdProps {\n className?: string\n symbol?: KbdSymbol\n symbolPosition?: 'start' | 'end'\n}\n\nexport interface KbdGroupProps {\n className?: string\n}\n\nconst symbolMap = {\n arrowLeft: (\n <svg
|
|
1
|
+
{"version":3,"file":"Kbd.js","names":[],"sources":["../src/components/Kbd/Kbd.tsx"],"sourcesContent":["import { cn } from '@utils/twUtils'\nimport { type ComponentProps } from 'react'\n\nexport type KbdSymbol = 'arrowLeft' | 'arrowRight' | 'arrowUp' | 'arrowDown' | 'command' | 'option' | 'shift' | 'control' | 'return' | 'delete'\n\nexport interface KbdProps {\n className?: string\n symbol?: KbdSymbol\n symbolPosition?: 'start' | 'end'\n}\n\nexport interface KbdGroupProps {\n className?: string\n}\n\nconst symbolMap = {\n arrowLeft: (\n <svg\n width='14'\n height='14'\n viewBox='0 0 24 24'\n fill='none'\n xmlns='http://www.w3.org/2000/svg'\n >\n <path\n d='M10 5.75L3.75 12L10 18.25M4.5 12H20.25'\n stroke='currentColor'\n strokeWidth='1.5'\n strokeLinecap='round'\n strokeLinejoin='round'\n />\n </svg>\n ),\n arrowRight: (\n <svg\n width='14'\n height='14'\n viewBox='0 0 24 24'\n fill='none'\n xmlns='http://www.w3.org/2000/svg'\n >\n <path\n d='M14 5.75L20.25 12L14 18.25M19.5 12H3.75'\n stroke='currentColor'\n strokeWidth='1.5'\n strokeLinecap='round'\n strokeLinejoin='round'\n />\n </svg>\n ),\n arrowUp: (\n <svg\n width='14'\n height='14'\n viewBox='0 0 24 24'\n fill='none'\n xmlns='http://www.w3.org/2000/svg'\n >\n <path\n d='M5.75 10L12 3.75L18.25 10M12 20.25V4.5'\n stroke='currentColor'\n strokeWidth='1.5'\n strokeLinecap='round'\n strokeLinejoin='round'\n />\n </svg>\n ),\n arrowDown: (\n <svg\n width='14'\n height='14'\n viewBox='0 0 24 24'\n fill='none'\n xmlns='http://www.w3.org/2000/svg'\n >\n <path\n d='M18 14L12 20L6 14M12 19V4'\n stroke='currentColor'\n strokeWidth='1.5'\n strokeLinecap='round'\n strokeLinejoin='round'\n />\n </svg>\n ),\n command: (\n <svg\n width='14'\n height='14'\n viewBox='0 0 24 24'\n fill='none'\n xmlns='http://www.w3.org/2000/svg'\n >\n <path\n d='M9.25 9.25V6.5C9.25 4.98122 8.01878 3.75 6.5 3.75C4.98122 3.75 3.75 4.98122 3.75 6.5C3.75 8.01878 4.98122 9.25 6.5 9.25H9.25ZM9.25 9.25H14.75M9.25 9.25V14.75M14.75 9.25V6.5C14.75 4.98122 15.9812 3.75 17.5 3.75C19.0188 3.75 20.25 4.98122 20.25 6.5C20.25 8.01878 19.0188 9.25 17.5 9.25H14.75ZM14.75 9.25V14.75M14.75 14.75H9.25M14.75 14.75V17.5C14.75 19.0188 15.9812 20.25 17.5 20.25C19.0188 20.25 20.25 19.0188 20.25 17.5C20.25 15.9812 19.0188 14.75 17.5 14.75H14.75ZM9.25 14.75V17.5C9.25 19.0188 8.01878 20.25 6.5 20.25C4.98122 20.25 3.75 19.0188 3.75 17.5C3.75 15.9812 4.98122 14.75 6.5 14.75H9.25Z'\n stroke='currentColor'\n strokeWidth='1.5'\n strokeLinecap='square'\n />\n </svg>\n ),\n option: (\n <svg\n width='14'\n height='14'\n viewBox='0 0 24 24'\n fill='none'\n xmlns='http://www.w3.org/2000/svg'\n >\n <path\n d='M3.75 4.75H7.40962C7.77384 4.75 8.10925 4.94802 8.2852 5.26692L15.7148 18.7331C15.8907 19.052 16.2262 19.25 16.5904 19.25H20.25M15.75 4.75H20.25'\n stroke='currentColor'\n strokeWidth='1.5'\n strokeLinecap='round'\n strokeLinejoin='round'\n />\n </svg>\n ),\n shift: (\n <svg\n width='14'\n height='14'\n viewBox='0 0 24 24'\n fill='none'\n xmlns='http://www.w3.org/2000/svg'\n >\n <path\n d='M2.91032 11.5511L11.2848 2.98182C11.6771 2.5804 12.3229 2.5804 12.7152 2.98182L21.0897 11.5511C21.7085 12.1843 21.2599 13.25 20.3745 13.25H17.1316V19.25C17.1316 19.8023 16.6839 20.25 16.1316 20.25H7.86842C7.31614 20.25 6.86842 19.8023 6.86842 19.25V13.25H3.62551C2.74013 13.25 2.2915 12.1843 2.91032 11.5511Z'\n stroke='currentColor'\n strokeWidth='1.5'\n strokeLinecap='square'\n strokeLinejoin='round'\n />\n </svg>\n ),\n control: (\n <svg\n className='-translate-y-0.5'\n width='14'\n height='14'\n viewBox='0 0 24 24'\n fill='none'\n xmlns='http://www.w3.org/2000/svg'\n >\n <path\n d='M8 13.9999L11.6464 10.3535C11.8417 10.1582 12.1583 10.1582 12.3536 10.3535L16 13.9999'\n stroke='currentColor'\n strokeWidth='1.5'\n strokeLinecap='round'\n strokeLinejoin='round'\n />\n </svg>\n ),\n return: (\n <svg\n width='14'\n height='14'\n viewBox='0 0 24 24'\n fill='none'\n xmlns='http://www.w3.org/2000/svg'\n >\n <path\n fillRule='evenodd'\n clipRule='evenodd'\n d='M20.25 4C19.8358 4 19.5 4.33579 19.5 4.75V14.25C19.5 14.3881 19.3881 14.5 19.25 14.5H5.56066L8.28033 11.7803C8.57322 11.4874 8.57322 11.0126 8.28033 10.7197C7.98744 10.4268 7.51256 10.4268 7.21967 10.7197L3.21967 14.7197C2.92678 15.0126 2.92678 15.4874 3.21967 15.7803L7.21967 19.7803C7.51256 20.0732 7.98744 20.0732 8.28033 19.7803C8.57322 19.4874 8.57322 19.0126 8.28033 18.7197L5.56066 16H19.25C20.2165 16 21 15.2165 21 14.25V4.75C21 4.33579 20.6642 4 20.25 4Z'\n fill='currentColor'\n />\n </svg>\n ),\n delete: (\n <svg\n width='14'\n height='14'\n viewBox='0 0 24 24'\n fill='none'\n xmlns='http://www.w3.org/2000/svg'\n >\n <path\n d='M14.9988 9.75L12.7488 12M12.7488 12L10.4988 14.25M12.7488 12L10.4988 9.75M12.7488 12L14.9988 14.25M6.55509 4.75H20.2488C20.8011 4.75 21.2488 5.19772 21.2488 5.75V18.25C21.2488 18.8023 20.8011 19.25 20.2488 19.25H6.55509C6.2092 19.25 5.88786 19.0712 5.70545 18.7774L1.82614 12.5274C1.62566 12.2044 1.62566 11.7956 1.82614 11.4726L5.70545 5.22264C5.88786 4.92875 6.2092 4.75 6.55509 4.75Z'\n stroke='currentColor'\n strokeWidth='1.5'\n strokeLinecap='round'\n strokeLinejoin='round'\n />\n </svg>\n ),\n} as const\n\nconst symbolLabelMap: Record<KbdSymbol, string> = {\n arrowLeft: 'Left Arrow',\n arrowRight: 'Right Arrow',\n arrowUp: 'Up Arrow',\n arrowDown: 'Down Arrow',\n command: 'Command',\n option: 'Option',\n shift: 'Shift',\n control: 'Control',\n return: 'Return',\n delete: 'Delete',\n}\n\nexport const Kbd = ({ className, symbol, symbolPosition = 'start', children, ...props }: KbdProps & ComponentProps<'kbd'>) => {\n const hasChildren = children !== undefined && children !== null && children !== ''\n const symbolElement = symbol && <span aria-hidden='true'>{symbolMap[symbol]}</span>\n\n // Derive aria-label from symbol when no children are provided, unless explicitly overridden\n const derivedAriaLabel = !hasChildren && symbol ? symbolLabelMap[symbol] : undefined\n const ariaLabel = props['aria-label'] ?? derivedAriaLabel\n\n return (\n <kbd\n className={cn(\n 'h-5 min-w-5 gap-1 rounded-sm px-1 text-xs font-medium pointer-events-none inline-flex w-fit items-center justify-center bg-kbd-bg font-mono! text-kbd-text select-none',\n `[&_svg:not([class*='size-'])]:size-3 in-data-[slot=tooltip-content]:bg-level-three`,\n className,\n )}\n data-slot='kbd'\n data-testid='spectral-kbd'\n {...props}\n aria-label={ariaLabel}\n >\n {symbolPosition === 'start' && symbolElement}\n {children}\n {symbolPosition === 'end' && symbolElement}\n </kbd>\n )\n}\n\nKbd.displayName = 'Kbd'\n\nexport const KbdGroup = ({ className, ...props }: KbdGroupProps & ComponentProps<'kbd'>) => {\n return (\n <kbd\n className={cn('gap-1 inline-flex items-center', className)}\n data-slot='kbd-group'\n data-testid='spectral-kbd-group'\n {...props}\n />\n )\n}\n\nKbdGroup.displayName = 'KbdGroup'\n"],"mappings":"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"}
|
package/dist/Label.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Label.js","names":[],"sources":["../src/components/Label/Label.tsx"],"sourcesContent":["import { cn } from '@utils/twUtils'\nimport { type ComponentPropsWithoutRef, type Ref } from 'react'\n\nexport interface LabelProps extends ComponentPropsWithoutRef<'label'> {\n ref?: Ref<HTMLLabelElement>\n}\n\nexport const Label = ({\n className,\n ref,\n ...props\n}: LabelProps & {\n ref?: Ref<HTMLLabelElement>\n}) => {\n return (\n // eslint-disable-next-line jsx-a11y/label-has-associated-control\n <label
|
|
1
|
+
{"version":3,"file":"Label.js","names":[],"sources":["../src/components/Label/Label.tsx"],"sourcesContent":["import { cn } from '@utils/twUtils'\nimport { type ComponentPropsWithoutRef, type Ref } from 'react'\n\nexport interface LabelProps extends ComponentPropsWithoutRef<'label'> {\n ref?: Ref<HTMLLabelElement>\n}\n\nexport const Label = ({\n className,\n ref,\n ...props\n}: LabelProps & {\n ref?: Ref<HTMLLabelElement>\n}) => {\n return (\n // eslint-disable-next-line jsx-a11y/label-has-associated-control\n <label\n className={cn('text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70', className)}\n data-slot='label'\n ref={ref}\n {...props}\n />\n )\n}\nLabel.displayName = 'Label'\n"],"mappings":";;;;;;AAOA,MAAa,SAAS,EACpB,WACA,KACA,GAAG,YAGC;AACJ,QAEE,oBAAC,SAAD;EACE,WAAW,GAAG,8FAA8F,UAAU;EACtH,aAAU;EACL;EACL,GAAI;EACJ;;AAGN,MAAM,cAAc"}
|
package/dist/Meter.js
CHANGED
|
@@ -10,7 +10,7 @@ const Meter = ({ className, dataTestId = "spectral-meter", label, max = 100, min
|
|
|
10
10
|
return /* @__PURE__ */ jsxs(Meter$1.Root, {
|
|
11
11
|
"aria-valuetext": `${value}%`,
|
|
12
12
|
...props,
|
|
13
|
-
className: cn("w-full flex
|
|
13
|
+
className: cn("gap-2 flex w-full flex-col", className),
|
|
14
14
|
"data-slot": "meter",
|
|
15
15
|
max,
|
|
16
16
|
min,
|
|
@@ -22,14 +22,14 @@ const Meter = ({ className, dataTestId = "spectral-meter", label, max = 100, min
|
|
|
22
22
|
"data-slot": "meter-label",
|
|
23
23
|
children: label
|
|
24
24
|
}), /* @__PURE__ */ jsx(Meter$1.Value, {
|
|
25
|
-
className: "text-text-
|
|
25
|
+
className: "text-xs text-text-primary tabular-nums",
|
|
26
26
|
"data-slot": "meter-value"
|
|
27
27
|
})]
|
|
28
28
|
}), /* @__PURE__ */ jsx(Meter$1.Track, {
|
|
29
|
-
className: "h-2 w-full
|
|
29
|
+
className: "h-2 flex w-full items-center overflow-hidden rounded-full bg-meter-track",
|
|
30
30
|
"data-slot": "meter-track",
|
|
31
31
|
children: /* @__PURE__ */ jsx(Meter$1.Indicator, {
|
|
32
|
-
className: "h-full origin-left transform-gpu transition-transform duration-150 ease-out
|
|
32
|
+
className: "h-full origin-left transform-gpu bg-meter-indicator transition-transform duration-150 ease-out",
|
|
33
33
|
"data-slot": "meter-indicator",
|
|
34
34
|
style: {
|
|
35
35
|
transform: `scaleX(${scale})`,
|
package/dist/Meter.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Meter.js","names":[],"sources":["../src/components/Meter/Meter.tsx"],"sourcesContent":["import { Meter as MeterPrimitive } from '@base-ui/react/meter'\nimport { cn } from '@utils/twUtils'\nimport { type ComponentProps, type ReactNode } from 'react'\n\nexport interface MeterProps extends ComponentProps<typeof MeterPrimitive.Root> {\n dataTestId?: string\n label: ReactNode\n value: number\n}\n\nexport const Meter = ({ className, dataTestId = 'spectral-meter', label, max = 100, min = 0, value, ...props }: MeterProps) => {\n const scale = max === min ? 0 : Math.min(1, Math.max(0, (value - min) / (max - min)))\n\n return (\n <MeterPrimitive.Root
|
|
1
|
+
{"version":3,"file":"Meter.js","names":[],"sources":["../src/components/Meter/Meter.tsx"],"sourcesContent":["import { Meter as MeterPrimitive } from '@base-ui/react/meter'\nimport { cn } from '@utils/twUtils'\nimport { type ComponentProps, type ReactNode } from 'react'\n\nexport interface MeterProps extends ComponentProps<typeof MeterPrimitive.Root> {\n dataTestId?: string\n label: ReactNode\n value: number\n}\n\nexport const Meter = ({ className, dataTestId = 'spectral-meter', label, max = 100, min = 0, value, ...props }: MeterProps) => {\n const scale = max === min ? 0 : Math.min(1, Math.max(0, (value - min) / (max - min)))\n\n return (\n <MeterPrimitive.Root\n aria-valuetext={`${value}%`}\n {...props}\n className={cn('gap-2 flex w-full flex-col', className)}\n data-slot='meter'\n data-testid={dataTestId}\n max={max}\n min={min}\n value={value}\n >\n <div className='gap-2 flex items-center justify-between'>\n <MeterPrimitive.Label\n className='text-sm font-medium'\n data-slot='meter-label'\n >\n {label}\n </MeterPrimitive.Label>\n <MeterPrimitive.Value\n className='text-xs text-text-primary tabular-nums'\n data-slot='meter-value'\n />\n </div>\n <MeterPrimitive.Track\n className='h-2 flex w-full items-center overflow-hidden rounded-full bg-meter-track'\n data-slot='meter-track'\n >\n <MeterPrimitive.Indicator\n className='h-full origin-left transform-gpu bg-meter-indicator transition-transform duration-150 ease-out'\n data-slot='meter-indicator'\n style={{ transform: `scaleX(${scale})`, width: '100%' }}\n />\n </MeterPrimitive.Track>\n </MeterPrimitive.Root>\n )\n}\n"],"mappings":";;;;;;;AAUA,MAAa,SAAS,EAAE,WAAW,aAAa,kBAAkB,OAAO,MAAM,KAAK,MAAM,GAAG,OAAO,GAAG,YAAwB;CAC7H,MAAM,QAAQ,QAAQ,MAAM,IAAI,KAAK,IAAI,GAAG,KAAK,IAAI,IAAI,QAAQ,QAAQ,MAAM,KAAK,CAAA;AAEpF,QACE,qBAAC,QAAe,MAAhB;EACE,kBAAgB,GAAG,MAAM;EACzB,GAAI;EACJ,WAAW,GAAG,8BAA8B,UAAU;EACtD,aAAU;EAEL;EACA;EACE;YART,CAUE,qBAAC,OAAD;GAAK,WAAU;aAAf,CACE,oBAAC,QAAe,OAAhB;IACE,WAAU;IACV,aAAU;cAET;IACmB,GACtB,oBAAC,QAAe,OAAhB;IACE,WAAU;IACV,aAAU;IACX,EACE;MACL,oBAAC,QAAe,OAAhB;GACE,WAAU;GACV,aAAU;aAEV,oBAAC,QAAe,WAAhB;IACE,WAAU;IACV,aAAU;IACV,OAAO;KAAE,WAAW,UAAU,MAAM;KAAI,OAAO;KAAQ;IACxD;GACmB,EACH"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MultiSelectBase.js","names":[],"sources":["../../src/components/MultiSelect/MultiSelectBase.tsx"],"sourcesContent":["import { CheckmarkIcon, ChevronDownIcon, CloseIcon, SearchIcon } from '@components/Icons'\nimport { Label } from '@components/Label/Label'\nimport { useUncontrolledState } from '@hooks/useUncontrolledState'\nimport * as Popover from '@radix-ui/react-popover'\nimport { useAutoDropdownHorizontalShift } from '@utils/dropdownPositioning'\nimport {\n EmptyState,\n ErrorMessage,\n getAriaProps,\n getDropdownSurfaceClasses,\n getDropdownWidthStyles,\n getErrorMessageId,\n getPasswordManagerIgnoreProps,\n getTriggerClasses,\n getWarningMessageId,\n LoadingState,\n useFormFieldId,\n WarningMessage,\n type BaseFormFieldProps,\n type DropdownWidth,\n type FormFieldState\n} from '@utils/formFieldUtils'\nimport { cn } from '@utils/twUtils'\nimport { useCallback, useEffect, useId, useMemo, useRef, useState, type ButtonHTMLAttributes, type ChangeEvent, type CSSProperties, type KeyboardEvent, type Ref } from 'react'\n\nexport type MultiSelectState = Exclude<FormFieldState, 'disabled'>\n\nexport interface MultiSelectOption {\n disabled?: boolean\n group?: string\n label: string\n value: string\n}\n\nexport interface MultiSelectBaseProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'value' | 'onChange'> {\n clearAllLabel?: string\n closeOnSelect?: boolean\n dropdownWidth?: DropdownWidth\n emptyMessage?: string\n errorMessage?: BaseFormFieldProps['errorMessage']\n id?: string\n label?: string\n loadingMessage?: string\n maxCount?: number\n messageReserveLines?: number\n messageReserveSpace?: boolean\n name?: string\n defaultValue?: string[]\n onChange?: (value: string[]) => void\n options: MultiSelectOption[]\n placeholder?: string\n required?: boolean\n searchPlaceholder?: string\n showClearAll?: boolean\n showSearch?: boolean\n showSelectAll?: boolean\n selectAllLabel?: string\n sortAlphabetically?: boolean\n state?: MultiSelectState\n value?: string[]\n warningMessage?: BaseFormFieldProps['errorMessage']\n 'aria-label'?: string\n 'aria-describedby'?: string\n}\n\nconst ICON_SIZE = 'h-4 w-4'\n\nconst getDropdownClasses = (): string => {\n return cn(\n 'max-h-80 z-50 overflow-hidden',\n getDropdownSurfaceClasses(),\n 'motion-safe:data-[state=closed]:animate-out motion-safe:data-[state=open]:animate-in',\n 'motion-safe:data-[state=closed]:fade-out-0 motion-safe:data-[state=open]:fade-in-0',\n 'motion-safe:data-[state=closed]:zoom-out-95 motion-safe:data-[state=open]:zoom-in-95',\n 'motion-safe:data-[side=bottom]:slide-in-from-top-2 motion-safe:data-[side=top]:slide-in-from-bottom-2',\n 'origin-(--radix-popover-content-transform-origin)',\n )\n}\n\ntype FocusableItem = { type: 'search' } | { type: 'select-all' } | { type: 'option'; index: number; value: string } | { type: 'clear-all' }\n\nconst useKeyboardNavigation = (\n options: MultiSelectOption[],\n onClearAll: () => void,\n onClose: () => void,\n onSelect: (value: string) => void,\n onSelectAll: () => void,\n searchInputRef: React.RefObject<HTMLInputElement | null>,\n showSearch: boolean,\n showSelectAll: boolean,\n showClearAll: boolean,\n) => {\n const [focusedIndex, setFocusedIndex] = useState(-1)\n\n // Build a flat list of all focusable items\n const focusableItems = useMemo((): FocusableItem[] => {\n const items: FocusableItem[] = []\n\n if (showSearch) {\n items.push({ type: 'search' })\n }\n\n if (showSelectAll) {\n items.push({ type: 'select-all' })\n }\n\n options.forEach((option, index) => {\n if (!option.disabled) {\n items.push({ type: 'option', index, value: option.value })\n }\n })\n\n if (showClearAll) {\n items.push({ type: 'clear-all' })\n }\n\n return items\n }, [options, showSearch, showSelectAll, showClearAll])\n\n // Focus the appropriate element when focusedIndex changes\n const focusCurrentItem = useCallback(\n (index: number) => {\n if (index < 0 || index >= focusableItems.length) return\n const item = focusableItems[index]\n if (item.type === 'search') {\n searchInputRef.current?.focus()\n }\n },\n [focusableItems, searchInputRef],\n )\n\n const handleKeyDown = useCallback(\n (event: KeyboardEvent<HTMLDivElement>) => {\n const currentItem = focusedIndex >= 0 && focusedIndex < focusableItems.length ? focusableItems[focusedIndex] : null\n\n // Don't prevent default for space in search input (allow typing spaces)\n if (event.key === ' ' && currentItem?.type === 'search') {\n return\n }\n\n // Don't prevent default for Enter in search input (allow form submission behavior)\n if (event.key === 'Enter' && currentItem?.type === 'search') {\n return\n }\n\n const keyHandlers: Record<string, () => void> = {\n ArrowDown: () => {\n event.preventDefault()\n const newIndex = Math.min(focusedIndex + 1, focusableItems.length - 1)\n setFocusedIndex(newIndex)\n focusCurrentItem(newIndex)\n },\n ArrowUp: () => {\n event.preventDefault()\n const newIndex = Math.max(focusedIndex - 1, 0)\n setFocusedIndex(newIndex)\n focusCurrentItem(newIndex)\n },\n Tab: () => {\n // Allow Tab to cycle through focusable items\n if (event.shiftKey) {\n if (focusedIndex <= 0) {\n // At start, close dropdown and return to trigger\n onClose()\n } else {\n event.preventDefault()\n const newIndex = focusedIndex - 1\n setFocusedIndex(newIndex)\n focusCurrentItem(newIndex)\n }\n } else {\n if (focusedIndex >= focusableItems.length - 1) {\n // At end, close dropdown and move to next element\n onClose()\n } else {\n event.preventDefault()\n const newIndex = focusedIndex + 1\n setFocusedIndex(newIndex)\n focusCurrentItem(newIndex)\n }\n }\n },\n Enter: () => {\n event.preventDefault()\n if (focusedIndex >= 0 && focusedIndex < focusableItems.length) {\n const item = focusableItems[focusedIndex]\n if (item.type === 'select-all') {\n onSelectAll()\n } else if (item.type === 'clear-all') {\n onClearAll()\n } else if (item.type === 'option') {\n onSelect(item.value)\n }\n }\n },\n ' ': () => {\n event.preventDefault()\n if (focusedIndex >= 0 && focusedIndex < focusableItems.length) {\n const item = focusableItems[focusedIndex]\n if (item.type === 'select-all') {\n onSelectAll()\n } else if (item.type === 'clear-all') {\n onClearAll()\n } else if (item.type === 'option') {\n onSelect(item.value)\n }\n }\n },\n Escape: () => {\n event.preventDefault()\n onClose()\n },\n }\n\n const handler = keyHandlers[event.key]\n if (handler) {\n handler()\n }\n },\n [focusableItems, focusedIndex, onSelect, onSelectAll, onClearAll, onClose, focusCurrentItem],\n )\n\n // Get the option index for visual focus styling (accounting for select-all offset)\n const getOptionFocusIndex = useCallback(\n (optionIndex: number): boolean => {\n if (focusedIndex < 0 || focusedIndex >= focusableItems.length) return false\n const item = focusableItems[focusedIndex]\n return item.type === 'option' && item.index === optionIndex\n },\n [focusedIndex, focusableItems],\n )\n\n const isSearchFocused = useMemo(() => {\n if (focusedIndex < 0 || focusedIndex >= focusableItems.length) return false\n return focusableItems[focusedIndex].type === 'search'\n }, [focusedIndex, focusableItems])\n\n const isSelectAllFocused = useMemo(() => {\n if (focusedIndex < 0 || focusedIndex >= focusableItems.length) return false\n return focusableItems[focusedIndex].type === 'select-all'\n }, [focusedIndex, focusableItems])\n\n const isClearAllFocused = useMemo(() => {\n if (focusedIndex < 0 || focusedIndex >= focusableItems.length) return false\n return focusableItems[focusedIndex].type === 'clear-all'\n }, [focusedIndex, focusableItems])\n\n const focusedOptionValue = useMemo(() => {\n if (focusedIndex < 0 || focusedIndex >= focusableItems.length) return null\n const item = focusableItems[focusedIndex]\n return item.type === 'option' ? item.value : null\n }, [focusedIndex, focusableItems])\n\n return {\n focusedIndex,\n setFocusedIndex,\n handleKeyDown,\n getOptionFocusIndex,\n isSearchFocused,\n isSelectAllFocused,\n isClearAllFocused,\n focusedOptionValue,\n }\n}\n\nexport const MultiSelectBase = ({\n className,\n clearAllLabel = 'Clear all',\n closeOnSelect = false,\n dropdownWidth = 'trigger',\n emptyMessage = 'No options found',\n errorMessage,\n defaultValue = [],\n disabled,\n id,\n label,\n loadingMessage = 'Loading options…',\n messageReserveLines = 1,\n messageReserveSpace = false,\n maxCount = 3,\n name,\n onChange,\n options,\n placeholder = 'Select options',\n ref,\n searchPlaceholder = 'Search options…',\n selectAllLabel = 'Select all',\n showClearAll = true,\n showSearch = true,\n showSelectAll = true,\n sortAlphabetically = false,\n state = 'default',\n value: valueProp,\n warningMessage,\n 'aria-label': ariaLabel,\n 'aria-describedby': ariaDescribedBy,\n ...props\n}: MultiSelectBaseProps & {\n ref?: Ref<HTMLButtonElement>\n}) => {\n const generatedId = useId()\n const fallbackName = name ?? `multiselect-${generatedId}`\n const multiSelectId = useFormFieldId(id, fallbackName)\n const listboxId = `${multiSelectId}-listbox`\n const errorMessageId = getErrorMessageId(multiSelectId)\n const warningMessageId = getWarningMessageId(multiSelectId)\n const messageId = state === 'error' ? errorMessageId : state === 'warning' && warningMessage ? warningMessageId : undefined\n\n const [isOpen, setIsOpen] = useState(false)\n const { dropdownShiftStyle, setDropdownElement } = useAutoDropdownHorizontalShift(isOpen)\n const [searchValue, setSearchValue] = useState('')\n const [value, setValue] = useUncontrolledState<string[]>({\n value: valueProp,\n defaultValue,\n onChange,\n })\n\n const searchInputRef = useRef<HTMLInputElement>(null)\n\n const isDisabled = Boolean(disabled)\n const isLoading = state === 'loading'\n const ariaProps = getAriaProps(state, ariaDescribedBy, props.required, messageId)\n const { dropdownOverflowStyle, dropdownWidthMode, resolvedDropdownWidth } = getDropdownWidthStyles({\n dropdownWidth,\n triggerWidth: 'var(--radix-popover-trigger-width)',\n })\n\n const filteredOptions = useMemo(() => {\n let filtered = options.filter((option) => option.label.toLowerCase().includes(searchValue.toLowerCase()))\n\n if (sortAlphabetically) {\n filtered = [...filtered].sort((a, b) => a.label.localeCompare(b.label))\n }\n\n return filtered\n }, [options, searchValue, sortAlphabetically])\n\n const groupedOptions = useMemo(() => {\n const groups: Record<string, MultiSelectOption[]> = {}\n const ungrouped: MultiSelectOption[] = []\n\n filteredOptions.forEach((option) => {\n if (option.group) {\n if (!groups[option.group]) {\n groups[option.group] = []\n }\n groups[option.group].push(option)\n } else {\n ungrouped.push(option)\n }\n })\n\n return { groups, ungrouped, hasGroups: Object.keys(groups).length > 0 }\n }, [filteredOptions])\n\n const toggleOption = useCallback(\n (optionValue: string) => {\n const option = options.find((o) => o.value === optionValue)\n if (option?.disabled) return\n\n const newValue = value.includes(optionValue) ? value.filter((v) => v !== optionValue) : [...value, optionValue]\n\n setValue(newValue)\n\n if (closeOnSelect) {\n setIsOpen(false)\n }\n },\n [closeOnSelect, options, setValue, value],\n )\n\n const handleSelectAll = useCallback(() => {\n const allValues = options.filter((o) => !o.disabled).map((o) => o.value)\n const isAllSelected = allValues.every((v) => value.includes(v))\n\n if (isAllSelected) {\n setValue([])\n } else {\n setValue(allValues)\n }\n }, [options, setValue, value])\n\n const handleClearAll = useCallback(() => {\n setValue([])\n }, [setValue])\n\n // Check if all non-disabled options are selected\n const allSelectableValues = useMemo(() => options.filter((o) => !o.disabled).map((o) => o.value), [options])\n const isAllSelected = allSelectableValues.length > 0 && allSelectableValues.every((v) => value.includes(v))\n\n const { focusedOptionValue, getOptionFocusIndex, handleKeyDown, isSelectAllFocused, setFocusedIndex } = useKeyboardNavigation(\n filteredOptions,\n handleClearAll,\n () => setIsOpen(false),\n toggleOption,\n handleSelectAll,\n searchInputRef,\n showSearch,\n showSelectAll,\n false, // No separate clear-all button in dropdown\n )\n\n // Set initial focus index when dropdown opens/closes\n useEffect(() => {\n if (isOpen) {\n setFocusedIndex(0)\n } else {\n setFocusedIndex(-1)\n }\n }, [isOpen, setFocusedIndex])\n\n const handleSearchChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {\n setSearchValue(e.target.value)\n }, [])\n\n const renderSelectedItems = () => {\n if (value.length === 0) {\n return <span className='min-h-8 flex items-center text-input-text-placeholder'>{placeholder}</span>\n }\n\n const displayedValues = value.slice(0, maxCount)\n const remainingCount = value.length - maxCount\n\n return (\n <div className='gap-1 flex flex-wrap items-center overflow-hidden'>\n {displayedValues.map((val) => {\n const option = options.find((o) => o.value === val)\n if (!option) return null\n\n return (\n <span className='gap-1 px-2 py-1 rounded-md text-xs max-w-48 inline-flex items-center bg-input-bg--selected text-input-text' key={val}>\n <span className='truncate'>{option.label}</span>\n <button\n aria-label={`Remove ${option.label}`}\n className='hover:text-danger rounded-sm cursor-pointer'\n data-testid='spectral-multiselect-remove-item-button'\n onClick={(e) => {\n e.preventDefault()\n e.stopPropagation()\n toggleOption(val)\n }}\n onPointerDown={(e) => {\n e.stopPropagation()\n }}\n type='button'\n >\n <CloseIcon size={12} />\n </button>\n </span>\n )\n })}\n {remainingCount > 0 && <span className='text-input-text-secondary text-xs py-1 flex items-center tabular-nums'>+{remainingCount} more</span>}\n </div>\n )\n }\n\n const renderOption = (option: MultiSelectOption, index: number) => {\n const isSelected = value.includes(option.value)\n const isFocused = getOptionFocusIndex(index)\n const optionId = `${listboxId}-option-${option.value}`\n\n return (\n <button\n aria-selected={isSelected}\n className={cn(\n 'my-0.5 first:mt-0 last:mb-0 gap-3 rounded-sm px-3 py-2 text-sm flex w-full items-center text-left hover:bg-input-bg--hover focus-visible:bg-input-bg--hover focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50',\n isFocused && 'bg-input-bg--hover',\n isSelected && 'font-medium text-input-text',\n )}\n disabled={option.disabled}\n id={optionId}\n key={option.value}\n onClick={() => toggleOption(option.value)}\n role='option'\n type='button'\n >\n <div data-testid='spectral-multiselect-selected-indicator' className={cn('w-4 h-4 rounded flex items-center justify-center border border-input-border', isSelected && 'bg-primary border-primary')}>\n {isSelected && <CheckmarkIcon size={12} />}\n </div>\n <span>{option.label}</span>\n </button>\n )\n }\n\n const getCSSCustomProperties = () => ({\n '--multiselect-border-radius': '0.5rem',\n '--multiselect-trigger-height': '3rem',\n '--multiselect-dropdown-max-height': '20rem',\n })\n\n return (\n <div className='w-full' data-testid='spectral-multiselect-root'>\n {label && (\n <Label className={cn('mb-2 block text-text-primary', isDisabled && 'text-text-secondary')} data-testid='spectral-multiselect-label' htmlFor={multiSelectId}>\n {label}\n </Label>\n )}\n <Popover.Root open={isOpen} onOpenChange={setIsOpen}>\n <div className='relative' data-testid='spectral-multiselect-wrapper' onKeyDown={isOpen ? handleKeyDown : undefined} role='none'>\n <Popover.Trigger asChild>\n <button\n aria-activedescendant={isOpen && focusedOptionValue ? `${listboxId}-option-${focusedOptionValue}` : undefined}\n aria-controls={isOpen ? listboxId : undefined}\n aria-expanded={isOpen}\n aria-label={ariaLabel ?? label}\n className={cn(getTriggerClasses(isOpen, state, className), 'max-h-22 py-2 text-sm')}\n data-state={state}\n data-testid='spectral-multiselect-trigger'\n disabled={isDisabled}\n id={multiSelectId}\n name={name}\n ref={ref}\n role='combobox' // oxlint-disable-line jsx-a11y/prefer-tag-over-role -- trigger uses button-based combobox semantics for listbox popup\n style={getCSSCustomProperties() as CSSProperties}\n type='button'\n {...ariaProps}\n {...props}\n >\n <div className='min-w-0 flex-1 overflow-hidden' data-testid='spectral-multiselect-selected-items'>\n {renderSelectedItems()}\n </div>\n <div className='gap-2 ml-2 flex shrink-0 items-center'>\n <ChevronDownIcon className={cn('text-input-icon transition-transform duration-200', isOpen && 'rotate-180')} size={20} />\n </div>\n </button>\n </Popover.Trigger>\n {showClearAll && value.length > 0 && (\n <button\n aria-label='Clear all selections'\n className='right-10 text-input-icon hover:text-input-icon--hover rounded-sm absolute top-1/2 z-10 -translate-y-1/2 cursor-pointer focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-1 disabled:pointer-events-none disabled:opacity-50'\n data-testid='spectral-multiselect-clear-all-button'\n disabled={isDisabled}\n onClick={(e) => {\n e.stopPropagation()\n handleClearAll()\n document.getElementById(multiSelectId)?.focus()\n }}\n type='button'\n >\n <CloseIcon size={12} />\n </button>\n )}\n\n <Popover.Portal>\n <Popover.Content\n align='start'\n avoidCollisions\n className={getDropdownClasses()}\n collisionPadding={10}\n data-dropdown-width-mode={dropdownWidthMode}\n data-dropdown-width-value={dropdownWidthMode === 'custom' ? dropdownWidth : undefined}\n data-testid='spectral-multiselect-dropdown'\n onOpenAutoFocus={(e) => {\n e.preventDefault()\n if (showSearch) {\n searchInputRef.current?.focus()\n }\n }}\n side='bottom'\n sideOffset={4}\n ref={setDropdownElement}\n style={{\n width: resolvedDropdownWidth,\n ...(dropdownWidth === 'trigger' ? {} : dropdownOverflowStyle),\n ...dropdownShiftStyle,\n }}\n >\n <div className='p-1'>\n {showSearch && (\n <div className='mb-2 relative'>\n <SearchIcon className={cn(ICON_SIZE, 'left-3 text-input-icon absolute top-1/2 -translate-y-1/2')} />\n <input\n aria-label='Search options'\n className='pl-9 pr-3 py-2 text-sm rounded-md focus-visible:ring-black w-full border border-input-border bg-input-bg focus-visible:border-input-border--focus focus-visible:ring-1 focus-visible:outline-none'\n data-testid='spectral-multiselect-search-input'\n onChange={handleSearchChange}\n placeholder={searchPlaceholder}\n ref={searchInputRef}\n type='text'\n value={searchValue}\n {...getPasswordManagerIgnoreProps('text')}\n />\n </div>\n )}\n <div aria-multiselectable='true' className='max-h-64 overflow-y-auto' id={listboxId} role='listbox' // oxlint-disable-line jsx-a11y/prefer-tag-over-role -- custom multi-select uses ARIA listbox semantics with option children\n >\n {isLoading ? (\n <LoadingState className='text-sm' message={loadingMessage} data-testid='spectral-multiselect-loading' />\n ) : filteredOptions.length === 0 ? (\n <EmptyState className='text-sm' data-testid='spectral-multiselect-empty-message' message={emptyMessage} />\n ) : (\n <>\n {showSelectAll && (\n <div className='mb-1'>\n <button\n className={cn(\n 'my-0.5 first:mt-0 last:mb-0 gap-3 rounded-sm px-3 py-2 text-sm font-medium text-input-text-secondary flex w-full items-center hover:bg-input-bg--hover focus-visible:bg-input-bg--hover focus-visible:outline-none',\n isSelectAllFocused && 'bg-input-bg--hover',\n )}\n data-testid='spectral-multiselect-select-all-button'\n onClick={handleSelectAll}\n type='button'\n >\n {isAllSelected ? clearAllLabel : selectAllLabel}\n </button>\n <div className='mx-3 my-1 h-px bg-input-border' />\n </div>\n )}\n\n {groupedOptions.ungrouped.length > 0 && <div className='mb-1'>{groupedOptions.ungrouped.map((option, index) => renderOption(option, index))}</div>}\n\n {Object.entries(groupedOptions.groups).map(([groupName, groupOptions]) => (\n <div key={groupName} className='mb-1' data-testid='spectral-multiselect-group'>\n {(groupedOptions.ungrouped.length > 0 || Object.keys(groupedOptions.groups).indexOf(groupName) > 0) && <div className='mx-3 my-1 h-px bg-input-border' />}\n <div data-testid='spectral-multiselect-group-name' className='px-3 py-1 text-xs font-semibold text-input-text-secondary tracking-wide uppercase'>\n {groupName}\n </div>\n {groupOptions.map((option, _index) => renderOption(option, filteredOptions.indexOf(option)))}\n </div>\n ))}\n </>\n )}\n </div>\n </div>\n </Popover.Content>\n </Popover.Portal>\n </div>\n </Popover.Root>\n\n <ErrorMessage\n dataTestId='spectral-multiselect-error-message'\n id={errorMessageId}\n message={state === 'error' ? errorMessage : null}\n messageReserveLines={messageReserveLines}\n messageReserveSpace={messageReserveSpace && state === 'error'}\n />\n <WarningMessage\n dataTestId='spectral-multiselect-warning-message'\n id={warningMessageId}\n message={state === 'warning' ? warningMessage : null}\n messageReserveLines={messageReserveLines}\n messageReserveSpace={messageReserveSpace && state === 'warning'}\n />\n </div>\n )\n}\nMultiSelectBase.displayName = 'MultiSelectBase'\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"MultiSelectBase.js","names":[],"sources":["../../src/components/MultiSelect/MultiSelectBase.tsx"],"sourcesContent":["import { CheckmarkIcon, ChevronDownIcon, CloseIcon, SearchIcon } from '@components/Icons'\nimport { Label } from '@components/Label/Label'\nimport { useUncontrolledState } from '@hooks/useUncontrolledState'\nimport * as Popover from '@radix-ui/react-popover'\nimport { useAutoDropdownHorizontalShift } from '@utils/dropdownPositioning'\nimport {\n EmptyState,\n ErrorMessage,\n getAriaProps,\n getDropdownSurfaceClasses,\n getDropdownWidthStyles,\n getErrorMessageId,\n getPasswordManagerIgnoreProps,\n getTriggerClasses,\n getWarningMessageId,\n LoadingState,\n useFormFieldId,\n WarningMessage,\n type BaseFormFieldProps,\n type DropdownWidth,\n type FormFieldState,\n} from '@utils/formFieldUtils'\nimport { cn } from '@utils/twUtils'\nimport { useCallback, useEffect, useId, useMemo, useRef, useState, type ButtonHTMLAttributes, type ChangeEvent, type CSSProperties, type KeyboardEvent, type Ref } from 'react'\n\nexport type MultiSelectState = Exclude<FormFieldState, 'disabled'>\n\nexport interface MultiSelectOption {\n disabled?: boolean\n group?: string\n label: string\n value: string\n}\n\nexport interface MultiSelectBaseProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'value' | 'onChange'> {\n clearAllLabel?: string\n closeOnSelect?: boolean\n dropdownWidth?: DropdownWidth\n emptyMessage?: string\n errorMessage?: BaseFormFieldProps['errorMessage']\n id?: string\n label?: string\n loadingMessage?: string\n maxCount?: number\n messageReserveLines?: number\n messageReserveSpace?: boolean\n name?: string\n defaultValue?: string[]\n onChange?: (value: string[]) => void\n options: MultiSelectOption[]\n placeholder?: string\n required?: boolean\n searchPlaceholder?: string\n showClearAll?: boolean\n showSearch?: boolean\n showSelectAll?: boolean\n selectAllLabel?: string\n sortAlphabetically?: boolean\n state?: MultiSelectState\n value?: string[]\n warningMessage?: BaseFormFieldProps['errorMessage']\n 'aria-label'?: string\n 'aria-describedby'?: string\n}\n\nconst ICON_SIZE = 'h-4 w-4'\n\nconst getDropdownClasses = (): string => {\n return cn(\n 'max-h-80 z-50 overflow-hidden',\n getDropdownSurfaceClasses(),\n 'motion-safe:data-[state=closed]:animate-out motion-safe:data-[state=open]:animate-in',\n 'motion-safe:data-[state=closed]:fade-out-0 motion-safe:data-[state=open]:fade-in-0',\n 'motion-safe:data-[state=closed]:zoom-out-95 motion-safe:data-[state=open]:zoom-in-95',\n 'motion-safe:data-[side=bottom]:slide-in-from-top-2 motion-safe:data-[side=top]:slide-in-from-bottom-2',\n 'origin-(--radix-popover-content-transform-origin)',\n )\n}\n\ntype FocusableItem = { type: 'search' } | { type: 'select-all' } | { type: 'option'; index: number; value: string } | { type: 'clear-all' }\n\nconst useKeyboardNavigation = (\n options: MultiSelectOption[],\n onClearAll: () => void,\n onClose: () => void,\n onSelect: (value: string) => void,\n onSelectAll: () => void,\n searchInputRef: React.RefObject<HTMLInputElement | null>,\n showSearch: boolean,\n showSelectAll: boolean,\n showClearAll: boolean,\n) => {\n const [focusedIndex, setFocusedIndex] = useState(-1)\n\n // Build a flat list of all focusable items\n const focusableItems = useMemo((): FocusableItem[] => {\n const items: FocusableItem[] = []\n\n if (showSearch) {\n items.push({ type: 'search' })\n }\n\n if (showSelectAll) {\n items.push({ type: 'select-all' })\n }\n\n options.forEach((option, index) => {\n if (!option.disabled) {\n items.push({ type: 'option', index, value: option.value })\n }\n })\n\n if (showClearAll) {\n items.push({ type: 'clear-all' })\n }\n\n return items\n }, [options, showSearch, showSelectAll, showClearAll])\n\n // Focus the appropriate element when focusedIndex changes\n const focusCurrentItem = useCallback(\n (index: number) => {\n if (index < 0 || index >= focusableItems.length) return\n const item = focusableItems[index]\n if (item.type === 'search') {\n searchInputRef.current?.focus()\n }\n },\n [focusableItems, searchInputRef],\n )\n\n const handleKeyDown = useCallback(\n (event: KeyboardEvent<HTMLDivElement>) => {\n const currentItem = focusedIndex >= 0 && focusedIndex < focusableItems.length ? focusableItems[focusedIndex] : null\n\n // Don't prevent default for space in search input (allow typing spaces)\n if (event.key === ' ' && currentItem?.type === 'search') {\n return\n }\n\n // Don't prevent default for Enter in search input (allow form submission behavior)\n if (event.key === 'Enter' && currentItem?.type === 'search') {\n return\n }\n\n const keyHandlers: Record<string, () => void> = {\n ArrowDown: () => {\n event.preventDefault()\n const newIndex = Math.min(focusedIndex + 1, focusableItems.length - 1)\n setFocusedIndex(newIndex)\n focusCurrentItem(newIndex)\n },\n ArrowUp: () => {\n event.preventDefault()\n const newIndex = Math.max(focusedIndex - 1, 0)\n setFocusedIndex(newIndex)\n focusCurrentItem(newIndex)\n },\n Tab: () => {\n // Allow Tab to cycle through focusable items\n if (event.shiftKey) {\n if (focusedIndex <= 0) {\n // At start, close dropdown and return to trigger\n onClose()\n } else {\n event.preventDefault()\n const newIndex = focusedIndex - 1\n setFocusedIndex(newIndex)\n focusCurrentItem(newIndex)\n }\n } else {\n if (focusedIndex >= focusableItems.length - 1) {\n // At end, close dropdown and move to next element\n onClose()\n } else {\n event.preventDefault()\n const newIndex = focusedIndex + 1\n setFocusedIndex(newIndex)\n focusCurrentItem(newIndex)\n }\n }\n },\n Enter: () => {\n event.preventDefault()\n if (focusedIndex >= 0 && focusedIndex < focusableItems.length) {\n const item = focusableItems[focusedIndex]\n if (item.type === 'select-all') {\n onSelectAll()\n } else if (item.type === 'clear-all') {\n onClearAll()\n } else if (item.type === 'option') {\n onSelect(item.value)\n }\n }\n },\n ' ': () => {\n event.preventDefault()\n if (focusedIndex >= 0 && focusedIndex < focusableItems.length) {\n const item = focusableItems[focusedIndex]\n if (item.type === 'select-all') {\n onSelectAll()\n } else if (item.type === 'clear-all') {\n onClearAll()\n } else if (item.type === 'option') {\n onSelect(item.value)\n }\n }\n },\n Escape: () => {\n event.preventDefault()\n onClose()\n },\n }\n\n const handler = keyHandlers[event.key]\n if (handler) {\n handler()\n }\n },\n [focusableItems, focusedIndex, onSelect, onSelectAll, onClearAll, onClose, focusCurrentItem],\n )\n\n // Get the option index for visual focus styling (accounting for select-all offset)\n const getOptionFocusIndex = useCallback(\n (optionIndex: number): boolean => {\n if (focusedIndex < 0 || focusedIndex >= focusableItems.length) return false\n const item = focusableItems[focusedIndex]\n return item.type === 'option' && item.index === optionIndex\n },\n [focusedIndex, focusableItems],\n )\n\n const isSearchFocused = useMemo(() => {\n if (focusedIndex < 0 || focusedIndex >= focusableItems.length) return false\n return focusableItems[focusedIndex].type === 'search'\n }, [focusedIndex, focusableItems])\n\n const isSelectAllFocused = useMemo(() => {\n if (focusedIndex < 0 || focusedIndex >= focusableItems.length) return false\n return focusableItems[focusedIndex].type === 'select-all'\n }, [focusedIndex, focusableItems])\n\n const isClearAllFocused = useMemo(() => {\n if (focusedIndex < 0 || focusedIndex >= focusableItems.length) return false\n return focusableItems[focusedIndex].type === 'clear-all'\n }, [focusedIndex, focusableItems])\n\n const focusedOptionValue = useMemo(() => {\n if (focusedIndex < 0 || focusedIndex >= focusableItems.length) return null\n const item = focusableItems[focusedIndex]\n return item.type === 'option' ? item.value : null\n }, [focusedIndex, focusableItems])\n\n return {\n focusedIndex,\n setFocusedIndex,\n handleKeyDown,\n getOptionFocusIndex,\n isSearchFocused,\n isSelectAllFocused,\n isClearAllFocused,\n focusedOptionValue,\n }\n}\n\nexport const MultiSelectBase = ({\n className,\n clearAllLabel = 'Clear all',\n closeOnSelect = false,\n dropdownWidth = 'trigger',\n emptyMessage = 'No options found',\n errorMessage,\n defaultValue = [],\n disabled,\n id,\n label,\n loadingMessage = 'Loading options…',\n messageReserveLines = 1,\n messageReserveSpace = false,\n maxCount = 3,\n name,\n onChange,\n options,\n placeholder = 'Select options',\n ref,\n searchPlaceholder = 'Search options…',\n selectAllLabel = 'Select all',\n showClearAll = true,\n showSearch = true,\n showSelectAll = true,\n sortAlphabetically = false,\n state = 'default',\n value: valueProp,\n warningMessage,\n 'aria-label': ariaLabel,\n 'aria-describedby': ariaDescribedBy,\n ...props\n}: MultiSelectBaseProps & {\n ref?: Ref<HTMLButtonElement>\n}) => {\n const generatedId = useId()\n const fallbackName = name ?? `multiselect-${generatedId}`\n const multiSelectId = useFormFieldId(id, fallbackName)\n const listboxId = `${multiSelectId}-listbox`\n const errorMessageId = getErrorMessageId(multiSelectId)\n const warningMessageId = getWarningMessageId(multiSelectId)\n const messageId = state === 'error' ? errorMessageId : state === 'warning' && warningMessage ? warningMessageId : undefined\n\n const [isOpen, setIsOpen] = useState(false)\n const { dropdownShiftStyle, setDropdownElement } = useAutoDropdownHorizontalShift(isOpen)\n const [searchValue, setSearchValue] = useState('')\n const [value, setValue] = useUncontrolledState<string[]>({\n value: valueProp,\n defaultValue,\n onChange,\n })\n\n const searchInputRef = useRef<HTMLInputElement>(null)\n\n const isDisabled = Boolean(disabled)\n const isLoading = state === 'loading'\n const ariaProps = getAriaProps(state, ariaDescribedBy, props.required, messageId)\n const { dropdownOverflowStyle, dropdownWidthMode, resolvedDropdownWidth } = getDropdownWidthStyles({\n dropdownWidth,\n triggerWidth: 'var(--radix-popover-trigger-width)',\n })\n\n const filteredOptions = useMemo(() => {\n let filtered = options.filter((option) => option.label.toLowerCase().includes(searchValue.toLowerCase()))\n\n if (sortAlphabetically) {\n filtered = [...filtered].sort((a, b) => a.label.localeCompare(b.label))\n }\n\n return filtered\n }, [options, searchValue, sortAlphabetically])\n\n const groupedOptions = useMemo(() => {\n const groups: Record<string, MultiSelectOption[]> = {}\n const ungrouped: MultiSelectOption[] = []\n\n filteredOptions.forEach((option) => {\n if (option.group) {\n if (!groups[option.group]) {\n groups[option.group] = []\n }\n groups[option.group].push(option)\n } else {\n ungrouped.push(option)\n }\n })\n\n return { groups, ungrouped, hasGroups: Object.keys(groups).length > 0 }\n }, [filteredOptions])\n\n const toggleOption = useCallback(\n (optionValue: string) => {\n const option = options.find((o) => o.value === optionValue)\n if (option?.disabled) return\n\n const newValue = value.includes(optionValue) ? value.filter((v) => v !== optionValue) : [...value, optionValue]\n\n setValue(newValue)\n\n if (closeOnSelect) {\n setIsOpen(false)\n }\n },\n [closeOnSelect, options, setValue, value],\n )\n\n const handleSelectAll = useCallback(() => {\n const allValues = options.filter((o) => !o.disabled).map((o) => o.value)\n const isAllSelected = allValues.every((v) => value.includes(v))\n\n if (isAllSelected) {\n setValue([])\n } else {\n setValue(allValues)\n }\n }, [options, setValue, value])\n\n const handleClearAll = useCallback(() => {\n setValue([])\n }, [setValue])\n\n // Check if all non-disabled options are selected\n const allSelectableValues = useMemo(() => options.filter((o) => !o.disabled).map((o) => o.value), [options])\n const isAllSelected = allSelectableValues.length > 0 && allSelectableValues.every((v) => value.includes(v))\n\n const { focusedOptionValue, getOptionFocusIndex, handleKeyDown, isSelectAllFocused, setFocusedIndex } = useKeyboardNavigation(\n filteredOptions,\n handleClearAll,\n () => setIsOpen(false),\n toggleOption,\n handleSelectAll,\n searchInputRef,\n showSearch,\n showSelectAll,\n false, // No separate clear-all button in dropdown\n )\n\n // Set initial focus index when dropdown opens/closes\n useEffect(() => {\n if (isOpen) {\n setFocusedIndex(0)\n } else {\n setFocusedIndex(-1)\n }\n }, [isOpen, setFocusedIndex])\n\n const handleSearchChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {\n setSearchValue(e.target.value)\n }, [])\n\n const renderSelectedItems = () => {\n if (value.length === 0) {\n return <span className='min-h-8 flex items-center text-input-text-placeholder'>{placeholder}</span>\n }\n\n const displayedValues = value.slice(0, maxCount)\n const remainingCount = value.length - maxCount\n\n return (\n <div className='gap-1 flex flex-wrap items-center overflow-hidden'>\n {displayedValues.map((val) => {\n const option = options.find((o) => o.value === val)\n if (!option) return null\n\n return (\n <span\n className='gap-1 px-2 py-1 rounded-md text-xs max-w-48 inline-flex items-center bg-input-bg--selected text-input-text'\n key={val}\n >\n <span className='truncate'>{option.label}</span>\n <button\n aria-label={`Remove ${option.label}`}\n className='hover:text-danger rounded-sm cursor-pointer'\n data-testid='spectral-multiselect-remove-item-button'\n onClick={(e) => {\n e.preventDefault()\n e.stopPropagation()\n toggleOption(val)\n }}\n onPointerDown={(e) => {\n e.stopPropagation()\n }}\n type='button'\n >\n <CloseIcon size={12} />\n </button>\n </span>\n )\n })}\n {remainingCount > 0 && <span className='text-input-text-secondary text-xs py-1 flex items-center tabular-nums'>+{remainingCount} more</span>}\n </div>\n )\n }\n\n const renderOption = (option: MultiSelectOption, index: number) => {\n const isSelected = value.includes(option.value)\n const isFocused = getOptionFocusIndex(index)\n const optionId = `${listboxId}-option-${option.value}`\n\n return (\n <button\n aria-selected={isSelected}\n className={cn(\n 'my-0.5 first:mt-0 last:mb-0 gap-3 rounded-sm px-3 py-2 text-sm flex w-full items-center text-left hover:bg-input-bg--hover focus-visible:bg-input-bg--hover focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50',\n isFocused && 'bg-input-bg--hover',\n isSelected && 'font-medium text-input-text',\n )}\n disabled={option.disabled}\n id={optionId}\n key={option.value}\n onClick={() => toggleOption(option.value)}\n role='option'\n type='button'\n >\n <div\n data-testid='spectral-multiselect-selected-indicator'\n className={cn('w-4 h-4 rounded flex items-center justify-center border border-input-border', isSelected && 'bg-primary border-primary')}\n >\n {isSelected && <CheckmarkIcon size={12} />}\n </div>\n <span>{option.label}</span>\n </button>\n )\n }\n\n const getCSSCustomProperties = () => ({\n '--multiselect-border-radius': '0.5rem',\n '--multiselect-trigger-height': '3rem',\n '--multiselect-dropdown-max-height': '20rem',\n })\n\n return (\n <div\n className='w-full'\n data-testid='spectral-multiselect-root'\n >\n {label && (\n <Label\n className={cn('mb-2 block text-text-primary', isDisabled && 'text-text-secondary')}\n data-testid='spectral-multiselect-label'\n htmlFor={multiSelectId}\n >\n {label}\n </Label>\n )}\n <Popover.Root\n open={isOpen}\n onOpenChange={setIsOpen}\n >\n <div\n className='relative'\n data-testid='spectral-multiselect-wrapper'\n onKeyDown={isOpen ? handleKeyDown : undefined}\n role='none'\n >\n <Popover.Trigger asChild>\n <button\n aria-activedescendant={isOpen && focusedOptionValue ? `${listboxId}-option-${focusedOptionValue}` : undefined}\n aria-controls={isOpen ? listboxId : undefined}\n aria-expanded={isOpen}\n aria-label={ariaLabel ?? label}\n className={cn(getTriggerClasses(isOpen, state, className), 'max-h-22 py-2 text-sm')}\n data-state={state}\n data-testid='spectral-multiselect-trigger'\n disabled={isDisabled}\n id={multiSelectId}\n name={name}\n ref={ref}\n role='combobox' // oxlint-disable-line jsx-a11y/prefer-tag-over-role -- trigger uses button-based combobox semantics for listbox popup\n style={getCSSCustomProperties() as CSSProperties}\n type='button'\n {...ariaProps}\n {...props}\n >\n <div\n className='min-w-0 flex-1 overflow-hidden'\n data-testid='spectral-multiselect-selected-items'\n >\n {renderSelectedItems()}\n </div>\n <div className='gap-2 ml-2 flex shrink-0 items-center'>\n <ChevronDownIcon\n className={cn('text-input-icon transition-transform duration-200', isOpen && 'rotate-180')}\n size={20}\n />\n </div>\n </button>\n </Popover.Trigger>\n {showClearAll && value.length > 0 && (\n <button\n aria-label='Clear all selections'\n className='right-10 text-input-icon hover:text-input-icon--hover rounded-sm absolute top-1/2 z-10 -translate-y-1/2 cursor-pointer focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-1 disabled:pointer-events-none disabled:opacity-50'\n data-testid='spectral-multiselect-clear-all-button'\n disabled={isDisabled}\n onClick={(e) => {\n e.stopPropagation()\n handleClearAll()\n document.getElementById(multiSelectId)?.focus()\n }}\n type='button'\n >\n <CloseIcon size={12} />\n </button>\n )}\n\n <Popover.Portal>\n <Popover.Content\n align='start'\n avoidCollisions\n className={getDropdownClasses()}\n collisionPadding={10}\n data-dropdown-width-mode={dropdownWidthMode}\n data-dropdown-width-value={dropdownWidthMode === 'custom' ? dropdownWidth : undefined}\n data-testid='spectral-multiselect-dropdown'\n onOpenAutoFocus={(e) => {\n e.preventDefault()\n if (showSearch) {\n searchInputRef.current?.focus()\n }\n }}\n side='bottom'\n sideOffset={4}\n ref={setDropdownElement}\n style={{\n width: resolvedDropdownWidth,\n ...(dropdownWidth === 'trigger' ? {} : dropdownOverflowStyle),\n ...dropdownShiftStyle,\n }}\n >\n <div className='p-1'>\n {showSearch && (\n <div className='mb-2 relative'>\n <SearchIcon className={cn(ICON_SIZE, 'left-3 text-input-icon absolute top-1/2 -translate-y-1/2')} />\n <input\n aria-label='Search options'\n className='pl-9 pr-3 py-2 text-sm rounded-md focus-visible:ring-black w-full border border-input-border bg-input-bg focus-visible:border-input-border--focus focus-visible:ring-1 focus-visible:outline-none'\n data-testid='spectral-multiselect-search-input'\n onChange={handleSearchChange}\n placeholder={searchPlaceholder}\n ref={searchInputRef}\n type='text'\n value={searchValue}\n {...getPasswordManagerIgnoreProps('text')}\n />\n </div>\n )}\n <div\n aria-multiselectable='true'\n className='max-h-64 overflow-y-auto'\n id={listboxId}\n role='listbox' // oxlint-disable-line jsx-a11y/prefer-tag-over-role -- custom multi-select uses ARIA listbox semantics with option children\n >\n {isLoading ? (\n <LoadingState\n className='text-sm'\n message={loadingMessage}\n data-testid='spectral-multiselect-loading'\n />\n ) : filteredOptions.length === 0 ? (\n <EmptyState\n className='text-sm'\n data-testid='spectral-multiselect-empty-message'\n message={emptyMessage}\n />\n ) : (\n <>\n {showSelectAll && (\n <div className='mb-1'>\n <button\n className={cn(\n 'my-0.5 first:mt-0 last:mb-0 gap-3 rounded-sm px-3 py-2 text-sm font-medium text-input-text-secondary flex w-full items-center hover:bg-input-bg--hover focus-visible:bg-input-bg--hover focus-visible:outline-none',\n isSelectAllFocused && 'bg-input-bg--hover',\n )}\n data-testid='spectral-multiselect-select-all-button'\n onClick={handleSelectAll}\n type='button'\n >\n {isAllSelected ? clearAllLabel : selectAllLabel}\n </button>\n <div className='mx-3 my-1 h-px bg-input-border' />\n </div>\n )}\n\n {groupedOptions.ungrouped.length > 0 && <div className='mb-1'>{groupedOptions.ungrouped.map((option, index) => renderOption(option, index))}</div>}\n\n {Object.entries(groupedOptions.groups).map(([groupName, groupOptions]) => (\n <div\n key={groupName}\n className='mb-1'\n data-testid='spectral-multiselect-group'\n >\n {(groupedOptions.ungrouped.length > 0 || Object.keys(groupedOptions.groups).indexOf(groupName) > 0) && <div className='mx-3 my-1 h-px bg-input-border' />}\n <div\n data-testid='spectral-multiselect-group-name'\n className='px-3 py-1 text-xs font-semibold text-input-text-secondary tracking-wide uppercase'\n >\n {groupName}\n </div>\n {groupOptions.map((option, _index) => renderOption(option, filteredOptions.indexOf(option)))}\n </div>\n ))}\n </>\n )}\n </div>\n </div>\n </Popover.Content>\n </Popover.Portal>\n </div>\n </Popover.Root>\n\n <ErrorMessage\n dataTestId='spectral-multiselect-error-message'\n id={errorMessageId}\n message={state === 'error' ? errorMessage : null}\n messageReserveLines={messageReserveLines}\n messageReserveSpace={messageReserveSpace && state === 'error'}\n />\n <WarningMessage\n dataTestId='spectral-multiselect-warning-message'\n id={warningMessageId}\n message={state === 'warning' ? warningMessage : null}\n messageReserveLines={messageReserveLines}\n messageReserveSpace={messageReserveSpace && state === 'warning'}\n />\n </div>\n )\n}\nMultiSelectBase.displayName = 'MultiSelectBase'\n"],"mappings":"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"}
|
package/dist/MultiSelect.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MultiSelect.js","names":[],"sources":["../src/components/MultiSelect/MultiSelect.tsx"],"sourcesContent":["import { type Ref } from 'react'\nimport { MultiSelectBase, type MultiSelectBaseProps, type MultiSelectOption } from './MultiSelectBase'\n\nexport interface MultiSelectProps extends Omit<MultiSelectBaseProps, 'defaultValue' | 'onChange' | 'options' | 'value'> {\n defaultValue?: string[]\n emptyText?: string\n loading?: boolean\n onChange?: (value: string[]) => void\n onSearchChange?: (search: string) => void\n onValueChange?: (value: string[]) => void\n options: {\n disabled?: boolean\n group?: string\n label: string\n value: string\n }[]\n sortAlphabetically?: boolean\n value?: string[]\n}\n\nexport const MultiSelect = ({\n emptyText = 'No matches found.',\n loading = false,\n onChange,\n onValueChange,\n options,\n defaultValue,\n ref,\n state,\n value,\n ...props\n}: MultiSelectProps & {\n ref?: Ref<HTMLButtonElement>\n}) => {\n const baseOptions: MultiSelectOption[] = options.map((option) => ({\n disabled: option.disabled ?? false,\n group: option.group,\n label: option.label,\n value: option.value,\n }))\n\n const derivedState = loading ? 'loading' : state\n\n const handleChange = (nextValue: string[]) => {\n onValueChange?.(nextValue)\n onChange?.(nextValue)\n }\n\n return <MultiSelectBase
|
|
1
|
+
{"version":3,"file":"MultiSelect.js","names":[],"sources":["../src/components/MultiSelect/MultiSelect.tsx"],"sourcesContent":["import { type Ref } from 'react'\nimport { MultiSelectBase, type MultiSelectBaseProps, type MultiSelectOption } from './MultiSelectBase'\n\nexport interface MultiSelectProps extends Omit<MultiSelectBaseProps, 'defaultValue' | 'onChange' | 'options' | 'value'> {\n defaultValue?: string[]\n emptyText?: string\n loading?: boolean\n onChange?: (value: string[]) => void\n onSearchChange?: (search: string) => void\n onValueChange?: (value: string[]) => void\n options: {\n disabled?: boolean\n group?: string\n label: string\n value: string\n }[]\n sortAlphabetically?: boolean\n value?: string[]\n}\n\nexport const MultiSelect = ({\n emptyText = 'No matches found.',\n loading = false,\n onChange,\n onValueChange,\n options,\n defaultValue,\n ref,\n state,\n value,\n ...props\n}: MultiSelectProps & {\n ref?: Ref<HTMLButtonElement>\n}) => {\n const baseOptions: MultiSelectOption[] = options.map((option) => ({\n disabled: option.disabled ?? false,\n group: option.group,\n label: option.label,\n value: option.value,\n }))\n\n const derivedState = loading ? 'loading' : state\n\n const handleChange = (nextValue: string[]) => {\n onValueChange?.(nextValue)\n onChange?.(nextValue)\n }\n\n return (\n <MultiSelectBase\n data-disabled={props.disabled}\n defaultValue={defaultValue}\n disabled={props.disabled}\n emptyMessage={emptyText}\n onChange={handleChange}\n options={baseOptions}\n ref={ref}\n state={derivedState}\n value={value}\n {...props}\n />\n )\n}\nMultiSelect.displayName = 'MultiSelect'\n"],"mappings":";;;;;;AAoBA,MAAa,eAAe,EAC1B,YAAY,qBACZ,UAAU,OACV,UACA,eACA,SACA,cACA,KACA,OACA,OACA,GAAG,YAGC;CACJ,MAAM,cAAmC,QAAQ,KAAK,YAAY;EAChE,UAAU,OAAO,YAAY;EAC7B,OAAO,OAAO;EACd,OAAO,OAAO;EACd,OAAO,OAAO;EACf,EAAE;CAEH,MAAM,eAAe,UAAU,YAAY;CAE3C,MAAM,gBAAgB,cAAwB;AAC5C,kBAAgB,UAAU;AAC1B,aAAW,UAAU;;AAGvB,QACE,oBAAC,iBAAD;EACE,iBAAe,MAAM;EACP;EACd,UAAU,MAAM;EAChB,cAAc;EACd,UAAU;EACV,SAAS;EACJ;EACL,OAAO;EACA;EACP,GAAI;EACJ;;AAGN,YAAY,cAAc"}
|
package/dist/Popover.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Popover.d.ts","names":[],"sources":["../src/components/Popover/Popover.tsx"],"mappings":";;;;;;KAIY,mBAAA,GAAsB,wBAAA,QAAgC,gBAAA,CAAiB,OAAA;EACjF,KAAA;AAAA;AAAA,cAGW,OAAA;EAAA,GAAO;AAAA,GAAkB,wBAAA,QAAgC,gBAAA,CAAiB,IAAA,MAAK,oBAAA,CAAA,GAAA,CAAA,OAAA;AAAA,
|
|
1
|
+
{"version":3,"file":"Popover.d.ts","names":[],"sources":["../src/components/Popover/Popover.tsx"],"mappings":";;;;;;KAIY,mBAAA,GAAsB,wBAAA,QAAgC,gBAAA,CAAiB,OAAA;EACjF,KAAA;AAAA;AAAA,cAGW,OAAA;EAAA,GAAO;AAAA,GAAkB,wBAAA,QAAgC,gBAAA,CAAiB,IAAA,MAAK,oBAAA,CAAA,GAAA,CAAA,OAAA;AAAA,cAU/E,cAAA;EAAA,GAAc;AAAA,GAAkB,wBAAA,QAAgC,gBAAA,CAAiB,OAAA,MAAQ,oBAAA,CAAA,GAAA,CAAA,OAAA;AAAA,cAUzF,cAAA;EAAc,KAAA;EAAA,SAAA;EAAA,gBAAA;EAAA,IAAA;EAAA,UAAA;EAAA,KAAA;EAAA,GAAA;AAAA,GAAuH,mBAAA,KAAmB,oBAAA,CAAA,GAAA,CAAA,OAAA;AAAA,cA6BxJ,aAAA;EAAA,GAAa;AAAA,GAAkB,wBAAA,QAAgC,gBAAA,CAAiB,MAAA,MAAO,oBAAA,CAAA,GAAA,CAAA,OAAA"}
|
package/dist/Popover.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Popover.js","names":[],"sources":["../src/components/Popover/Popover.tsx"],"sourcesContent":["import * as PopoverPrimitive from '@radix-ui/react-popover'\nimport { cn } from '@utils/twUtils'\nimport { type ComponentPropsWithoutRef, type CSSProperties } from 'react'\n\nexport type PopoverContentProps = ComponentPropsWithoutRef<typeof PopoverPrimitive.Content> & {\n width?: number | string\n}\n\nexport const Popover = ({ ...props }: ComponentPropsWithoutRef<typeof PopoverPrimitive.Root>) => {\n return <PopoverPrimitive.Root
|
|
1
|
+
{"version":3,"file":"Popover.js","names":[],"sources":["../src/components/Popover/Popover.tsx"],"sourcesContent":["import * as PopoverPrimitive from '@radix-ui/react-popover'\nimport { cn } from '@utils/twUtils'\nimport { type ComponentPropsWithoutRef, type CSSProperties } from 'react'\n\nexport type PopoverContentProps = ComponentPropsWithoutRef<typeof PopoverPrimitive.Content> & {\n width?: number | string\n}\n\nexport const Popover = ({ ...props }: ComponentPropsWithoutRef<typeof PopoverPrimitive.Root>) => {\n return (\n <PopoverPrimitive.Root\n data-slot='popover'\n data-testid='spectral-popover'\n {...props}\n />\n )\n}\n\nexport const PopoverTrigger = ({ ...props }: ComponentPropsWithoutRef<typeof PopoverPrimitive.Trigger>) => {\n return (\n <PopoverPrimitive.Trigger\n data-slot='popover-trigger'\n data-testid='spectral-popover-trigger'\n {...props}\n />\n )\n}\n\nexport const PopoverContent = ({ align = 'center', className, collisionPadding = 8, side = 'bottom', sideOffset = 4, width = 'w-fit', ...props }: PopoverContentProps) => {\n return (\n <PopoverPrimitive.Portal>\n <PopoverPrimitive.Content\n align={align}\n collisionPadding={collisionPadding}\n data-slot='popover-content'\n data-testid='spectral-popover-content'\n side={side}\n sideOffset={sideOffset}\n className={cn(\n 'bg-popover-bg text-popover-text motion-safe:data-[state=closed]:animate-out motion-safe:data-[state=closed]:fade-out-0 motion-safe:data-[state=open]:animate-in',\n 'motion-safe:data-[side=bottom]:slide-in-from-top-2 motion-safe:data-[state=closed]:zoom-out-95 motion-safe:data-[state=open]:fade-in-0 motion-safe:data-[state=open]:zoom-in-95',\n 'z-50 h-fit motion-safe:data-[side=left]:slide-in-from-right-2 motion-safe:data-[side=right]:slide-in-from-left-2 motion-safe:data-[side=top]:slide-in-from-bottom-2',\n 'rounded-lg p-4 max-h-(--radix-popover-content-available-height) origin-[--radix-popover-content-transform-origin] border-none',\n 'shadow-md w-(--popover-width) overflow-x-hidden overflow-y-auto outline-none',\n className,\n )}\n style={\n {\n '--popover-width': typeof width === 'number' ? `${width}px` : width === 'w-fit' ? '320px' : width,\n } as CSSProperties\n }\n {...props}\n />\n </PopoverPrimitive.Portal>\n )\n}\n\nexport const PopoverAnchor = ({ ...props }: ComponentPropsWithoutRef<typeof PopoverPrimitive.Anchor>) => {\n return (\n <PopoverPrimitive.Anchor\n data-slot='popover-anchor'\n {...props}\n />\n )\n}\n"],"mappings":";;;;;;;AAQA,MAAa,WAAW,EAAE,GAAG,YAAoE;AAC/F,QACE,oBAAC,iBAAiB,MAAlB;EACE,aAAU;EAEV,GAAI;EACL;;AAIL,MAAa,kBAAkB,EAAE,GAAG,YAAuE;AACzG,QACE,oBAAC,iBAAiB,SAAlB;EACE,aAAU;EAEV,GAAI;EACL;;AAIL,MAAa,kBAAkB,EAAE,QAAQ,UAAU,WAAW,mBAAmB,GAAG,OAAO,UAAU,aAAa,GAAG,QAAQ,SAAS,GAAG,YAAiC;AACxK,QACE,oBAAC,iBAAiB,QAAlB,YACE,oBAAC,iBAAiB,SAAlB;EACS;EACW;EAClB,aAAU;EAEJ;EACM;EACZ,WAAW,GACT,mKACA,mLACA,uKACA,iIACA,gFACA,UACD;EACD,OACE,EACE,mBAAmB,OAAO,UAAU,WAAW,GAAG,MAAM,MAAM,UAAU,UAAU,UAAU,OAC7F;EAEH,GAAI;EACL,GACsB;;AAI7B,MAAa,iBAAiB,EAAE,GAAG,YAAsE;AACvG,QACE,oBAAC,iBAAiB,QAAlB;EACE,aAAU;EACV,GAAI;EACL"}
|
package/dist/RadialMenu.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RadialMenu.d.ts","names":[],"sources":["../src/components/RadialMenu/RadialMenu.tsx"],"mappings":";;;;;UAMiB,cAAA;;EAEf,WAAA;EACA,WAAA;EACA,QAAA;EACA,IAAA,EAAM,SAAA;EACN,EAAA;EACA,KAAA,GAAQ,cAAA;EACR,KAAA;EACA,QAAA;AAAA;AAAA,UAGe,eAAA;EACf,SAAA;EACA,SAAA;EACA,SAAA;EACA,UAAA;EACA,KAAA,EAAO,cAAA;EACP,OAAA;EACA,QAAA;IAAY,CAAA;IAAW,CAAA;EAAA;AAAA;AAAA;
|
|
1
|
+
{"version":3,"file":"RadialMenu.d.ts","names":[],"sources":["../src/components/RadialMenu/RadialMenu.tsx"],"mappings":";;;;;UAMiB,cAAA;;EAEf,WAAA;EACA,WAAA;EACA,QAAA;EACA,IAAA,EAAM,SAAA;EACN,EAAA;EACA,KAAA,GAAQ,cAAA;EACR,KAAA;EACA,QAAA;AAAA;AAAA,UAGe,eAAA;EACf,SAAA;EACA,SAAA;EACA,SAAA;EACA,UAAA;EACA,KAAA,EAAO,cAAA;EACP,OAAA;EACA,QAAA;IAAY,CAAA;IAAW,CAAA;EAAA;AAAA;AAAA;EA2CI,SAAA;EAAW,SAAA;EAAoB,SAAA;EAAW,UAAA;EAAqC,KAAA;EAAO,OAAA;EAAS,QAAA;EAAU;AAAA,GAAO,eAAA;EAAoB,GAAA,GAAM,GAAA,CAAI,cAAA;AAAA,IAAD,OAAA,CAAkB,WAAA;AAAA"}
|
package/dist/RadialMenu.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RadialMenu.js","names":[],"sources":["../src/components/RadialMenu/RadialMenu.tsx"],"sourcesContent":["import { ChevronDownIcon } from '@components/Icons'\nimport { Tooltip, TooltipContent, TooltipTrigger } from '@components/Tooltip/Tooltip'\nimport { cn } from '@utils/twUtils'\nimport { Fragment, useCallback, useEffect, useMemo, useRef, useState, type ReactNode, type Ref } from 'react'\nimport { createPortal } from 'react-dom'\n\nexport interface RadialMenuItem {\n /** Optional longer description shown in a tooltip on hover, since labels are kept short. */\n description?: string\n destructive?: boolean\n disabled?: boolean\n icon: ReactNode\n id: string\n items?: RadialMenuItem[]\n label: string\n onSelect?: () => void\n}\n\nexport interface RadialMenuProps {\n ariaLabel: string\n backLabel?: string\n className?: string\n dataTestId?: string\n items: RadialMenuItem[]\n onClose: () => void\n position: { x: number; y: number } | null\n}\n\nconst BOUNCE_EASE = 'motion-safe:ease-[cubic-bezier(0.34,1.56,0.64,1)]'\n// Stacked soft shadows form a dense-but-soft halo so labels stay legible over any background.\nconst LABEL_SHADOW = '[text-shadow:0_0_5px_var(--color-text-inverted),0_0_5px_var(--color-text-inverted),0_0_10px_var(--color-text-inverted),0_0_10px_var(--color-text-inverted),0_0_16px_var(--color-text-inverted),0_0_16px_var(--color-text-inverted),0_0_22px_var(--color-text-inverted)]'\nconst CLOSE_MS = 260\nconst LEVEL_MS = 200\nconst LABEL_OFFSET = 30\nconst PRIMARY_RADIUS = 70\nconst SUB_RADIUS = 96\nconst ITEM_REACH = 44\nconst VIEWPORT_MARGIN = 16\n\n// When the menu is larger than the viewport (min > max), center it; otherwise clamp normally.\nconst clamp = (value: number, min: number, max: number) => (min > max ? (min + max) / 2 : Math.min(Math.max(value, min), max))\n\n// Double rAF so the collapsed \"from\" state paints before the transition target is applied.\nconst runAfterPaint = (callback: () => void) => {\n let inner = 0\n const outer = requestAnimationFrame(() => {\n inner = requestAnimationFrame(callback)\n })\n return () => {\n cancelAnimationFrame(outer)\n cancelAnimationFrame(inner)\n }\n}\n\nconst positionForIndex = (index: number, count: number, radius: number) => {\n const angle = -Math.PI / 2 + (index * 2 * Math.PI) / count\n return { x: Math.cos(angle) * radius, y: Math.sin(angle) * radius }\n}\n\n// Fanned across an arc centered straight up, so a sub-ring arches above its parent.\nconst SUB_ARC_CENTER = -Math.PI / 2\nconst SUB_ARC_SPAN = Math.PI * 0.7\nconst positionOnArc = (index: number, count: number, radius: number) => {\n const angle = count <= 1 ? SUB_ARC_CENTER : SUB_ARC_CENTER - SUB_ARC_SPAN / 2 + (index / (count - 1)) * SUB_ARC_SPAN\n return { x: Math.cos(angle) * radius, y: Math.sin(angle) * radius }\n}\n\nexport const RadialMenu = ({ ariaLabel, backLabel = 'Back', className, dataTestId = 'spectral-radial-menu', items, onClose, position, ref }: RadialMenuProps & { ref?: Ref<HTMLDivElement> }) => {\n const containerRef = useRef<HTMLDivElement>(null)\n const levelTimersRef = useRef<number[]>([])\n const [activeSubmenuId, setActiveSubmenuId] = useState<string | null>(null)\n const [highlightIndex, setHighlightIndex] = useState(-1)\n // Items animate between `spreadOrigin` (collapsed) and `base + ring offset` (expanded).\n const [renderCenter, setRenderCenter] = useState(position)\n const [expanded, setExpanded] = useState(false)\n const [spreadOrigin, setSpreadOrigin] = useState<{ x: number; y: number }>({ x: 0, y: 0 })\n // Parent re-skinned as a back control, kept mounted so it swaps cleanly with the real item.\n const [backSkin, setBackSkin] = useState<{ item: RadialMenuItem; offset: { x: number; y: number } } | null>(null)\n // The returned item appears instantly at its spot (swapping in for the chevron) rather than fading.\n const [returningId, setReturningId] = useState<string | null>(null)\n\n const clearLevelTimers = useCallback(() => {\n levelTimersRef.current.forEach((id) => clearTimeout(id))\n levelTimersRef.current = []\n }, [])\n\n useEffect(() => {\n // Cancel any in-flight level-change timers so a stale callback can't mutate a new cycle.\n clearLevelTimers()\n if (position !== null) {\n setRenderCenter(position)\n setActiveSubmenuId(null)\n setHighlightIndex(-1)\n setSpreadOrigin({ x: 0, y: 0 })\n setBackSkin(null)\n setReturningId(null)\n setExpanded(false)\n return runAfterPaint(() => {\n setExpanded(true)\n containerRef.current?.focus()\n })\n }\n setExpanded(false)\n // Collapse to the center, not the lingering sub-ring origin, so close mirrors open.\n setSpreadOrigin({ x: 0, y: 0 })\n const timer = setTimeout(() => {\n setRenderCenter(null)\n setBackSkin(null)\n }, CLOSE_MS)\n return () => {\n clearTimeout(timer)\n }\n }, [clearLevelTimers, position])\n\n useEffect(() => {\n if (position === null) {\n return () => {}\n }\n window.addEventListener('scroll', onClose, true)\n window.addEventListener('resize', onClose)\n return () => {\n window.removeEventListener('scroll', onClose, true)\n window.removeEventListener('resize', onClose)\n }\n }, [position, onClose])\n\n const renderCenterRef = useRef(renderCenter)\n renderCenterRef.current = renderCenter\n useEffect(() => {\n if (renderCenterRef.current === null) {\n return () => {}\n }\n return runAfterPaint(() => {\n setExpanded(true)\n })\n }, [activeSubmenuId])\n\n useEffect(() => clearLevelTimers, [clearLevelTimers])\n\n const activeItems = useMemo(() => {\n if (activeSubmenuId === null) return items\n return items.find((item) => item.id === activeSubmenuId)?.items ?? items\n }, [activeSubmenuId, items])\n\n const activate = useCallback(\n (item: RadialMenuItem) => {\n if (item.disabled === true) return\n if (item.items && item.items.length > 0) {\n clearLevelTimers()\n const index = items.findIndex((candidate) => candidate.id === item.id)\n const offset = positionForIndex(index, items.length, PRIMARY_RADIUS)\n setSpreadOrigin(offset)\n setReturningId(null)\n setBackSkin({\n item: {\n ...item,\n description: undefined,\n icon: (\n <ChevronDownIcon\n className='rotate-90'\n size={20}\n />\n ),\n id: `${item.id}__back`,\n items: undefined,\n label: backLabel,\n },\n offset,\n })\n setActiveSubmenuId(item.id)\n setHighlightIndex(-1)\n setExpanded(false)\n return\n }\n item.onSelect?.()\n onClose()\n },\n [backLabel, clearLevelTimers, items, onClose],\n )\n\n const goBack = useCallback(() => {\n const returningSubmenuId = activeSubmenuId\n const index = items.findIndex((candidate) => candidate.id === returningSubmenuId)\n setSpreadOrigin(index >= 0 ? positionForIndex(index, items.length, PRIMARY_RADIUS) : { x: 0, y: 0 })\n setExpanded(false)\n setHighlightIndex(-1)\n clearLevelTimers()\n levelTimersRef.current = [\n window.setTimeout(() => {\n if (renderCenterRef.current === null) return\n setActiveSubmenuId(null)\n setBackSkin(null)\n setReturningId(returningSubmenuId)\n }, LEVEL_MS),\n window.setTimeout(() => {\n if (renderCenterRef.current !== null) {\n setReturningId(null)\n }\n }, LEVEL_MS + 350),\n ]\n }, [activeSubmenuId, clearLevelTimers, items])\n\n const handleKeyDown = useCallback(\n (event: React.KeyboardEvent<HTMLDivElement>) => {\n const count = activeItems.length\n if (count === 0) return\n\n switch (event.key) {\n case 'ArrowRight':\n case 'ArrowDown': {\n event.preventDefault()\n setHighlightIndex((index) => (index < 0 ? 0 : (index + 1) % count))\n break\n }\n case 'ArrowLeft':\n case 'ArrowUp': {\n event.preventDefault()\n setHighlightIndex((index) => (index < 0 ? count - 1 : (index - 1 + count) % count))\n break\n }\n case 'Enter':\n case ' ': {\n event.preventDefault()\n const item = activeItems[highlightIndex]\n if (item) activate(item)\n break\n }\n case 'Escape': {\n event.preventDefault()\n if (activeSubmenuId === null) {\n onClose()\n } else {\n goBack()\n }\n break\n }\n case 'Tab': {\n event.preventDefault()\n break\n }\n default: {\n break\n }\n }\n },\n [activate, activeItems, activeSubmenuId, goBack, highlightIndex, onClose],\n )\n\n if (renderCenter === null || typeof document === 'undefined') {\n return null\n }\n\n const inSubmenu = activeSubmenuId !== null\n const parentIndex = inSubmenu ? items.findIndex((item) => item.id === activeSubmenuId) : -1\n const parentItem = parentIndex >= 0 ? (items[parentIndex] ?? null) : null\n const base = parentItem === null ? { x: 0, y: 0 } : positionForIndex(parentIndex, items.length, PRIMARY_RADIUS)\n const ringRadius = inSubmenu ? SUB_RADIUS : PRIMARY_RADIUS\n\n const reach = (inSubmenu ? PRIMARY_RADIUS + SUB_RADIUS : PRIMARY_RADIUS) + ITEM_REACH\n const cx = clamp(renderCenter.x, reach + VIEWPORT_MARGIN, window.innerWidth - reach - VIEWPORT_MARGIN)\n const cy = clamp(renderCenter.y, reach + VIEWPORT_MARGIN, window.innerHeight - reach - VIEWPORT_MARGIN)\n\n const renderSpoke = (options: { delay?: number; isActive?: boolean; isHighlighted?: boolean; item: RadialMenuItem; nonInteractive?: boolean; onClick: () => void; opacity: number; testId: string; x: number; y: number }) => {\n const { delay = 0, isActive = false, isHighlighted = false, item, nonInteractive = false, onClick, opacity, testId, x, y } = options\n const hasSubmenu = Boolean(item.items && item.items.length > 0)\n const circle = (\n <button\n aria-disabled={item.disabled}\n aria-haspopup={hasSubmenu ? 'menu' : undefined}\n aria-label={item.label}\n className={cn(\n 'size-12 absolute flex -translate-x-1/2 -translate-y-1/2 items-center justify-center rounded-full',\n 'border border-border-primary bg-popover-bg text-popover-text shadow-elevation-3',\n 'hover:scale-110 hover:cursor-pointer hover:border-toggle-border hover:bg-level-three',\n 'motion-safe:transition-[left,top,opacity,transform] motion-safe:duration-300',\n BOUNCE_EASE,\n (isHighlighted || isActive) && 'border-toggle-border bg-level-three',\n isHighlighted && 'scale-110',\n item.destructive === true && 'text-danger-300',\n item.disabled === true && 'pointer-events-none hover:scale-100',\n nonInteractive && 'pointer-events-none',\n )}\n data-testid={testId}\n disabled={item.disabled}\n onClick={onClick}\n role='menuitem'\n style={{ left: x, opacity, top: y, transitionDelay: `${delay}ms` }}\n type='button'\n >\n {item.icon}\n {hasSubmenu && (\n <span\n aria-hidden\n className='size-4 -right-0.5 -top-0.5 font-semibold absolute flex items-center justify-center rounded-full border border-border-primary bg-level-four text-[9px] leading-none text-text-primary tabular-nums'\n data-testid={`${testId}-submenu-count`}\n >\n {item.items?.length}\n </span>\n )}\n </button>\n )\n return (\n <Fragment key={item.id}>\n {item.description ? (\n <Tooltip delayDuration={400}>\n <TooltipTrigger asChild>{circle}</TooltipTrigger>\n <TooltipContent>{item.description}</TooltipContent>\n </Tooltip>\n ) : (\n circle\n )}\n <span\n aria-hidden\n className={cn(\n 'w-20 font-medium leading-tight pointer-events-none absolute -translate-x-1/2 text-center text-[10px]',\n LABEL_SHADOW,\n 'motion-safe:transition-[left,top,opacity] motion-safe:duration-300',\n BOUNCE_EASE,\n item.destructive === true ? 'text-danger-300' : 'text-text-primary',\n )}\n data-testid={`${testId}-label`}\n style={{ left: x, opacity, top: y + LABEL_OFFSET, transitionDelay: `${delay}ms` }}\n >\n {item.label}\n </span>\n </Fragment>\n )\n }\n\n return createPortal(\n <div\n className='inset-0 fixed z-50 motion-safe:duration-150 motion-safe:animate-in motion-safe:fade-in-0'\n data-testid={dataTestId}\n onContextMenu={(event) => {\n event.preventDefault()\n }}\n onPointerDown={(event) => {\n if (event.target === event.currentTarget) {\n onClose()\n }\n }}\n ref={ref}\n >\n <div\n aria-label={ariaLabel}\n className={cn('focus-visible:ring-ring absolute origin-center focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none', className)}\n data-testid={`${dataTestId}-menu`}\n onKeyDown={handleKeyDown}\n ref={containerRef}\n role='menu'\n style={{ left: cx, top: cy }}\n tabIndex={-1}\n >\n {!inSubmenu && (\n <span\n aria-hidden\n className='size-1.5 absolute -translate-x-1/2 -translate-y-1/2 rounded-full bg-text-secondary opacity-60'\n data-testid={`${dataTestId}-center`}\n />\n )}\n {backSkin !== null &&\n renderSpoke({\n isActive: true,\n item: backSkin.item,\n nonInteractive: !inSubmenu,\n onClick: goBack,\n opacity: inSubmenu ? 1 : 0,\n testId: `${dataTestId}-back`,\n x: backSkin.offset.x,\n y: backSkin.offset.y,\n })}\n {activeItems.map((item, index) => {\n const offset = inSubmenu ? positionOnArc(index, activeItems.length, ringRadius) : positionForIndex(index, activeItems.length, ringRadius)\n const settled = expanded || item.id === returningId\n return renderSpoke({\n delay: item.id === returningId ? 0 : expanded ? index * 25 : 0,\n isHighlighted: index === highlightIndex,\n item,\n onClick: () => {\n activate(item)\n },\n opacity: settled ? (item.disabled === true ? 0.4 : 1) : 0,\n testId: `${dataTestId}-item`,\n x: settled ? base.x + offset.x : spreadOrigin.x,\n y: settled ? base.y + offset.y : spreadOrigin.y,\n })\n })}\n </div>\n </div>,\n document.body,\n )\n}\n\nRadialMenu.displayName = 'RadialMenu'\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"RadialMenu.js","names":[],"sources":["../src/components/RadialMenu/RadialMenu.tsx"],"sourcesContent":["import { ChevronDownIcon } from '@components/Icons'\nimport { Tooltip, TooltipContent, TooltipTrigger } from '@components/Tooltip/Tooltip'\nimport { cn } from '@utils/twUtils'\nimport { Fragment, useCallback, useEffect, useMemo, useRef, useState, type ReactNode, type Ref } from 'react'\nimport { createPortal } from 'react-dom'\n\nexport interface RadialMenuItem {\n /** Optional longer description shown in a tooltip on hover, since labels are kept short. */\n description?: string\n destructive?: boolean\n disabled?: boolean\n icon: ReactNode\n id: string\n items?: RadialMenuItem[]\n label: string\n onSelect?: () => void\n}\n\nexport interface RadialMenuProps {\n ariaLabel: string\n backLabel?: string\n className?: string\n dataTestId?: string\n items: RadialMenuItem[]\n onClose: () => void\n position: { x: number; y: number } | null\n}\n\nconst BOUNCE_EASE = 'motion-safe:ease-[cubic-bezier(0.34,1.56,0.64,1)]'\n// Stacked soft shadows form a dense-but-soft halo so labels stay legible over any background.\nconst LABEL_SHADOW =\n '[text-shadow:0_0_5px_var(--color-text-inverted),0_0_5px_var(--color-text-inverted),0_0_10px_var(--color-text-inverted),0_0_10px_var(--color-text-inverted),0_0_16px_var(--color-text-inverted),0_0_16px_var(--color-text-inverted),0_0_22px_var(--color-text-inverted)]'\nconst CLOSE_MS = 260\nconst LEVEL_MS = 200\nconst LABEL_OFFSET = 30\nconst PRIMARY_RADIUS = 70\nconst SUB_RADIUS = 96\nconst ITEM_REACH = 44\nconst VIEWPORT_MARGIN = 16\n\n// When the menu is larger than the viewport (min > max), center it; otherwise clamp normally.\nconst clamp = (value: number, min: number, max: number) => (min > max ? (min + max) / 2 : Math.min(Math.max(value, min), max))\n\n// Double rAF so the collapsed \"from\" state paints before the transition target is applied.\nconst runAfterPaint = (callback: () => void) => {\n let inner = 0\n const outer = requestAnimationFrame(() => {\n inner = requestAnimationFrame(callback)\n })\n return () => {\n cancelAnimationFrame(outer)\n cancelAnimationFrame(inner)\n }\n}\n\nconst positionForIndex = (index: number, count: number, radius: number) => {\n const angle = -Math.PI / 2 + (index * 2 * Math.PI) / count\n return { x: Math.cos(angle) * radius, y: Math.sin(angle) * radius }\n}\n\n// Fanned across an arc centered straight up, so a sub-ring arches above its parent.\nconst SUB_ARC_CENTER = -Math.PI / 2\nconst SUB_ARC_SPAN = Math.PI * 0.7\nconst positionOnArc = (index: number, count: number, radius: number) => {\n const angle = count <= 1 ? SUB_ARC_CENTER : SUB_ARC_CENTER - SUB_ARC_SPAN / 2 + (index / (count - 1)) * SUB_ARC_SPAN\n return { x: Math.cos(angle) * radius, y: Math.sin(angle) * radius }\n}\n\nexport const RadialMenu = ({ ariaLabel, backLabel = 'Back', className, dataTestId = 'spectral-radial-menu', items, onClose, position, ref }: RadialMenuProps & { ref?: Ref<HTMLDivElement> }) => {\n const containerRef = useRef<HTMLDivElement>(null)\n const levelTimersRef = useRef<number[]>([])\n const [activeSubmenuId, setActiveSubmenuId] = useState<string | null>(null)\n const [highlightIndex, setHighlightIndex] = useState(-1)\n // Items animate between `spreadOrigin` (collapsed) and `base + ring offset` (expanded).\n const [renderCenter, setRenderCenter] = useState(position)\n const [expanded, setExpanded] = useState(false)\n const [spreadOrigin, setSpreadOrigin] = useState<{ x: number; y: number }>({ x: 0, y: 0 })\n // Parent re-skinned as a back control, kept mounted so it swaps cleanly with the real item.\n const [backSkin, setBackSkin] = useState<{ item: RadialMenuItem; offset: { x: number; y: number } } | null>(null)\n // The returned item appears instantly at its spot (swapping in for the chevron) rather than fading.\n const [returningId, setReturningId] = useState<string | null>(null)\n\n const clearLevelTimers = useCallback(() => {\n levelTimersRef.current.forEach((id) => clearTimeout(id))\n levelTimersRef.current = []\n }, [])\n\n useEffect(() => {\n // Cancel any in-flight level-change timers so a stale callback can't mutate a new cycle.\n clearLevelTimers()\n if (position !== null) {\n setRenderCenter(position)\n setActiveSubmenuId(null)\n setHighlightIndex(-1)\n setSpreadOrigin({ x: 0, y: 0 })\n setBackSkin(null)\n setReturningId(null)\n setExpanded(false)\n return runAfterPaint(() => {\n setExpanded(true)\n containerRef.current?.focus()\n })\n }\n setExpanded(false)\n // Collapse to the center, not the lingering sub-ring origin, so close mirrors open.\n setSpreadOrigin({ x: 0, y: 0 })\n const timer = setTimeout(() => {\n setRenderCenter(null)\n setBackSkin(null)\n }, CLOSE_MS)\n return () => {\n clearTimeout(timer)\n }\n }, [clearLevelTimers, position])\n\n useEffect(() => {\n if (position === null) {\n return () => {}\n }\n window.addEventListener('scroll', onClose, true)\n window.addEventListener('resize', onClose)\n return () => {\n window.removeEventListener('scroll', onClose, true)\n window.removeEventListener('resize', onClose)\n }\n }, [position, onClose])\n\n const renderCenterRef = useRef(renderCenter)\n renderCenterRef.current = renderCenter\n useEffect(() => {\n if (renderCenterRef.current === null) {\n return () => {}\n }\n return runAfterPaint(() => {\n setExpanded(true)\n })\n }, [activeSubmenuId])\n\n useEffect(() => clearLevelTimers, [clearLevelTimers])\n\n const activeItems = useMemo(() => {\n if (activeSubmenuId === null) return items\n return items.find((item) => item.id === activeSubmenuId)?.items ?? items\n }, [activeSubmenuId, items])\n\n const activate = useCallback(\n (item: RadialMenuItem) => {\n if (item.disabled === true) return\n if (item.items && item.items.length > 0) {\n clearLevelTimers()\n const index = items.findIndex((candidate) => candidate.id === item.id)\n const offset = positionForIndex(index, items.length, PRIMARY_RADIUS)\n setSpreadOrigin(offset)\n setReturningId(null)\n setBackSkin({\n item: {\n ...item,\n description: undefined,\n icon: (\n <ChevronDownIcon\n className='rotate-90'\n size={20}\n />\n ),\n id: `${item.id}__back`,\n items: undefined,\n label: backLabel,\n },\n offset,\n })\n setActiveSubmenuId(item.id)\n setHighlightIndex(-1)\n setExpanded(false)\n return\n }\n item.onSelect?.()\n onClose()\n },\n [backLabel, clearLevelTimers, items, onClose],\n )\n\n const goBack = useCallback(() => {\n const returningSubmenuId = activeSubmenuId\n const index = items.findIndex((candidate) => candidate.id === returningSubmenuId)\n setSpreadOrigin(index >= 0 ? positionForIndex(index, items.length, PRIMARY_RADIUS) : { x: 0, y: 0 })\n setExpanded(false)\n setHighlightIndex(-1)\n clearLevelTimers()\n levelTimersRef.current = [\n window.setTimeout(() => {\n if (renderCenterRef.current === null) return\n setActiveSubmenuId(null)\n setBackSkin(null)\n setReturningId(returningSubmenuId)\n }, LEVEL_MS),\n window.setTimeout(() => {\n if (renderCenterRef.current !== null) {\n setReturningId(null)\n }\n }, LEVEL_MS + 350),\n ]\n }, [activeSubmenuId, clearLevelTimers, items])\n\n const handleKeyDown = useCallback(\n (event: React.KeyboardEvent<HTMLDivElement>) => {\n const count = activeItems.length\n if (count === 0) return\n\n switch (event.key) {\n case 'ArrowRight':\n case 'ArrowDown': {\n event.preventDefault()\n setHighlightIndex((index) => (index < 0 ? 0 : (index + 1) % count))\n break\n }\n case 'ArrowLeft':\n case 'ArrowUp': {\n event.preventDefault()\n setHighlightIndex((index) => (index < 0 ? count - 1 : (index - 1 + count) % count))\n break\n }\n case 'Enter':\n case ' ': {\n event.preventDefault()\n const item = activeItems[highlightIndex]\n if (item) activate(item)\n break\n }\n case 'Escape': {\n event.preventDefault()\n if (activeSubmenuId === null) {\n onClose()\n } else {\n goBack()\n }\n break\n }\n case 'Tab': {\n event.preventDefault()\n break\n }\n default: {\n break\n }\n }\n },\n [activate, activeItems, activeSubmenuId, goBack, highlightIndex, onClose],\n )\n\n if (renderCenter === null || typeof document === 'undefined') {\n return null\n }\n\n const inSubmenu = activeSubmenuId !== null\n const parentIndex = inSubmenu ? items.findIndex((item) => item.id === activeSubmenuId) : -1\n const parentItem = parentIndex >= 0 ? (items[parentIndex] ?? null) : null\n const base = parentItem === null ? { x: 0, y: 0 } : positionForIndex(parentIndex, items.length, PRIMARY_RADIUS)\n const ringRadius = inSubmenu ? SUB_RADIUS : PRIMARY_RADIUS\n\n const reach = (inSubmenu ? PRIMARY_RADIUS + SUB_RADIUS : PRIMARY_RADIUS) + ITEM_REACH\n const cx = clamp(renderCenter.x, reach + VIEWPORT_MARGIN, window.innerWidth - reach - VIEWPORT_MARGIN)\n const cy = clamp(renderCenter.y, reach + VIEWPORT_MARGIN, window.innerHeight - reach - VIEWPORT_MARGIN)\n\n const renderSpoke = (options: { delay?: number; isActive?: boolean; isHighlighted?: boolean; item: RadialMenuItem; nonInteractive?: boolean; onClick: () => void; opacity: number; testId: string; x: number; y: number }) => {\n const { delay = 0, isActive = false, isHighlighted = false, item, nonInteractive = false, onClick, opacity, testId, x, y } = options\n const hasSubmenu = Boolean(item.items && item.items.length > 0)\n const circle = (\n <button\n aria-disabled={item.disabled}\n aria-haspopup={hasSubmenu ? 'menu' : undefined}\n aria-label={item.label}\n className={cn(\n 'size-12 absolute flex -translate-x-1/2 -translate-y-1/2 items-center justify-center rounded-full',\n 'border border-border-primary bg-popover-bg text-popover-text shadow-elevation-3',\n 'hover:scale-110 hover:cursor-pointer hover:border-toggle-border hover:bg-level-three',\n 'motion-safe:transition-[left,top,opacity,transform] motion-safe:duration-300',\n BOUNCE_EASE,\n (isHighlighted || isActive) && 'border-toggle-border bg-level-three',\n isHighlighted && 'scale-110',\n item.destructive === true && 'text-danger-300',\n item.disabled === true && 'pointer-events-none hover:scale-100',\n nonInteractive && 'pointer-events-none',\n )}\n data-testid={testId}\n disabled={item.disabled}\n onClick={onClick}\n role='menuitem'\n style={{ left: x, opacity, top: y, transitionDelay: `${delay}ms` }}\n type='button'\n >\n {item.icon}\n {hasSubmenu && (\n <span\n aria-hidden\n className='size-4 -right-0.5 -top-0.5 font-semibold absolute flex items-center justify-center rounded-full border border-border-primary bg-level-four text-[9px] leading-none text-text-primary tabular-nums'\n data-testid={`${testId}-submenu-count`}\n >\n {item.items?.length}\n </span>\n )}\n </button>\n )\n return (\n <Fragment key={item.id}>\n {item.description ? (\n <Tooltip delayDuration={400}>\n <TooltipTrigger asChild>{circle}</TooltipTrigger>\n <TooltipContent>{item.description}</TooltipContent>\n </Tooltip>\n ) : (\n circle\n )}\n <span\n aria-hidden\n className={cn(\n 'w-20 font-medium leading-tight pointer-events-none absolute -translate-x-1/2 text-center text-[10px]',\n LABEL_SHADOW,\n 'motion-safe:transition-[left,top,opacity] motion-safe:duration-300',\n BOUNCE_EASE,\n item.destructive === true ? 'text-danger-300' : 'text-text-primary',\n )}\n data-testid={`${testId}-label`}\n style={{ left: x, opacity, top: y + LABEL_OFFSET, transitionDelay: `${delay}ms` }}\n >\n {item.label}\n </span>\n </Fragment>\n )\n }\n\n return createPortal(\n <div\n className='inset-0 fixed z-50 motion-safe:duration-150 motion-safe:animate-in motion-safe:fade-in-0'\n data-testid={dataTestId}\n onContextMenu={(event) => {\n event.preventDefault()\n }}\n onPointerDown={(event) => {\n if (event.target === event.currentTarget) {\n onClose()\n }\n }}\n ref={ref}\n >\n <div\n aria-label={ariaLabel}\n className={cn('focus-visible:ring-ring absolute origin-center focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none', className)}\n data-testid={`${dataTestId}-menu`}\n onKeyDown={handleKeyDown}\n ref={containerRef}\n role='menu'\n style={{ left: cx, top: cy }}\n tabIndex={-1}\n >\n {!inSubmenu && (\n <span\n aria-hidden\n className='size-1.5 absolute -translate-x-1/2 -translate-y-1/2 rounded-full bg-text-secondary opacity-60'\n data-testid={`${dataTestId}-center`}\n />\n )}\n {backSkin !== null &&\n renderSpoke({\n isActive: true,\n item: backSkin.item,\n nonInteractive: !inSubmenu,\n onClick: goBack,\n opacity: inSubmenu ? 1 : 0,\n testId: `${dataTestId}-back`,\n x: backSkin.offset.x,\n y: backSkin.offset.y,\n })}\n {activeItems.map((item, index) => {\n const offset = inSubmenu ? positionOnArc(index, activeItems.length, ringRadius) : positionForIndex(index, activeItems.length, ringRadius)\n const settled = expanded || item.id === returningId\n return renderSpoke({\n delay: item.id === returningId ? 0 : expanded ? index * 25 : 0,\n isHighlighted: index === highlightIndex,\n item,\n onClick: () => {\n activate(item)\n },\n opacity: settled ? (item.disabled === true ? 0.4 : 1) : 0,\n testId: `${dataTestId}-item`,\n x: settled ? base.x + offset.x : spreadOrigin.x,\n y: settled ? base.y + offset.y : spreadOrigin.y,\n })\n })}\n </div>\n </div>,\n document.body,\n )\n}\n\nRadialMenu.displayName = 'RadialMenu'\n"],"mappings":"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"}
|