entangle-ui 0.9.0 → 0.11.0
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/CHANGELOG.md +766 -0
- package/README.md +8 -6
- package/dist/esm/assets/src/components/controls/FileTree/FileTree.css.ts.vanilla-YmktPvnr.css +48 -0
- package/dist/esm/assets/src/components/controls/Select/{Select.css.ts.vanilla-oZnFUorL.css → Select.css.ts.vanilla-Cpu2BvWL.css} +3 -0
- package/dist/esm/assets/src/components/controls/TreeView/{TreeNode.css.ts.vanilla-B_f8pUb8.css → TreeNode.css.ts.vanilla-DXPJRWZZ.css} +31 -24
- package/dist/esm/assets/src/components/editor/AssetBrowser/AssetBrowser.css.ts.vanilla-B2S4cEiY.css +262 -0
- package/dist/esm/assets/src/components/editor/Minimap/Minimap.css.ts.vanilla--rNdWQwy.css +133 -0
- package/dist/esm/assets/src/components/editor/NodeGraph/NodeGraph.css.ts.vanilla-ClF4YZvl.css +401 -0
- package/dist/esm/assets/src/components/editor/PropertyInspector/{PropertyPanel.css.ts.vanilla-RYv_XgOu.css → PropertyPanel.css.ts.vanilla-Dpr_k5I3.css} +4 -1
- package/dist/esm/assets/src/components/editor/PropertyInspector/{PropertyRow.css.ts.vanilla-C3YZnnHb.css → PropertyRow.css.ts.vanilla-pq1Uhd2a.css} +15 -8
- package/dist/esm/assets/src/components/editor/PropertyInspector/{PropertySection.css.ts.vanilla-DK12opZc.css → PropertySection.css.ts.vanilla-6MWWAqyd.css} +50 -32
- package/dist/esm/assets/src/components/editor/Timeline/Timeline.css.ts.vanilla-DDX6Zalo.css +203 -0
- package/dist/esm/assets/src/components/feedback/LogView/LogView.css.ts.vanilla-nB5ENOpw.css +312 -0
- package/dist/esm/assets/src/components/navigation/Menu/Menu.css.ts.vanilla-B5iVe5S2.css +88 -0
- package/dist/esm/assets/src/components/navigation/PathBar/PathBar.css.ts.vanilla-BIEzQQOf.css +10 -0
- package/dist/esm/assets/src/components/primitives/viewport/Viewport.css.ts.vanilla-CkFBJ7Vo.css +65 -0
- package/dist/esm/assets/src/components/shell/FloatingPanel/{FloatingPanel.css.ts.vanilla-1rIAXEFp.css → FloatingPanel.css.ts.vanilla-CRaeYJ2b.css} +20 -13
- package/dist/esm/assets/src/theme/{darkTheme.css.ts.vanilla-ab1WD4dr.css → darkTheme.css.ts.vanilla-BHZ3ecRn.css} +3 -3
- package/dist/esm/assets/src/theme/{lightTheme.css.ts.vanilla-OaRI_wIc.css → lightTheme.css.ts.vanilla-Ct6f9Iym.css} +3 -3
- package/dist/esm/components/Icons/AddIcon.js.map +1 -1
- package/dist/esm/components/Icons/AiChatIcon.js.map +1 -1
- package/dist/esm/components/Icons/AiSparklesIcon.js.map +1 -1
- package/dist/esm/components/Icons/ArchiveIcon.js.map +1 -1
- package/dist/esm/components/Icons/ArrowDownIcon.js.map +1 -1
- package/dist/esm/components/Icons/ArrowLeftIcon.js.map +1 -1
- package/dist/esm/components/Icons/ArrowRightIcon.js.map +1 -1
- package/dist/esm/components/Icons/ArrowUpIcon.js.map +1 -1
- package/dist/esm/components/Icons/BookmarkIcon.js.map +1 -1
- package/dist/esm/components/Icons/BugIcon.js.map +1 -1
- package/dist/esm/components/Icons/BuildingIcon.js.map +1 -1
- package/dist/esm/components/Icons/CalendarIcon.js.map +1 -1
- package/dist/esm/components/Icons/CameraIcon.js +31 -0
- package/dist/esm/components/Icons/CameraIcon.js.map +1 -0
- package/dist/esm/components/Icons/CheckIcon.js.map +1 -1
- package/dist/esm/components/Icons/ChevronDownIcon.js.map +1 -1
- package/dist/esm/components/Icons/ChevronLeftIcon.js.map +1 -1
- package/dist/esm/components/Icons/ChevronRightIcon.js.map +1 -1
- package/dist/esm/components/Icons/ChevronUpIcon.js.map +1 -1
- package/dist/esm/components/Icons/CircleIcon.js.map +1 -1
- package/dist/esm/components/Icons/ClockIcon.js.map +1 -1
- package/dist/esm/components/Icons/CloseIcon.js.map +1 -1
- package/dist/esm/components/Icons/CloudUploadIcon.js.map +1 -1
- package/dist/esm/components/Icons/CodeIcon.js.map +1 -1
- package/dist/esm/components/Icons/CopyIcon.js.map +1 -1
- package/dist/esm/components/Icons/CubeIcon.js +31 -0
- package/dist/esm/components/Icons/CubeIcon.js.map +1 -0
- package/dist/esm/components/Icons/CutIcon.js.map +1 -1
- package/dist/esm/components/Icons/DotsHorizontalIcon.js.map +1 -1
- package/dist/esm/components/Icons/DotsVerticalIcon.js.map +1 -1
- package/dist/esm/components/Icons/DownloadIcon.js.map +1 -1
- package/dist/esm/components/Icons/EditIcon.js.map +1 -1
- package/dist/esm/components/Icons/ErrorIcon.js.map +1 -1
- package/dist/esm/components/Icons/ExternalLinkIcon.js.map +1 -1
- package/dist/esm/components/Icons/EyeDropperIcon.js.map +1 -1
- package/dist/esm/components/Icons/EyeIcon.js.map +1 -1
- package/dist/esm/components/Icons/FileTextIcon.js.map +1 -1
- package/dist/esm/components/Icons/FilterIcon.js.map +1 -1
- package/dist/esm/components/Icons/FirstIcon.js.map +1 -1
- package/dist/esm/components/Icons/FolderCogIcon.js.map +1 -1
- package/dist/esm/components/Icons/FolderIcon.js.map +1 -1
- package/dist/esm/components/Icons/FolderOpenIcon.js +2 -2
- package/dist/esm/components/Icons/FolderOpenIcon.js.map +1 -1
- package/dist/esm/components/Icons/FullscreenIcon.js.map +1 -1
- package/dist/esm/components/Icons/GitBranchIcon.js.map +1 -1
- package/dist/esm/components/Icons/GridIcon.js.map +1 -1
- package/dist/esm/components/Icons/HeartIcon.js.map +1 -1
- package/dist/esm/components/Icons/HelpIcon.js.map +1 -1
- package/dist/esm/components/Icons/HomeIcon.js.map +1 -1
- package/dist/esm/components/Icons/ImageIcon.js +31 -0
- package/dist/esm/components/Icons/ImageIcon.js.map +1 -0
- package/dist/esm/components/Icons/InfoIcon.js.map +1 -1
- package/dist/esm/components/Icons/LastIcon.js.map +1 -1
- package/dist/esm/components/Icons/LinkIcon.js.map +1 -1
- package/dist/esm/components/Icons/ListIcon.js.map +1 -1
- package/dist/esm/components/Icons/LockIcon.js.map +1 -1
- package/dist/esm/components/Icons/MaximizeIcon.js.map +1 -1
- package/dist/esm/components/Icons/MenuIcon.js.map +1 -1
- package/dist/esm/components/Icons/MinimizeIcon.js.map +1 -1
- package/dist/esm/components/Icons/MinusIcon.js.map +1 -1
- package/dist/esm/components/Icons/OrbitIcon.js +31 -0
- package/dist/esm/components/Icons/OrbitIcon.js.map +1 -0
- package/dist/esm/components/Icons/PasteIcon.js.map +1 -1
- package/dist/esm/components/Icons/PauseIcon.js +2 -2
- package/dist/esm/components/Icons/PauseIcon.js.map +1 -1
- package/dist/esm/components/Icons/PinIcon.js.map +1 -1
- package/dist/esm/components/Icons/PlayIcon.js.map +1 -1
- package/dist/esm/components/Icons/RedoIcon.js.map +1 -1
- package/dist/esm/components/Icons/RefreshIcon.js.map +1 -1
- package/dist/esm/components/Icons/RobotIcon.js.map +1 -1
- package/dist/esm/components/Icons/RotateIcon.js +32 -0
- package/dist/esm/components/Icons/RotateIcon.js.map +1 -0
- package/dist/esm/components/Icons/SaveIcon.js.map +1 -1
- package/dist/esm/components/Icons/SearchIcon.js.map +1 -1
- package/dist/esm/components/Icons/SendIcon.js.map +1 -1
- package/dist/esm/components/Icons/SettingsIcon.js.map +1 -1
- package/dist/esm/components/Icons/SortIcon.js.map +1 -1
- package/dist/esm/components/Icons/SpinIcon.js +32 -0
- package/dist/esm/components/Icons/SpinIcon.js.map +1 -0
- package/dist/esm/components/Icons/StarIcon.js.map +1 -1
- package/dist/esm/components/Icons/StopIcon.js.map +1 -1
- package/dist/esm/components/Icons/SuccessIcon.js.map +1 -1
- package/dist/esm/components/Icons/TagIcon.js.map +1 -1
- package/dist/esm/components/Icons/TangentAlignedIcon.js.map +1 -1
- package/dist/esm/components/Icons/TangentAutoIcon.js.map +1 -1
- package/dist/esm/components/Icons/TangentFreeIcon.js.map +1 -1
- package/dist/esm/components/Icons/TangentLinearIcon.js.map +1 -1
- package/dist/esm/components/Icons/TangentMirroredIcon.js.map +1 -1
- package/dist/esm/components/Icons/TangentStepIcon.js.map +1 -1
- package/dist/esm/components/Icons/TerminalIcon.js.map +1 -1
- package/dist/esm/components/Icons/TrashIcon.js.map +1 -1
- package/dist/esm/components/Icons/UndoIcon.js.map +1 -1
- package/dist/esm/components/Icons/UnlinkIcon.js.map +1 -1
- package/dist/esm/components/Icons/UnlockIcon.js.map +1 -1
- package/dist/esm/components/Icons/UploadIcon.js.map +1 -1
- package/dist/esm/components/Icons/UserIcon.js.map +1 -1
- package/dist/esm/components/Icons/UsersIcon.js.map +1 -1
- package/dist/esm/components/Icons/WarningIcon.js.map +1 -1
- package/dist/esm/components/Icons/ZoomFitIcon.js +32 -0
- package/dist/esm/components/Icons/ZoomFitIcon.js.map +1 -0
- package/dist/esm/components/Icons/ZoomInIcon.js.map +1 -1
- package/dist/esm/components/Icons/ZoomOutIcon.js.map +1 -1
- package/dist/esm/components/controls/CartesianPicker/CartesianPicker.js +3 -3
- package/dist/esm/components/controls/CartesianPicker/CartesianPicker.js.map +1 -1
- package/dist/esm/components/controls/CartesianPicker/useCartesianInteraction.js +7 -5
- package/dist/esm/components/controls/CartesianPicker/useCartesianInteraction.js.map +1 -1
- package/dist/esm/components/controls/CartesianPicker/useCartesianRenderer.js.map +1 -1
- package/dist/esm/components/controls/ColorPicker/AlphaSlider.js.map +1 -1
- package/dist/esm/components/controls/ColorPicker/ColorArea.js +2 -2
- package/dist/esm/components/controls/ColorPicker/ColorArea.js.map +1 -1
- package/dist/esm/components/controls/ColorPicker/ColorInputs.js +16 -2
- package/dist/esm/components/controls/ColorPicker/ColorInputs.js.map +1 -1
- package/dist/esm/components/controls/ColorPicker/ColorPalette.js +3 -1
- package/dist/esm/components/controls/ColorPicker/ColorPalette.js.map +1 -1
- package/dist/esm/components/controls/ColorPicker/ColorPicker.js +6 -6
- package/dist/esm/components/controls/ColorPicker/ColorPicker.js.map +1 -1
- package/dist/esm/components/controls/ColorPicker/ColorPresets.js +3 -1
- package/dist/esm/components/controls/ColorPicker/ColorPresets.js.map +1 -1
- package/dist/esm/components/controls/ColorPicker/ColorSwatch.js.map +1 -1
- package/dist/esm/components/controls/ColorPicker/EyeDropper.js.map +1 -1
- package/dist/esm/components/controls/ColorPicker/HueSlider.js.map +1 -1
- package/dist/esm/components/controls/ColorPicker/colorUtils.js.map +1 -1
- package/dist/esm/components/controls/ColorPicker/palettes/material.js.map +1 -1
- package/dist/esm/components/controls/ColorPicker/palettes/professional.js.map +1 -1
- package/dist/esm/components/controls/ColorPicker/palettes/tailwind.js.map +1 -1
- package/dist/esm/components/controls/ColorPicker/useColor.js.map +1 -1
- package/dist/esm/components/controls/Combobox/Combobox.js +46 -23
- package/dist/esm/components/controls/Combobox/Combobox.js.map +1 -1
- package/dist/esm/components/controls/CurveEditor/CurveCanvas.js +11 -4
- package/dist/esm/components/controls/CurveEditor/CurveCanvas.js.map +1 -1
- package/dist/esm/components/controls/CurveEditor/CurveEditor.js +4 -4
- package/dist/esm/components/controls/CurveEditor/CurveEditor.js.map +1 -1
- package/dist/esm/components/controls/CurveEditor/CurveToolbar.js +7 -5
- package/dist/esm/components/controls/CurveEditor/CurveToolbar.js.map +1 -1
- package/dist/esm/components/controls/CurveEditor/curvePresets.js.map +1 -1
- package/dist/esm/components/controls/CurveEditor/curveUtils.js +13 -15
- package/dist/esm/components/controls/CurveEditor/curveUtils.js.map +1 -1
- package/dist/esm/components/controls/CurveEditor/useCurveInteraction.js +72 -50
- package/dist/esm/components/controls/CurveEditor/useCurveInteraction.js.map +1 -1
- package/dist/esm/components/controls/CurveEditor/useCurveRenderer.js +11 -3
- package/dist/esm/components/controls/CurveEditor/useCurveRenderer.js.map +1 -1
- package/dist/esm/components/controls/FileTree/FileTree.css.js +9 -0
- package/dist/esm/components/controls/FileTree/FileTree.css.js.map +1 -0
- package/dist/esm/components/controls/FileTree/FileTree.js +252 -0
- package/dist/esm/components/controls/FileTree/FileTree.js.map +1 -0
- package/dist/esm/components/controls/FileTree/fileTreeIcons.js +129 -0
- package/dist/esm/components/controls/FileTree/fileTreeIcons.js.map +1 -0
- package/dist/esm/components/controls/FileTree/fileTreeLabels.js +8 -0
- package/dist/esm/components/controls/FileTree/fileTreeLabels.js.map +1 -0
- package/dist/esm/components/controls/FileTree/useFileTreeDrop.js +115 -0
- package/dist/esm/components/controls/FileTree/useFileTreeDrop.js.map +1 -0
- package/dist/esm/components/controls/FileUploader/FileUploader.js +3 -6
- package/dist/esm/components/controls/FileUploader/FileUploader.js.map +1 -1
- package/dist/esm/components/controls/MultiSelect/MultiSelect.js +46 -15
- package/dist/esm/components/controls/MultiSelect/MultiSelect.js.map +1 -1
- package/dist/esm/components/controls/NumberInput/NumberInput.js +11 -4
- package/dist/esm/components/controls/NumberInput/NumberInput.js.map +1 -1
- package/dist/esm/components/controls/NumberInput/useNumberInput.js +6 -2
- package/dist/esm/components/controls/NumberInput/useNumberInput.js.map +1 -1
- package/dist/esm/components/controls/Select/Select.css.js +1 -1
- package/dist/esm/components/controls/Select/Select.js +44 -16
- package/dist/esm/components/controls/Select/Select.js.map +1 -1
- package/dist/esm/components/controls/Slider/Slider.js +6 -8
- package/dist/esm/components/controls/Slider/Slider.js.map +1 -1
- package/dist/esm/components/controls/TagInput/TagInput.js +4 -2
- package/dist/esm/components/controls/TagInput/TagInput.js.map +1 -1
- package/dist/esm/components/controls/TreeView/TreeNode.css.js +9 -8
- package/dist/esm/components/controls/TreeView/TreeNode.css.js.map +1 -1
- package/dist/esm/components/controls/TreeView/TreeNode.js +42 -7
- package/dist/esm/components/controls/TreeView/TreeNode.js.map +1 -1
- package/dist/esm/components/controls/TreeView/TreeView.js +18 -6
- package/dist/esm/components/controls/TreeView/TreeView.js.map +1 -1
- package/dist/esm/components/controls/TreeView/useTreeState.js +2 -6
- package/dist/esm/components/controls/TreeView/useTreeState.js.map +1 -1
- package/dist/esm/components/controls/VectorInput/VectorInput.js +13 -4
- package/dist/esm/components/controls/VectorInput/VectorInput.js.map +1 -1
- package/dist/esm/components/data/DataTable/DataTable.js +25 -8
- package/dist/esm/components/data/DataTable/DataTable.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/AssetBrowser.css.js +46 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowser.css.js.map +1 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowser.js +254 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowser.js.map +1 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserAnnouncementLive.js +17 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserAnnouncementLive.js.map +1 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserBreadcrumbs.js +25 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserBreadcrumbs.js.map +1 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserContent.js +140 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserContent.js.map +1 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserContext.js +81 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserContext.js.map +1 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserContextMenu.js +123 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserContextMenu.js.map +1 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserDefaultMenus.js +136 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserDefaultMenus.js.map +1 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserGrid.js +137 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserGrid.js.map +1 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserGridItem.js +226 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserGridItem.js.map +1 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserList.js +189 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserList.js.map +1 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserRenameField.js +63 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserRenameField.js.map +1 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserSidebar.js +19 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserSidebar.js.map +1 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserStatusBar.js +24 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserStatusBar.js.map +1 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserStore.js +176 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserStore.js.map +1 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserSurface.js +120 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserSurface.js.map +1 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserToolbar.js +134 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserToolbar.js.map +1 -0
- package/dist/esm/components/editor/AssetBrowser/assetBrowserFilter.js +90 -0
- package/dist/esm/components/editor/AssetBrowser/assetBrowserFilter.js.map +1 -0
- package/dist/esm/components/editor/AssetBrowser/assetBrowserFormat.js +32 -0
- package/dist/esm/components/editor/AssetBrowser/assetBrowserFormat.js.map +1 -0
- package/dist/esm/components/editor/AssetBrowser/assetBrowserGeometry.js +70 -0
- package/dist/esm/components/editor/AssetBrowser/assetBrowserGeometry.js.map +1 -0
- package/dist/esm/components/editor/AssetBrowser/assetBrowserKeyboard.js +62 -0
- package/dist/esm/components/editor/AssetBrowser/assetBrowserKeyboard.js.map +1 -0
- package/dist/esm/components/editor/AssetBrowser/assetBrowserLabels.js +52 -0
- package/dist/esm/components/editor/AssetBrowser/assetBrowserLabels.js.map +1 -0
- package/dist/esm/components/editor/AssetBrowser/slots.js +18 -0
- package/dist/esm/components/editor/AssetBrowser/slots.js.map +1 -0
- package/dist/esm/components/editor/AssetBrowser/useAssetBrowserAnnouncement.js +19 -0
- package/dist/esm/components/editor/AssetBrowser/useAssetBrowserAnnouncement.js.map +1 -0
- package/dist/esm/components/editor/AssetBrowser/useAssetBrowserHandle.js +70 -0
- package/dist/esm/components/editor/AssetBrowser/useAssetBrowserHandle.js.map +1 -0
- package/dist/esm/components/editor/AssetBrowser/useAssetBrowserViewState.js +108 -0
- package/dist/esm/components/editor/AssetBrowser/useAssetBrowserViewState.js.map +1 -0
- package/dist/esm/components/editor/AssetBrowser/useAssetDnd.js +192 -0
- package/dist/esm/components/editor/AssetBrowser/useAssetDnd.js.map +1 -0
- package/dist/esm/components/editor/AssetBrowser/useAssetGridKeyboardNav.js +91 -0
- package/dist/esm/components/editor/AssetBrowser/useAssetGridKeyboardNav.js.map +1 -0
- package/dist/esm/components/editor/AssetBrowser/useAssetGridVirtualizer.js +91 -0
- package/dist/esm/components/editor/AssetBrowser/useAssetGridVirtualizer.js.map +1 -0
- package/dist/esm/components/editor/AssetBrowser/useAssetMarqueeGesture.js +128 -0
- package/dist/esm/components/editor/AssetBrowser/useAssetMarqueeGesture.js.map +1 -0
- package/dist/esm/components/editor/AssetBrowser/useAssetMutationKeyboard.js +72 -0
- package/dist/esm/components/editor/AssetBrowser/useAssetMutationKeyboard.js.map +1 -0
- package/dist/esm/components/editor/AssetBrowser/useAssetMutations.js +141 -0
- package/dist/esm/components/editor/AssetBrowser/useAssetMutations.js.map +1 -0
- package/dist/esm/components/editor/AssetBrowser/useAssetNavigation.js +113 -0
- package/dist/esm/components/editor/AssetBrowser/useAssetNavigation.js.map +1 -0
- package/dist/esm/components/editor/AssetBrowser/useAssetSelectionController.js +174 -0
- package/dist/esm/components/editor/AssetBrowser/useAssetSelectionController.js.map +1 -0
- package/dist/esm/components/editor/ChatPanel/ChatActionBar.js.map +1 -1
- package/dist/esm/components/editor/ChatPanel/ChatAttachment.js +8 -2
- package/dist/esm/components/editor/ChatPanel/ChatAttachment.js.map +1 -1
- package/dist/esm/components/editor/ChatPanel/ChatBubble.js.map +1 -1
- package/dist/esm/components/editor/ChatPanel/ChatCodeBlock.js +20 -4
- package/dist/esm/components/editor/ChatPanel/ChatCodeBlock.js.map +1 -1
- package/dist/esm/components/editor/ChatPanel/ChatContextChip.js +1 -1
- package/dist/esm/components/editor/ChatPanel/ChatContextChip.js.map +1 -1
- package/dist/esm/components/editor/ChatPanel/ChatEmptyState.js +3 -1
- package/dist/esm/components/editor/ChatPanel/ChatEmptyState.js.map +1 -1
- package/dist/esm/components/editor/ChatPanel/ChatIcons.js.map +1 -1
- package/dist/esm/components/editor/ChatPanel/ChatInput.js +10 -3
- package/dist/esm/components/editor/ChatPanel/ChatInput.js.map +1 -1
- package/dist/esm/components/editor/ChatPanel/ChatInputToolbar.js.map +1 -1
- package/dist/esm/components/editor/ChatPanel/ChatMarkdownRenderer.js +1 -1
- package/dist/esm/components/editor/ChatPanel/ChatMarkdownRenderer.js.map +1 -1
- package/dist/esm/components/editor/ChatPanel/ChatMessage.js +10 -3
- package/dist/esm/components/editor/ChatPanel/ChatMessage.js.map +1 -1
- package/dist/esm/components/editor/ChatPanel/ChatMessageList.js +5 -3
- package/dist/esm/components/editor/ChatPanel/ChatMessageList.js.map +1 -1
- package/dist/esm/components/editor/ChatPanel/ChatPanel.js.map +1 -1
- package/dist/esm/components/editor/ChatPanel/ChatToolCall.js +1 -1
- package/dist/esm/components/editor/ChatPanel/ChatToolCall.js.map +1 -1
- package/dist/esm/components/editor/ChatPanel/ChatTypingIndicator.js.map +1 -1
- package/dist/esm/components/editor/ChatPanel/useChatInput.js.map +1 -1
- package/dist/esm/components/editor/ChatPanel/useChatMessages.js +7 -4
- package/dist/esm/components/editor/ChatPanel/useChatMessages.js.map +1 -1
- package/dist/esm/components/editor/ChatPanel/useChatScroll.js +6 -1
- package/dist/esm/components/editor/ChatPanel/useChatScroll.js.map +1 -1
- package/dist/esm/components/editor/Minimap/Minimap.css.js +18 -0
- package/dist/esm/components/editor/Minimap/Minimap.css.js.map +1 -0
- package/dist/esm/components/editor/Minimap/Minimap.js +257 -0
- package/dist/esm/components/editor/Minimap/Minimap.js.map +1 -0
- package/dist/esm/components/editor/Minimap/Minimap.types.js +11 -0
- package/dist/esm/components/editor/Minimap/Minimap.types.js.map +1 -0
- package/dist/esm/components/editor/Minimap/MinimapContext.js +48 -0
- package/dist/esm/components/editor/Minimap/MinimapContext.js.map +1 -0
- package/dist/esm/components/editor/Minimap/MinimapCorner.js +16 -0
- package/dist/esm/components/editor/Minimap/MinimapCorner.js.map +1 -0
- package/dist/esm/components/editor/Minimap/MinimapFooter.js +16 -0
- package/dist/esm/components/editor/Minimap/MinimapFooter.js.map +1 -0
- package/dist/esm/components/editor/Minimap/MinimapStore.js +166 -0
- package/dist/esm/components/editor/Minimap/MinimapStore.js.map +1 -0
- package/dist/esm/components/editor/Minimap/MinimapTitle.js +16 -0
- package/dist/esm/components/editor/Minimap/MinimapTitle.js.map +1 -0
- package/dist/esm/components/editor/Minimap/ViewportMinimap.js +69 -0
- package/dist/esm/components/editor/Minimap/ViewportMinimap.js.map +1 -0
- package/dist/esm/components/editor/Minimap/computeBoundsFromItems.js +65 -0
- package/dist/esm/components/editor/Minimap/computeBoundsFromItems.js.map +1 -0
- package/dist/esm/components/editor/Minimap/index.js +15 -0
- package/dist/esm/components/editor/Minimap/index.js.map +1 -0
- package/dist/esm/components/editor/Minimap/minimapCoords.js +105 -0
- package/dist/esm/components/editor/Minimap/minimapCoords.js.map +1 -0
- package/dist/esm/components/editor/Minimap/minimapDrawing.js +93 -0
- package/dist/esm/components/editor/Minimap/minimapDrawing.js.map +1 -0
- package/dist/esm/components/editor/Minimap/minimapHitTest.js +59 -0
- package/dist/esm/components/editor/Minimap/minimapHitTest.js.map +1 -0
- package/dist/esm/components/editor/Minimap/useMinimapGestures.js +299 -0
- package/dist/esm/components/editor/Minimap/useMinimapGestures.js.map +1 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraph.css.js +36 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraph.css.js.map +1 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraph.js +627 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraph.js.map +1 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraph.types.js +10 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraph.types.js.map +1 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraphContext.js +54 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraphContext.js.map +1 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraphEdgeLabels.js +137 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraphEdgeLabels.js.map +1 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraphGroup.js +211 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraphGroup.js.map +1 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraphMinimap.js +122 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraphMinimap.js.map +1 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraphNode.js +170 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraphNode.js.map +1 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraphNodeContext.js +21 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraphNodeContext.js.map +1 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraphNodeParts.js +56 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraphNodeParts.js.map +1 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraphNodeSection.js +59 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraphNodeSection.js.map +1 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraphNodeSectionContext.js +17 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraphNodeSectionContext.js.map +1 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraphPin.js +35 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraphPin.js.map +1 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraphPort.js +191 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraphPort.js.map +1 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraphPortVisual.js +46 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraphPortVisual.js.map +1 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraphSlots.js +50 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraphSlots.js.map +1 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraphSpawnPalette.js +70 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraphSpawnPalette.js.map +1 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraphStore.js +543 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraphStore.js.map +1 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraphToolbar.js +100 -0
- package/dist/esm/components/editor/NodeGraph/NodeGraphToolbar.js.map +1 -0
- package/dist/esm/components/editor/NodeGraph/createTypeMatchValidator.js +37 -0
- package/dist/esm/components/editor/NodeGraph/createTypeMatchValidator.js.map +1 -0
- package/dist/esm/components/editor/NodeGraph/nodeGraphActions.js +53 -0
- package/dist/esm/components/editor/NodeGraph/nodeGraphActions.js.map +1 -0
- package/dist/esm/components/editor/NodeGraph/nodeGraphDrawing.js +461 -0
- package/dist/esm/components/editor/NodeGraph/nodeGraphDrawing.js.map +1 -0
- package/dist/esm/components/editor/NodeGraph/nodeGraphIds.js +31 -0
- package/dist/esm/components/editor/NodeGraph/nodeGraphIds.js.map +1 -0
- package/dist/esm/components/editor/NodeGraph/nodeGraphMath.js +350 -0
- package/dist/esm/components/editor/NodeGraph/nodeGraphMath.js.map +1 -0
- package/dist/esm/components/editor/NodeGraph/sortSlots.js +69 -0
- package/dist/esm/components/editor/NodeGraph/sortSlots.js.map +1 -0
- package/dist/esm/components/editor/NodeGraph/useDragGesture.js +63 -0
- package/dist/esm/components/editor/NodeGraph/useDragGesture.js.map +1 -0
- package/dist/esm/components/editor/NodeGraph/useNodeGraph.js +207 -0
- package/dist/esm/components/editor/NodeGraph/useNodeGraph.js.map +1 -0
- package/dist/esm/components/editor/NodeGraph/useNodeGraphConnection.js +334 -0
- package/dist/esm/components/editor/NodeGraph/useNodeGraphConnection.js.map +1 -0
- package/dist/esm/components/editor/NodeGraph/useNodeGraphEdgeInteraction.js +137 -0
- package/dist/esm/components/editor/NodeGraph/useNodeGraphEdgeInteraction.js.map +1 -0
- package/dist/esm/components/editor/NodeGraph/useNodeGraphGroupDrag.js +392 -0
- package/dist/esm/components/editor/NodeGraph/useNodeGraphGroupDrag.js.map +1 -0
- package/dist/esm/components/editor/NodeGraph/useNodeGraphKeyboard.js +164 -0
- package/dist/esm/components/editor/NodeGraph/useNodeGraphKeyboard.js.map +1 -0
- package/dist/esm/components/editor/NodeGraph/useNodeGraphNodeDrag.js +234 -0
- package/dist/esm/components/editor/NodeGraph/useNodeGraphNodeDrag.js.map +1 -0
- package/dist/esm/components/editor/NodeGraph/useStoreSlice.js +49 -0
- package/dist/esm/components/editor/NodeGraph/useStoreSlice.js.map +1 -0
- package/dist/esm/components/editor/PropertyInspector/PropertyGroup.js +1 -1
- package/dist/esm/components/editor/PropertyInspector/PropertyGroup.js.map +1 -1
- package/dist/esm/components/editor/PropertyInspector/PropertyPanel.css.js +4 -3
- package/dist/esm/components/editor/PropertyInspector/PropertyPanel.css.js.map +1 -1
- package/dist/esm/components/editor/PropertyInspector/PropertyPanel.js +12 -7
- package/dist/esm/components/editor/PropertyInspector/PropertyPanel.js.map +1 -1
- package/dist/esm/components/editor/PropertyInspector/PropertyRow.css.js +7 -6
- package/dist/esm/components/editor/PropertyInspector/PropertyRow.css.js.map +1 -1
- package/dist/esm/components/editor/PropertyInspector/PropertyRow.js +21 -5
- package/dist/esm/components/editor/PropertyInspector/PropertyRow.js.map +1 -1
- package/dist/esm/components/editor/PropertyInspector/PropertySection.css.js +11 -10
- package/dist/esm/components/editor/PropertyInspector/PropertySection.css.js.map +1 -1
- package/dist/esm/components/editor/PropertyInspector/PropertySection.js +32 -11
- package/dist/esm/components/editor/PropertyInspector/PropertySection.js.map +1 -1
- package/dist/esm/components/editor/PropertyInspector/usePropertyUndo.js.map +1 -1
- package/dist/esm/components/editor/Timeline/Timeline.css.js +23 -0
- package/dist/esm/components/editor/Timeline/Timeline.css.js.map +1 -0
- package/dist/esm/components/editor/Timeline/Timeline.js +450 -0
- package/dist/esm/components/editor/Timeline/Timeline.js.map +1 -0
- package/dist/esm/components/editor/Timeline/Timeline.types.js +10 -0
- package/dist/esm/components/editor/Timeline/Timeline.types.js.map +1 -0
- package/dist/esm/components/editor/Timeline/TimelineContext.js +69 -0
- package/dist/esm/components/editor/Timeline/TimelineContext.js.map +1 -0
- package/dist/esm/components/editor/Timeline/TimelineFooter.js +16 -0
- package/dist/esm/components/editor/Timeline/TimelineFooter.js.map +1 -0
- package/dist/esm/components/editor/Timeline/TimelineMinimap.js +280 -0
- package/dist/esm/components/editor/Timeline/TimelineMinimap.js.map +1 -0
- package/dist/esm/components/editor/Timeline/TimelineStore.js +289 -0
- package/dist/esm/components/editor/Timeline/TimelineStore.js.map +1 -0
- package/dist/esm/components/editor/Timeline/TimelineToolbar.js +15 -0
- package/dist/esm/components/editor/Timeline/TimelineToolbar.js.map +1 -0
- package/dist/esm/components/editor/Timeline/TimelineTrackHeaders.js +142 -0
- package/dist/esm/components/editor/Timeline/TimelineTrackHeaders.js.map +1 -0
- package/dist/esm/components/editor/Timeline/TimelineTrackScale.js +18 -0
- package/dist/esm/components/editor/Timeline/TimelineTrackScale.js.map +1 -0
- package/dist/esm/components/editor/Timeline/index.js +18 -0
- package/dist/esm/components/editor/Timeline/index.js.map +1 -0
- package/dist/esm/components/editor/Timeline/timelineCoords.js +118 -0
- package/dist/esm/components/editor/Timeline/timelineCoords.js.map +1 -0
- package/dist/esm/components/editor/Timeline/timelineDrawing.js +390 -0
- package/dist/esm/components/editor/Timeline/timelineDrawing.js.map +1 -0
- package/dist/esm/components/editor/Timeline/timelineEdits.js +323 -0
- package/dist/esm/components/editor/Timeline/timelineEdits.js.map +1 -0
- package/dist/esm/components/editor/Timeline/timelineHitTest.js +153 -0
- package/dist/esm/components/editor/Timeline/timelineHitTest.js.map +1 -0
- package/dist/esm/components/editor/Timeline/timelineLayout.js +39 -0
- package/dist/esm/components/editor/Timeline/timelineLayout.js.map +1 -0
- package/dist/esm/components/editor/Timeline/timelineSelection.js +0 -0
- package/dist/esm/components/editor/Timeline/timelineSelection.js.map +1 -0
- package/dist/esm/components/editor/Timeline/useTimelineDraw.js +156 -0
- package/dist/esm/components/editor/Timeline/useTimelineDraw.js.map +1 -0
- package/dist/esm/components/editor/Timeline/useTimelineGestures.js +587 -0
- package/dist/esm/components/editor/Timeline/useTimelineGestures.js.map +1 -0
- package/dist/esm/components/editor/Timeline/useTimelinePlayback.js +85 -0
- package/dist/esm/components/editor/Timeline/useTimelinePlayback.js.map +1 -0
- package/dist/esm/components/editor/TransformControl/TransformControl.js +4 -4
- package/dist/esm/components/editor/TransformControl/TransformControl.js.map +1 -1
- package/dist/esm/components/editor/ViewportGizmo/ViewportGizmo.js +8 -4
- package/dist/esm/components/editor/ViewportGizmo/ViewportGizmo.js.map +1 -1
- package/dist/esm/components/editor/ViewportGizmo/gizmoMath.js +6 -2
- package/dist/esm/components/editor/ViewportGizmo/gizmoMath.js.map +1 -1
- package/dist/esm/components/editor/ViewportGizmo/useGizmoInteraction.js +8 -6
- package/dist/esm/components/editor/ViewportGizmo/useGizmoInteraction.js.map +1 -1
- package/dist/esm/components/editor/ViewportGizmo/useGizmoRenderer.js +1 -2
- package/dist/esm/components/editor/ViewportGizmo/useGizmoRenderer.js.map +1 -1
- package/dist/esm/components/feedback/Alert/Alert.js +8 -8
- package/dist/esm/components/feedback/Alert/Alert.js.map +1 -1
- package/dist/esm/components/feedback/Alert/AlertActions.js.map +1 -1
- package/dist/esm/components/feedback/Alert/AlertDescription.js.map +1 -1
- package/dist/esm/components/feedback/Alert/AlertTitle.js.map +1 -1
- package/dist/esm/components/feedback/CommandPalette/CommandPalette.js +32 -22
- package/dist/esm/components/feedback/CommandPalette/CommandPalette.js.map +1 -1
- package/dist/esm/components/feedback/CommandPalette/fuzzySearch.js.map +1 -1
- package/dist/esm/components/feedback/CommandPalette/useRecentItems.js +3 -1
- package/dist/esm/components/feedback/CommandPalette/useRecentItems.js.map +1 -1
- package/dist/esm/components/feedback/Dialog/Dialog.js +6 -3
- package/dist/esm/components/feedback/Dialog/Dialog.js.map +1 -1
- package/dist/esm/components/feedback/Dialog/DialogBody.js.map +1 -1
- package/dist/esm/components/feedback/Dialog/DialogClose.js.map +1 -1
- package/dist/esm/components/feedback/Dialog/DialogFooter.js.map +1 -1
- package/dist/esm/components/feedback/Dialog/DialogHeader.js +1 -1
- package/dist/esm/components/feedback/Dialog/DialogHeader.js.map +1 -1
- package/dist/esm/components/feedback/Dialog/useDialogAnimation.js +10 -2
- package/dist/esm/components/feedback/Dialog/useDialogAnimation.js.map +1 -1
- package/dist/esm/components/feedback/Drawer/Drawer.js +4 -1
- package/dist/esm/components/feedback/Drawer/Drawer.js.map +1 -1
- package/dist/esm/components/feedback/Drawer/useDrawerAnimation.js +4 -0
- package/dist/esm/components/feedback/Drawer/useDrawerAnimation.js.map +1 -1
- package/dist/esm/components/feedback/EmptyState/EmptyState.js +1 -1
- package/dist/esm/components/feedback/EmptyState/EmptyState.js.map +1 -1
- package/dist/esm/components/feedback/LogView/LogView.css.js +35 -0
- package/dist/esm/components/feedback/LogView/LogView.css.js.map +1 -0
- package/dist/esm/components/feedback/LogView/LogView.js +297 -0
- package/dist/esm/components/feedback/LogView/LogView.js.map +1 -0
- package/dist/esm/components/feedback/LogView/LogViewBody.js +205 -0
- package/dist/esm/components/feedback/LogView/LogViewBody.js.map +1 -0
- package/dist/esm/components/feedback/LogView/LogViewContext.js +31 -0
- package/dist/esm/components/feedback/LogView/LogViewContext.js.map +1 -0
- package/dist/esm/components/feedback/LogView/LogViewParts.js +73 -0
- package/dist/esm/components/feedback/LogView/LogViewParts.js.map +1 -0
- package/dist/esm/components/feedback/LogView/LogViewStore.js +240 -0
- package/dist/esm/components/feedback/LogView/LogViewStore.js.map +1 -0
- package/dist/esm/components/feedback/LogView/logViewLabels.js +25 -0
- package/dist/esm/components/feedback/LogView/logViewLabels.js.map +1 -0
- package/dist/esm/components/feedback/LogView/logViewLevels.js +74 -0
- package/dist/esm/components/feedback/LogView/logViewLevels.js.map +1 -0
- package/dist/esm/components/feedback/LogView/logViewUtils.js +128 -0
- package/dist/esm/components/feedback/LogView/logViewUtils.js.map +1 -0
- package/dist/esm/components/feedback/LogView/useFollowTail.js +95 -0
- package/dist/esm/components/feedback/LogView/useFollowTail.js.map +1 -0
- package/dist/esm/components/feedback/LogView/useLogSelection.js +113 -0
- package/dist/esm/components/feedback/LogView/useLogSelection.js.map +1 -0
- package/dist/esm/components/feedback/ProgressBar/CircularProgress.js +1 -1
- package/dist/esm/components/feedback/ProgressBar/CircularProgress.js.map +1 -1
- package/dist/esm/components/feedback/ProgressBar/ProgressBar.js +1 -1
- package/dist/esm/components/feedback/ProgressBar/ProgressBar.js.map +1 -1
- package/dist/esm/components/feedback/Skeleton/Skeleton.js +1 -1
- package/dist/esm/components/feedback/Skeleton/Skeleton.js.map +1 -1
- package/dist/esm/components/feedback/Skeleton/SkeletonGroup.js +1 -1
- package/dist/esm/components/feedback/Skeleton/SkeletonGroup.js.map +1 -1
- package/dist/esm/components/feedback/Skeleton/SkeletonLayout.js +1 -1
- package/dist/esm/components/feedback/Skeleton/SkeletonLayout.js.map +1 -1
- package/dist/esm/components/feedback/Spinner/Spinner.js +1 -1
- package/dist/esm/components/feedback/Spinner/Spinner.js.map +1 -1
- package/dist/esm/components/feedback/Stat/Stat.js.map +1 -1
- package/dist/esm/components/feedback/Toast/ToastContainer.js +1 -1
- package/dist/esm/components/feedback/Toast/ToastContainer.js.map +1 -1
- package/dist/esm/components/feedback/Toast/ToastItem.js +7 -3
- package/dist/esm/components/feedback/Toast/ToastItem.js.map +1 -1
- package/dist/esm/components/feedback/Toast/ToastProvider.js.map +1 -1
- package/dist/esm/components/feedback/Toast/useToast.js.map +1 -1
- package/dist/esm/components/form/FormHelperText.js.map +1 -1
- package/dist/esm/components/form/FormLabel.js.map +1 -1
- package/dist/esm/components/form/InputWrapper.js.map +1 -1
- package/dist/esm/components/layout/Accordion/Accordion.js +1 -1
- package/dist/esm/components/layout/Accordion/Accordion.js.map +1 -1
- package/dist/esm/components/layout/Accordion/AccordionContent.js +1 -1
- package/dist/esm/components/layout/Accordion/AccordionContent.js.map +1 -1
- package/dist/esm/components/layout/Accordion/AccordionItem.js +1 -1
- package/dist/esm/components/layout/Accordion/AccordionItem.js.map +1 -1
- package/dist/esm/components/layout/Accordion/AccordionTrigger.js +7 -2
- package/dist/esm/components/layout/Accordion/AccordionTrigger.js.map +1 -1
- package/dist/esm/components/layout/Card/Card.js +2 -2
- package/dist/esm/components/layout/Card/Card.js.map +1 -1
- package/dist/esm/components/layout/Divider/Divider.js.map +1 -1
- package/dist/esm/components/layout/Flex/Flex.js +1 -1
- package/dist/esm/components/layout/Flex/Flex.js.map +1 -1
- package/dist/esm/components/layout/Grid/Grid.js +1 -1
- package/dist/esm/components/layout/Grid/Grid.js.map +1 -1
- package/dist/esm/components/layout/ListItem/ListItem.js +1 -1
- package/dist/esm/components/layout/ListItem/ListItem.js.map +1 -1
- package/dist/esm/components/layout/PageHeader/PageHeader.js.map +1 -1
- package/dist/esm/components/layout/PanelSurface/PanelSurface.js +3 -3
- package/dist/esm/components/layout/PanelSurface/PanelSurface.js.map +1 -1
- package/dist/esm/components/layout/ScrollArea/ScrollArea.js +91 -23
- package/dist/esm/components/layout/ScrollArea/ScrollArea.js.map +1 -1
- package/dist/esm/components/layout/Spacer/Spacer.js.map +1 -1
- package/dist/esm/components/layout/SplitPane/SplitPane.js +29 -16
- package/dist/esm/components/layout/SplitPane/SplitPane.js.map +1 -1
- package/dist/esm/components/layout/SplitPane/SplitPanePanel.js.map +1 -1
- package/dist/esm/components/layout/Stack/Stack.js +1 -1
- package/dist/esm/components/layout/Stack/Stack.js.map +1 -1
- package/dist/esm/components/navigation/Breadcrumbs/BreadcrumbEllipsis.js +4 -0
- package/dist/esm/components/navigation/Breadcrumbs/BreadcrumbEllipsis.js.map +1 -1
- package/dist/esm/components/navigation/Breadcrumbs/BreadcrumbItem.js +6 -2
- package/dist/esm/components/navigation/Breadcrumbs/BreadcrumbItem.js.map +1 -1
- package/dist/esm/components/navigation/Breadcrumbs/BreadcrumbSeparator.js.map +1 -1
- package/dist/esm/components/navigation/Breadcrumbs/Breadcrumbs.js +13 -2
- package/dist/esm/components/navigation/Breadcrumbs/Breadcrumbs.js.map +1 -1
- package/dist/esm/components/navigation/ContextMenu/ContextMenu.js +45 -39
- package/dist/esm/components/navigation/ContextMenu/ContextMenu.js.map +1 -1
- package/dist/esm/components/navigation/Menu/Menu.css.js +9 -12
- package/dist/esm/components/navigation/Menu/Menu.css.js.map +1 -1
- package/dist/esm/components/navigation/Menu/Menu.js +97 -63
- package/dist/esm/components/navigation/Menu/Menu.js.map +1 -1
- package/dist/esm/components/navigation/Menu/MenuGapContext.js +19 -0
- package/dist/esm/components/navigation/Menu/MenuGapContext.js.map +1 -0
- package/dist/esm/components/navigation/Pagination/Pagination.js.map +1 -1
- package/dist/esm/components/navigation/Pagination/usePagination.js.map +1 -1
- package/dist/esm/components/navigation/PathBar/PathBar.css.js +6 -0
- package/dist/esm/components/navigation/PathBar/PathBar.css.js.map +1 -0
- package/dist/esm/components/navigation/PathBar/PathBar.js +184 -0
- package/dist/esm/components/navigation/PathBar/PathBar.js.map +1 -0
- package/dist/esm/components/navigation/PathBar/PathBarSiblingMenu.js +151 -0
- package/dist/esm/components/navigation/PathBar/PathBarSiblingMenu.js.map +1 -0
- package/dist/esm/components/navigation/PathBar/pathUtils.js +118 -0
- package/dist/esm/components/navigation/PathBar/pathUtils.js.map +1 -0
- package/dist/esm/components/navigation/SegmentedControl/SegmentedControl.js +16 -6
- package/dist/esm/components/navigation/SegmentedControl/SegmentedControl.js.map +1 -1
- package/dist/esm/components/navigation/SegmentedControl/SegmentedControlItem.js +1 -1
- package/dist/esm/components/navigation/SegmentedControl/SegmentedControlItem.js.map +1 -1
- package/dist/esm/components/navigation/Tabs/Tab.js +10 -3
- package/dist/esm/components/navigation/Tabs/Tab.js.map +1 -1
- package/dist/esm/components/navigation/Tabs/TabList.js +5 -2
- package/dist/esm/components/navigation/Tabs/TabList.js.map +1 -1
- package/dist/esm/components/navigation/Tabs/TabPanel.js +1 -1
- package/dist/esm/components/navigation/Tabs/TabPanel.js.map +1 -1
- package/dist/esm/components/navigation/Tabs/Tabs.js.map +1 -1
- package/dist/esm/components/primitives/Avatar/Avatar.js +11 -4
- package/dist/esm/components/primitives/Avatar/Avatar.js.map +1 -1
- package/dist/esm/components/primitives/Avatar/AvatarGroup.js +2 -2
- package/dist/esm/components/primitives/Avatar/AvatarGroup.js.map +1 -1
- package/dist/esm/components/primitives/Badge/Badge.js +10 -3
- package/dist/esm/components/primitives/Badge/Badge.js.map +1 -1
- package/dist/esm/components/primitives/Button/Button.js.map +1 -1
- package/dist/esm/components/primitives/Checkbox/Checkbox.js +6 -4
- package/dist/esm/components/primitives/Checkbox/Checkbox.js.map +1 -1
- package/dist/esm/components/primitives/Checkbox/CheckboxGroup.js +2 -2
- package/dist/esm/components/primitives/Checkbox/CheckboxGroup.js.map +1 -1
- package/dist/esm/components/primitives/Code/Code.js.map +1 -1
- package/dist/esm/components/primitives/Collapsible/Collapsible.js +2 -2
- package/dist/esm/components/primitives/Collapsible/Collapsible.js.map +1 -1
- package/dist/esm/components/primitives/HoverCard/HoverCard.js +5 -3
- package/dist/esm/components/primitives/HoverCard/HoverCard.js.map +1 -1
- package/dist/esm/components/primitives/Icon/Icon.js +1 -1
- package/dist/esm/components/primitives/Icon/Icon.js.map +1 -1
- package/dist/esm/components/primitives/IconButton/IconButton.js.map +1 -1
- package/dist/esm/components/primitives/Input/Input.js +1 -1
- package/dist/esm/components/primitives/Input/Input.js.map +1 -1
- package/dist/esm/components/primitives/Kbd/Kbd.js +1 -1
- package/dist/esm/components/primitives/Kbd/Kbd.js.map +1 -1
- package/dist/esm/components/primitives/Link/Link.js.map +1 -1
- package/dist/esm/components/primitives/Paper/Paper.js.map +1 -1
- package/dist/esm/components/primitives/Popover/Popover.js +1 -1
- package/dist/esm/components/primitives/Popover/Popover.js.map +1 -1
- package/dist/esm/components/primitives/Popover/PopoverClose.js +1 -1
- package/dist/esm/components/primitives/Popover/PopoverClose.js.map +1 -1
- package/dist/esm/components/primitives/Popover/PopoverContent.js +6 -4
- package/dist/esm/components/primitives/Popover/PopoverContent.js.map +1 -1
- package/dist/esm/components/primitives/Popover/PopoverTrigger.js.map +1 -1
- package/dist/esm/components/primitives/Radio/Radio.js +3 -3
- package/dist/esm/components/primitives/Radio/Radio.js.map +1 -1
- package/dist/esm/components/primitives/Radio/RadioGroup.js +2 -2
- package/dist/esm/components/primitives/Radio/RadioGroup.js.map +1 -1
- package/dist/esm/components/primitives/Switch/Switch.js +1 -1
- package/dist/esm/components/primitives/Switch/Switch.js.map +1 -1
- package/dist/esm/components/primitives/Text/Text.js.map +1 -1
- package/dist/esm/components/primitives/TextArea/TextArea.js +3 -4
- package/dist/esm/components/primitives/TextArea/TextArea.js.map +1 -1
- package/dist/esm/components/primitives/Tooltip/Arrow.js +1 -1
- package/dist/esm/components/primitives/Tooltip/Arrow.js.map +1 -1
- package/dist/esm/components/primitives/Tooltip/Tooltip.js +9 -7
- package/dist/esm/components/primitives/Tooltip/Tooltip.js.map +1 -1
- package/dist/esm/components/primitives/Tooltip/utils.js +1 -1
- package/dist/esm/components/primitives/Tooltip/utils.js.map +1 -1
- package/dist/esm/components/primitives/VisuallyHidden/VisuallyHidden.js.map +1 -1
- package/dist/esm/components/primitives/canvas/CanvasContainer.js +1 -1
- package/dist/esm/components/primitives/canvas/CanvasContainer.js.map +1 -1
- package/dist/esm/components/primitives/canvas/canvasCoords.js.map +1 -1
- package/dist/esm/components/primitives/canvas/canvasDrawing.js +1 -1
- package/dist/esm/components/primitives/canvas/canvasDrawing.js.map +1 -1
- package/dist/esm/components/primitives/canvas/canvasTheme.js.map +1 -1
- package/dist/esm/components/primitives/canvas/useCanvasRenderer.js +8 -2
- package/dist/esm/components/primitives/canvas/useCanvasRenderer.js.map +1 -1
- package/dist/esm/components/primitives/viewport/Viewport.css.js +12 -0
- package/dist/esm/components/primitives/viewport/Viewport.css.js.map +1 -0
- package/dist/esm/components/primitives/viewport/Viewport.js +189 -0
- package/dist/esm/components/primitives/viewport/Viewport.js.map +1 -0
- package/dist/esm/components/primitives/viewport/ViewportContext.js +47 -0
- package/dist/esm/components/primitives/viewport/ViewportContext.js.map +1 -0
- package/dist/esm/components/primitives/viewport/ViewportLayer.js +113 -0
- package/dist/esm/components/primitives/viewport/ViewportLayer.js.map +1 -0
- package/dist/esm/components/primitives/viewport/ViewportOverlay.js +47 -0
- package/dist/esm/components/primitives/viewport/ViewportOverlay.js.map +1 -0
- package/dist/esm/components/primitives/viewport/ViewportStore.js +210 -0
- package/dist/esm/components/primitives/viewport/ViewportStore.js.map +1 -0
- package/dist/esm/components/primitives/viewport/ViewportWorld.js +38 -0
- package/dist/esm/components/primitives/viewport/ViewportWorld.js.map +1 -0
- package/dist/esm/components/primitives/viewport/useViewportGestures.js +333 -0
- package/dist/esm/components/primitives/viewport/useViewportGestures.js.map +1 -0
- package/dist/esm/components/primitives/viewport/viewportCoords.js +37 -0
- package/dist/esm/components/primitives/viewport/viewportCoords.js.map +1 -0
- package/dist/esm/components/primitives/viewport/viewportMath.js +77 -0
- package/dist/esm/components/primitives/viewport/viewportMath.js.map +1 -0
- package/dist/esm/components/shell/AppShell/AppShell.js +1 -1
- package/dist/esm/components/shell/AppShell/AppShell.js.map +1 -1
- package/dist/esm/components/shell/FloatingPanel/FloatingPanel.css.js +11 -10
- package/dist/esm/components/shell/FloatingPanel/FloatingPanel.css.js.map +1 -1
- package/dist/esm/components/shell/FloatingPanel/FloatingPanel.js +60 -24
- package/dist/esm/components/shell/FloatingPanel/FloatingPanel.js.map +1 -1
- package/dist/esm/components/shell/MenuBar/MenuBar.js +23 -16
- package/dist/esm/components/shell/MenuBar/MenuBar.js.map +1 -1
- package/dist/esm/components/shell/StatusBar/StatusBar.js.map +1 -1
- package/dist/esm/components/shell/Toolbar/Toolbar.js +4 -3
- package/dist/esm/components/shell/Toolbar/Toolbar.js.map +1 -1
- package/dist/esm/context/KeyboardContext.js.map +1 -1
- package/dist/esm/hooks/useBreakpoint/useBreakpoint.js +1 -1
- package/dist/esm/hooks/useBreakpoint/useBreakpoint.js.map +1 -1
- package/dist/esm/hooks/useClickOutside/useClickOutside.js +9 -9
- package/dist/esm/hooks/useClickOutside/useClickOutside.js.map +1 -1
- package/dist/esm/hooks/useClipboard/useClipboard.js +4 -0
- package/dist/esm/hooks/useClipboard/useClipboard.js.map +1 -1
- package/dist/esm/hooks/useControlledState/useControlledState.js.map +1 -1
- package/dist/esm/hooks/useDebounced/useDebouncedCallback.js +11 -6
- package/dist/esm/hooks/useDebounced/useDebouncedCallback.js.map +1 -1
- package/dist/esm/hooks/useDebounced/useDebouncedValue.js +16 -4
- package/dist/esm/hooks/useDebounced/useDebouncedValue.js.map +1 -1
- package/dist/esm/hooks/useDisclosure/useDisclosure.js.map +1 -1
- package/dist/esm/hooks/useEventCallback/useEventCallback.js +49 -0
- package/dist/esm/hooks/useEventCallback/useEventCallback.js.map +1 -0
- package/dist/esm/hooks/useFocusTrap/useFocusTrap.js.map +1 -1
- package/dist/esm/hooks/useHotkey/useHotkey.js +7 -12
- package/dist/esm/hooks/useHotkey/useHotkey.js.map +1 -1
- package/dist/esm/hooks/useIntersectionObserver/useIntersectionObserver.js +2 -0
- package/dist/esm/hooks/useIntersectionObserver/useIntersectionObserver.js.map +1 -1
- package/dist/esm/hooks/useIsMounted/useIsMounted.js +43 -0
- package/dist/esm/hooks/useIsMounted/useIsMounted.js.map +1 -0
- package/dist/esm/hooks/useKeyboard/useKeyboard.js.map +1 -1
- package/dist/esm/hooks/useKeyboard/utils.js.map +1 -1
- package/dist/esm/hooks/useLatest/useLatest.js +31 -0
- package/dist/esm/hooks/useLatest/useLatest.js.map +1 -0
- package/dist/esm/hooks/useListboxNav/useListboxNav.js +29 -13
- package/dist/esm/hooks/useListboxNav/useListboxNav.js.map +1 -1
- package/dist/esm/hooks/useMediaQuery/useMediaQuery.js +7 -1
- package/dist/esm/hooks/useMediaQuery/useMediaQuery.js.map +1 -1
- package/dist/esm/hooks/useMergedRef/useMergedRef.js +6 -1
- package/dist/esm/hooks/useMergedRef/useMergedRef.js.map +1 -1
- package/dist/esm/hooks/useNavigationHistory/useNavigationHistory.js +146 -0
- package/dist/esm/hooks/useNavigationHistory/useNavigationHistory.js.map +1 -0
- package/dist/esm/hooks/useResizeObserver/useResizeObserver.js +6 -8
- package/dist/esm/hooks/useResizeObserver/useResizeObserver.js.map +1 -1
- package/dist/esm/hooks/useTheme/useTheme.js +6 -3
- package/dist/esm/hooks/useTheme/useTheme.js.map +1 -1
- package/dist/esm/hooks/useThrottledCallback/useThrottledCallback.js +9 -4
- package/dist/esm/hooks/useThrottledCallback/useThrottledCallback.js.map +1 -1
- package/dist/esm/index.js +63 -4
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/node_modules/tslib/tslib.es6.js +37 -0
- package/dist/esm/node_modules/tslib/tslib.es6.js.map +1 -0
- package/dist/esm/styles.js +3 -0
- package/dist/esm/styles.js.map +1 -0
- package/dist/esm/theme/ThemeProvider.js +6 -4
- package/dist/esm/theme/ThemeProvider.js.map +1 -1
- package/dist/esm/theme/VanillaThemeProvider.js.map +1 -1
- package/dist/esm/theme/breakpoints.js.map +1 -1
- package/dist/esm/theme/contract.css.js +1 -1
- package/dist/esm/theme/createCustomTheme.js +2 -2
- package/dist/esm/theme/createCustomTheme.js.map +1 -1
- package/dist/esm/theme/createLightTheme.js.map +1 -1
- package/dist/esm/theme/darkTheme.css.js +2 -2
- package/dist/esm/theme/darkThemeValues.js +1 -1
- package/dist/esm/theme/darkThemeValues.js.map +1 -1
- package/dist/esm/theme/lightTheme.css.js +1 -1
- package/dist/esm/theme/lightThemeValues.js +1 -1
- package/dist/esm/theme/lightThemeValues.js.map +1 -1
- package/dist/esm/theme/themeContractData.js +4 -4
- package/dist/esm/theme/themeContractData.js.map +1 -1
- package/dist/esm/utils/cx.js.map +1 -1
- package/dist/esm/utils/devWarn.js +1 -1
- package/dist/esm/utils/devWarn.js.map +1 -1
- package/dist/esm/utils/mathExpression.js +1 -1
- package/dist/esm/utils/mathExpression.js.map +1 -1
- package/dist/esm/utils/mathUtils.js.map +1 -1
- package/dist/esm/utils/objects.js.map +1 -1
- package/dist/esm/utils/platform.js.map +1 -1
- package/dist/tokens/tokens.dark.css +1 -1
- package/dist/tokens/tokens.json +1 -1
- package/dist/tokens/tokens.light.css +1 -1
- package/dist/types/components/Icons/AddIcon.d.ts +1 -1
- package/dist/types/components/Icons/AiChatIcon.d.ts +1 -1
- package/dist/types/components/Icons/AiSparklesIcon.d.ts +1 -1
- package/dist/types/components/Icons/ArchiveIcon.d.ts +1 -1
- package/dist/types/components/Icons/ArrowDownIcon.d.ts +1 -1
- package/dist/types/components/Icons/ArrowLeftIcon.d.ts +1 -1
- package/dist/types/components/Icons/ArrowRightIcon.d.ts +1 -1
- package/dist/types/components/Icons/ArrowUpIcon.d.ts +1 -1
- package/dist/types/components/Icons/BookmarkIcon.d.ts +1 -1
- package/dist/types/components/Icons/BugIcon.d.ts +1 -1
- package/dist/types/components/Icons/BuildingIcon.d.ts +1 -1
- package/dist/types/components/Icons/CalendarIcon.d.ts +1 -1
- package/dist/types/components/Icons/CameraIcon.d.ts +34 -0
- package/dist/types/components/Icons/CheckIcon.d.ts +1 -1
- package/dist/types/components/Icons/ChevronDownIcon.d.ts +1 -1
- package/dist/types/components/Icons/ChevronLeftIcon.d.ts +1 -1
- package/dist/types/components/Icons/ChevronRightIcon.d.ts +1 -1
- package/dist/types/components/Icons/ChevronUpIcon.d.ts +1 -1
- package/dist/types/components/Icons/CircleIcon.d.ts +1 -1
- package/dist/types/components/Icons/ClockIcon.d.ts +1 -1
- package/dist/types/components/Icons/CloseIcon.d.ts +1 -1
- package/dist/types/components/Icons/CloudUploadIcon.d.ts +1 -1
- package/dist/types/components/Icons/CodeIcon.d.ts +1 -1
- package/dist/types/components/Icons/CopyIcon.d.ts +1 -1
- package/dist/types/components/Icons/CubeIcon.d.ts +34 -0
- package/dist/types/components/Icons/CutIcon.d.ts +1 -1
- package/dist/types/components/Icons/DotsHorizontalIcon.d.ts +1 -1
- package/dist/types/components/Icons/DotsVerticalIcon.d.ts +1 -1
- package/dist/types/components/Icons/DownloadIcon.d.ts +1 -1
- package/dist/types/components/Icons/EditIcon.d.ts +1 -1
- package/dist/types/components/Icons/ErrorIcon.d.ts +1 -1
- package/dist/types/components/Icons/ExternalLinkIcon.d.ts +1 -1
- package/dist/types/components/Icons/EyeDropperIcon.d.ts +1 -1
- package/dist/types/components/Icons/EyeIcon.d.ts +1 -1
- package/dist/types/components/Icons/FileTextIcon.d.ts +1 -1
- package/dist/types/components/Icons/FilterIcon.d.ts +1 -1
- package/dist/types/components/Icons/FirstIcon.d.ts +1 -1
- package/dist/types/components/Icons/FolderCogIcon.d.ts +1 -1
- package/dist/types/components/Icons/FolderIcon.d.ts +1 -1
- package/dist/types/components/Icons/FolderOpenIcon.d.ts +1 -1
- package/dist/types/components/Icons/FullscreenIcon.d.ts +1 -1
- package/dist/types/components/Icons/GitBranchIcon.d.ts +1 -1
- package/dist/types/components/Icons/GridIcon.d.ts +1 -1
- package/dist/types/components/Icons/HeartIcon.d.ts +1 -1
- package/dist/types/components/Icons/HelpIcon.d.ts +1 -1
- package/dist/types/components/Icons/HomeIcon.d.ts +1 -1
- package/dist/types/components/Icons/ImageIcon.d.ts +34 -0
- package/dist/types/components/Icons/InfoIcon.d.ts +1 -1
- package/dist/types/components/Icons/LastIcon.d.ts +1 -1
- package/dist/types/components/Icons/LinkIcon.d.ts +1 -1
- package/dist/types/components/Icons/ListIcon.d.ts +1 -1
- package/dist/types/components/Icons/LockIcon.d.ts +1 -1
- package/dist/types/components/Icons/MaximizeIcon.d.ts +1 -1
- package/dist/types/components/Icons/MenuIcon.d.ts +1 -1
- package/dist/types/components/Icons/MinimizeIcon.d.ts +1 -1
- package/dist/types/components/Icons/MinusIcon.d.ts +1 -1
- package/dist/types/components/Icons/OrbitIcon.d.ts +34 -0
- package/dist/types/components/Icons/PasteIcon.d.ts +1 -1
- package/dist/types/components/Icons/PauseIcon.d.ts +1 -1
- package/dist/types/components/Icons/PinIcon.d.ts +1 -1
- package/dist/types/components/Icons/PlayIcon.d.ts +1 -1
- package/dist/types/components/Icons/RedoIcon.d.ts +1 -1
- package/dist/types/components/Icons/RefreshIcon.d.ts +1 -1
- package/dist/types/components/Icons/RobotIcon.d.ts +1 -1
- package/dist/types/components/Icons/RotateIcon.d.ts +35 -0
- package/dist/types/components/Icons/SaveIcon.d.ts +1 -1
- package/dist/types/components/Icons/SearchIcon.d.ts +1 -1
- package/dist/types/components/Icons/SendIcon.d.ts +1 -1
- package/dist/types/components/Icons/SettingsIcon.d.ts +1 -1
- package/dist/types/components/Icons/SortIcon.d.ts +1 -1
- package/dist/types/components/Icons/SpinIcon.d.ts +35 -0
- package/dist/types/components/Icons/StarIcon.d.ts +1 -1
- package/dist/types/components/Icons/StopIcon.d.ts +1 -1
- package/dist/types/components/Icons/SuccessIcon.d.ts +1 -1
- package/dist/types/components/Icons/TagIcon.d.ts +1 -1
- package/dist/types/components/Icons/TangentAlignedIcon.d.ts +1 -1
- package/dist/types/components/Icons/TangentAutoIcon.d.ts +1 -1
- package/dist/types/components/Icons/TangentFreeIcon.d.ts +1 -1
- package/dist/types/components/Icons/TangentLinearIcon.d.ts +1 -1
- package/dist/types/components/Icons/TangentMirroredIcon.d.ts +1 -1
- package/dist/types/components/Icons/TangentStepIcon.d.ts +1 -1
- package/dist/types/components/Icons/TerminalIcon.d.ts +1 -1
- package/dist/types/components/Icons/TrashIcon.d.ts +1 -1
- package/dist/types/components/Icons/UndoIcon.d.ts +1 -1
- package/dist/types/components/Icons/UnlinkIcon.d.ts +1 -1
- package/dist/types/components/Icons/UnlockIcon.d.ts +1 -1
- package/dist/types/components/Icons/UploadIcon.d.ts +1 -1
- package/dist/types/components/Icons/UserIcon.d.ts +1 -1
- package/dist/types/components/Icons/UsersIcon.d.ts +1 -1
- package/dist/types/components/Icons/WarningIcon.d.ts +1 -1
- package/dist/types/components/Icons/ZoomFitIcon.d.ts +35 -0
- package/dist/types/components/Icons/ZoomInIcon.d.ts +1 -1
- package/dist/types/components/Icons/ZoomOutIcon.d.ts +1 -1
- package/dist/types/components/controls/CartesianPicker/CartesianPicker.types.d.ts +3 -3
- package/dist/types/components/controls/ColorPicker/ColorPicker.types.d.ts +2 -2
- package/dist/types/components/controls/ColorPicker/EyeDropper.d.ts +1 -1
- package/dist/types/components/controls/Combobox/Combobox.types.d.ts +1 -1
- package/dist/types/components/controls/CurveEditor/CurveEditor.types.d.ts +8 -58
- package/dist/types/components/controls/CurveEditor/curveUtils.d.ts +2 -1
- package/dist/types/components/controls/FileTree/FileTree.d.ts +34 -0
- package/dist/types/components/controls/FileTree/FileTree.types.d.ts +133 -0
- package/dist/types/components/controls/FileTree/fileTreeIcons.d.ts +29 -0
- package/dist/types/components/controls/FileTree/fileTreeLabels.d.ts +19 -0
- package/dist/types/components/controls/FileUploader/FileUploader.types.d.ts +2 -2
- package/dist/types/components/controls/MultiSelect/MultiSelect.types.d.ts +1 -1
- package/dist/types/components/controls/NumberInput/NumberInput.d.ts +2 -2
- package/dist/types/components/controls/Select/Select.d.ts +1 -1
- package/dist/types/components/controls/Select/Select.types.d.ts +45 -11
- package/dist/types/components/controls/Slider/Slider.d.ts +1 -1
- package/dist/types/components/controls/TagInput/TagInput.types.d.ts +2 -2
- package/dist/types/components/controls/TreeView/TreeView.d.ts +1 -1
- package/dist/types/components/controls/TreeView/TreeView.types.d.ts +24 -2
- package/dist/types/components/controls/VectorInput/VectorInput.types.d.ts +1 -1
- package/dist/types/components/data/DataTable/DataTable.types.d.ts +2 -2
- package/dist/types/components/editor/AssetBrowser/AssetBrowser.d.ts +13 -0
- package/dist/types/components/editor/AssetBrowser/AssetBrowser.types.d.ts +241 -0
- package/dist/types/components/editor/AssetBrowser/assetBrowserLabels.d.ts +70 -0
- package/dist/types/components/editor/ChatPanel/ChatMarkdownRenderer.types.d.ts +1 -1
- package/dist/types/components/editor/ChatPanel/ChatPanel.types.d.ts +2 -2
- package/dist/types/components/editor/Minimap/Minimap.d.ts +26 -0
- package/dist/types/components/editor/Minimap/Minimap.types.d.ts +249 -0
- package/dist/types/components/editor/Minimap/MinimapContext.d.ts +27 -0
- package/dist/types/components/editor/Minimap/MinimapCorner.d.ts +8 -0
- package/dist/types/components/editor/Minimap/MinimapFooter.d.ts +8 -0
- package/dist/types/components/editor/Minimap/MinimapStore.d.ts +17 -0
- package/dist/types/components/editor/Minimap/MinimapTitle.d.ts +8 -0
- package/dist/types/components/editor/Minimap/ViewportMinimap.d.ts +52 -0
- package/dist/types/components/editor/Minimap/computeBoundsFromItems.d.ts +13 -0
- package/dist/types/components/editor/Minimap/index.d.ts +13 -0
- package/dist/types/components/editor/NodeGraph/NodeGraph.d.ts +63 -0
- package/dist/types/components/editor/NodeGraph/NodeGraph.types.d.ts +568 -0
- package/dist/types/components/editor/NodeGraph/NodeGraphContext.d.ts +30 -0
- package/dist/types/components/editor/NodeGraph/NodeGraphNodeParts.d.ts +86 -0
- package/dist/types/components/editor/NodeGraph/NodeGraphNodeSection.d.ts +47 -0
- package/dist/types/components/editor/NodeGraph/NodeGraphPin.d.ts +57 -0
- package/dist/types/components/editor/NodeGraph/NodeGraphPort.d.ts +30 -0
- package/dist/types/components/editor/NodeGraph/NodeGraphPortVisual.d.ts +46 -0
- package/dist/types/components/editor/NodeGraph/NodeGraphSlots.d.ts +16 -0
- package/dist/types/components/editor/NodeGraph/NodeGraphStore.d.ts +256 -0
- package/dist/types/components/editor/NodeGraph/NodeGraphToolbar.d.ts +54 -0
- package/dist/types/components/editor/NodeGraph/createTypeMatchValidator.d.ts +43 -0
- package/dist/types/components/editor/NodeGraph/nodeGraphActions.d.ts +39 -0
- package/dist/types/components/editor/NodeGraph/nodeGraphIds.d.ts +16 -0
- package/dist/types/components/editor/NodeGraph/useNodeGraph.d.ts +103 -0
- package/dist/types/components/editor/PropertyInspector/PropertyInspector.types.d.ts +51 -5
- package/dist/types/components/editor/Timeline/Timeline.d.ts +18 -0
- package/dist/types/components/editor/Timeline/Timeline.types.d.ts +348 -0
- package/dist/types/components/editor/Timeline/TimelineContext.d.ts +51 -0
- package/dist/types/components/editor/Timeline/TimelineFooter.d.ts +8 -0
- package/dist/types/components/editor/Timeline/TimelineMinimap.d.ts +21 -0
- package/dist/types/components/editor/Timeline/TimelineStore.d.ts +41 -0
- package/dist/types/components/editor/Timeline/TimelineToolbar.d.ts +8 -0
- package/dist/types/components/editor/Timeline/TimelineTrackScale.d.ts +8 -0
- package/dist/types/components/editor/Timeline/index.d.ts +15 -0
- package/dist/types/components/editor/Timeline/timelineCoords.d.ts +7 -0
- package/dist/types/components/editor/Timeline/timelineEdits.d.ts +18 -0
- package/dist/types/components/editor/TransformControl/TransformControl.types.d.ts +2 -2
- package/dist/types/components/editor/ViewportGizmo/ViewportGizmo.types.d.ts +1 -1
- package/dist/types/components/feedback/Alert/Alert.d.ts +2 -2
- package/dist/types/components/feedback/Alert/Alert.types.d.ts +5 -5
- package/dist/types/components/feedback/CommandPalette/CommandPalette.types.d.ts +1 -3
- package/dist/types/components/feedback/Dialog/Dialog.types.d.ts +2 -2
- package/dist/types/components/feedback/Drawer/Drawer.types.d.ts +1 -1
- package/dist/types/components/feedback/EmptyState/EmptyState.types.d.ts +2 -2
- package/dist/types/components/feedback/LogView/LogView.d.ts +58 -0
- package/dist/types/components/feedback/LogView/LogView.types.d.ts +333 -0
- package/dist/types/components/feedback/LogView/LogViewBody.d.ts +15 -0
- package/dist/types/components/feedback/LogView/LogViewContext.d.ts +9 -0
- package/dist/types/components/feedback/LogView/LogViewParts.d.ts +35 -0
- package/dist/types/components/feedback/LogView/LogViewStore.d.ts +4 -0
- package/dist/types/components/feedback/LogView/logViewLabels.d.ts +9 -0
- package/dist/types/components/feedback/LogView/logViewUtils.d.ts +41 -0
- package/dist/types/components/feedback/ProgressBar/ProgressBar.types.d.ts +3 -3
- package/dist/types/components/feedback/Skeleton/Skeleton.types.d.ts +4 -4
- package/dist/types/components/feedback/Stat/Stat.types.d.ts +2 -2
- package/dist/types/components/feedback/Toast/Toast.types.d.ts +1 -1
- package/dist/types/components/form/FormHelperText.d.ts +2 -2
- package/dist/types/components/form/FormLabel.d.ts +1 -1
- package/dist/types/components/form/InputWrapper.d.ts +2 -2
- package/dist/types/components/layout/Accordion/Accordion.types.d.ts +2 -2
- package/dist/types/components/layout/Card/Card.types.d.ts +6 -6
- package/dist/types/components/layout/Divider/Divider.types.d.ts +2 -2
- package/dist/types/components/layout/Flex/Flex.d.ts +1 -1
- package/dist/types/components/layout/Grid/Grid.d.ts +1 -1
- package/dist/types/components/layout/ListItem/ListItem.types.d.ts +2 -2
- package/dist/types/components/layout/PageHeader/PageHeader.types.d.ts +1 -1
- package/dist/types/components/layout/PanelSurface/PanelSurface.types.d.ts +6 -6
- package/dist/types/components/layout/ScrollArea/ScrollArea.types.d.ts +2 -2
- package/dist/types/components/layout/Stack/Stack.d.ts +1 -1
- package/dist/types/components/navigation/Breadcrumbs/BreadcrumbItem.d.ts +1 -0
- package/dist/types/components/navigation/Breadcrumbs/Breadcrumbs.types.d.ts +7 -0
- package/dist/types/components/navigation/ContextMenu/ContextMenu.d.ts +18 -3
- package/dist/types/components/navigation/ContextMenu/ContextMenu.types.d.ts +55 -63
- package/dist/types/components/navigation/Menu/Menu.d.ts +38 -35
- package/dist/types/components/navigation/Menu/Menu.types.d.ts +190 -71
- package/dist/types/components/navigation/Pagination/Pagination.types.d.ts +1 -1
- package/dist/types/components/navigation/PathBar/PathBar.d.ts +31 -0
- package/dist/types/components/navigation/PathBar/PathBar.types.d.ts +79 -0
- package/dist/types/components/navigation/SegmentedControl/SegmentedControl.types.d.ts +3 -3
- package/dist/types/components/navigation/Tabs/Tabs.types.d.ts +2 -2
- package/dist/types/components/primitives/Avatar/Avatar.types.d.ts +2 -2
- package/dist/types/components/primitives/Badge/Badge.types.d.ts +1 -1
- package/dist/types/components/primitives/Button/Button.d.ts +2 -2
- package/dist/types/components/primitives/Checkbox/Checkbox.types.d.ts +2 -2
- package/dist/types/components/primitives/Code/Code.types.d.ts +1 -1
- package/dist/types/components/primitives/Collapsible/Collapsible.types.d.ts +2 -2
- package/dist/types/components/primitives/HoverCard/HoverCard.types.d.ts +3 -3
- package/dist/types/components/primitives/Icon/Icon.d.ts +2 -2
- package/dist/types/components/primitives/IconButton/IconButton.d.ts +1 -1
- package/dist/types/components/primitives/Input/Input.d.ts +1 -1
- package/dist/types/components/primitives/Kbd/Kbd.types.d.ts +1 -1
- package/dist/types/components/primitives/Link/Link.types.d.ts +1 -1
- package/dist/types/components/primitives/Popover/Popover.types.d.ts +2 -2
- package/dist/types/components/primitives/Radio/Radio.types.d.ts +3 -3
- package/dist/types/components/primitives/Switch/Switch.d.ts +1 -1
- package/dist/types/components/primitives/TextArea/TextArea.types.d.ts +1 -1
- package/dist/types/components/primitives/Tooltip/Tooltip.d.ts +1 -1
- package/dist/types/components/primitives/VisuallyHidden/VisuallyHidden.types.d.ts +1 -1
- package/dist/types/components/primitives/canvas/canvas.types.d.ts +16 -1
- package/dist/types/components/primitives/viewport/Viewport.d.ts +38 -0
- package/dist/types/components/primitives/viewport/Viewport.types.d.ts +257 -0
- package/dist/types/components/primitives/viewport/ViewportContext.d.ts +23 -0
- package/dist/types/components/primitives/viewport/ViewportLayer.d.ts +26 -0
- package/dist/types/components/primitives/viewport/ViewportOverlay.d.ts +27 -0
- package/dist/types/components/primitives/viewport/ViewportWorld.d.ts +25 -0
- package/dist/types/components/primitives/viewport/viewportCoords.d.ts +25 -0
- package/dist/types/components/primitives/viewport/viewportMath.d.ts +35 -0
- package/dist/types/components/shell/AppShell/AppShell.types.d.ts +2 -2
- package/dist/types/components/shell/FloatingPanel/FloatingPanel.types.d.ts +2 -2
- package/dist/types/components/shell/MenuBar/MenuBar.types.d.ts +5 -5
- package/dist/types/components/shell/StatusBar/StatusBar.types.d.ts +3 -3
- package/dist/types/components/shell/Toolbar/Toolbar.types.d.ts +5 -5
- package/dist/types/hooks/useDebounced/useDebouncedValue.d.ts +13 -4
- package/dist/types/hooks/useEventCallback/useEventCallback.d.ts +32 -0
- package/dist/types/hooks/useIsMounted/useIsMounted.d.ts +28 -0
- package/dist/types/hooks/useKeyboard/types.d.ts +1 -1
- package/dist/types/hooks/useLatest/useLatest.d.ts +23 -0
- package/dist/types/hooks/useResizeObserver/useResizeObserver.d.ts +3 -3
- package/dist/types/index.d.ts +71 -6
- package/dist/types/theme/ThemeProvider.d.ts +5 -3
- package/dist/types/theme/contract.css.d.ts +1 -1
- package/dist/types/theme/createCustomTheme.d.ts +1 -1
- package/dist/types/theme/darkThemeValues.d.ts +1 -1
- package/dist/types/theme/lightThemeValues.d.ts +1 -1
- package/dist/types/theme/themeContractData.d.ts +4 -4
- package/dist/types/theme-values.d.ts +1 -1
- package/dist/types/types/keyframe.d.ts +64 -0
- package/package.json +34 -18
- package/dist/esm/_virtual/_commonjsHelpers.js +0 -6
- package/dist/esm/_virtual/_commonjsHelpers.js.map +0 -1
- package/dist/esm/_virtual/cjs.js +0 -4
- package/dist/esm/_virtual/cjs.js.map +0 -1
- package/dist/esm/_virtual/cssesc.js +0 -8
- package/dist/esm/_virtual/cssesc.js.map +0 -1
- package/dist/esm/_virtual/picocolors.js +0 -8
- package/dist/esm/_virtual/picocolors.js.map +0 -1
- package/dist/esm/_virtual/picocolors2.js +0 -4
- package/dist/esm/_virtual/picocolors2.js.map +0 -1
- package/dist/esm/assets/src/components/navigation/Menu/Menu.css.ts.vanilla-DD0c5D10.css +0 -67
- package/dist/esm/components/navigation/ContextMenu/useContextMenuTarget.js +0 -53
- package/dist/esm/components/navigation/ContextMenu/useContextMenuTarget.js.map +0 -1
- package/dist/esm/components/navigation/Menu/Menu.helpers.js +0 -53
- package/dist/esm/components/navigation/Menu/Menu.helpers.js.map +0 -1
- package/dist/esm/components/navigation/Menu/useMenu.js +0 -88
- package/dist/esm/components/navigation/Menu/useMenu.js.map +0 -1
- package/dist/esm/node_modules/@emotion/hash/dist/emotion-hash.esm.js +0 -56
- package/dist/esm/node_modules/@emotion/hash/dist/emotion-hash.esm.js.map +0 -1
- package/dist/esm/node_modules/@vanilla-extract/css/adapter/dist/vanilla-extract-css-adapter.esm.js +0 -46
- package/dist/esm/node_modules/@vanilla-extract/css/adapter/dist/vanilla-extract-css-adapter.esm.js.map +0 -1
- package/dist/esm/node_modules/@vanilla-extract/css/dist/taggedTemplateLiteral-10998315.esm.js +0 -13
- package/dist/esm/node_modules/@vanilla-extract/css/dist/taggedTemplateLiteral-10998315.esm.js.map +0 -1
- package/dist/esm/node_modules/@vanilla-extract/css/dist/transformCss-fd0786e1.esm.js +0 -962
- package/dist/esm/node_modules/@vanilla-extract/css/dist/transformCss-fd0786e1.esm.js.map +0 -1
- package/dist/esm/node_modules/@vanilla-extract/css/dist/vanilla-extract-css.esm.js +0 -309
- package/dist/esm/node_modules/@vanilla-extract/css/dist/vanilla-extract-css.esm.js.map +0 -1
- package/dist/esm/node_modules/@vanilla-extract/css/fileScope/dist/vanilla-extract-css-fileScope.esm.js +0 -18
- package/dist/esm/node_modules/@vanilla-extract/css/fileScope/dist/vanilla-extract-css-fileScope.esm.js.map +0 -1
- package/dist/esm/node_modules/@vanilla-extract/css/injectStyles/dist/vanilla-extract-css-injectStyles.esm.js +0 -23
- package/dist/esm/node_modules/@vanilla-extract/css/injectStyles/dist/vanilla-extract-css-injectStyles.esm.js.map +0 -1
- package/dist/esm/node_modules/@vanilla-extract/css/node_modules/lru-cache/dist/esm/index.js +0 -1543
- package/dist/esm/node_modules/@vanilla-extract/css/node_modules/lru-cache/dist/esm/index.js.map +0 -1
- package/dist/esm/node_modules/@vanilla-extract/private/dist/vanilla-extract-private.esm.js +0 -38
- package/dist/esm/node_modules/@vanilla-extract/private/dist/vanilla-extract-private.esm.js.map +0 -1
- package/dist/esm/node_modules/css-what/lib/es/parse.js +0 -424
- package/dist/esm/node_modules/css-what/lib/es/parse.js.map +0 -1
- package/dist/esm/node_modules/css-what/lib/es/types.js +0 -29
- package/dist/esm/node_modules/css-what/lib/es/types.js.map +0 -1
- package/dist/esm/node_modules/cssesc/cssesc.js +0 -120
- package/dist/esm/node_modules/cssesc/cssesc.js.map +0 -1
- package/dist/esm/node_modules/dedent/dist/dedent.js +0 -100
- package/dist/esm/node_modules/dedent/dist/dedent.js.map +0 -1
- package/dist/esm/node_modules/deep-object-diff/mjs/diff.js +0 -40
- package/dist/esm/node_modules/deep-object-diff/mjs/diff.js.map +0 -1
- package/dist/esm/node_modules/deep-object-diff/mjs/utils.js +0 -9
- package/dist/esm/node_modules/deep-object-diff/mjs/utils.js.map +0 -1
- package/dist/esm/node_modules/deepmerge/dist/cjs.js +0 -143
- package/dist/esm/node_modules/deepmerge/dist/cjs.js.map +0 -1
- package/dist/esm/node_modules/media-query-parser/dist/media-query-parser.esm.js +0 -1386
- package/dist/esm/node_modules/media-query-parser/dist/media-query-parser.esm.js.map +0 -1
- package/dist/esm/node_modules/modern-ahocorasick/dist/index.js +0 -102
- package/dist/esm/node_modules/modern-ahocorasick/dist/index.js.map +0 -1
- package/dist/esm/node_modules/picocolors/picocolors.js +0 -87
- package/dist/esm/node_modules/picocolors/picocolors.js.map +0 -1
- package/dist/types/components/navigation/ContextMenu/useContextMenuTarget.d.ts +0 -5
- package/dist/types/components/navigation/Menu/useMenu.d.ts +0 -16
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
3
|
-
import { useId, useRef, useState, useMemo, useCallback, useEffect } from 'react';
|
|
3
|
+
import { useId, useRef, useState, useMemo, useDeferredValue, useCallback, useEffect } from 'react';
|
|
4
4
|
import { createPortal } from 'react-dom';
|
|
5
|
+
import { FormHelperText } from '../../form/FormHelperText.js';
|
|
6
|
+
import { FormLabel } from '../../form/FormLabel.js';
|
|
5
7
|
import { CheckIcon } from '../../Icons/CheckIcon.js';
|
|
6
8
|
import { ChevronDownIcon } from '../../Icons/ChevronDownIcon.js';
|
|
7
9
|
import { CloseIcon } from '../../Icons/CloseIcon.js';
|
|
8
|
-
import { FormHelperText } from '../../form/FormHelperText.js';
|
|
9
|
-
import { FormLabel } from '../../form/FormLabel.js';
|
|
10
10
|
import { ScrollArea } from '../../layout/ScrollArea/ScrollArea.js';
|
|
11
11
|
import { useControlledState } from '../../../hooks/useControlledState/useControlledState.js';
|
|
12
12
|
import { useListboxNav } from '../../../hooks/useListboxNav/useListboxNav.js';
|
|
@@ -80,12 +80,16 @@ function MultiSelect({ value, defaultValue, options, placeholder = 'Select...',
|
|
|
80
80
|
const [searchQuery, setSearchQuery] = useState('');
|
|
81
81
|
const [dropdownPos, setDropdownPos] = useState({});
|
|
82
82
|
const allOptions = useMemo(() => flattenOptions(options), [options]);
|
|
83
|
+
// Defer the query that drives filtering so the search input stays
|
|
84
|
+
// responsive while a large option list re-filters. The immediate
|
|
85
|
+
// `searchQuery` still drives the input value.
|
|
86
|
+
const deferredSearchQuery = useDeferredValue(searchQuery);
|
|
83
87
|
const filter = filterFn ?? defaultFilter;
|
|
84
88
|
const filteredOptions = useMemo(() => {
|
|
85
|
-
if (!searchable || !
|
|
89
|
+
if (!searchable || !deferredSearchQuery)
|
|
86
90
|
return allOptions;
|
|
87
|
-
return allOptions.filter(opt => filter(opt,
|
|
88
|
-
}, [allOptions, searchable,
|
|
91
|
+
return allOptions.filter(opt => filter(opt, deferredSearchQuery));
|
|
92
|
+
}, [allOptions, searchable, deferredSearchQuery, filter]);
|
|
89
93
|
const selectedSet = useMemo(() => new Set(selected), [selected]);
|
|
90
94
|
const toggleValue = useCallback((val, isDisabled) => {
|
|
91
95
|
if (isDisabled)
|
|
@@ -210,7 +214,9 @@ function MultiSelect({ value, defaultValue, options, placeholder = 'Select...',
|
|
|
210
214
|
return jsx("div", { className: emptyMessageStyle, children: emptyMessage });
|
|
211
215
|
}
|
|
212
216
|
const hasGroups = options.some(isOptionGroup);
|
|
213
|
-
|
|
217
|
+
// Mirror the deferred query used by `filteredOptions` so the grouped /
|
|
218
|
+
// flat switch stays consistent with the list being rendered.
|
|
219
|
+
if (hasGroups && !deferredSearchQuery) {
|
|
214
220
|
let flatIndex = 0;
|
|
215
221
|
return options.map((item, groupIdx) => {
|
|
216
222
|
if (isOptionGroup(item)) {
|
|
@@ -232,15 +238,21 @@ function MultiSelect({ value, defaultValue, options, placeholder = 'Select...',
|
|
|
232
238
|
const isDisabled = opt.disabled ?? false;
|
|
233
239
|
const reachedMax = typeof max === 'number' && !isSelected && selected.length >= max;
|
|
234
240
|
const interactiveDisabled = isDisabled || reachedMax;
|
|
235
|
-
|
|
241
|
+
const selectOption = () => {
|
|
242
|
+
if (!interactiveDisabled) {
|
|
243
|
+
toggleValue(opt.value, false);
|
|
244
|
+
if (closeOnSelect)
|
|
245
|
+
close();
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
return (jsxs("div", { role: "option", tabIndex: -1, "aria-selected": isSelected, "aria-disabled": interactiveDisabled || undefined, id: `${listboxId}-${opt.value}`, className: optionItemRecipe({
|
|
236
249
|
active: isActive,
|
|
237
250
|
selected: isSelected,
|
|
238
251
|
disabled: interactiveDisabled,
|
|
239
|
-
}), onClick:
|
|
240
|
-
if (
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
close();
|
|
252
|
+
}), onClick: selectOption, onKeyDown: e => {
|
|
253
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
254
|
+
e.preventDefault();
|
|
255
|
+
selectOption();
|
|
244
256
|
}
|
|
245
257
|
}, onMouseEnter: () => {
|
|
246
258
|
if (!interactiveDisabled)
|
|
@@ -248,7 +260,14 @@ function MultiSelect({ value, defaultValue, options, placeholder = 'Select...',
|
|
|
248
260
|
}, children: [jsx("span", { className: checkboxRecipe({ selected: isSelected }), "aria-hidden": "true", children: isSelected && jsx(CheckIcon, { size: 10, decorative: true }) }), opt.icon && jsx("span", { children: opt.icon }), jsx("span", { children: opt.label ?? opt.value })] }, opt.value));
|
|
249
261
|
};
|
|
250
262
|
const formValue = selected.join(',');
|
|
251
|
-
return (jsxs("div", { className: cx(containerStyle, className), style: style, children: [label && (jsx(FormLabel, { id: labelId, htmlFor: fieldId, required: required, disabled: disabled, children: label })), jsxs("button", { ref: setTriggerRef, id: fieldId, type: "button", role: "combobox", "aria-haspopup": "listbox", "aria-expanded": isOpen, "aria-controls": isOpen ? listboxId : undefined, "aria-labelledby": label ? labelId : undefined, "aria-required": required || undefined, "aria-invalid": error || undefined, "aria-describedby": showHelperText ? helperId : undefined, disabled: disabled, onClick: () =>
|
|
263
|
+
return (jsxs("div", { className: cx(containerStyle, className), style: style, children: [label && (jsx(FormLabel, { id: labelId, htmlFor: fieldId, required: required, disabled: disabled, children: label })), jsxs("button", { ref: setTriggerRef, id: fieldId, type: "button", role: "combobox", "aria-haspopup": "listbox", "aria-expanded": isOpen, "aria-controls": isOpen ? listboxId : undefined, "aria-labelledby": label ? labelId : undefined, "aria-required": required || undefined, "aria-invalid": error || undefined, "aria-describedby": showHelperText ? helperId : undefined, disabled: disabled, onClick: () => {
|
|
264
|
+
if (isOpen) {
|
|
265
|
+
close();
|
|
266
|
+
}
|
|
267
|
+
else {
|
|
268
|
+
open();
|
|
269
|
+
}
|
|
270
|
+
}, onKeyDown: handleTriggerKeyDown, className: triggerRecipe({
|
|
252
271
|
size,
|
|
253
272
|
variant,
|
|
254
273
|
open: isOpen,
|
|
@@ -258,7 +277,19 @@ function MultiSelect({ value, defaultValue, options, placeholder = 'Select...',
|
|
|
258
277
|
}), "data-testid": testId, ...rest, children: [jsx("span", { className: triggerContentStyle, children: isEmpty ? (jsx("span", { className: placeholderStyle, children: placeholder })) : (jsxs(Fragment, { children: [visibleChips.map(opt => (jsxs("span", { className: chipRecipe({ size }), "data-testid": testId ? `${testId}-chip-${opt.value}` : undefined, children: [opt.icon && jsx("span", { children: opt.icon }), jsx("span", { className: chipLabelStyle, children: opt.label ?? opt.value }), !disabled && (jsx("span", { role: "button", tabIndex: -1, "aria-label": `Remove ${opt.label ?? opt.value}`, className: chipRemoveStyle, onClick: e => {
|
|
259
278
|
e.stopPropagation();
|
|
260
279
|
removeValue(opt.value);
|
|
261
|
-
},
|
|
280
|
+
}, onKeyDown: e => {
|
|
281
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
282
|
+
e.preventDefault();
|
|
283
|
+
e.stopPropagation();
|
|
284
|
+
removeValue(opt.value);
|
|
285
|
+
}
|
|
286
|
+
}, children: jsx(CloseIcon, { size: "sm", decorative: true }) }))] }, opt.value))), overflowCount > 0 && (jsxs("span", { className: moreBadgeStyle, children: ["+", overflowCount, " more"] }))] })) }), clearable && !isEmpty && !disabled && (jsx("span", { role: "button", tabIndex: -1, "aria-label": "Clear selection", className: clearButtonStyle, onClick: handleClearAll, onKeyDown: e => {
|
|
287
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
288
|
+
e.preventDefault();
|
|
289
|
+
e.stopPropagation();
|
|
290
|
+
setSelected([]);
|
|
291
|
+
}
|
|
292
|
+
}, children: jsx(CloseIcon, { size: "sm", decorative: true }) })), jsx("span", { className: chevronRecipe({ open: isOpen }), children: jsx(ChevronDownIcon, { size: chevronSize, decorative: true }) })] }), name && jsx("input", { type: "hidden", name: name, value: formValue }), isOpen &&
|
|
262
293
|
createPortal(jsxs("div", { ref: dropdownRef, role: "listbox", id: listboxId, "aria-multiselectable": "true", "aria-labelledby": label ? labelId : undefined, className: dropdownStyle, style: dropdownPos, onKeyDown: handleDropdownKeyDown, tabIndex: -1, children: [searchable && (jsx("input", { ref: searchRef, type: "text", placeholder: searchPlaceholder, value: searchQuery, onChange: e => {
|
|
263
294
|
setSearchQuery(e.target.value);
|
|
264
295
|
}, onKeyDown: handleDropdownKeyDown, className: searchInputStyle, "aria-label": "Search options" })), jsx(ScrollArea, { className: optionsListStyle, maxHeight: maxDropdownHeight - (searchable ? 32 : 0), scrollbarWidth: 4, scrollbarVisibility: "auto", hideDelay: 600, children: renderOptionsList() })] }), document.body), showHelperText && (jsx(FormHelperText, { id: helperId, error: error, children: showHelperText }))] }));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MultiSelect.js","sources":["../../../../../../src/components/controls/MultiSelect/MultiSelect.tsx"],"sourcesContent":[null],"names":[],"mappings":";;;;;;;;;;;;;;;;AA+CA;AAGE;AACF;AAEA;;AAIE;AACE;;;;AAGE;;;AAGJ;AACF;AAEA;AAIE;;AAEF;AAEA;AACE;AACA;AACA;;AAGF;;;;;;;;;;;;;;;;;;;;;AAqBG;AACG;AAiCJ;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAIA;;;;AAIE;AACD;;;;AAMD;AAEA;AACA;AACE;AAAiC;AACjC;;AAGF;;AAII;;;;AAGE;;;;;;;;AASN;AACE;;;;AAGA;AACF;AAEA;;;AAGE;AACA;AACF;;AAGE;;;AAGE;AACA;AAAmB;;AAErB;AACD;AAED;AACE;AACA;;AACA;;AAEA;AAGA;;AAGI;AAEJ;;AAEE;AACA;AACE;;AAEI;AACD;AACH;AACH;AACH;;AAGE;;AACA;;;AAGI;AACF;;;;;AAMF;;AACA;AACE;;;AAKE;;AAEJ;AACA;AACA;AACE;AACF;AACF;AAEA;AAEI;;AACA;AACE;AACA;AACA;AACA;;AAEE;AAAa;;;;AAOrB;AAEI;AACF;AAIF;AAEI;AACF;AAIF;;;AAIE;AAIF;AACA;AAEA;AAGO;AACA;AAIP;AACA;AAEI;;;;;AAMF;AACE;;;AAIF;;;AAGI;AACE;;AAEA;;;;;AAgBF;AACF;;AAGF;AACF;AAEA;;AAEE;AACA;AACA;AAEA;;AAUM;AACA;AACA;AACD;;AAGG;AACA;AAAmB;;AAEvB;AAEE;AAA0B;;AAalC;;AAIA;;;AA+BQ;;;AAGA;;;AA8Bc;;AAmChB;AAmBU;AACF;AA0BhB;AAEA;;"}
|
|
1
|
+
{"version":3,"file":"MultiSelect.js","sources":["src/components/controls/MultiSelect/MultiSelect.tsx"],"sourcesContent":["'use client';\n\nimport React, {\n useCallback,\n useDeferredValue,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { createPortal } from 'react-dom';\n\nimport { FormHelperText } from '@/components/form/FormHelperText';\nimport { FormLabel } from '@/components/form/FormLabel';\nimport { CheckIcon } from '@/components/Icons/CheckIcon';\nimport { ChevronDownIcon } from '@/components/Icons/ChevronDownIcon';\nimport { CloseIcon } from '@/components/Icons/CloseIcon';\nimport { ScrollArea } from '@/components/layout/ScrollArea';\nimport { useControlledState } from '@/hooks/useControlledState';\nimport { useListboxNav } from '@/hooks/useListboxNav';\nimport { useMergedRef } from '@/hooks/useMergedRef';\nimport { cx } from '@/utils/cx';\n\nimport {\n checkboxRecipe,\n chevronRecipe,\n chipLabelStyle,\n chipRecipe,\n chipRemoveStyle,\n clearButtonStyle,\n containerStyle,\n dropdownStyle,\n emptyMessageStyle,\n groupLabelStyle,\n moreBadgeStyle,\n optionItemRecipe,\n optionsListStyle,\n placeholderStyle,\n searchInputStyle,\n triggerContentStyle,\n triggerRecipe,\n} from './MultiSelect.css';\n\nimport type {\n MultiSelectOption,\n MultiSelectOptionGroup,\n MultiSelectProps,\n MultiSelectSize,\n} from './MultiSelect.types';\n\nfunction isOptionGroup<T extends string>(\n item: MultiSelectOption<T> | MultiSelectOptionGroup<T>\n): item is MultiSelectOptionGroup<T> {\n return 'options' in item && Array.isArray(item.options);\n}\n\nfunction flattenOptions<T extends string>(\n items: Array<MultiSelectOption<T> | MultiSelectOptionGroup<T>>\n): MultiSelectOption<T>[] {\n const result: MultiSelectOption<T>[] = [];\n for (const item of items) {\n if (isOptionGroup(item)) {\n result.push(...item.options);\n } else {\n result.push(item);\n }\n }\n return result;\n}\n\nfunction defaultFilter<T extends string>(\n option: MultiSelectOption<T>,\n query: string\n): boolean {\n const label = (option.label ?? option.value).toLowerCase();\n return label.includes(query.toLowerCase());\n}\n\nconst CHEVRON_SIZES: Record<MultiSelectSize, number> = {\n sm: 10,\n md: 12,\n lg: 14,\n};\n\n/**\n * Multi-value select that lets the user pick several options from a dropdown.\n * Selected values are rendered as inline chips inside the trigger; the rest\n * collapse into a \"+N more\" badge.\n *\n * Supports searchable mode, grouped options, controlled and uncontrolled\n * modes, keyboard navigation, an optional `max` cap, and a clear-all button.\n *\n * @example\n * ```tsx\n * <MultiSelect\n * label=\"Tags\"\n * options={[\n * { value: 'react', label: 'React' },\n * { value: 'vue', label: 'Vue' },\n * { value: 'svelte', label: 'Svelte' },\n * ]}\n * value={tags}\n * onChange={setTags}\n * />\n * ```\n */\nexport function MultiSelect<T extends string = string>({\n value,\n defaultValue,\n options,\n placeholder = 'Select...',\n searchable = false,\n searchPlaceholder = 'Search...',\n filterFn,\n emptyMessage = 'No results found',\n max,\n maxInlineChips = 3,\n size = 'md',\n variant = 'default',\n label,\n helperText,\n error = false,\n errorMessage,\n disabled = false,\n required = false,\n clearable = false,\n maxDropdownHeight = 240,\n minDropdownWidth,\n closeOnSelect = false,\n name,\n onChange,\n onOpenChange,\n className,\n style,\n testId,\n ref,\n id: idProp,\n ...rest\n}: MultiSelectProps<T>) {\n const autoId = useId();\n const fieldId = idProp ?? autoId;\n const labelId = `${fieldId}-label`;\n const helperId = `${fieldId}-helper`;\n const listboxId = `${fieldId}-listbox`;\n\n const triggerRef = useRef<HTMLButtonElement | null>(null);\n const dropdownRef = useRef<HTMLDivElement | null>(null);\n const searchRef = useRef<HTMLInputElement | null>(null);\n\n const setTriggerRef = useMergedRef(triggerRef, ref);\n\n const [selected, setSelected] = useControlledState<T[]>({\n value,\n defaultValue,\n onChange,\n fallback: [],\n });\n\n const [isOpen, setIsOpen] = useState(false);\n const [searchQuery, setSearchQuery] = useState('');\n const [dropdownPos, setDropdownPos] = useState<React.CSSProperties>({});\n\n const allOptions = useMemo(() => flattenOptions(options), [options]);\n\n // Defer the query that drives filtering so the search input stays\n // responsive while a large option list re-filters. The immediate\n // `searchQuery` still drives the input value.\n const deferredSearchQuery = useDeferredValue(searchQuery);\n\n const filter = filterFn ?? defaultFilter;\n const filteredOptions = useMemo(() => {\n if (!searchable || !deferredSearchQuery) return allOptions;\n return allOptions.filter(opt => filter(opt, deferredSearchQuery));\n }, [allOptions, searchable, deferredSearchQuery, filter]);\n\n const selectedSet = useMemo(() => new Set(selected), [selected]);\n\n const toggleValue = useCallback(\n (val: T, isDisabled: boolean) => {\n if (isDisabled) return;\n const isSelected = selectedSet.has(val);\n if (isSelected) {\n setSelected(selected.filter(v => v !== val));\n } else {\n if (typeof max === 'number' && selected.length >= max) return;\n setSelected([...selected, val]);\n }\n },\n [max, selected, selectedSet, setSelected]\n );\n\n const open = useCallback(() => {\n if (disabled) return;\n setIsOpen(true);\n setSearchQuery('');\n onOpenChange?.(true);\n }, [disabled, onOpenChange]);\n\n const close = useCallback(() => {\n setIsOpen(false);\n setSearchQuery('');\n onOpenChange?.(false);\n triggerRef.current?.focus();\n }, [onOpenChange]);\n\n const listbox = useListboxNav({\n items: filteredOptions,\n isItemDisabled: opt => Boolean(opt.disabled),\n onSelect: opt => {\n toggleValue(opt.value, Boolean(opt.disabled));\n if (closeOnSelect) close();\n },\n onEscape: close,\n });\n\n const updateDropdownPosition = useCallback(() => {\n const trigger = triggerRef.current;\n if (!trigger) return;\n const rect = trigger.getBoundingClientRect();\n const spaceBelow = window.innerHeight - rect.bottom;\n const openAbove =\n spaceBelow < maxDropdownHeight + 8 && rect.top > spaceBelow;\n\n const dropdownW =\n minDropdownWidth !== undefined\n ? Math.max(rect.width, minDropdownWidth)\n : rect.width;\n\n setDropdownPos({\n left: rect.left,\n width: dropdownW,\n ...(openAbove\n ? {\n bottom: window.innerHeight - rect.top + 4,\n transformOrigin: 'bottom',\n }\n : { top: rect.bottom + 4, transformOrigin: 'top' }),\n });\n }, [maxDropdownHeight, minDropdownWidth]);\n\n useEffect(() => {\n if (!isOpen) return;\n updateDropdownPosition();\n if (searchable) {\n requestAnimationFrame(() => {\n searchRef.current?.focus();\n });\n }\n }, [isOpen, updateDropdownPosition, searchable]);\n\n // Outside click handling\n useEffect(() => {\n if (!isOpen) return;\n const handleClick = (e: MouseEvent) => {\n const target = e.target as Node;\n if (\n !triggerRef.current?.contains(target) &&\n !dropdownRef.current?.contains(target)\n ) {\n close();\n }\n };\n document.addEventListener('mousedown', handleClick);\n return () => {\n document.removeEventListener('mousedown', handleClick);\n };\n }, [isOpen, close]);\n\n const handleTriggerKeyDown = useCallback(\n (e: React.KeyboardEvent) => {\n if (disabled) return;\n switch (e.key) {\n case 'Enter':\n case ' ':\n case 'ArrowDown':\n case 'ArrowUp':\n e.preventDefault();\n if (!isOpen) open();\n break;\n }\n },\n [disabled, isOpen, open]\n );\n\n const handleDropdownKeyDown = useCallback(\n (e: React.KeyboardEvent) => {\n listbox.handleKeyDown(e);\n },\n [listbox]\n );\n\n const removeValue = useCallback(\n (val: T) => {\n setSelected(selected.filter(v => v !== val));\n },\n [selected, setSelected]\n );\n\n const handleClearAll = useCallback(\n (e: React.MouseEvent) => {\n e.stopPropagation();\n setSelected([]);\n },\n [setSelected]\n );\n\n const showHelperText = error && errorMessage ? errorMessage : helperText;\n const chevronSize = CHEVRON_SIZES[size];\n\n const selectedOptions = useMemo(\n () =>\n selected\n .map(v => allOptions.find(o => o.value === v))\n .filter((o): o is MultiSelectOption<T> => Boolean(o)),\n [allOptions, selected]\n );\n\n const isEmpty = selected.length === 0;\n const visibleChips =\n maxInlineChips === Infinity\n ? selectedOptions\n : selectedOptions.slice(0, maxInlineChips);\n const overflowCount = selectedOptions.length - visibleChips.length;\n\n // Render options\n const renderOptionsList = () => {\n if (filteredOptions.length === 0) {\n return <div className={emptyMessageStyle}>{emptyMessage}</div>;\n }\n\n const hasGroups = options.some(isOptionGroup);\n // Mirror the deferred query used by `filteredOptions` so the grouped /\n // flat switch stays consistent with the list being rendered.\n if (hasGroups && !deferredSearchQuery) {\n let flatIndex = 0;\n return options.map((item, groupIdx) => {\n if (isOptionGroup(item)) {\n const groupOptions = item.options;\n const startIdx = flatIndex;\n flatIndex += groupOptions.length;\n return (\n <div\n key={`group-${String(groupIdx)}`}\n role=\"group\"\n aria-label={item.label}\n >\n <div className={groupLabelStyle}>{item.label}</div>\n {groupOptions.map((opt, optIdx) =>\n renderOptionItem(opt, startIdx + optIdx)\n )}\n </div>\n );\n }\n const idx = flatIndex;\n flatIndex += 1;\n return renderOptionItem(item, idx);\n });\n }\n\n return filteredOptions.map((opt, idx) => renderOptionItem(opt, idx));\n };\n\n const renderOptionItem = (opt: MultiSelectOption<T>, index: number) => {\n const isSelected = selectedSet.has(opt.value);\n const isActive = index === listbox.activeIndex;\n const isDisabled = opt.disabled ?? false;\n const reachedMax =\n typeof max === 'number' && !isSelected && selected.length >= max;\n const interactiveDisabled = isDisabled || reachedMax;\n\n const selectOption = () => {\n if (!interactiveDisabled) {\n toggleValue(opt.value, false);\n if (closeOnSelect) close();\n }\n };\n\n return (\n <div\n key={opt.value}\n role=\"option\"\n tabIndex={-1}\n aria-selected={isSelected}\n aria-disabled={interactiveDisabled || undefined}\n id={`${listboxId}-${opt.value}`}\n className={optionItemRecipe({\n active: isActive,\n selected: isSelected,\n disabled: interactiveDisabled,\n })}\n onClick={selectOption}\n onKeyDown={e => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n selectOption();\n }\n }}\n onMouseEnter={() => {\n if (!interactiveDisabled) listbox.setActiveIndex(index);\n }}\n >\n <span\n className={checkboxRecipe({ selected: isSelected })}\n aria-hidden=\"true\"\n >\n {isSelected && <CheckIcon size={10} decorative />}\n </span>\n {opt.icon && <span>{opt.icon}</span>}\n <span>{opt.label ?? opt.value}</span>\n </div>\n );\n };\n\n const formValue = selected.join(',');\n\n return (\n <div className={cx(containerStyle, className)} style={style}>\n {label && (\n <FormLabel\n id={labelId}\n htmlFor={fieldId}\n required={required}\n disabled={disabled}\n >\n {label}\n </FormLabel>\n )}\n\n <button\n ref={setTriggerRef}\n id={fieldId}\n type=\"button\"\n role=\"combobox\"\n aria-haspopup=\"listbox\"\n aria-expanded={isOpen}\n aria-controls={isOpen ? listboxId : undefined}\n aria-labelledby={label ? labelId : undefined}\n aria-required={required || undefined}\n aria-invalid={error || undefined}\n aria-describedby={showHelperText ? helperId : undefined}\n disabled={disabled}\n onClick={() => {\n if (isOpen) {\n close();\n } else {\n open();\n }\n }}\n onKeyDown={handleTriggerKeyDown}\n className={triggerRecipe({\n size,\n variant,\n open: isOpen,\n disabled,\n error,\n empty: isEmpty,\n })}\n data-testid={testId}\n {...rest}\n >\n <span className={triggerContentStyle}>\n {isEmpty ? (\n <span className={placeholderStyle}>{placeholder}</span>\n ) : (\n <>\n {visibleChips.map(opt => (\n <span\n key={opt.value}\n className={chipRecipe({ size })}\n data-testid={\n testId ? `${testId}-chip-${opt.value}` : undefined\n }\n >\n {opt.icon && <span>{opt.icon}</span>}\n <span className={chipLabelStyle}>\n {opt.label ?? opt.value}\n </span>\n {!disabled && (\n <span\n role=\"button\"\n tabIndex={-1}\n aria-label={`Remove ${opt.label ?? opt.value}`}\n className={chipRemoveStyle}\n onClick={e => {\n e.stopPropagation();\n removeValue(opt.value);\n }}\n onKeyDown={e => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n e.stopPropagation();\n removeValue(opt.value);\n }\n }}\n >\n <CloseIcon size=\"sm\" decorative />\n </span>\n )}\n </span>\n ))}\n {overflowCount > 0 && (\n <span className={moreBadgeStyle}>+{overflowCount} more</span>\n )}\n </>\n )}\n </span>\n\n {clearable && !isEmpty && !disabled && (\n <span\n role=\"button\"\n tabIndex={-1}\n aria-label=\"Clear selection\"\n className={clearButtonStyle}\n onClick={handleClearAll}\n onKeyDown={e => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n e.stopPropagation();\n setSelected([]);\n }\n }}\n >\n <CloseIcon size=\"sm\" decorative />\n </span>\n )}\n\n <span className={chevronRecipe({ open: isOpen })}>\n <ChevronDownIcon size={chevronSize} decorative />\n </span>\n </button>\n\n {name && <input type=\"hidden\" name={name} value={formValue} />}\n\n {isOpen &&\n createPortal(\n <div\n ref={dropdownRef}\n role=\"listbox\"\n id={listboxId}\n aria-multiselectable=\"true\"\n aria-labelledby={label ? labelId : undefined}\n className={dropdownStyle}\n style={dropdownPos}\n onKeyDown={handleDropdownKeyDown}\n tabIndex={-1}\n >\n {searchable && (\n <input\n ref={searchRef}\n type=\"text\"\n placeholder={searchPlaceholder}\n value={searchQuery}\n onChange={e => {\n setSearchQuery(e.target.value);\n }}\n onKeyDown={handleDropdownKeyDown}\n className={searchInputStyle}\n aria-label=\"Search options\"\n />\n )}\n <ScrollArea\n className={optionsListStyle}\n maxHeight={maxDropdownHeight - (searchable ? 32 : 0)}\n scrollbarWidth={4}\n scrollbarVisibility=\"auto\"\n hideDelay={600}\n >\n {renderOptionsList()}\n </ScrollArea>\n </div>,\n document.body\n )}\n\n {showHelperText && (\n <FormHelperText id={helperId} error={error}>\n {showHelperText}\n </FormHelperText>\n )}\n </div>\n );\n}\n\nMultiSelect.displayName = 'MultiSelect';\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAmDA;AAGE;AACF;AAEA;;AAIE;AACE;;;;AAGE;;;AAGJ;AACF;AAEA;AAIE;;AAEF;AAEA;AACE;AACA;AACA;;AAGF;;;;;;;;;;;;;;;;;;;;;AAqBG;AACG;AAiCJ;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAIA;;;;AAIE;AACD;;;;AAMD;;;;AAKA;AAEA;AACA;AACE;AAAyC;AACzC;;AAGF;;AAII;;;;AAGE;;;;;;;;AASN;AACE;;;;AAGA;AACF;AAEA;;;AAGE;AACA;AACF;;AAGE;;;AAGE;AACA;AAAmB;;AAErB;AACD;AAED;AACE;AACA;;AACA;;AAEA;AAGA;;AAGI;AAEJ;;AAEE;AACA;AACE;;AAEI;AACD;AACH;AACH;AACH;;AAGE;;AACA;;;AAGI;AACF;;;;;AAMF;;AACA;AACE;;;AAKE;;AAEJ;AACA;AACA;AACE;AACF;AACF;AAEA;AAEI;;AACA;AACE;AACA;AACA;AACA;;AAEE;AAAa;;;;AAOrB;AAEI;AACF;AAIF;AAEI;AACF;AAIF;;;AAIE;AAIF;AACA;AAEA;AAGO;AACA;AAIP;AACA;AAEI;;;;;AAMF;AACE;;;;;AAMF;;;AAGI;AACE;;AAEA;;;;;AAgBF;AACF;;AAGF;AACF;AAEA;;AAEE;AACA;AACA;AAEA;;;AAII;AACA;AAAmB;;AAEvB;AAEA;AASM;AACA;AACA;;AAIA;;AAEE;;AAEJ;AAEE;AAA0B;;AAalC;;;;AAgCU;;;AAEA;;;;;AAOF;;;AAGA;;;AA8Bc;AACF;AAEE;;;AAGE;;AAEJ;AAsBR;;;;;;AAmBN;AAmBU;AACF;AA0BhB;AAEA;;"}
|
|
@@ -3,9 +3,9 @@ import { jsxs, jsx } from 'react/jsx-runtime';
|
|
|
3
3
|
import React, { useRef, useEffect } from 'react';
|
|
4
4
|
import { inputWrapperRecipe } from '../../form/InputWrapper.css.js';
|
|
5
5
|
import { ChevronDownIcon } from '../../Icons/ChevronDownIcon.js';
|
|
6
|
-
import { useNumberInput } from './useNumberInput.js';
|
|
7
6
|
import { cx } from '../../../utils/cx.js';
|
|
8
7
|
import { labelRecipe, stepButtonIconLeftStyle, stepButtonRecipe, inputRecipe, unitLabelStyle, valueDisplayRecipe, stepButtonIconRightStyle, helperTextRecipe, containerDraggingStyle, containerHoveredStyle, numberInputContainerStyle } from './NumberInput.css.js';
|
|
8
|
+
import { useNumberInput } from './useNumberInput.js';
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* A specialized number input component with Blender-like functionality.
|
|
@@ -82,9 +82,9 @@ size = 'md', disabled = false, readOnly = false, errorMessage, placeholder, labe
|
|
|
82
82
|
...(precisionStep !== undefined && { precisionStep }),
|
|
83
83
|
...(largeStep !== undefined && { largeStep }),
|
|
84
84
|
...(precision !== undefined && { precision }),
|
|
85
|
-
|
|
85
|
+
allowExpressions,
|
|
86
86
|
disabled: disabled || readOnly,
|
|
87
|
-
|
|
87
|
+
dragSensitivity,
|
|
88
88
|
...(formatValue !== undefined && { formatValue }),
|
|
89
89
|
...(parseValue !== undefined && { parseValue }),
|
|
90
90
|
});
|
|
@@ -204,7 +204,14 @@ size = 'md', disabled = false, readOnly = false, errorMessage, placeholder, labe
|
|
|
204
204
|
// Format display value
|
|
205
205
|
const formattedValue = formatValue ? formatValue(value) : displayValue;
|
|
206
206
|
const inputId = React.useId();
|
|
207
|
-
return (
|
|
207
|
+
return (
|
|
208
|
+
// The mouse handlers below implement a pointer-only drag-to-scrub
|
|
209
|
+
// enhancement on this presentational wrapper. Keyboard accessibility is
|
|
210
|
+
// provided by the inner native <input> (handleInputKeyDown) and the
|
|
211
|
+
// native step <button>s, so no role/keyboard handlers belong on the
|
|
212
|
+
// wrapper itself.
|
|
213
|
+
// eslint-disable-next-line jsx-a11y/no-static-element-interactions
|
|
214
|
+
jsxs("div", { ref: containerRef, className: cx(numberInputContainerStyle, isHovered && !disabled && !isDragging && containerHoveredStyle, isDragging && containerDraggingStyle, className), onMouseEnter: handleContainerMouseEnter, onMouseLeave: handleContainerMouseLeave, onMouseDown: handleMouseDown, onMouseMove: handleMouseMove, onMouseUp: handleMouseUp, "data-testid": testId, children: [label && (jsxs("label", { htmlFor: inputId, className: labelRecipe({ disabled }), children: [label, required && jsx("span", { style: { color: 'var(--accent-error)' }, children: " *" })] })), jsxs("div", { className: inputWrapperRecipe({
|
|
208
215
|
size,
|
|
209
216
|
error: effectiveError,
|
|
210
217
|
disabled,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NumberInput.js","sources":["../../../../../../src/components/controls/NumberInput/NumberInput.tsx"],"sourcesContent":[null],"names":[],"mappings":";;;;;;;;;AAyIA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2CG;AACI;AACL;AACA;AAGA;AACA;AAMA;AACA;AAWA;AACA;AAgBA;AACA;AACA;;AAGA;AAKA;AACA;;;;AAKC;AAED;;;;;;;;;;;;;;;;AAgCC;;AAGD;AACA;AACA;;AAGA;;;;AAIE;;;AAGA;;;;;;AAME;AACA;;;AAIJ;AAEA;AACE;;AAEA;;;AAKE;;AAGA;AACA;;AAEJ;;AAGE;;AAEA;AACA;;AAIA;AACE;;;AAGE;;;AAIJ;AACF;;;AAIE;;AAEA;AACE;AACF;;AAGE;AACA;AACF;AAEA;AACA;AAEA;AACE;AACA;AACF;;;AAIF;AACE;AACF;AAEA;;AAEE;AACF;AAEA;;AAEE;AACA;AACF;AAEA;;AAEE;AACF;;;AAIE;;;AAGF;;;;AAKE;AACF;;AAGA;;;AAGE;;AAEF;AAEA;;;AAGE;;AAEF;;AAGA;;AAIA;;AAGA;AAEA;AAEA;;AA0BQ;;;AAGD;AAOK;;AAEA;;;;;;AAwCA;AACD;AAcC;;AAEA;AACD;AAgBD;AACA;AAKZ;AAEA;;"}
|
|
1
|
+
{"version":3,"file":"NumberInput.js","sources":["src/components/controls/NumberInput/NumberInput.tsx"],"sourcesContent":["'use client';\n\n// src/controls/NumberInput/NumberInput.tsx\nimport React, { useRef, useEffect } from 'react';\n\nimport { inputWrapperRecipe } from '@/components/form/InputWrapper.css';\nimport { ChevronDownIcon } from '@/components/Icons/ChevronDownIcon';\nimport { cx } from '@/utils/cx';\n\nimport {\n numberInputContainerStyle,\n containerHoveredStyle,\n containerDraggingStyle,\n labelRecipe,\n stepButtonRecipe,\n stepButtonIconLeftStyle,\n stepButtonIconRightStyle,\n inputRecipe,\n valueDisplayRecipe,\n unitLabelStyle,\n helperTextRecipe,\n} from './NumberInput.css';\nimport { useNumberInput } from './useNumberInput';\n\nimport type { UseNumberInputOptions } from './useNumberInput';\nimport type { BaseComponent, Size } from '@/types/common';\nimport type { Prettify } from '@/types/utilities';\n\n/**\n * Props specific to NumberInput component\n */\nexport interface NumberInputBaseProps\n extends Omit<BaseComponent, 'onChange'>, UseNumberInputOptions {\n /**\n * Current numeric value\n */\n value: number;\n\n /**\n * Callback when value changes\n */\n onChange: (value: number) => void;\n\n /**\n * Unit suffix to display (e.g., \"px\", \"%\", \"°\")\n * Can be a string or a function that formats the value\n */\n unit?: string | ((value: number) => string);\n\n /**\n * Whether to show increment/decrement buttons on hover\n * @default true\n */\n showStepButtons?: boolean;\n\n /**\n * Custom validation function\n * Return error message string if invalid, undefined if valid\n */\n validate?: (value: number) => string | undefined;\n\n /**\n * Format value for display (e.g., add commas, currency symbols)\n * This is separate from unit display\n */\n formatValue?: (value: number) => string;\n\n /**\n * Parse custom formatted input back to number\n * Used in conjunction with formatValue\n */\n parseValue?: (input: string) => number | null;\n\n /**\n * Input size using standard library sizing\n * - `sm`: 20px height, compact for toolbars\n * - `md`: 24px height, standard for forms\n * - `lg`: 32px height, prominent inputs\n * @default \"md\"\n */\n size?: Size;\n\n /**\n * Whether the input is disabled\n * @default false\n */\n disabled?: boolean;\n\n /**\n * Whether the input is read-only\n * @default false\n */\n readOnly?: boolean;\n\n /**\n * Error message displayed when error is true\n */\n errorMessage?: string | undefined;\n\n /**\n * Placeholder text\n */\n placeholder?: string | undefined;\n\n /**\n * Input label\n */\n label?: string | undefined;\n\n /**\n * Helper text displayed below the input\n */\n helperText?: string | undefined;\n\n /**\n * Whether the input is required\n * @default false\n */\n required?: boolean;\n\n /**\n * Focus event handler\n */\n onFocus?: (event: React.FocusEvent<HTMLInputElement>) => void;\n\n /**\n * Blur event handler\n */\n onBlur?: (event: React.FocusEvent<HTMLInputElement>) => void;\n\n /**\n * Key down event handler\n */\n onKeyDown?: (event: React.KeyboardEvent<HTMLInputElement>) => void;\n}\n\n/**\n * Props for the NumberInput component with prettified type for better IntelliSense\n */\nexport type NumberInputProps = Prettify<NumberInputBaseProps>;\n\n/**\n * A specialized number input component with Blender-like functionality.\n *\n * Features:\n * - Drag left/right to change values with mouse\n * - Click chevron buttons to increment/decrement\n * - Keyboard input with mathematical expression support\n * - Modifier keys: Ctrl (large steps), Shift (precision), Minus (negate)\n * - Soft and hard value limits\n * - Unit display support\n * - Expression evaluation (pi, sqrt, sin, etc.)\n * - Custom validation and formatting\n * - Centered value and unit display\n *\n * @example\n * ```tsx\n * // Basic number input\n * <NumberInput\n * value={rotation}\n * onChange={setRotation}\n * unit=\"°\"\n * step={1}\n * />\n *\n * // With limits and precision\n * <NumberInput\n * value={opacity}\n * onChange={setOpacity}\n * min={0}\n * max={1}\n * step={0.1}\n * precision={2}\n * unit=\"%\"\n * />\n *\n * // With custom validation\n * <NumberInput\n * value={dimension}\n * onChange={setDimension}\n * validate={(v) => v <= 0 ? \"Must be positive\" : undefined}\n * placeholder=\"Enter value or expression\"\n * />\n * ```\n */\nexport const NumberInput = ({\n // Value props\n value,\n onChange,\n\n // NumberInput specific props\n unit,\n showStepButtons = true,\n validate,\n formatValue,\n parseValue,\n\n // UseNumberInput options\n min,\n max,\n softMin,\n softMax,\n step,\n precisionStep,\n largeStep,\n precision,\n allowExpressions = true,\n dragSensitivity = 1,\n\n // Input props\n size = 'md',\n disabled = false,\n readOnly = false,\n errorMessage,\n placeholder,\n label,\n helperText,\n required = false,\n className,\n testId,\n onFocus,\n onBlur,\n onKeyDown,\n ref,\n ...props\n}: NumberInputProps) => {\n const containerRef = useRef<HTMLDivElement>(null);\n const inputRef = useRef<HTMLInputElement>(null);\n const [focused, setFocused] = React.useState(false);\n\n // Track mouse state for distinguishing click vs drag\n const mouseDownRef = useRef<{\n x: number;\n time: number;\n hasMoved: boolean;\n } | null>(null);\n const dragThreshold = 3; // pixels\n const clickTimeThreshold = 200; // milliseconds\n\n // Combine refs\n React.useImperativeHandle(ref, () => inputRef.current as HTMLInputElement, [\n inputRef,\n ]);\n\n const {\n displayValue,\n isEditing,\n isDragging,\n isHovered,\n error: internalError,\n increment,\n decrement,\n startDrag,\n updateDrag,\n endDrag,\n startEditing,\n endEditing,\n updateDisplayValue,\n setHovered,\n handleKeyDown,\n } = useNumberInput({\n value,\n onChange,\n ...(min !== undefined && { min }),\n ...(max !== undefined && { max }),\n ...(softMin !== undefined && { softMin }),\n ...(softMax !== undefined && { softMax }),\n ...(step !== undefined && { step }),\n ...(precisionStep !== undefined && { precisionStep }),\n ...(largeStep !== undefined && { largeStep }),\n ...(precision !== undefined && { precision }),\n allowExpressions,\n disabled: disabled || readOnly,\n dragSensitivity,\n ...(formatValue !== undefined && { formatValue }),\n ...(parseValue !== undefined && { parseValue }),\n });\n\n // External validation\n const externalError = validate ? validate(value) : undefined;\n const effectiveError = !!errorMessage || !!externalError || !!internalError;\n const effectiveErrorMessage = errorMessage ?? externalError ?? internalError;\n\n // Mouse event handlers for dragging vs clicking\n const handleMouseDown = (event: React.MouseEvent) => {\n if (disabled || readOnly) return;\n\n // Only handle left mouse button\n if (event.button !== 0) return;\n\n // Don't handle if clicking on step buttons\n const target = event.target as HTMLElement;\n if (target.tagName === 'BUTTON' || target.closest('button')) return;\n\n // Record mouse down state\n mouseDownRef.current = {\n x: event.clientX,\n time: Date.now(),\n hasMoved: false,\n };\n\n event.preventDefault();\n };\n\n const handleMouseMove = (event: React.MouseEvent) => {\n if (!mouseDownRef.current || disabled || readOnly) return;\n\n const deltaX = Math.abs(event.clientX - mouseDownRef.current.x);\n\n // If we've moved beyond threshold and not already dragging, start drag\n if (\n deltaX > dragThreshold &&\n !mouseDownRef.current.hasMoved &&\n !isDragging\n ) {\n mouseDownRef.current.hasMoved = true;\n startDrag(mouseDownRef.current.x);\n }\n };\n\n const handleMouseUp = () => {\n if (!mouseDownRef.current || disabled || readOnly) return;\n\n const timeDelta = Date.now() - mouseDownRef.current.time;\n const wasClick =\n !mouseDownRef.current.hasMoved && timeDelta < clickTimeThreshold;\n\n // If it was a click (not drag), start editing\n if (wasClick && !isEditing) {\n startEditing();\n // Focus the input after a short delay to ensure it's visible\n setTimeout(() => {\n inputRef.current?.focus();\n }, 0);\n }\n\n mouseDownRef.current = null;\n };\n\n // Global mouse move and up handlers for dragging\n useEffect(() => {\n if (!isDragging) return;\n\n const handleGlobalMouseMove = (event: MouseEvent) => {\n updateDrag(event.clientX);\n };\n\n const handleGlobalMouseUp = () => {\n endDrag();\n mouseDownRef.current = null;\n };\n\n document.addEventListener('mousemove', handleGlobalMouseMove);\n document.addEventListener('mouseup', handleGlobalMouseUp);\n\n return () => {\n document.removeEventListener('mousemove', handleGlobalMouseMove);\n document.removeEventListener('mouseup', handleGlobalMouseUp);\n };\n }, [isDragging, updateDrag, endDrag]);\n\n // Input event handlers\n const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {\n updateDisplayValue(event.target.value);\n };\n\n const handleInputFocus = (event: React.FocusEvent<HTMLInputElement>) => {\n setFocused(true);\n onFocus?.(event);\n };\n\n const handleInputBlur = (event: React.FocusEvent<HTMLInputElement>) => {\n setFocused(false);\n endEditing();\n onBlur?.(event);\n };\n\n const handleInputKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {\n handleKeyDown(event);\n onKeyDown?.(event);\n };\n\n // Container event handlers\n const handleContainerMouseEnter = () => {\n if (!disabled && !readOnly) {\n setHovered(true);\n }\n };\n\n const handleContainerMouseLeave = () => {\n setHovered(false);\n // Reset mouse state if leaving container\n mouseDownRef.current = null;\n };\n\n // Step button handlers\n const handleIncrementClick = (event: React.MouseEvent) => {\n event.preventDefault();\n event.stopPropagation();\n increment();\n // Don't focus input after step button click\n };\n\n const handleDecrementClick = (event: React.MouseEvent) => {\n event.preventDefault();\n event.stopPropagation();\n decrement();\n // Don't focus input after step button click\n };\n\n // Show step buttons when appropriate\n const shouldShowStepButtons =\n showStepButtons && isHovered && !isEditing && !disabled && !readOnly;\n\n // Format unit display\n const unitDisplay = typeof unit === 'function' ? unit(value) : unit;\n\n // Format display value\n const formattedValue = formatValue ? formatValue(value) : displayValue;\n\n const inputId = React.useId();\n\n return (\n // The mouse handlers below implement a pointer-only drag-to-scrub\n // enhancement on this presentational wrapper. Keyboard accessibility is\n // provided by the inner native <input> (handleInputKeyDown) and the\n // native step <button>s, so no role/keyboard handlers belong on the\n // wrapper itself.\n // eslint-disable-next-line jsx-a11y/no-static-element-interactions\n <div\n ref={containerRef}\n className={cx(\n numberInputContainerStyle,\n isHovered && !disabled && !isDragging && containerHoveredStyle,\n isDragging && containerDraggingStyle,\n className\n )}\n onMouseEnter={handleContainerMouseEnter}\n onMouseLeave={handleContainerMouseLeave}\n onMouseDown={handleMouseDown}\n onMouseMove={handleMouseMove}\n onMouseUp={handleMouseUp}\n data-testid={testId}\n >\n {label && (\n <label htmlFor={inputId} className={labelRecipe({ disabled })}>\n {label}\n {required && <span style={{ color: 'var(--accent-error)' }}> *</span>}\n </label>\n )}\n\n <div\n className={inputWrapperRecipe({\n size,\n error: effectiveError,\n disabled,\n focused,\n })}\n >\n {/* Decrement button */}\n {showStepButtons && (\n <button\n type=\"button\"\n className={stepButtonRecipe({\n position: 'left',\n size,\n visible: shouldShowStepButtons,\n })}\n onClick={handleDecrementClick}\n disabled={disabled}\n tabIndex={-1}\n aria-label=\"Decrement value\"\n >\n <span className={stepButtonIconLeftStyle}>\n <ChevronDownIcon size={size === 'sm' ? 'sm' : 'md'} />\n </span>\n </button>\n )}\n\n {/* Input field - hidden when not editing */}\n <input\n {...props}\n ref={inputRef}\n id={inputId}\n type=\"text\"\n value={isEditing ? displayValue : ''}\n onChange={handleInputChange}\n onFocus={handleInputFocus}\n onBlur={handleInputBlur}\n onKeyDown={handleInputKeyDown}\n placeholder={isEditing ? placeholder : ''}\n disabled={disabled}\n required={required}\n readOnly={readOnly}\n className={inputRecipe({ size })}\n style={{\n opacity: isEditing ? 1 : 0,\n pointerEvents: isEditing ? 'auto' : 'none',\n }}\n />\n\n {/* Value display - shown when not editing */}\n {!isEditing && (\n <div\n className={valueDisplayRecipe({\n size,\n hasStepButtons: shouldShowStepButtons,\n })}\n >\n <span>{formattedValue}</span>\n {unitDisplay && (\n <span className={unitLabelStyle}>{unitDisplay}</span>\n )}\n </div>\n )}\n\n {/* Increment button */}\n {showStepButtons && (\n <button\n type=\"button\"\n className={stepButtonRecipe({\n position: 'right',\n size,\n visible: shouldShowStepButtons,\n })}\n onClick={handleIncrementClick}\n disabled={disabled}\n tabIndex={-1}\n aria-label=\"Increment value\"\n >\n <span className={stepButtonIconRightStyle}>\n <ChevronDownIcon size={size === 'sm' ? 'sm' : 'md'} />\n </span>\n </button>\n )}\n </div>\n\n {(helperText ?? (effectiveError && effectiveErrorMessage)) && (\n <div className={helperTextRecipe({ error: effectiveError })}>\n {effectiveError && effectiveErrorMessage\n ? effectiveErrorMessage\n : helperText}\n </div>\n )}\n </div>\n );\n};\n\nNumberInput.displayName = 'NumberInput';\n"],"names":[],"mappings":";;;;;;;;;AA6IA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2CG;AACI;AACL;AACA;AAGA;AACA;AAMA;AACA;AAWA;AACA;AAgBA;AACA;AACA;;AAGA;AAKA;AACA;;;;AAKC;AAED;;;;;;;;;;;;;;;;AAgCC;;AAGD;AACA;AACA;;AAGA;;;;AAIE;;;AAGA;;;;;;AAME;AACA;;;AAIJ;AAEA;AACE;;AAEA;;;AAKE;;AAGA;AACA;;AAEJ;;AAGE;;AAEA;AACA;;AAIA;AACE;;;AAGE;;;AAIJ;AACF;;;AAIE;;AAEA;AACE;AACF;;AAGE;AACA;AACF;AAEA;AACA;AAEA;AACE;AACA;AACF;;;AAIF;AACE;AACF;AAEA;;AAEE;AACF;AAEA;;AAEE;AACA;AACF;AAEA;;AAEE;AACF;;;AAIE;;;AAGF;;;;AAKE;AACF;;AAGA;;;AAGE;;AAEF;AAEA;;;AAGE;;AAEF;;AAGA;;AAIA;;AAGA;AAEA;;;;;;;;AASE;;AAyBM;;;AAGD;AAOK;;AAEA;;;;;;AAwCA;AACD;AAcC;;AAEA;AACD;AAgBD;AACA;AAKZ;AAEA;;"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { useState, useRef, useEffect, useCallback } from 'react';
|
|
3
3
|
import { useKeyboardContext } from '../../../context/KeyboardContext.js';
|
|
4
|
-
import { parseNumericInput, isExpression } from '../../../utils/mathExpression.js';
|
|
5
4
|
import { devError } from '../../../utils/devWarn.js';
|
|
5
|
+
import { parseNumericInput, isExpression } from '../../../utils/mathExpression.js';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Clamps a value between min and max bounds
|
|
@@ -73,6 +73,10 @@ function useNumberInput({ value, onChange, min, max, softMin, softMax, step = 1,
|
|
|
73
73
|
const formatted = formatValue
|
|
74
74
|
? formatValue(value)
|
|
75
75
|
: defaultFormatValue(value, effectivePrecision);
|
|
76
|
+
// Sync the display string to the controlled `value` prop while the user
|
|
77
|
+
// is not actively editing/dragging — an external-value sync that the
|
|
78
|
+
// edit guard makes safe from cascading renders.
|
|
79
|
+
// eslint-disable-next-line react-hooks/set-state-in-effect
|
|
76
80
|
setDisplayValue(formatted);
|
|
77
81
|
setError(undefined);
|
|
78
82
|
}
|
|
@@ -223,7 +227,7 @@ function useNumberInput({ value, onChange, min, max, softMin, softMax, step = 1,
|
|
|
223
227
|
// Place cursor at end of input
|
|
224
228
|
setTimeout(() => {
|
|
225
229
|
const input = document.activeElement;
|
|
226
|
-
if (input) {
|
|
230
|
+
if (input instanceof HTMLInputElement) {
|
|
227
231
|
const len = input.value.length;
|
|
228
232
|
input.setSelectionRange(len, len);
|
|
229
233
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useNumberInput.js","sources":["../../../../../../src/components/controls/NumberInput/useNumberInput.ts"],"sourcesContent":[null],"names":[],"mappings":";;;;;;AA0MA;;AAEG;AACH;;AAEE;;AACA;;AACA;AACF;AAEA;;AAEG;AACH;;AAC+B;;;AAG/B;AAEA;;AAEG;AACH;AACE;AACE;;;AAGF;AACF;AAEA;;;;;;;;;;;;AAYG;;;AAmBD;AACA;AACA;;;AAKI;;;;;;;;AAUJ;AACA;AACA;;AAGA;;;AAIE;;AAEI;AACA;;;;AAIN;AAEA;;AAEG;AACH;;AACuB;AACrB;AAAyC;AACzC;AACF;AACE;AACA;AACA;;;;AAID;AAED;;AAEG;;AAGC;;;;;AAMA;AACA;;AAGA;;;AAGF;AAIF;;AAEG;AACH;AACE;;;AAGI;;;;AAIE;;;;;AAMF;;AAEE;;AAEA;;;AAEA;AACA;;;;AAKJ;AACA;;;AAGE;;;AAIF;;;;AAGA;AACA;;;;AAKJ;AACE;;AACA;AACA;;AAGF;AACE;;AACA;AACA;;AAGF;AAEI;;;AAEA;AACA;AACA;;AAEF;AAIF;;;AAII;AACA;;;AAIA;;;;;AAME;;;AAIA;;;AAIE;AACA;;;AAGN;;;;;;;;AASC;AAGH;;;;;AAKI;AACA;;;AAIJ;AACE;;;;;;AAKE;;AAEE;AACA;;;AAGN;AAEA;;;;;AAKA;AAEA;;;AAGI;AACA;;;;AAKJ;;;;;;AAOE;AAIF;AAEI;;AAEA;AACE;;;AAGI;;;AAEA;;;AAIJ;;;AAGI;;;AAIJ;;;AAGI;;;AAIJ;;;AAGI;;;AAIJ;;;;AAII;;;;AAIR;;;;;;;;;;AAWC;;;;;;;;;;;AAcD;;;;;;;;;;;AAYA;AACA;;;AAGJ;;"}
|
|
1
|
+
{"version":3,"file":"useNumberInput.js","sources":["src/components/controls/NumberInput/useNumberInput.ts"],"sourcesContent":["'use client';\n\nimport React, { useState, useCallback, useRef, useEffect } from 'react';\n\nimport { useKeyboardContext } from '@/context/KeyboardContext';\nimport { devError } from '@/utils/devWarn';\nimport { parseNumericInput, isExpression } from '@/utils/mathExpression';\n\n/**\n * Configuration options for NumberInput behavior\n */\nexport interface UseNumberInputOptions {\n /**\n * Minimum allowed value (hard limit)\n */\n min?: number;\n\n /**\n * Maximum allowed value (hard limit)\n */\n max?: number;\n\n /**\n * Soft minimum for drag operations (can be overridden by keyboard)\n */\n softMin?: number;\n\n /**\n * Soft maximum for drag operations (can be overridden by keyboard)\n */\n softMax?: number;\n\n /**\n * Step size for increment/decrement operations\n * @default 1\n */\n step?: number;\n\n /**\n * Step size when Shift is held (precision mode)\n * @default step / 10\n */\n precisionStep?: number;\n\n /**\n * Step size when Ctrl is held (large steps)\n * @default step * 10\n */\n largeStep?: number;\n\n /**\n * Number of decimal places to round to\n * @default 2\n */\n precision?: number;\n\n /**\n * Whether to allow mathematical expressions\n * @default true\n */\n allowExpressions?: boolean;\n\n /**\n * Whether the input is disabled\n * @default false\n */\n disabled?: boolean;\n\n /**\n * Drag sensitivity multiplier\n * @default 1\n */\n dragSensitivity?: number;\n\n /**\n * Custom format function for display\n */\n formatValue?: (value: number) => string;\n\n /**\n * Custom parse function for input\n */\n parseValue?: (input: string) => number | null;\n}\n\n/**\n * Props for useNumberInput hook\n */\nexport interface UseNumberInputProps extends UseNumberInputOptions {\n /**\n * Current numeric value\n */\n value: number;\n\n /**\n * Callback when value changes\n */\n onChange: (value: number) => void;\n}\n\n/**\n * State returned by useNumberInput hook\n */\nexport interface UseNumberInputReturn {\n /**\n * Current display value (string representation)\n */\n displayValue: string;\n\n /**\n * Whether currently in text editing mode\n */\n isEditing: boolean;\n\n /**\n * Whether currently dragging\n */\n isDragging: boolean;\n\n /**\n * Whether mouse is hovering over the input\n */\n isHovered: boolean;\n\n /**\n * Whether input has focus\n */\n isFocused: boolean;\n\n /**\n * Current error message (if any)\n */\n error: string | undefined;\n\n /**\n * Whether the current input looks like an expression\n */\n isExpression: boolean;\n\n /**\n * Increment value by step amount\n */\n increment: () => void;\n\n /**\n * Decrement value by step amount\n */\n decrement: () => void;\n\n /**\n * Start dragging operation\n */\n startDrag: (clientX: number) => void;\n\n /**\n * Update drag operation\n */\n updateDrag: (clientX: number) => void;\n\n /**\n * End dragging operation\n */\n endDrag: () => void;\n\n /**\n * Start text editing mode\n */\n startEditing: () => void;\n\n /**\n * End text editing mode and apply value\n */\n endEditing: () => void;\n\n /**\n * Cancel text editing mode without applying\n */\n cancelEditing: () => void;\n\n /**\n * Update the display value (during text editing)\n */\n updateDisplayValue: (value: string) => void;\n\n /**\n * Handle hover state changes\n */\n setHovered: (hovered: boolean) => void;\n\n /**\n * Handle focus state changes\n */\n setFocused: (focused: boolean) => void;\n\n /**\n * Handle keyboard input\n */\n handleKeyDown: (event: React.KeyboardEvent) => void;\n}\n\n/**\n * Clamps a value between min and max bounds\n */\nfunction clamp(value: number, min?: number, max?: number): number {\n let result = value;\n if (min !== undefined && result < min) result = min;\n if (max !== undefined && result > max) result = max;\n return result;\n}\n\n/**\n * Rounds a number to specified decimal places\n */\nfunction roundToPrecision(value: number, precision?: number): number {\n if (precision === undefined) return value;\n const factor = Math.pow(10, precision);\n return Math.round(value * factor) / factor;\n}\n\n/**\n * Default formatter for display values\n */\nfunction defaultFormatValue(value: number, precision?: number): string {\n if (precision !== undefined) {\n return value.toFixed(precision);\n }\n // Remove trailing zeros and unnecessary decimal point\n return value.toString();\n}\n\n/**\n * Hook for managing NumberInput state and interactions.\n *\n * Provides Blender-like number input functionality including:\n * - Drag to change values\n * - Keyboard input with expression evaluation\n * - Modifier key support (Ctrl, Shift)\n * - Soft and hard limits\n * - Precision control\n *\n * @param props Configuration options\n * @returns Object with state and actions\n */\nexport function useNumberInput({\n value,\n onChange,\n min,\n max,\n softMin,\n softMax,\n step = 1,\n precisionStep,\n largeStep,\n precision,\n allowExpressions = true,\n disabled = false,\n dragSensitivity = 1,\n formatValue,\n parseValue,\n}: UseNumberInputProps): UseNumberInputReturn {\n // Calculate default step sizes if not provided\n const effectivePrecisionStep = precisionStep ?? step / 10;\n const effectiveLargeStep = largeStep ?? step * 10;\n const effectivePrecision = precision ?? 2;\n\n // State\n const [displayValue, setDisplayValue] = useState(() =>\n formatValue\n ? formatValue(value)\n : defaultFormatValue(value, effectivePrecision)\n );\n const [isEditing, setIsEditing] = useState(false);\n const [isDragging, setIsDragging] = useState(false);\n const [isHovered, setIsHovered] = useState(false);\n const [isFocused, setIsFocused] = useState(false);\n const [error, setError] = useState<string | undefined>();\n\n // Refs for drag state\n const dragStartX = useRef(0);\n const dragStartValue = useRef(0);\n const accumulatedDelta = useRef(0);\n\n // Keyboard state\n const { modifiers } = useKeyboardContext();\n\n // Update display value when external value changes (but not during editing)\n useEffect(() => {\n if (!isEditing && !isDragging) {\n const formatted = formatValue\n ? formatValue(value)\n : defaultFormatValue(value, effectivePrecision);\n // Sync the display string to the controlled `value` prop while the user\n // is not actively editing/dragging — an external-value sync that the\n // edit guard makes safe from cascading renders.\n // eslint-disable-next-line react-hooks/set-state-in-effect\n setDisplayValue(formatted);\n setError(undefined);\n }\n }, [value, effectivePrecision, isEditing, isDragging, formatValue]);\n\n /**\n * Gets the appropriate step size based on modifier keys\n */\n const getStepSize = useCallback((): number => {\n if (modifiers.shift) return effectivePrecisionStep;\n if (modifiers.control || modifiers.meta) return effectiveLargeStep;\n return step;\n }, [\n modifiers.shift,\n modifiers.control,\n modifiers.meta,\n effectivePrecisionStep,\n effectiveLargeStep,\n step,\n ]);\n\n /**\n * Applies a new value with proper bounds checking and rounding\n */\n const applyValue = useCallback(\n (newValue: number, useSoftLimits = false): void => {\n if (disabled) return;\n\n // Apply rounding\n const rounded = roundToPrecision(newValue, effectivePrecision);\n\n // Apply limits\n const minLimit = useSoftLimits && softMin !== undefined ? softMin : min;\n const maxLimit = useSoftLimits && softMax !== undefined ? softMax : max;\n const clamped = clamp(rounded, minLimit, maxLimit);\n\n if (clamped !== value) {\n onChange(clamped);\n }\n },\n [disabled, effectivePrecision, min, max, softMin, softMax, value, onChange]\n );\n\n /**\n * Parses and applies text input value\n */\n const parseAndApplyInput = useCallback((): boolean => {\n try {\n // Try custom parser first\n if (parseValue) {\n const parsed = parseValue(displayValue);\n if (parsed !== null && !isNaN(parsed)) {\n applyValue(parsed);\n setError(undefined);\n return true;\n }\n }\n\n // Try expression evaluation if allowed\n if (allowExpressions) {\n const result = parseNumericInput(displayValue);\n if (result.success && result.value !== undefined) {\n applyValue(result.value);\n setError(undefined);\n return true;\n } else {\n setError(result.error ?? 'Invalid expression');\n return false;\n }\n }\n\n // Fall back to simple number parsing\n const numValue = parseFloat(displayValue);\n if (!isNaN(numValue)) {\n applyValue(numValue);\n setError(undefined);\n return true;\n }\n\n setError('Invalid number');\n return false;\n } catch (err) {\n setError('Invalid input');\n devError('Error parsing input:', err);\n return false;\n }\n }, [displayValue, parseValue, allowExpressions, applyValue]);\n\n // Actions\n const increment = useCallback(() => {\n if (disabled) return;\n const currentStep = getStepSize();\n applyValue(value + currentStep);\n }, [disabled, getStepSize, value, applyValue]);\n\n const decrement = useCallback(() => {\n if (disabled) return;\n const currentStep = getStepSize();\n applyValue(value - currentStep);\n }, [disabled, getStepSize, value, applyValue]);\n\n const startDrag = useCallback(\n (clientX: number) => {\n if (disabled) return;\n setIsDragging(true);\n dragStartX.current = clientX;\n dragStartValue.current = value;\n accumulatedDelta.current = 0;\n document.body.style.cursor = 'ew-resize';\n },\n [disabled, value]\n );\n\n const updateDrag = useCallback(\n (clientX: number) => {\n if (!isDragging || disabled) return;\n\n const deltaX = clientX - dragStartX.current;\n const currentStep = getStepSize();\n const sensitivity = dragSensitivity;\n\n // Accumulate fractional changes for smooth dragging\n const pixelsPerStep = 5; // Pixels needed to trigger one step\n const totalDelta = (deltaX * sensitivity) / pixelsPerStep;\n\n // Only update if we've moved enough for at least one step\n const steps = Math.floor(totalDelta);\n if (steps !== Math.floor(accumulatedDelta.current)) {\n accumulatedDelta.current = totalDelta;\n const newValue = dragStartValue.current + steps * currentStep;\n\n // Use soft limits for dragging\n applyValue(newValue, true);\n\n // Update display to show current value\n const formatted = formatValue\n ? formatValue(newValue)\n : defaultFormatValue(newValue, effectivePrecision);\n setDisplayValue(formatted);\n }\n },\n [\n isDragging,\n disabled,\n getStepSize,\n dragSensitivity,\n applyValue,\n formatValue,\n effectivePrecision,\n ]\n );\n\n const endDrag = useCallback(() => {\n setIsDragging(false);\n document.body.style.cursor = '';\n // Refresh display value to ensure it matches the current value\n const formatted = formatValue\n ? formatValue(value)\n : defaultFormatValue(value, effectivePrecision);\n setDisplayValue(formatted);\n }, [value, formatValue, effectivePrecision]);\n\n const startEditing = useCallback(() => {\n if (disabled) return;\n setIsEditing(true);\n setError(undefined);\n // Place cursor at end of input\n setTimeout(() => {\n const input = document.activeElement;\n if (input instanceof HTMLInputElement) {\n const len = input.value.length;\n input.setSelectionRange(len, len);\n }\n }, 0);\n }, [disabled]);\n\n const endEditing = useCallback(() => {\n if (parseAndApplyInput()) {\n setIsEditing(false);\n }\n // If parsing failed, stay in editing mode to show error\n }, [parseAndApplyInput]);\n\n const cancelEditing = useCallback(() => {\n setIsEditing(false);\n const formatted = formatValue\n ? formatValue(value)\n : defaultFormatValue(value, effectivePrecision);\n setDisplayValue(formatted);\n setError(undefined);\n }, [value, effectivePrecision, formatValue]);\n\n const updateDisplayValue = useCallback(\n (newValue: string) => {\n setDisplayValue(newValue);\n // Clear error when user starts typing\n if (error) {\n setError(undefined);\n }\n },\n [error]\n );\n\n const handleKeyDown = useCallback(\n (event: React.KeyboardEvent) => {\n if (disabled) return;\n\n switch (event.key) {\n case 'Enter':\n event.preventDefault();\n if (isEditing) {\n endEditing();\n } else {\n startEditing();\n }\n break;\n\n case 'Escape':\n event.preventDefault();\n if (isEditing) {\n cancelEditing();\n }\n break;\n\n case 'ArrowUp':\n if (!isEditing) {\n event.preventDefault();\n increment();\n }\n break;\n\n case 'ArrowDown':\n if (!isEditing) {\n event.preventDefault();\n decrement();\n }\n break;\n\n case '-':\n // Only negate when not editing and minus is pressed alone (not in the middle of text)\n if (!isEditing) {\n event.preventDefault();\n applyValue(-value);\n }\n break;\n }\n },\n [\n disabled,\n isEditing,\n endEditing,\n startEditing,\n cancelEditing,\n increment,\n decrement,\n value,\n applyValue,\n ]\n );\n\n // Determine if current input looks like an expression\n const isExpressionValue = allowExpressions && isExpression(displayValue);\n\n return {\n // State\n displayValue,\n isEditing,\n isDragging,\n isHovered,\n isFocused,\n error,\n isExpression: isExpressionValue,\n\n // Actions\n increment,\n decrement,\n startDrag,\n updateDrag,\n endDrag,\n startEditing,\n endEditing,\n cancelEditing,\n updateDisplayValue,\n setHovered: setIsHovered,\n setFocused: setIsFocused,\n handleKeyDown,\n };\n}\n"],"names":[],"mappings":";;;;;;AAwMA;;AAEG;AACH;;AAEE;;AACA;;AACA;AACF;AAEA;;AAEG;AACH;;AAC+B;;;AAG/B;AAEA;;AAEG;AACH;AACE;AACE;;;AAGF;AACF;AAEA;;;;;;;;;;;;AAYG;;;AAmBD;AACA;AACA;;;AAKI;;;;;;;;AAUJ;AACA;AACA;;AAGA;;;AAIE;;AAEI;AACA;;;;;;;;AAQN;AAEA;;AAEG;AACH;;AACuB;AACrB;AAAyC;AACzC;AACF;AACE;AACA;AACA;;;;AAID;AAED;;AAEG;;AAGC;;;;;AAMA;AACA;;AAGA;;;AAGF;AAIF;;AAEG;AACH;AACE;;;AAGI;;;;AAIE;;;;;AAMF;;AAEE;;AAEA;;;AAEA;AACA;;;;AAKJ;AACA;;;AAGE;;;AAIF;;;;AAGA;AACA;;;;AAKJ;AACE;;AACA;AACA;;AAGF;AACE;;AACA;AACA;;AAGF;AAEI;;;AAEA;AACA;AACA;;AAEF;AAIF;;;AAII;AACA;;;AAIA;;;;;AAME;;;AAIA;;;AAIE;AACA;;;AAGN;;;;;;;;AASC;AAGH;;;;;AAKI;AACA;;;AAIJ;AACE;;;;;;AAKE;AACA;AACE;AACA;;;AAGN;AAEA;;;;;AAKA;AAEA;;;AAGI;AACA;;;;AAKJ;;;;;;AAOE;AAIF;AAEI;;AAEA;AACE;;;AAGI;;;AAEA;;;AAIJ;;;AAGI;;;AAIJ;;;AAGI;;;AAIJ;;;AAGI;;;AAIJ;;;;AAII;;;;AAIR;;;;;;;;;;AAWC;;;;;;;;;;;AAcD;;;;;;;;;;;AAYA;AACA;;;AAGJ;;"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import './../../../assets/src/components/controls/Select/Select.css.ts.vanilla-
|
|
1
|
+
import './../../../assets/src/components/controls/Select/Select.css.ts.vanilla-Cpu2BvWL.css';
|
|
2
2
|
import { createRuntimeFn } from '@vanilla-extract/recipes/createRuntimeFn';
|
|
3
3
|
|
|
4
4
|
var checkmarkStyle = 'Select_checkmarkStyle__ycpn7018';
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
3
3
|
import { useId, useRef, useState, useMemo, useCallback, useEffect } from 'react';
|
|
4
4
|
import { createPortal } from 'react-dom';
|
|
5
|
+
import { FormHelperText } from '../../form/FormHelperText.js';
|
|
6
|
+
import { FormLabel } from '../../form/FormLabel.js';
|
|
5
7
|
import { CheckIcon } from '../../Icons/CheckIcon.js';
|
|
6
8
|
import { ChevronDownIcon } from '../../Icons/ChevronDownIcon.js';
|
|
7
9
|
import { CloseIcon } from '../../Icons/CloseIcon.js';
|
|
8
|
-
import { FormLabel } from '../../form/FormLabel.js';
|
|
9
|
-
import { FormHelperText } from '../../form/FormHelperText.js';
|
|
10
10
|
import { ScrollArea } from '../../layout/ScrollArea/ScrollArea.js';
|
|
11
11
|
import { cx } from '../../../utils/cx.js';
|
|
12
12
|
import { triggerContentStyle, clearButtonStyle, chevronRecipe, triggerRecipe, dropdownStyle, searchInputStyle, optionsListStyle, selectContainerStyle, emptyMessageStyle, groupLabelStyle, checkmarkStyle, optionItemRecipe } from './Select.css.js';
|
|
@@ -58,7 +58,7 @@ const CHEVRON_SIZES = {
|
|
|
58
58
|
* />
|
|
59
59
|
* ```
|
|
60
60
|
*/
|
|
61
|
-
function Select({ value: valueProp, defaultValue, options, placeholder = 'Select...', searchable = false, searchPlaceholder = 'Search...', filterFn, emptyMessage = 'No results found', size = 'md', variant = 'default', label, helperText, error = false, errorMessage, disabled = false, required = false, clearable = false, maxDropdownHeight = 240, minDropdownWidth, name, onChange, onOpenChange, className, style, testId, ref, id: idProp, ...rest }) {
|
|
61
|
+
function Select({ value: valueProp, defaultValue, options, placeholder = 'Select...', searchable = false, searchPlaceholder = 'Search...', filterFn, emptyMessage = 'No results found', size = 'md', variant = 'default', label, helperText, error = false, errorMessage, disabled = false, required = false, clearable = false, maxDropdownHeight = 240, minDropdownWidth, fullWidth = false, name, onChange, onOpenChange, className, style, testId, ref, id: idProp, ...rest }) {
|
|
62
62
|
const autoId = useId();
|
|
63
63
|
const selectId = idProp ?? autoId;
|
|
64
64
|
const labelId = `${selectId}-label`;
|
|
@@ -93,12 +93,17 @@ function Select({ value: valueProp, defaultValue, options, placeholder = 'Select
|
|
|
93
93
|
const rect = trigger.getBoundingClientRect();
|
|
94
94
|
const spaceBelow = window.innerHeight - rect.bottom;
|
|
95
95
|
const openAbove = spaceBelow < maxDropdownHeight + 8 && rect.top > spaceBelow;
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
96
|
+
// The dropdown sizes to its content (`width: max-content` in CSS). Anchor
|
|
97
|
+
// that growth between a minimum (at least the trigger, and `minDropdownWidth`
|
|
98
|
+
// when set) and a maximum that keeps it inside the viewport so long option
|
|
99
|
+
// labels are never clipped by a narrow trigger.
|
|
100
|
+
const VIEWPORT_MARGIN = 8;
|
|
101
|
+
const minWidth = Math.max(rect.width, minDropdownWidth ?? 0);
|
|
102
|
+
const maxWidth = Math.max(minWidth, window.innerWidth - rect.left - VIEWPORT_MARGIN);
|
|
99
103
|
setDropdownStyleState({
|
|
100
104
|
left: rect.left,
|
|
101
|
-
|
|
105
|
+
minWidth,
|
|
106
|
+
maxWidth,
|
|
102
107
|
...(openAbove
|
|
103
108
|
? {
|
|
104
109
|
bottom: window.innerHeight - rect.top + 4,
|
|
@@ -127,6 +132,9 @@ function Select({ value: valueProp, defaultValue, options, placeholder = 'Select
|
|
|
127
132
|
if (!isControlled) {
|
|
128
133
|
setInternalValue(val);
|
|
129
134
|
}
|
|
135
|
+
// `onChange` is typed per `clearable` (it only receives `null` from the
|
|
136
|
+
// clear button, which requires `clearable`). Internally we always call it
|
|
137
|
+
// with `T | null`; the public discriminated union enforces the contract.
|
|
130
138
|
onChange?.(val);
|
|
131
139
|
close();
|
|
132
140
|
}, [isControlled, onChange, close]);
|
|
@@ -154,7 +162,9 @@ function Select({ value: valueProp, defaultValue, options, placeholder = 'Select
|
|
|
154
162
|
}
|
|
155
163
|
};
|
|
156
164
|
document.addEventListener('mousedown', handleClick);
|
|
157
|
-
return () =>
|
|
165
|
+
return () => {
|
|
166
|
+
document.removeEventListener('mousedown', handleClick);
|
|
167
|
+
};
|
|
158
168
|
}, [isOpen, close]);
|
|
159
169
|
// Get navigable options (non-disabled)
|
|
160
170
|
const navigableIndices = useMemo(() => filteredOptions
|
|
@@ -261,8 +271,7 @@ function Select({ value: valueProp, defaultValue, options, placeholder = 'Select
|
|
|
261
271
|
ref(node);
|
|
262
272
|
}
|
|
263
273
|
else if (ref) {
|
|
264
|
-
ref.current =
|
|
265
|
-
node;
|
|
274
|
+
ref.current = node;
|
|
266
275
|
}
|
|
267
276
|
}, [ref]);
|
|
268
277
|
// Render options list
|
|
@@ -294,13 +303,19 @@ function Select({ value: valueProp, defaultValue, options, placeholder = 'Select
|
|
|
294
303
|
const renderOptionItem = (opt, index) => {
|
|
295
304
|
const isSelected = opt.value === currentValue;
|
|
296
305
|
const isHighlighted = index === highlightedIndex;
|
|
297
|
-
|
|
306
|
+
const selectOption = () => {
|
|
307
|
+
if (!opt.disabled) {
|
|
308
|
+
selectValue(opt.value);
|
|
309
|
+
}
|
|
310
|
+
};
|
|
311
|
+
return (jsxs("div", { role: "option", tabIndex: -1, "aria-selected": isSelected, "aria-disabled": opt.disabled ?? undefined, className: optionItemRecipe({
|
|
298
312
|
highlighted: isHighlighted,
|
|
299
313
|
selected: isSelected,
|
|
300
314
|
disabled: opt.disabled ?? false,
|
|
301
|
-
}), onClick:
|
|
302
|
-
if (
|
|
303
|
-
|
|
315
|
+
}), onClick: selectOption, onKeyDown: e => {
|
|
316
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
317
|
+
e.preventDefault();
|
|
318
|
+
selectOption();
|
|
304
319
|
}
|
|
305
320
|
}, onMouseEnter: () => {
|
|
306
321
|
if (!opt.disabled) {
|
|
@@ -308,14 +323,27 @@ function Select({ value: valueProp, defaultValue, options, placeholder = 'Select
|
|
|
308
323
|
}
|
|
309
324
|
}, children: [opt.icon && jsx("span", { children: opt.icon }), jsx("span", { children: opt.label ?? opt.value }), isSelected && (jsx("span", { className: checkmarkStyle, children: jsx(CheckIcon, { size: 10, decorative: true }) }))] }, opt.value));
|
|
310
325
|
};
|
|
311
|
-
return (jsxs("div", { className: cx(selectContainerStyle, className), style: style, children: [label && (jsx(FormLabel, { id: labelId, htmlFor: selectId, required: required, disabled: disabled, children: label })), jsxs("button", { ref: setTriggerRef, type: "button", role: "combobox", "aria-haspopup": "listbox", "aria-expanded": isOpen, "aria-controls": isOpen ? listboxId : undefined, "aria-labelledby": label ? labelId : undefined, "aria-required": required || undefined, "aria-invalid": error || undefined, "aria-describedby": showHelperText ? helperId : undefined, disabled: disabled, onClick: () =>
|
|
326
|
+
return (jsxs("div", { className: cx(selectContainerStyle, className), style: { ...(fullWidth ? { width: '100%' } : null), ...style }, children: [label && (jsx(FormLabel, { id: labelId, htmlFor: selectId, required: required, disabled: disabled, children: label })), jsxs("button", { ref: setTriggerRef, type: "button", role: "combobox", "aria-haspopup": "listbox", "aria-expanded": isOpen, "aria-controls": isOpen ? listboxId : undefined, "aria-labelledby": label ? labelId : undefined, "aria-required": required || undefined, "aria-invalid": error || undefined, "aria-describedby": showHelperText ? helperId : undefined, disabled: disabled, onClick: () => {
|
|
327
|
+
if (isOpen) {
|
|
328
|
+
close();
|
|
329
|
+
}
|
|
330
|
+
else {
|
|
331
|
+
open();
|
|
332
|
+
}
|
|
333
|
+
}, onKeyDown: handleTriggerKeyDown, className: triggerRecipe({
|
|
312
334
|
size,
|
|
313
335
|
variant,
|
|
314
336
|
open: isOpen,
|
|
315
337
|
disabled,
|
|
316
338
|
error,
|
|
317
339
|
hasValue: currentValue !== null,
|
|
318
|
-
}), "data-testid": testId, ...rest, children: [jsx("span", { className: triggerContentStyle, children: selectedOption ? (jsxs(Fragment, { children: [selectedOption.icon && jsx("span", { children: selectedOption.icon }), jsx("span", { children: selectedOption.label ?? selectedOption.value })] })) : (jsx("span", { children: placeholder })) }), clearable && currentValue !== null && (jsx("span", { className: clearButtonStyle, role: "button", onClick: handleClear,
|
|
340
|
+
}), style: fullWidth ? { width: '100%' } : undefined, "data-testid": testId, ...rest, children: [jsx("span", { className: triggerContentStyle, children: selectedOption ? (jsxs(Fragment, { children: [selectedOption.icon && jsx("span", { children: selectedOption.icon }), jsx("span", { children: selectedOption.label ?? selectedOption.value })] })) : (jsx("span", { children: placeholder })) }), clearable && currentValue !== null && (jsx("span", { className: clearButtonStyle, role: "button", onClick: handleClear, onKeyDown: e => {
|
|
341
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
342
|
+
e.preventDefault();
|
|
343
|
+
e.stopPropagation();
|
|
344
|
+
selectValue(null);
|
|
345
|
+
}
|
|
346
|
+
}, "aria-label": "Clear selection", tabIndex: -1, children: jsx(CloseIcon, { size: "sm", decorative: true }) })), jsx("span", { className: chevronRecipe({ open: isOpen }), children: jsx(ChevronDownIcon, { size: chevronSize, decorative: true }) })] }), name && jsx("input", { type: "hidden", name: name, value: currentValue ?? '' }), isOpen &&
|
|
319
347
|
createPortal(jsxs("div", { ref: dropdownRef, role: "listbox", id: listboxId, "aria-labelledby": label ? labelId : undefined, className: dropdownStyle, style: dropdownStyleState, onKeyDown: handleDropdownKeyDown, tabIndex: -1, children: [searchable && (jsx("input", { ref: searchRef, type: "text", placeholder: searchPlaceholder, value: searchQuery, onChange: e => {
|
|
320
348
|
setSearchQuery(e.target.value);
|
|
321
349
|
setHighlightedIndex(-1);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Select.js","sources":["../../../../../../src/components/controls/Select/Select.tsx"],"sourcesContent":[null],"names":[],"mappings":";;;;;;;;;;;;;AAuCA;AAEA;AAGE;AACF;AAEA;;AAIE;AACE;;;;AAGE;;;AAGJ;AACF;AAEA;AAIE;;AAEF;AAEA;AAEA;AACE;AACA;AACA;;AAGF;AAEA;;;;;;;;;;;;;;;;;;;AAmBG;;AA+BD;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAGA;;;;AAQA;;;AAIA;;AAGA;AACA;AACE;AAAiC;AACjC;;;AAIF;;AAMA;AACE;AACA;;AAEA;;AAEA;AAGA;;AAGI;AAEJ;;AAEE;AACA;AACE;;AAEI;AACD;AACH;AACH;AACH;AAEA;AACE;;;;AAGA;AACA;;AAEF;AAEA;;;AAGE;AACA;AACA;AACF;AAEA;;;;AAKI;AACA;;;;;AAQA;;;;AAII;AACF;;;;;;AAOJ;;AAEA;AACE;;;AAKE;;AAEJ;AAEA;;AAEF;;AAGA;;AAIO;AAIP;AAEI;;AAEA;;;AAIE;AACE;;AAEF;;;AAGA;;AAGI;AAEA;;;;;;AAKJ;AACE;;;;;;;;;AASN;;AAKF;AAEI;;AAEA;AACE;AACA;AACA;;;AAGI;;;AAGJ;;;AAGI;;;;;;AASV;AAEI;AACE;;;;AAIA;;;;AAIA;;;;AAIA;;;;AAIA;;;AAII;AAEA;AACA;AACE;;;;AAIN;;AAEE;;;AAGN;AAIF;;;AAIE;AAIF;AACA;;AAGA;AAEI;AACA;;;;AAGG;AACC;;AAEN;;;AAMA;AACE;;;;AAMF;;;AAGI;AACE;;AAEA;AAEA;AAIM;AACA;AACF;;;;AAON;AACF;;AAGF;AACF;AAEA;AACE;AACA;AAEA;AAOM;AACA;AACA;AACD;AAEC;AACE;;AAEJ;AAEE;;;;AAcR;AAEA;;;AA8BQ;;;;AAID;AAmCD;AAkBU;AACA;AACF;AA0BhB;AAEA;;"}
|
|
1
|
+
{"version":3,"file":"Select.js","sources":["src/components/controls/Select/Select.tsx"],"sourcesContent":["'use client';\n\nimport React, {\n useCallback,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { createPortal } from 'react-dom';\n\nimport { FormHelperText } from '@/components/form/FormHelperText';\nimport { FormLabel } from '@/components/form/FormLabel';\nimport { CheckIcon } from '@/components/Icons/CheckIcon';\nimport { ChevronDownIcon } from '@/components/Icons/ChevronDownIcon';\nimport { CloseIcon } from '@/components/Icons/CloseIcon';\nimport { ScrollArea } from '@/components/layout/ScrollArea';\nimport { cx } from '@/utils/cx';\n\nimport {\n selectContainerStyle,\n triggerRecipe,\n triggerContentStyle,\n chevronRecipe,\n clearButtonStyle,\n dropdownStyle,\n searchInputStyle,\n optionsListStyle,\n optionItemRecipe,\n groupLabelStyle,\n emptyMessageStyle,\n checkmarkStyle,\n} from './Select.css';\n\nimport type {\n SelectOptionItem,\n SelectOptionGroup,\n SelectProps,\n SelectSize,\n} from './Select.types';\n\n// --- Helpers ---\n\nfunction isOptionGroup<T extends string>(\n item: SelectOptionItem<T> | SelectOptionGroup<T>\n): item is SelectOptionGroup<T> {\n return 'options' in item && Array.isArray(item.options);\n}\n\nfunction flattenOptions<T extends string>(\n items: Array<SelectOptionItem<T> | SelectOptionGroup<T>>\n): SelectOptionItem<T>[] {\n const result: SelectOptionItem<T>[] = [];\n for (const item of items) {\n if (isOptionGroup(item)) {\n result.push(...item.options);\n } else {\n result.push(item);\n }\n }\n return result;\n}\n\nfunction defaultFilter<T extends string>(\n option: SelectOptionItem<T>,\n query: string\n): boolean {\n const label = (option.label ?? option.value).toLowerCase();\n return label.includes(query.toLowerCase());\n}\n\n// --- Size maps ---\n\nconst CHEVRON_SIZES: Record<SelectSize, number> = {\n sm: 10,\n md: 12,\n lg: 14,\n};\n\n// --- Component ---\n\n/**\n * Select component for choosing single values from a dropdown list.\n *\n * Supports searchable mode, grouped options, keyboard navigation,\n * clearable state, and multiple visual variants.\n *\n * @example\n * ```tsx\n * <Select\n * label=\"Blend Mode\"\n * options={[\n * { value: 'normal', label: 'Normal' },\n * { value: 'multiply', label: 'Multiply' },\n * { value: 'screen', label: 'Screen' },\n * ]}\n * value={blendMode}\n * onChange={setBlendMode}\n * />\n * ```\n */\nexport function Select<T extends string = string>({\n value: valueProp,\n defaultValue,\n options,\n placeholder = 'Select...',\n searchable = false,\n searchPlaceholder = 'Search...',\n filterFn,\n emptyMessage = 'No results found',\n size = 'md',\n variant = 'default',\n label,\n helperText,\n error = false,\n errorMessage,\n disabled = false,\n required = false,\n clearable = false,\n maxDropdownHeight = 240,\n minDropdownWidth,\n fullWidth = false,\n name,\n onChange,\n onOpenChange,\n className,\n style,\n testId,\n ref,\n id: idProp,\n ...rest\n}: SelectProps<T>) {\n const autoId = useId();\n const selectId = idProp ?? autoId;\n const labelId = `${selectId}-label`;\n const helperId = `${selectId}-helper`;\n const listboxId = `${selectId}-listbox`;\n\n const triggerRef = useRef<HTMLButtonElement | null>(null);\n const dropdownRef = useRef<HTMLDivElement>(null);\n const searchRef = useRef<HTMLInputElement>(null);\n\n const [isOpen, setIsOpen] = useState(false);\n const [internalValue, setInternalValue] = useState<T | null>(\n defaultValue ?? null\n );\n const [searchQuery, setSearchQuery] = useState('');\n const [highlightedIndex, setHighlightedIndex] = useState(-1);\n const [dropdownStyleState, setDropdownStyleState] =\n useState<React.CSSProperties>({});\n\n const isControlled = valueProp !== undefined;\n const currentValue = isControlled ? valueProp : internalValue;\n\n // Flatten all options for navigation\n const allOptions = useMemo(() => flattenOptions(options), [options]);\n\n // Filter options when searchable\n const filter = filterFn ?? defaultFilter;\n const filteredOptions = useMemo(() => {\n if (!searchable || !searchQuery) return allOptions;\n return allOptions.filter(opt => filter(opt, searchQuery));\n }, [allOptions, searchable, searchQuery, filter]);\n\n // Find selected option\n const selectedOption = useMemo(\n () => allOptions.find(o => o.value === currentValue) ?? null,\n [allOptions, currentValue]\n );\n\n // Position dropdown\n const updateDropdownPosition = useCallback(() => {\n const trigger = triggerRef.current;\n if (!trigger) return;\n\n const rect = trigger.getBoundingClientRect();\n const spaceBelow = window.innerHeight - rect.bottom;\n const openAbove =\n spaceBelow < maxDropdownHeight + 8 && rect.top > spaceBelow;\n\n // The dropdown sizes to its content (`width: max-content` in CSS). Anchor\n // that growth between a minimum (at least the trigger, and `minDropdownWidth`\n // when set) and a maximum that keeps it inside the viewport so long option\n // labels are never clipped by a narrow trigger.\n const VIEWPORT_MARGIN = 8;\n const minWidth = Math.max(rect.width, minDropdownWidth ?? 0);\n const maxWidth = Math.max(\n minWidth,\n window.innerWidth - rect.left - VIEWPORT_MARGIN\n );\n\n setDropdownStyleState({\n left: rect.left,\n minWidth,\n maxWidth,\n ...(openAbove\n ? {\n bottom: window.innerHeight - rect.top + 4,\n transformOrigin: 'bottom',\n }\n : { top: rect.bottom + 4, transformOrigin: 'top' }),\n });\n }, [maxDropdownHeight, minDropdownWidth]);\n\n const open = useCallback(() => {\n if (disabled) return;\n setIsOpen(true);\n setSearchQuery('');\n setHighlightedIndex(-1);\n onOpenChange?.(true);\n // Position is calculated in useEffect after render\n }, [disabled, onOpenChange]);\n\n const close = useCallback(() => {\n setIsOpen(false);\n setSearchQuery('');\n setHighlightedIndex(-1);\n onOpenChange?.(false);\n triggerRef.current?.focus();\n }, [onOpenChange]);\n\n const selectValue = useCallback(\n (val: T | null) => {\n if (!isControlled) {\n setInternalValue(val);\n }\n // `onChange` is typed per `clearable` (it only receives `null` from the\n // clear button, which requires `clearable`). Internally we always call it\n // with `T | null`; the public discriminated union enforces the contract.\n (onChange as ((value: T | null) => void) | undefined)?.(val);\n close();\n },\n [isControlled, onChange, close]\n );\n\n // Update position when open\n useEffect(() => {\n if (isOpen) {\n updateDropdownPosition();\n if (searchable) {\n // Focus search input after portal renders\n requestAnimationFrame(() => {\n searchRef.current?.focus();\n });\n }\n }\n }, [isOpen, updateDropdownPosition, searchable]);\n\n // Close on outside click\n useEffect(() => {\n if (!isOpen) return;\n\n const handleClick = (e: MouseEvent) => {\n const target = e.target as Node;\n if (\n !triggerRef.current?.contains(target) &&\n !dropdownRef.current?.contains(target)\n ) {\n close();\n }\n };\n\n document.addEventListener('mousedown', handleClick);\n return () => {\n document.removeEventListener('mousedown', handleClick);\n };\n }, [isOpen, close]);\n\n // Get navigable options (non-disabled)\n const navigableIndices = useMemo(\n () =>\n filteredOptions\n .map((opt, i) => (opt.disabled ? -1 : i))\n .filter(i => i !== -1),\n [filteredOptions]\n );\n\n const navigateHighlight = useCallback(\n (direction: 'next' | 'prev' | 'first' | 'last') => {\n if (navigableIndices.length === 0) return;\n\n let nextIndex: number;\n const currentNavIdx = navigableIndices.indexOf(highlightedIndex);\n\n switch (direction) {\n case 'first':\n nextIndex = navigableIndices[0] ?? 0;\n break;\n case 'last':\n nextIndex = navigableIndices[navigableIndices.length - 1] ?? 0;\n break;\n case 'next':\n if (\n currentNavIdx === -1 ||\n currentNavIdx >= navigableIndices.length - 1\n ) {\n nextIndex = navigableIndices[0] ?? 0;\n } else {\n nextIndex = navigableIndices[currentNavIdx + 1] ?? 0;\n }\n break;\n case 'prev':\n if (currentNavIdx <= 0) {\n nextIndex = navigableIndices[navigableIndices.length - 1] ?? 0;\n } else {\n nextIndex = navigableIndices[currentNavIdx - 1] ?? 0;\n }\n break;\n }\n\n setHighlightedIndex(nextIndex);\n },\n [navigableIndices, highlightedIndex]\n );\n\n // Keyboard handling for trigger\n const handleTriggerKeyDown = useCallback(\n (e: React.KeyboardEvent) => {\n if (disabled) return;\n\n switch (e.key) {\n case 'Enter':\n case ' ':\n case 'ArrowDown':\n e.preventDefault();\n if (!isOpen) {\n open();\n }\n break;\n case 'ArrowUp':\n e.preventDefault();\n if (!isOpen) {\n open();\n }\n break;\n }\n },\n [disabled, isOpen, open]\n );\n\n // Keyboard handling for dropdown\n const handleDropdownKeyDown = useCallback(\n (e: React.KeyboardEvent) => {\n switch (e.key) {\n case 'ArrowDown':\n e.preventDefault();\n navigateHighlight('next');\n break;\n case 'ArrowUp':\n e.preventDefault();\n navigateHighlight('prev');\n break;\n case 'Home':\n e.preventDefault();\n navigateHighlight('first');\n break;\n case 'End':\n e.preventDefault();\n navigateHighlight('last');\n break;\n case 'Enter':\n e.preventDefault();\n if (\n highlightedIndex >= 0 &&\n highlightedIndex < filteredOptions.length\n ) {\n const opt = filteredOptions[highlightedIndex];\n if (opt && !opt.disabled) {\n selectValue(opt.value);\n }\n }\n break;\n case 'Escape':\n e.preventDefault();\n close();\n break;\n }\n },\n [navigateHighlight, highlightedIndex, filteredOptions, selectValue, close]\n );\n\n const handleClear = useCallback(\n (e: React.MouseEvent) => {\n e.stopPropagation();\n selectValue(null);\n },\n [selectValue]\n );\n\n const showHelperText = error && errorMessage ? errorMessage : helperText;\n const chevronSize = CHEVRON_SIZES[size];\n\n // Assign ref\n const setTriggerRef = useCallback(\n (node: HTMLButtonElement | null) => {\n triggerRef.current = node;\n if (typeof ref === 'function') {\n ref(node);\n } else if (ref) {\n ref.current = node;\n }\n },\n [ref]\n );\n\n // Render options list\n const renderOptions = () => {\n if (filteredOptions.length === 0) {\n return <div className={emptyMessageStyle}>{emptyMessage}</div>;\n }\n\n // If options are grouped, render with group headers\n const hasGroups = options.some(isOptionGroup);\n\n if (hasGroups && !searchQuery) {\n let flatIndex = 0;\n return options.map((item, groupIdx) => {\n if (isOptionGroup(item)) {\n const groupOptions = item.options;\n const startIdx = flatIndex;\n flatIndex += groupOptions.length;\n\n return (\n <div key={`group-${groupIdx}`} role=\"group\" aria-label={item.label}>\n <div className={groupLabelStyle}>{item.label}</div>\n {groupOptions.map((opt, optIdx) => {\n const idx = startIdx + optIdx;\n return renderOptionItem(opt, idx);\n })}\n </div>\n );\n }\n\n const idx = flatIndex;\n flatIndex += 1;\n return renderOptionItem(item, idx);\n });\n }\n\n return filteredOptions.map((opt, idx) => renderOptionItem(opt, idx));\n };\n\n const renderOptionItem = (opt: SelectOptionItem<T>, index: number) => {\n const isSelected = opt.value === currentValue;\n const isHighlighted = index === highlightedIndex;\n\n const selectOption = () => {\n if (!opt.disabled) {\n selectValue(opt.value);\n }\n };\n\n return (\n <div\n key={opt.value}\n role=\"option\"\n tabIndex={-1}\n aria-selected={isSelected}\n aria-disabled={opt.disabled ?? undefined}\n className={optionItemRecipe({\n highlighted: isHighlighted,\n selected: isSelected,\n disabled: opt.disabled ?? false,\n })}\n onClick={selectOption}\n onKeyDown={e => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n selectOption();\n }\n }}\n onMouseEnter={() => {\n if (!opt.disabled) {\n setHighlightedIndex(index);\n }\n }}\n >\n {opt.icon && <span>{opt.icon}</span>}\n <span>{opt.label ?? opt.value}</span>\n {isSelected && (\n <span className={checkmarkStyle}>\n <CheckIcon size={10} decorative />\n </span>\n )}\n </div>\n );\n };\n\n return (\n <div\n className={cx(selectContainerStyle, className)}\n style={{ ...(fullWidth ? { width: '100%' } : null), ...style }}\n >\n {label && (\n <FormLabel\n id={labelId}\n htmlFor={selectId}\n required={required}\n disabled={disabled}\n >\n {label}\n </FormLabel>\n )}\n\n <button\n ref={setTriggerRef}\n type=\"button\"\n role=\"combobox\"\n aria-haspopup=\"listbox\"\n aria-expanded={isOpen}\n aria-controls={isOpen ? listboxId : undefined}\n aria-labelledby={label ? labelId : undefined}\n aria-required={required || undefined}\n aria-invalid={error || undefined}\n aria-describedby={showHelperText ? helperId : undefined}\n disabled={disabled}\n onClick={() => {\n if (isOpen) {\n close();\n } else {\n open();\n }\n }}\n onKeyDown={handleTriggerKeyDown}\n className={triggerRecipe({\n size,\n variant,\n open: isOpen,\n disabled,\n error,\n hasValue: currentValue !== null,\n })}\n style={fullWidth ? { width: '100%' } : undefined}\n data-testid={testId}\n {...rest}\n >\n <span className={triggerContentStyle}>\n {selectedOption ? (\n <>\n {selectedOption.icon && <span>{selectedOption.icon}</span>}\n <span>{selectedOption.label ?? selectedOption.value}</span>\n </>\n ) : (\n <span>{placeholder}</span>\n )}\n </span>\n\n {clearable && currentValue !== null && (\n <span\n className={clearButtonStyle}\n role=\"button\"\n onClick={handleClear}\n onKeyDown={e => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n e.stopPropagation();\n selectValue(null);\n }\n }}\n aria-label=\"Clear selection\"\n tabIndex={-1}\n >\n <CloseIcon size=\"sm\" decorative />\n </span>\n )}\n\n <span className={chevronRecipe({ open: isOpen })}>\n <ChevronDownIcon size={chevronSize} decorative />\n </span>\n </button>\n\n {name && <input type=\"hidden\" name={name} value={currentValue ?? ''} />}\n\n {isOpen &&\n createPortal(\n <div\n ref={dropdownRef}\n role=\"listbox\"\n id={listboxId}\n aria-labelledby={label ? labelId : undefined}\n className={dropdownStyle}\n style={dropdownStyleState}\n onKeyDown={handleDropdownKeyDown}\n tabIndex={-1}\n >\n {searchable && (\n <input\n ref={searchRef}\n type=\"text\"\n placeholder={searchPlaceholder}\n value={searchQuery}\n onChange={e => {\n setSearchQuery(e.target.value);\n setHighlightedIndex(-1);\n }}\n onKeyDown={handleDropdownKeyDown}\n className={searchInputStyle}\n aria-label=\"Search options\"\n />\n )}\n <ScrollArea\n className={optionsListStyle}\n maxHeight={maxDropdownHeight - (searchable ? 32 : 0)}\n scrollbarWidth={4}\n scrollbarVisibility=\"auto\"\n hideDelay={600}\n >\n {renderOptions()}\n </ScrollArea>\n </div>,\n document.body\n )}\n\n {showHelperText && (\n <FormHelperText id={helperId} error={error}>\n {showHelperText}\n </FormHelperText>\n )}\n </div>\n );\n}\n\nSelect.displayName = 'Select';\n"],"names":[],"mappings":";;;;;;;;;;;;;AA0CA;AAEA;AAGE;AACF;AAEA;;AAIE;AACE;;;;AAGE;;;AAGJ;AACF;AAEA;AAIE;;AAEF;AAEA;AAEA;AACE;AACA;AACA;;AAGF;AAEA;;;;;;;;;;;;;;;;;;;AAmBG;;AAgCD;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;;AAGA;;;;AAQA;;;AAIA;;AAGA;AACA;AACE;AAAiC;AACjC;;;AAIF;;AAMA;AACE;AACA;;AAEA;;AAEA;;;;;;AAQA;AACA;AAKA;;;;AAIE;AACE;;AAEI;AACD;AACH;AACH;AACH;AAEA;AACE;;;;AAGA;AACA;;AAEF;AAEA;;;AAGE;AACA;AACA;AACF;AAEA;;;;;;;AAQK;AACD;;;;;AAQA;;;;AAII;AACF;;;;;;AAOJ;;AAEA;AACE;;;AAKE;;AAEJ;AAEA;AACA;AACE;AACF;AACF;;AAGA;;AAIO;AAIP;AAEI;;AAEA;;;AAIE;AACE;;AAEF;;;AAGA;;AAGI;AAEA;;;;;;AAKJ;AACE;;;;;;;;;AASN;;AAKF;AAEI;;AAEA;AACE;AACA;AACA;;;AAGI;;;AAGJ;;;AAGI;;;;;;AASV;AAEI;AACE;;;;AAIA;;;;AAIA;;;;AAIA;;;;AAIA;;;AAII;AAEA;AACA;AACE;;;;AAIN;;AAEE;;;AAGN;AAIF;;;AAIE;AAIF;AACA;;AAGA;AAEI;AACA;;;;AAGE;;AAEJ;;;AAMA;AACE;;;;AAMF;;;AAGI;AACE;;AAEA;AAEA;AAIM;AACA;AACF;;;;AAON;AACF;;AAGF;AACF;AAEA;AACE;AACA;;AAGE;AACE;;AAEJ;;AAUM;AACA;AACA;;AAIA;;AAEE;;AAEJ;AAEE;;;;AAcR;;;AAgCU;;;AAEA;;;;;AAOF;;;;AAID;AAsBK;;;;;AAKF;AAgBJ;AAkBU;AACA;AACF;AA0BhB;AAEA;;"}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
3
|
-
import React, { useState, useRef, useCallback, useEffect } from 'react';
|
|
4
3
|
import { assignInlineVars } from '@vanilla-extract/dynamic';
|
|
5
|
-
import {
|
|
6
|
-
import { FormLabel } from '../../form/FormLabel.js';
|
|
4
|
+
import React, { useState, useRef, useCallback, useEffect } from 'react';
|
|
7
5
|
import { FormHelperText } from '../../form/FormHelperText.js';
|
|
6
|
+
import { FormLabel } from '../../form/FormLabel.js';
|
|
8
7
|
import { InputWrapper } from '../../form/InputWrapper.js';
|
|
8
|
+
import { useKeyboardContext } from '../../../context/KeyboardContext.js';
|
|
9
9
|
import { cx } from '../../../utils/cx.js';
|
|
10
10
|
import { sliderWrapperStyle, fillPercentageVar, fillRecipe, trackRecipe, thumbPercentageVar, thumbRecipe, ticksStyle, tickStyle, tooltipPercentageVar, tooltipRecipe, sliderContainerRecipe } from './Slider.css.js';
|
|
11
11
|
|
|
@@ -104,10 +104,8 @@ const Slider = ({ value, onChange, min = 0, max = 100, step = 1, precisionStep,
|
|
|
104
104
|
const displayValue = formatValue
|
|
105
105
|
? formatValue(clampedValue)
|
|
106
106
|
: unit
|
|
107
|
-
? `${
|
|
108
|
-
: precision
|
|
109
|
-
? clampedValue.toFixed(precision)
|
|
110
|
-
: clampedValue.toString();
|
|
107
|
+
? `${clampedValue.toFixed(precision)}${unit}`
|
|
108
|
+
: clampedValue.toFixed(precision);
|
|
111
109
|
/**
|
|
112
110
|
* Applies a new value with proper bounds checking and rounding
|
|
113
111
|
*/
|
|
@@ -252,7 +250,7 @@ const Slider = ({ value, onChange, min = 0, max = 100, step = 1, precisionStep,
|
|
|
252
250
|
background: 'transparent',
|
|
253
251
|
padding: 0,
|
|
254
252
|
height: 5,
|
|
255
|
-
}, children: jsxs("div", { ref: sliderRef, className: sliderWrapperStyle, onMouseDown: handleMouseDown, tabIndex: disabled ? -1 : 0, role: "slider", "aria-valuemin": min, "aria-valuemax": max, "aria-valuenow": clampedValue, "aria-valuetext": displayValue, "aria-disabled": disabled, "aria-readonly": readOnly, "aria-
|
|
253
|
+
}, children: jsxs("div", { ref: sliderRef, className: sliderWrapperStyle, onMouseDown: handleMouseDown, tabIndex: disabled ? -1 : 0, role: "slider", "aria-valuemin": min, "aria-valuemax": max, "aria-valuenow": clampedValue, "aria-valuetext": displayValue, "aria-disabled": disabled, "aria-readonly": readOnly, "aria-invalid": error, "aria-labelledby": label ? sliderId : undefined, onFocus: handleFocus, onBlur: handleBlur, onKeyDown: handleKeyDown, children: [jsx("div", { ref: trackRef, className: trackRecipe({ size, error }), children: jsx("div", { className: fillRecipe({ error, isDragging }), style: assignInlineVars({
|
|
256
254
|
[fillPercentageVar]: `${percentage}%`,
|
|
257
255
|
}) }) }), jsx("div", { className: thumbRecipe({ size, error, isDragging }), style: assignInlineVars({
|
|
258
256
|
[thumbPercentageVar]: `${percentage}%`,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Slider.js","sources":["../../../../../../src/components/controls/Slider/Slider.tsx"],"sourcesContent":[null],"names":[],"mappings":";;;;;;;;;;;AA4KA;;AAEG;AACH;AACE;AACF;AAEA;;AAEG;AACH;;AAC+B;;;AAG/B;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgDG;AACI;;;;AAkCL;AACA;AACA;AAIA;;AAGA;AACA;AACA;;AAGA;;AACuB;AACrB;AAAyC;AACzC;AACF;AACE;AACA;AACA;;;;AAID;;;AAIC;AACA;;;AAGI;AACA;AAEN;;AAEG;AACH;;;;;;;AASI;;;AAGF;AAIF;;AAEG;AACH;;AAE2B;;;;;AAOvB;;AAGA;AACA;;AAKJ;;AAEG;AACH;;;;;;;;;;;AAcM;;AAEJ;AAIF;;AAEG;AACH;AAEI;;;;;;;AAMA;;AAKJ;;AAEG;AACH;;;AAGE;;AAGF;;AAEG;AACH;;;AAII;;AAGA;AACE;AACA;AACE;;AAEF;AACA;AACE;;AAEF;;;AAGA;;;AAGA;AACE;;AAEF;AACE;;AAEF;;;;;;AAQF;AACF;;;;;;;;;;AAWC;AAGH;;AAEG;AACH;;AAGI;AACF;AAIF;;AAGI;AACF;;;AAMA;;AAEA;AACA;AAEA;AACE;AACA;AACF;;;AAIF;AAEA;AAEA;AAmBQ;AACA;AACA;AACA;;AA0BM;;AAQF;;AAgBE;AACD;AAef;AAEA;;"}
|
|
1
|
+
{"version":3,"file":"Slider.js","sources":["src/components/controls/Slider/Slider.tsx"],"sourcesContent":["'use client';\n\n// src/components/controls/Slider/Slider.tsx\nimport { assignInlineVars } from '@vanilla-extract/dynamic';\nimport React, { useRef, useCallback, useState, useEffect } from 'react';\n\nimport { FormHelperText } from '@/components/form/FormHelperText';\nimport { FormLabel } from '@/components/form/FormLabel';\nimport { InputWrapper } from '@/components/form/InputWrapper';\nimport { useKeyboardContext } from '@/context/KeyboardContext';\nimport { cx } from '@/utils/cx';\n\nimport {\n sliderContainerRecipe,\n sliderWrapperStyle,\n trackRecipe,\n fillRecipe,\n fillPercentageVar,\n thumbRecipe,\n thumbPercentageVar,\n ticksStyle,\n tickStyle,\n tooltipRecipe,\n tooltipPercentageVar,\n} from './Slider.css';\n\nimport type { BaseComponent, Size } from '@/types/common';\nimport type { Prettify } from '@/types/utilities';\n\n/**\n * Props specific to Slider component\n */\nexport interface SliderBaseProps extends Omit<BaseComponent, 'onChange'> {\n /**\n * Current numeric value\n */\n value: number;\n\n /**\n * Callback when value changes\n */\n onChange: (value: number) => void;\n\n /**\n * Minimum allowed value\n * @default 0\n */\n min?: number;\n\n /**\n * Maximum allowed value\n * @default 100\n */\n max?: number;\n\n /**\n * Step size for value increments\n * @default 1\n */\n step?: number;\n\n /**\n * Step size when Shift is held (precision mode)\n * @default step / 10\n */\n precisionStep?: number;\n\n /**\n * Step size when Ctrl is held (large steps)\n * @default step * 10\n */\n largeStep?: number;\n\n /**\n * Number of decimal places to round to\n */\n precision?: number;\n\n /**\n * Slider size using standard library sizing\n * - `sm`: Compact for toolbars\n * - `md`: Standard for forms\n * - `lg`: Prominent sliders\n * @default \"md\"\n */\n size?: Size;\n\n /**\n * Whether the slider is disabled\n * @default false\n */\n disabled?: boolean;\n\n /**\n * Whether the slider is read-only\n * @default false\n */\n readOnly?: boolean;\n\n /**\n * Slider label\n */\n label?: string;\n\n /**\n * Helper text displayed below the slider\n */\n helperText?: string;\n\n /**\n * Error message displayed when error is true\n */\n errorMessage?: string;\n\n /**\n * Whether the slider has an error state\n * @default false\n */\n error?: boolean;\n\n /**\n * Whether the slider is required\n * @default false\n */\n required?: boolean;\n\n /**\n * Unit suffix to display (e.g., \"px\", \"%\", \"°\")\n */\n unit?: string;\n\n /**\n * Format value for display\n */\n formatValue?: (value: number) => string;\n\n /**\n * Show value tooltip while dragging\n * @default true\n */\n showTooltip?: boolean;\n\n /**\n * Show tick marks along the track\n * @default false\n */\n showTicks?: boolean;\n\n /**\n * Number of tick marks to show (when showTicks is true)\n * @default 5\n */\n tickCount?: number;\n\n /**\n * Focus event handler\n */\n onFocus?: (event: React.FocusEvent<HTMLElement>) => void;\n\n /**\n * Blur event handler\n */\n onBlur?: (event: React.FocusEvent<HTMLElement>) => void;\n\n /**\n * Key down event handler\n */\n onKeyDown?: (event: React.KeyboardEvent<HTMLElement>) => void;\n}\n\n/**\n * Props for the Slider component with prettified type for better IntelliSense\n */\nexport type SliderProps = Prettify<SliderBaseProps>;\n\n/**\n * Clamps a value between min and max bounds\n */\nfunction clamp(value: number, min: number, max: number): number {\n return Math.min(Math.max(value, min), max);\n}\n\n/**\n * Rounds a number to specified decimal places\n */\nfunction roundToPrecision(value: number, precision?: number): number {\n if (precision === undefined) return value;\n const factor = Math.pow(10, precision);\n return Math.round(value * factor) / factor;\n}\n\n/**\n * A professional slider component with drag interaction and keyboard support.\n *\n * Features:\n * - Smooth drag interaction with visual feedback\n * - Keyboard navigation with arrow keys\n * - Modifier key support: Ctrl (large steps), Shift (precision)\n * - Optional tick marks and value tooltip\n * - Comprehensive theming and size variants\n * - Accessible with proper ARIA attributes\n *\n * @example\n * ```tsx\n * // Basic slider\n * <Slider\n * value={opacity}\n * onChange={setOpacity}\n * min={0}\n * max={1}\n * step={0.1}\n * unit=\"%\"\n * />\n *\n * // Slider with ticks and custom formatting\n * <Slider\n * value={rotation}\n * onChange={setRotation}\n * min={0}\n * max={360}\n * step={15}\n * unit=\"°\"\n * showTicks\n * tickCount={8}\n * formatValue={(v) => `${v}°`}\n * />\n *\n * // Precision slider for fine control\n * <Slider\n * value={scale}\n * onChange={setScale}\n * min={0.1}\n * max={5}\n * step={0.1}\n * precisionStep={0.01}\n * largeStep={1}\n * precision={2}\n * />\n * ```\n */\nexport const Slider: React.FC<SliderProps> = ({\n value,\n onChange,\n min = 0,\n max = 100,\n step = 1,\n precisionStep,\n largeStep,\n precision = 2,\n size = 'md',\n disabled = false,\n readOnly = false,\n label,\n helperText,\n errorMessage,\n error = false,\n required = false,\n unit,\n formatValue,\n showTooltip = true,\n showTicks = false,\n tickCount = 5,\n className,\n testId,\n onFocus,\n onBlur,\n onKeyDown,\n ref,\n ...props\n}) => {\n const [isDragging, setIsDragging] = useState(false);\n const [isFocused, setIsFocused] = useState(false);\n const [showTooltipState, setShowTooltipState] = useState(false);\n\n const sliderRef = useRef<HTMLDivElement>(null);\n const trackRef = useRef<HTMLDivElement>(null);\n const dragStartRef = useRef<{ startX: number; startValue: number } | null>(\n null\n );\n\n const { modifiers } = useKeyboardContext();\n\n // Calculate derived values\n const effectivePrecisionStep = precisionStep ?? step / 10;\n const effectiveLargeStep = largeStep ?? step * 10;\n const percentage = ((value - min) / (max - min)) * 100;\n const clampedValue = clamp(value, min, max);\n\n const getStepSize = useCallback((): number => {\n if (modifiers.shift) return effectivePrecisionStep;\n if (modifiers.control || modifiers.meta) return effectiveLargeStep;\n return step;\n }, [\n modifiers.shift,\n modifiers.control,\n modifiers.meta,\n effectivePrecisionStep,\n effectiveLargeStep,\n step,\n ]);\n\n // Format display value with consistent precision to prevent tooltip size changes\n const displayValue = formatValue\n ? formatValue(clampedValue)\n : unit\n ? `${clampedValue.toFixed(precision)}${unit}`\n : clampedValue.toFixed(precision);\n\n /**\n * Applies a new value with proper bounds checking and rounding\n */\n const applyValue = useCallback(\n (newValue: number): void => {\n if (disabled || readOnly) return;\n\n if (newValue === value) return;\n\n const rounded = roundToPrecision(newValue, precision);\n const clamped = clamp(rounded, min, max);\n\n if (clamped !== value) {\n onChange(clamped);\n }\n },\n [disabled, readOnly, precision, min, max, value, onChange]\n );\n\n /**\n * Converts mouse position to value\n */\n const positionToValue = useCallback(\n (clientX: number): number => {\n if (!trackRef.current) return value;\n\n const rect = trackRef.current.getBoundingClientRect();\n const percentage = clamp((clientX - rect.left) / rect.width, 0, 1);\n const newValue = min + percentage * (max - min);\n\n // Use the current step size (depends on keyboard modifiers)\n const currentStep = getStepSize();\n\n // Snap to step increments\n const steps = Math.round((newValue - min) / currentStep);\n return min + steps * currentStep;\n },\n [min, max, value, getStepSize]\n );\n\n /**\n * Handle mouse down on track or thumb\n */\n const handleMouseDown = useCallback(\n (event: React.MouseEvent) => {\n if (disabled || readOnly) return;\n\n event.preventDefault();\n setIsDragging(true);\n setShowTooltipState(showTooltip);\n\n // Use the current step size (depends on keyboard modifiers)\n const newValue = positionToValue(event.clientX);\n applyValue(newValue);\n\n dragStartRef.current = {\n startX: event.clientX,\n startValue: newValue,\n };\n },\n [disabled, readOnly, positionToValue, applyValue, showTooltip]\n );\n\n /**\n * Handle global mouse move during drag\n */\n const handleMouseMove = useCallback(\n (event: MouseEvent) => {\n if (!isDragging || !dragStartRef.current) return;\n // Use requestAnimationFrame for smoother performance\n requestAnimationFrame(() => {\n // Use the current step size (depends on keyboard modifiers)\n const newValue = positionToValue(event.clientX);\n applyValue(newValue);\n });\n },\n [isDragging, positionToValue, applyValue]\n );\n\n /**\n * Handle global mouse up to end drag\n */\n const handleMouseUp = useCallback(() => {\n setIsDragging(false);\n setShowTooltipState(false);\n dragStartRef.current = null;\n }, []);\n\n /**\n * Handle keyboard navigation\n */\n const handleKeyDown = useCallback(\n (event: React.KeyboardEvent<HTMLElement>) => {\n if (disabled || readOnly) return;\n\n const currentStep = getStepSize();\n let handled = true;\n\n switch (event.key) {\n case 'ArrowRight':\n case 'ArrowUp':\n applyValue(value + currentStep);\n break;\n case 'ArrowLeft':\n case 'ArrowDown':\n applyValue(value - currentStep);\n break;\n case 'Home':\n applyValue(min);\n break;\n case 'End':\n applyValue(max);\n break;\n case 'PageUp':\n applyValue(value + effectiveLargeStep);\n break;\n case 'PageDown':\n applyValue(value - effectiveLargeStep);\n break;\n default:\n handled = false;\n }\n\n if (handled) {\n event.preventDefault();\n }\n\n onKeyDown?.(event);\n },\n [\n disabled,\n readOnly,\n getStepSize,\n applyValue,\n value,\n min,\n max,\n effectiveLargeStep,\n onKeyDown,\n ]\n );\n\n /**\n * Handle focus events\n */\n const handleFocus = useCallback(\n (event: React.FocusEvent<HTMLElement>) => {\n setIsFocused(true);\n onFocus?.(event);\n },\n [onFocus]\n );\n\n const handleBlur = useCallback(\n (event: React.FocusEvent<HTMLElement>) => {\n setIsFocused(false);\n onBlur?.(event);\n },\n [onBlur]\n );\n\n // Global mouse events for dragging\n useEffect(() => {\n if (!isDragging) return;\n\n document.addEventListener('mousemove', handleMouseMove);\n document.addEventListener('mouseup', handleMouseUp);\n\n return () => {\n document.removeEventListener('mousemove', handleMouseMove);\n document.removeEventListener('mouseup', handleMouseUp);\n };\n }, [isDragging, handleMouseMove, handleMouseUp]);\n\n // Generate tick marks\n const ticks = showTicks ? Array.from({ length: tickCount }, (_, i) => i) : [];\n\n const sliderId = React.useId();\n\n return (\n <div\n ref={ref}\n className={cx(sliderContainerRecipe({ disabled }), className)}\n data-testid={testId}\n {...props}\n >\n {label && (\n <FormLabel htmlFor={sliderId} disabled={disabled} required={required}>\n {label}\n </FormLabel>\n )}\n\n <InputWrapper\n size={size}\n error={error}\n disabled={disabled}\n focused={isFocused}\n style={{\n border: 'none',\n background: 'transparent',\n padding: 0,\n height: 5,\n }}\n >\n <div\n ref={sliderRef}\n className={sliderWrapperStyle}\n onMouseDown={handleMouseDown}\n tabIndex={disabled ? -1 : 0}\n role=\"slider\"\n aria-valuemin={min}\n aria-valuemax={max}\n aria-valuenow={clampedValue}\n aria-valuetext={displayValue}\n aria-disabled={disabled}\n aria-readonly={readOnly}\n aria-invalid={error}\n aria-labelledby={label ? sliderId : undefined}\n onFocus={handleFocus}\n onBlur={handleBlur}\n onKeyDown={handleKeyDown}\n >\n <div ref={trackRef} className={trackRecipe({ size, error })}>\n <div\n className={fillRecipe({ error, isDragging })}\n style={assignInlineVars({\n [fillPercentageVar]: `${percentage}%`,\n })}\n />\n </div>\n\n <div\n className={thumbRecipe({ size, error, isDragging })}\n style={assignInlineVars({\n [thumbPercentageVar]: `${percentage}%`,\n })}\n />\n\n {showTicks && (\n <div className={ticksStyle}>\n {ticks.map(i => (\n <div key={i} className={tickStyle} />\n ))}\n </div>\n )}\n\n {showTooltip && (\n <div\n className={tooltipRecipe({ visible: showTooltipState })}\n style={assignInlineVars({\n [tooltipPercentageVar]: `${percentage}%`,\n })}\n >\n {displayValue}\n </div>\n )}\n </div>\n </InputWrapper>\n\n {(helperText ?? (error && errorMessage)) && (\n <FormHelperText error={error}>\n {error && errorMessage ? errorMessage : helperText}\n </FormHelperText>\n )}\n </div>\n );\n};\n\nSlider.displayName = 'Slider';\n"],"names":[],"mappings":";;;;;;;;;;;AA+KA;;AAEG;AACH;AACE;AACF;AAEA;;AAEG;AACH;;AAC+B;;;AAG/B;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgDG;AACI;;;;AAkCL;AACA;AACA;AAIA;;AAGA;AACA;AACA;;AAGA;;AACuB;AACrB;AAAyC;AACzC;AACF;AACE;AACA;AACA;;;;AAID;;;AAIC;AACA;;AAEE;AAEJ;;AAEG;AACH;;;;;;;AASI;;;AAGF;AAIF;;AAEG;AACH;;AAE2B;;;;;AAOvB;;AAGA;AACA;;AAKJ;;AAEG;AACH;;;;;;;;;;;AAcM;;AAEJ;AAIF;;AAEG;AACH;AAEI;;;;;;;AAMA;;AAKJ;;AAEG;AACH;;;AAGE;;AAGF;;AAEG;AACH;;;AAII;;AAGA;AACE;AACA;AACE;;AAEF;AACA;AACE;;AAEF;;;AAGA;;;AAGA;AACE;;AAEF;AACE;;AAEF;;;;;;AAQF;AACF;;;;;;;;;;AAWC;AAGH;;AAEG;AACH;;AAGI;AACF;AAIF;;AAGI;AACF;;;AAMA;;AAEA;AACA;AAEA;AACE;AACA;AACF;;;AAIF;AAEA;AAEA;AAmBQ;AACA;AACA;AACA;;AAyBM;;AAQF;;AAgBE;AACD;AAef;AAEA;;"}
|