entangle-ui 0.10.0 → 0.12.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 +145 -0
- package/README.md +7 -5
- 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/Slider/{Slider.css.ts.vanilla-Cqm3fQ0S.css → Slider.css.ts.vanilla-CLf_RC0A.css} +50 -47
- 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/navigation/Menu/{Menu.css.ts.vanilla-B5iVe5S2.css → Menu.css.ts.vanilla-Bhh1L5yl.css} +14 -11
- package/dist/esm/assets/src/components/primitives/Tooltip/{Tooltip.css.ts.vanilla-CrEFoLBX.css → Tooltip.css.ts.vanilla-BqaiGrFQ.css} +12 -9
- package/dist/esm/assets/src/components/shell/AppShell/{AppShell.css.ts.vanilla-BYmtU0O_.css → AppShell.css.ts.vanilla-BLK2-DRL.css} +1 -0
- package/dist/esm/assets/src/components/shell/MenuBar/{MenuBar.css.ts.vanilla-TgPcl4yA.css → MenuBar.css.ts.vanilla-Dt3w4f-q.css} +18 -6
- 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.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.map +1 -1
- 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.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.map +1 -1
- 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.map +1 -1
- package/dist/esm/components/controls/ColorPicker/ColorInputs.js.map +1 -1
- package/dist/esm/components/controls/ColorPicker/ColorPalette.js.map +1 -1
- package/dist/esm/components/controls/ColorPicker/ColorPicker.js.map +1 -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.map +1 -1
- package/dist/esm/components/controls/CurveEditor/CurveCanvas.js.map +1 -1
- package/dist/esm/components/controls/CurveEditor/CurveEditor.js.map +1 -1
- 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.map +1 -1
- package/dist/esm/components/controls/CurveEditor/useCurveInteraction.js.map +1 -1
- package/dist/esm/components/controls/CurveEditor/useCurveRenderer.js.map +1 -1
- package/dist/esm/components/controls/FileTree/FileTree.js +7 -1
- package/dist/esm/components/controls/FileTree/FileTree.js.map +1 -1
- package/dist/esm/components/controls/FileTree/fileTreeIcons.js.map +1 -1
- package/dist/esm/components/controls/FileTree/fileTreeLabels.js.map +1 -1
- package/dist/esm/components/controls/FileTree/useFileTreeDrop.js +1 -1
- package/dist/esm/components/controls/FileTree/useFileTreeDrop.js.map +1 -1
- package/dist/esm/components/controls/FileUploader/FileUploader.js.map +1 -1
- package/dist/esm/components/controls/MultiSelect/MultiSelect.js.map +1 -1
- package/dist/esm/components/controls/NumberInput/NumberInput.js.map +1 -1
- 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 +15 -7
- package/dist/esm/components/controls/Select/Select.js.map +1 -1
- package/dist/esm/components/controls/Slider/Slider.css.js +10 -11
- package/dist/esm/components/controls/Slider/Slider.css.js.map +1 -1
- package/dist/esm/components/controls/Slider/Slider.js +40 -7
- package/dist/esm/components/controls/Slider/Slider.js.map +1 -1
- package/dist/esm/components/controls/TagInput/TagInput.js.map +1 -1
- package/dist/esm/components/controls/TreeView/TreeNode.js +6 -0
- package/dist/esm/components/controls/TreeView/TreeNode.js.map +1 -1
- package/dist/esm/components/controls/TreeView/TreeView.js.map +1 -1
- package/dist/esm/components/controls/TreeView/useTreeState.js.map +1 -1
- package/dist/esm/components/controls/VectorInput/VectorInput.js +6 -0
- package/dist/esm/components/controls/VectorInput/VectorInput.js.map +1 -1
- package/dist/esm/components/data/DataTable/DataTable.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/AssetBrowser.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserAnnouncementLive.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserBreadcrumbs.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserContent.js +6 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserContent.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserContext.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserContextMenu.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserDefaultMenus.js +6 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserDefaultMenus.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserGrid.js +1 -1
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserGrid.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserGridItem.js +6 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserGridItem.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserList.js +6 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserList.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserRenameField.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserSidebar.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserStatusBar.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserStore.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserSurface.js +6 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserSurface.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserToolbar.js +6 -0
- package/dist/esm/components/editor/AssetBrowser/AssetBrowserToolbar.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/assetBrowserFilter.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/assetBrowserFormat.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/assetBrowserGeometry.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/assetBrowserKeyboard.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/assetBrowserLabels.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/slots.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/useAssetBrowserAnnouncement.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/useAssetBrowserHandle.js +1 -1
- package/dist/esm/components/editor/AssetBrowser/useAssetBrowserHandle.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/useAssetBrowserViewState.js +1 -1
- package/dist/esm/components/editor/AssetBrowser/useAssetBrowserViewState.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/useAssetDnd.js +1 -1
- package/dist/esm/components/editor/AssetBrowser/useAssetDnd.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/useAssetGridKeyboardNav.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/useAssetGridVirtualizer.js +1 -1
- package/dist/esm/components/editor/AssetBrowser/useAssetGridVirtualizer.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/useAssetMarqueeGesture.js +1 -1
- package/dist/esm/components/editor/AssetBrowser/useAssetMarqueeGesture.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/useAssetMutationKeyboard.js +1 -1
- package/dist/esm/components/editor/AssetBrowser/useAssetMutationKeyboard.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/useAssetMutations.js +1 -1
- package/dist/esm/components/editor/AssetBrowser/useAssetMutations.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/useAssetNavigation.js +1 -1
- package/dist/esm/components/editor/AssetBrowser/useAssetNavigation.js.map +1 -1
- package/dist/esm/components/editor/AssetBrowser/useAssetSelectionController.js +1 -1
- package/dist/esm/components/editor/AssetBrowser/useAssetSelectionController.js.map +1 -1
- package/dist/esm/components/editor/ChatPanel/ChatActionBar.js.map +1 -1
- 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 +6 -0
- package/dist/esm/components/editor/ChatPanel/ChatCodeBlock.js.map +1 -1
- package/dist/esm/components/editor/ChatPanel/ChatContextChip.js.map +1 -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 +6 -0
- 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.map +1 -1
- package/dist/esm/components/editor/ChatPanel/ChatMessage.js +6 -0
- package/dist/esm/components/editor/ChatPanel/ChatMessage.js.map +1 -1
- 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.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.map +1 -1
- package/dist/esm/components/editor/ChatPanel/useChatScroll.js.map +1 -1
- package/dist/esm/components/editor/Minimap/Minimap.js +1 -1
- package/dist/esm/components/editor/Minimap/Minimap.js.map +1 -1
- package/dist/esm/components/editor/Minimap/Minimap.types.js.map +1 -1
- package/dist/esm/components/editor/Minimap/MinimapContext.js.map +1 -1
- package/dist/esm/components/editor/Minimap/MinimapCorner.js.map +1 -1
- package/dist/esm/components/editor/Minimap/MinimapFooter.js.map +1 -1
- package/dist/esm/components/editor/Minimap/MinimapStore.js.map +1 -1
- package/dist/esm/components/editor/Minimap/MinimapTitle.js.map +1 -1
- package/dist/esm/components/editor/Minimap/ViewportMinimap.js +1 -1
- package/dist/esm/components/editor/Minimap/ViewportMinimap.js.map +1 -1
- package/dist/esm/components/editor/Minimap/computeBoundsFromItems.js.map +1 -1
- package/dist/esm/components/editor/Minimap/index.js.map +1 -1
- package/dist/esm/components/editor/Minimap/minimapCoords.js.map +1 -1
- package/dist/esm/components/editor/Minimap/minimapDrawing.js.map +1 -1
- package/dist/esm/components/editor/Minimap/minimapHitTest.js.map +1 -1
- package/dist/esm/components/editor/Minimap/useMinimapGestures.js +1 -1
- package/dist/esm/components/editor/Minimap/useMinimapGestures.js.map +1 -1
- package/dist/esm/components/editor/NodeGraph/NodeGraph.js +1 -1
- package/dist/esm/components/editor/NodeGraph/NodeGraph.js.map +1 -1
- package/dist/esm/components/editor/NodeGraph/NodeGraph.types.js.map +1 -1
- package/dist/esm/components/editor/NodeGraph/NodeGraphContext.js.map +1 -1
- package/dist/esm/components/editor/NodeGraph/NodeGraphEdgeLabels.js.map +1 -1
- package/dist/esm/components/editor/NodeGraph/NodeGraphGroup.js.map +1 -1
- package/dist/esm/components/editor/NodeGraph/NodeGraphMinimap.js.map +1 -1
- package/dist/esm/components/editor/NodeGraph/NodeGraphNode.js +1 -1
- package/dist/esm/components/editor/NodeGraph/NodeGraphNode.js.map +1 -1
- package/dist/esm/components/editor/NodeGraph/NodeGraphNodeContext.js.map +1 -1
- package/dist/esm/components/editor/NodeGraph/NodeGraphNodeParts.js.map +1 -1
- package/dist/esm/components/editor/NodeGraph/NodeGraphNodeSection.js +1 -1
- package/dist/esm/components/editor/NodeGraph/NodeGraphNodeSection.js.map +1 -1
- package/dist/esm/components/editor/NodeGraph/NodeGraphNodeSectionContext.js.map +1 -1
- package/dist/esm/components/editor/NodeGraph/NodeGraphPin.js.map +1 -1
- package/dist/esm/components/editor/NodeGraph/NodeGraphPort.js +1 -1
- package/dist/esm/components/editor/NodeGraph/NodeGraphPort.js.map +1 -1
- package/dist/esm/components/editor/NodeGraph/NodeGraphPortVisual.js.map +1 -1
- package/dist/esm/components/editor/NodeGraph/NodeGraphSlots.js.map +1 -1
- package/dist/esm/components/editor/NodeGraph/NodeGraphSpawnPalette.js.map +1 -1
- package/dist/esm/components/editor/NodeGraph/NodeGraphStore.js.map +1 -1
- package/dist/esm/components/editor/NodeGraph/NodeGraphToolbar.js.map +1 -1
- package/dist/esm/components/editor/NodeGraph/createTypeMatchValidator.js.map +1 -1
- package/dist/esm/components/editor/NodeGraph/nodeGraphActions.js.map +1 -1
- package/dist/esm/components/editor/NodeGraph/nodeGraphDrawing.js.map +1 -1
- package/dist/esm/components/editor/NodeGraph/nodeGraphIds.js.map +1 -1
- package/dist/esm/components/editor/NodeGraph/nodeGraphMath.js.map +1 -1
- package/dist/esm/components/editor/NodeGraph/sortSlots.js.map +1 -1
- package/dist/esm/components/editor/NodeGraph/useDragGesture.js.map +1 -1
- package/dist/esm/components/editor/NodeGraph/useNodeGraph.js +1 -1
- package/dist/esm/components/editor/NodeGraph/useNodeGraph.js.map +1 -1
- package/dist/esm/components/editor/NodeGraph/useNodeGraphConnection.js +1 -1
- package/dist/esm/components/editor/NodeGraph/useNodeGraphConnection.js.map +1 -1
- package/dist/esm/components/editor/NodeGraph/useNodeGraphEdgeInteraction.js +1 -1
- package/dist/esm/components/editor/NodeGraph/useNodeGraphEdgeInteraction.js.map +1 -1
- package/dist/esm/components/editor/NodeGraph/useNodeGraphGroupDrag.js +1 -1
- package/dist/esm/components/editor/NodeGraph/useNodeGraphGroupDrag.js.map +1 -1
- package/dist/esm/components/editor/NodeGraph/useNodeGraphKeyboard.js +1 -1
- package/dist/esm/components/editor/NodeGraph/useNodeGraphKeyboard.js.map +1 -1
- package/dist/esm/components/editor/NodeGraph/useNodeGraphNodeDrag.js +1 -1
- package/dist/esm/components/editor/NodeGraph/useNodeGraphNodeDrag.js.map +1 -1
- package/dist/esm/components/editor/NodeGraph/useStoreSlice.js.map +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 +5 -4
- 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 +18 -3
- 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 +29 -21
- 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.js +1 -1
- package/dist/esm/components/editor/Timeline/Timeline.js.map +1 -1
- package/dist/esm/components/editor/Timeline/Timeline.types.js.map +1 -1
- package/dist/esm/components/editor/Timeline/TimelineContext.js.map +1 -1
- package/dist/esm/components/editor/Timeline/TimelineFooter.js.map +1 -1
- package/dist/esm/components/editor/Timeline/TimelineMinimap.js +1 -1
- package/dist/esm/components/editor/Timeline/TimelineMinimap.js.map +1 -1
- package/dist/esm/components/editor/Timeline/TimelineStore.js.map +1 -1
- package/dist/esm/components/editor/Timeline/TimelineToolbar.js.map +1 -1
- package/dist/esm/components/editor/Timeline/TimelineTrackHeaders.js +1 -1
- package/dist/esm/components/editor/Timeline/TimelineTrackHeaders.js.map +1 -1
- package/dist/esm/components/editor/Timeline/TimelineTrackScale.js.map +1 -1
- package/dist/esm/components/editor/Timeline/index.js.map +1 -1
- package/dist/esm/components/editor/Timeline/timelineCoords.js.map +1 -1
- package/dist/esm/components/editor/Timeline/timelineDrawing.js.map +1 -1
- package/dist/esm/components/editor/Timeline/timelineEdits.js.map +1 -1
- package/dist/esm/components/editor/Timeline/timelineHitTest.js.map +1 -1
- package/dist/esm/components/editor/Timeline/timelineLayout.js.map +1 -1
- package/dist/esm/components/editor/Timeline/timelineSelection.js.map +1 -1
- package/dist/esm/components/editor/Timeline/useTimelineDraw.js +1 -1
- package/dist/esm/components/editor/Timeline/useTimelineDraw.js.map +1 -1
- package/dist/esm/components/editor/Timeline/useTimelineGestures.js +1 -1
- package/dist/esm/components/editor/Timeline/useTimelineGestures.js.map +1 -1
- package/dist/esm/components/editor/Timeline/useTimelinePlayback.js.map +1 -1
- package/dist/esm/components/editor/TransformControl/TransformControl.js.map +1 -1
- package/dist/esm/components/editor/ViewportGizmo/ViewportGizmo.js.map +1 -1
- package/dist/esm/components/editor/ViewportGizmo/gizmoMath.js.map +1 -1
- package/dist/esm/components/editor/ViewportGizmo/useGizmoInteraction.js.map +1 -1
- package/dist/esm/components/editor/ViewportGizmo/useGizmoRenderer.js.map +1 -1
- 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.map +1 -1
- package/dist/esm/components/feedback/CommandPalette/fuzzySearch.js.map +1 -1
- package/dist/esm/components/feedback/CommandPalette/useRecentItems.js.map +1 -1
- 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.map +1 -1
- package/dist/esm/components/feedback/Dialog/useDialogAnimation.js.map +1 -1
- package/dist/esm/components/feedback/Drawer/Drawer.js.map +1 -1
- package/dist/esm/components/feedback/Drawer/useDrawerAnimation.js.map +1 -1
- package/dist/esm/components/feedback/EmptyState/EmptyState.js.map +1 -1
- package/dist/esm/components/feedback/LogView/LogView.js.map +1 -1
- package/dist/esm/components/feedback/LogView/LogViewBody.js.map +1 -1
- package/dist/esm/components/feedback/LogView/LogViewContext.js.map +1 -1
- package/dist/esm/components/feedback/LogView/LogViewParts.js.map +1 -1
- package/dist/esm/components/feedback/LogView/LogViewStore.js.map +1 -1
- package/dist/esm/components/feedback/LogView/logViewLabels.js.map +1 -1
- package/dist/esm/components/feedback/LogView/logViewLevels.js.map +1 -1
- package/dist/esm/components/feedback/LogView/logViewUtils.js.map +1 -1
- package/dist/esm/components/feedback/LogView/useFollowTail.js.map +1 -1
- package/dist/esm/components/feedback/LogView/useLogSelection.js.map +1 -1
- package/dist/esm/components/feedback/ProgressBar/CircularProgress.js.map +1 -1
- package/dist/esm/components/feedback/ProgressBar/ProgressBar.js.map +1 -1
- package/dist/esm/components/feedback/Skeleton/Skeleton.js.map +1 -1
- package/dist/esm/components/feedback/Skeleton/SkeletonGroup.js.map +1 -1
- package/dist/esm/components/feedback/Skeleton/SkeletonLayout.js.map +1 -1
- package/dist/esm/components/feedback/Spinner/Spinner.js +12 -3
- 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.map +1 -1
- 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.map +1 -1
- package/dist/esm/components/layout/Accordion/AccordionContent.js.map +1 -1
- package/dist/esm/components/layout/Accordion/AccordionItem.js.map +1 -1
- package/dist/esm/components/layout/Accordion/AccordionTrigger.js.map +1 -1
- 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.map +1 -1
- package/dist/esm/components/layout/Grid/Grid.js.map +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.map +1 -1
- 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.map +1 -1
- package/dist/esm/components/layout/SplitPane/SplitPanePanel.js.map +1 -1
- package/dist/esm/components/layout/Stack/Stack.js.map +1 -1
- package/dist/esm/components/navigation/Breadcrumbs/BreadcrumbEllipsis.js.map +1 -1
- 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 +6 -0
- package/dist/esm/components/navigation/Breadcrumbs/Breadcrumbs.js.map +1 -1
- package/dist/esm/components/navigation/ContextMenu/ContextMenu.js +2 -2
- package/dist/esm/components/navigation/ContextMenu/ContextMenu.js.map +1 -1
- package/dist/esm/components/navigation/Menu/Menu.css.js +11 -10
- package/dist/esm/components/navigation/Menu/Menu.css.js.map +1 -1
- package/dist/esm/components/navigation/Menu/Menu.js +2 -2
- package/dist/esm/components/navigation/Menu/Menu.js.map +1 -1
- package/dist/esm/components/navigation/Menu/MenuGapContext.js.map +1 -1
- 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.js +7 -1
- package/dist/esm/components/navigation/PathBar/PathBar.js.map +1 -1
- package/dist/esm/components/navigation/PathBar/PathBarSiblingMenu.js +6 -0
- package/dist/esm/components/navigation/PathBar/PathBarSiblingMenu.js.map +1 -1
- package/dist/esm/components/navigation/PathBar/pathUtils.js.map +1 -1
- package/dist/esm/components/navigation/SegmentedControl/SegmentedControl.js.map +1 -1
- package/dist/esm/components/navigation/SegmentedControl/SegmentedControlItem.js.map +1 -1
- package/dist/esm/components/navigation/Tabs/Tab.js.map +1 -1
- package/dist/esm/components/navigation/Tabs/TabList.js.map +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 +6 -0
- package/dist/esm/components/primitives/Avatar/Avatar.js.map +1 -1
- package/dist/esm/components/primitives/Avatar/AvatarGroup.js.map +1 -1
- package/dist/esm/components/primitives/Badge/Badge.js +6 -0
- 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.map +1 -1
- 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.map +1 -1
- package/dist/esm/components/primitives/HoverCard/HoverCard.js.map +1 -1
- package/dist/esm/components/primitives/Icon/Icon.js.map +1 -1
- package/dist/esm/components/primitives/IconButton/IconButton.js +4 -2
- package/dist/esm/components/primitives/IconButton/IconButton.js.map +1 -1
- package/dist/esm/components/primitives/Input/Input.js.map +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.map +1 -1
- package/dist/esm/components/primitives/Popover/PopoverClose.js.map +1 -1
- 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.map +1 -1
- package/dist/esm/components/primitives/Radio/RadioGroup.js.map +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.map +1 -1
- package/dist/esm/components/primitives/Tooltip/Arrow.js.map +1 -1
- package/dist/esm/components/primitives/Tooltip/Tooltip.css.js +6 -5
- package/dist/esm/components/primitives/Tooltip/Tooltip.css.js.map +1 -1
- package/dist/esm/components/primitives/Tooltip/Tooltip.js +12 -2
- package/dist/esm/components/primitives/Tooltip/Tooltip.js.map +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.map +1 -1
- package/dist/esm/components/primitives/canvas/canvasCoords.js.map +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.map +1 -1
- package/dist/esm/components/primitives/viewport/Viewport.js +6 -3
- package/dist/esm/components/primitives/viewport/Viewport.js.map +1 -1
- package/dist/esm/components/primitives/viewport/ViewportContext.js.map +1 -1
- package/dist/esm/components/primitives/viewport/ViewportLayer.js +1 -1
- package/dist/esm/components/primitives/viewport/ViewportLayer.js.map +1 -1
- package/dist/esm/components/primitives/viewport/ViewportOverlay.js.map +1 -1
- package/dist/esm/components/primitives/viewport/ViewportStore.js.map +1 -1
- package/dist/esm/components/primitives/viewport/ViewportWorld.js.map +1 -1
- package/dist/esm/components/primitives/viewport/useViewportGestures.js +1 -1
- package/dist/esm/components/primitives/viewport/useViewportGestures.js.map +1 -1
- package/dist/esm/components/primitives/viewport/viewportCoords.js.map +1 -1
- package/dist/esm/components/primitives/viewport/viewportMath.js.map +1 -1
- package/dist/esm/components/shell/AppShell/AppShell.css.js +1 -1
- package/dist/esm/components/shell/AppShell/AppShell.js.map +1 -1
- package/dist/esm/components/shell/FloatingPanel/FloatingPanel.js.map +1 -1
- package/dist/esm/components/shell/MenuBar/MenuBar.css.js +7 -6
- package/dist/esm/components/shell/MenuBar/MenuBar.css.js.map +1 -1
- package/dist/esm/components/shell/MenuBar/MenuBar.js +52 -3
- 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.map +1 -1
- package/dist/esm/context/KeyboardContext.js.map +1 -1
- package/dist/esm/hooks/useBreakpoint/useBreakpoint.js.map +1 -1
- package/dist/esm/hooks/useClickOutside/useClickOutside.js.map +1 -1
- 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.map +1 -1
- 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.map +1 -1
- package/dist/esm/hooks/useFocusTrap/useFocusTrap.js.map +1 -1
- package/dist/esm/hooks/useHotkey/useHotkey.js.map +1 -1
- package/dist/esm/hooks/useIntersectionObserver/useIntersectionObserver.js.map +1 -1
- package/dist/esm/hooks/useIsMounted/useIsMounted.js.map +1 -1
- 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.map +1 -1
- package/dist/esm/hooks/useListboxNav/useListboxNav.js.map +1 -1
- package/dist/esm/hooks/useMediaQuery/useMediaQuery.js.map +1 -1
- package/dist/esm/hooks/useMergedRef/useMergedRef.js.map +1 -1
- package/dist/esm/hooks/useNavigationHistory/useNavigationHistory.js.map +1 -1
- package/dist/esm/hooks/useResizeObserver/useResizeObserver.js.map +1 -1
- package/dist/esm/hooks/useTheme/useTheme.js +1 -1
- package/dist/esm/hooks/useTheme/useTheme.js.map +1 -1
- package/dist/esm/hooks/useThrottledCallback/useThrottledCallback.js.map +1 -1
- package/dist/esm/index.js +6 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/node_modules/tslib/tslib.es6.js.map +1 -1
- package/dist/esm/styles.css.js +3 -0
- package/dist/esm/styles.css.js.map +1 -0
- package/dist/esm/theme/ThemeProvider.js +5 -3
- 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/createCustomTheme.js +1 -1
- package/dist/esm/theme/createCustomTheme.js.map +1 -1
- package/dist/esm/theme/createLightTheme.js.map +1 -1
- package/dist/esm/theme/darkThemeValues.js.map +1 -1
- package/dist/esm/theme/lightThemeValues.js.map +1 -1
- package/dist/esm/theme/themeContractData.js.map +1 -1
- package/dist/esm/utils/cx.js.map +1 -1
- package/dist/esm/utils/devWarn.js.map +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/CameraIcon.d.ts +34 -0
- package/dist/types/components/Icons/CubeIcon.d.ts +34 -0
- package/dist/types/components/Icons/OrbitIcon.d.ts +34 -0
- package/dist/types/components/Icons/RotateIcon.d.ts +35 -0
- package/dist/types/components/Icons/SpinIcon.d.ts +35 -0
- package/dist/types/components/Icons/ZoomFitIcon.d.ts +35 -0
- package/dist/types/components/controls/Select/Select.d.ts +1 -1
- package/dist/types/components/controls/Select/Select.types.d.ts +43 -9
- package/dist/types/components/controls/Slider/Slider.d.ts +7 -0
- package/dist/types/components/editor/PropertyInspector/PropertyInspector.types.d.ts +49 -3
- package/dist/types/components/feedback/Spinner/Spinner.d.ts +6 -1
- package/dist/types/components/feedback/Spinner/Spinner.types.d.ts +8 -0
- package/dist/types/components/primitives/Button/Button.d.ts +1 -1
- package/dist/types/components/primitives/IconButton/IconButton.d.ts +3 -2
- package/dist/types/components/primitives/viewport/Viewport.d.ts +5 -2
- package/dist/types/components/shell/MenuBar/MenuBar.d.ts +862 -0
- package/dist/types/index.d.ts +6 -0
- package/dist/types/theme/ThemeProvider.d.ts +5 -3
- package/package.json +6 -2
- 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/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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FloatingPanel.js","sources":["../../../../../../src/components/shell/FloatingPanel/FloatingPanel.tsx"],"sourcesContent":[null],"names":[],"mappings":";;;;;;;;;;;;;AA+CA;AAEA;AACE;AAEF;AAEO;AAIL;;AAGA;;;;;;AAOA;AACE;;;AAIF;AACE;;AACoC;;;;AAKtC;;AAGI;AACF;AAIF;;;;;;AASA;AAKF;AAEA;AAEA;;AA6BE;AACA;AACA;;;AAKA;AACA;AAEI;;AACA;AACF;;;AAOF;AACA;AAEI;;AACA;AACF;;;AAMF;AACA;AACE;;;AAEA;;AAGF;AAEI;;AAEE;;AAEJ;AAIF;AAEI;;;;AAIF;;;;AAOF;;AAGE;;;;;;;;;AASF;AAEA;AACE;AACF;;;AAKA;;AAGA;;AAGI;AACA;;;AAIA;AACA;;;AAIA;AACE;AACA;;;;AAIE;AACA;;AAEA;AACA;AACF;AACE;AACA;AACD;AACH;;AAGE;AACA;AACF;AAEA;AACA;AACF;;AAKF;;;AAII;AACA;AAEA;AACA;AACA;AACA;AAEA;AACE;AACA;AACA;AACA;;AAEF;;AAGE;AACA;AACF;AAEA;AACA;AACF;;AAKA;AACA;AACA;AACA;AACA;AACD;AAED;AAWM;AACA;;AA0DR;AAEA;;"}
|
|
1
|
+
{"version":3,"file":"FloatingPanel.js","sources":["src/components/shell/FloatingPanel/FloatingPanel.tsx"],"sourcesContent":["'use client';\n\nimport { assignInlineVars } from '@vanilla-extract/dynamic';\nimport React, {\n createContext,\n useContext,\n useState,\n useCallback,\n useRef,\n useEffect,\n useId,\n useMemo,\n} from 'react';\n\nimport { ChevronDownIcon } from '@/components/Icons/ChevronDownIcon';\nimport { ChevronUpIcon } from '@/components/Icons/ChevronUpIcon';\nimport { CloseIcon } from '@/components/Icons/CloseIcon';\nimport { ScrollArea } from '@/components/layout/ScrollArea';\nimport { useLatest } from '@/hooks/useLatest';\nimport { useMergedRef } from '@/hooks/useMergedRef';\nimport { cx } from '@/utils/cx';\n\nimport {\n posXVar,\n posYVar,\n panelWidthVar,\n panelHeightVar,\n panelZIndexVar,\n managerRoot,\n panel,\n header,\n title,\n headerActions,\n headerButton,\n collapsibleVisible,\n collapsibleHidden,\n resizeHandle,\n} from './FloatingPanel.css';\n\nimport type {\n FloatingPanelProps,\n FloatingManagerProps,\n FloatingManagerContextValue,\n Position,\n FloatingPanelSize,\n} from './FloatingPanel.types';\n\n// --- FloatingManager Context ---\n\nconst FloatingManagerContext =\n /*#__PURE__*/ createContext<FloatingManagerContextValue | null>(null);\n\nconst useFloatingManager = () => useContext(FloatingManagerContext);\n\nexport const FloatingManager: React.FC<FloatingManagerProps> = ({\n baseZIndex = 100,\n children,\n}) => {\n const stackRef = useRef<string[]>([]);\n const [revision, setRevision] = useState(0);\n\n const register = useCallback((id: string) => {\n if (!stackRef.current.includes(id)) {\n stackRef.current = [...stackRef.current, id];\n setRevision(c => c + 1);\n }\n }, []);\n\n const unregister = useCallback((id: string) => {\n stackRef.current = stackRef.current.filter(s => s !== id);\n setRevision(c => c + 1);\n }, []);\n\n const bringToFront = useCallback((id: string) => {\n const stack = stackRef.current;\n if (stack[stack.length - 1] === id) return; // already on top\n stackRef.current = [...stack.filter(s => s !== id), id];\n setRevision(c => c + 1);\n }, []);\n\n const getZIndex = useCallback(\n (id: string) => {\n const index = stackRef.current.indexOf(id);\n return baseZIndex + (index >= 0 ? index : 0);\n },\n [baseZIndex]\n );\n\n const contextValue = useMemo<FloatingManagerContextValue>(\n () => ({ bringToFront, getZIndex, register, unregister }),\n // `revision` is bumped whenever the panel stack changes; it intentionally\n // busts this memo so consumers re-render and re-read z-indices from the\n // (otherwise stable) getZIndex, even though it is not read in the factory.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [revision, bringToFront, getZIndex, register, unregister]\n );\n\n return (\n <FloatingManagerContext.Provider value={contextValue}>\n <div className={managerRoot}>{children}</div>\n </FloatingManagerContext.Provider>\n );\n};\n\nFloatingManager.displayName = 'FloatingManager';\n\n// --- FloatingPanel Component ---\n\nexport const FloatingPanel: React.FC<FloatingPanelProps> = ({\n title: panelTitle,\n position: controlledPosition,\n defaultPosition = { x: 100, y: 100 },\n onPositionChange,\n size: controlledSize,\n defaultSize = { width: 280, height: 200 },\n onSizeChange,\n minWidth = 150,\n minHeight = 100,\n maxWidth = Infinity,\n maxHeight = Infinity,\n collapsed: controlledCollapsed,\n defaultCollapsed = false,\n onCollapsedChange,\n onClose,\n closable = true,\n resizable = true,\n children,\n\n className,\n panelId,\n style,\n testId,\n ref: externalRef,\n ...rest\n}) => {\n const generatedId = useId();\n const id = panelId ?? generatedId;\n const manager = useFloatingManager();\n\n // Position state\n const [internalPosition, setInternalPosition] =\n useState<Position>(defaultPosition);\n const pos = controlledPosition ?? internalPosition;\n const setPos = useCallback(\n (p: Position) => {\n if (!controlledPosition) setInternalPosition(p);\n onPositionChange?.(p);\n },\n [controlledPosition, onPositionChange]\n );\n\n // Size state\n const [internalSize, setInternalSize] =\n useState<FloatingPanelSize>(defaultSize);\n const currentSize = controlledSize ?? internalSize;\n const setSize = useCallback(\n (s: FloatingPanelSize) => {\n if (!controlledSize) setInternalSize(s);\n onSizeChange?.(s);\n },\n [controlledSize, onSizeChange]\n );\n\n // Collapsed state\n const [internalCollapsed, setInternalCollapsed] = useState(defaultCollapsed);\n const isCollapsed = controlledCollapsed ?? internalCollapsed;\n const toggleCollapse = useCallback(() => {\n const next = !isCollapsed;\n if (controlledCollapsed === undefined) setInternalCollapsed(next);\n onCollapsedChange?.(next);\n }, [isCollapsed, controlledCollapsed, onCollapsedChange]);\n\n const handleCollapseKeyDown = useCallback(\n (e: React.KeyboardEvent) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n toggleCollapse();\n }\n },\n [toggleCollapse]\n );\n\n const handleCloseKeyDown = useCallback(\n (e: React.KeyboardEvent) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n onClose?.();\n }\n },\n [onClose]\n );\n\n // FloatingManager registration -- use stable id, run once. Latest manager via\n // useLatest so the register/unregister effect and handlers read it without\n // writing a ref during render.\n const managerRef = useLatest(manager);\n\n useEffect(() => {\n managerRef.current?.register(id);\n // Reading the latest manager from the ref at cleanup is intentional.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n return () => managerRef.current?.unregister(id);\n // Register/unregister exactly once per id. managerRef is a stable useLatest\n // ref read at both register and unregister time; the manager is read via\n // the ref because the manager context value changes on every panel\n // registration and must not re-trigger this effect.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [id]);\n\n const handleBringToFront = useCallback(() => {\n managerRef.current?.bringToFront(id);\n }, [id, managerRef]);\n\n const zIndex = manager?.getZIndex(id) ?? 100;\n\n // --- Drag ---\n const panelRef = useRef<HTMLDivElement>(null);\n const setPanelRef = useMergedRef<HTMLDivElement>(panelRef, externalRef);\n\n const handleDragStart = useCallback(\n (e: React.PointerEvent) => {\n e.preventDefault();\n const target = e.currentTarget as HTMLElement;\n target.setPointerCapture(e.pointerId);\n\n // Track the pointer delta from the drag start, so positioning is\n // independent of the coordinate space the panel lives in.\n const startPointer = { x: e.clientX, y: e.clientY };\n const startPos = { x: pos.x, y: pos.y };\n const bounds = panelRef.current?.offsetParent?.getBoundingClientRect();\n const panelRect = panelRef.current?.getBoundingClientRect();\n\n const handleDragMove = (me: PointerEvent) => {\n const newX = startPos.x + (me.clientX - startPointer.x);\n const newY = startPos.y + (me.clientY - startPointer.y);\n // Clamp within the positioned container when one is available,\n // keeping a small margin of the panel on-screen.\n const maxX = bounds\n ? Math.max(0, bounds.width - (panelRect?.width ?? 50))\n : Number.POSITIVE_INFINITY;\n const maxY = bounds\n ? Math.max(0, bounds.height - 30)\n : Number.POSITIVE_INFINITY;\n setPos({\n x: Math.max(0, Math.min(newX, maxX)),\n y: Math.max(0, Math.min(newY, maxY)),\n });\n };\n\n const handleDragEnd = () => {\n target.removeEventListener('pointermove', handleDragMove);\n target.removeEventListener('pointerup', handleDragEnd);\n };\n\n target.addEventListener('pointermove', handleDragMove);\n target.addEventListener('pointerup', handleDragEnd);\n },\n [pos, setPos]\n );\n\n // --- Resize (bottom-right corner only) ---\n const handleResizeStart = useCallback(\n (e: React.PointerEvent) => {\n e.preventDefault();\n e.stopPropagation();\n const target = e.currentTarget as HTMLElement;\n target.setPointerCapture(e.pointerId);\n\n const startX = e.clientX;\n const startY = e.clientY;\n const startW = currentSize.width;\n const startH = currentSize.height;\n\n const handleResizeMove = (me: PointerEvent) => {\n const dx = me.clientX - startX;\n const dy = me.clientY - startY;\n const newW = Math.min(maxWidth, Math.max(minWidth, startW + dx));\n const newH = Math.min(maxHeight, Math.max(minHeight, startH + dy));\n setSize({ width: newW, height: newH });\n };\n\n const handleResizeEnd = () => {\n target.removeEventListener('pointermove', handleResizeMove);\n target.removeEventListener('pointerup', handleResizeEnd);\n };\n\n target.addEventListener('pointermove', handleResizeMove);\n target.addEventListener('pointerup', handleResizeEnd);\n },\n [currentSize, minWidth, minHeight, maxWidth, maxHeight, setSize]\n );\n\n const inlineVars = assignInlineVars({\n [posXVar]: `${pos.x}px`,\n [posYVar]: `${pos.y}px`,\n [panelWidthVar]: `${currentSize.width}px`,\n [panelHeightVar]: isCollapsed ? 'auto' : `${currentSize.height}px`,\n [panelZIndexVar]: String(zIndex),\n });\n\n return (\n <div\n ref={setPanelRef}\n className={cx(panel, className)}\n data-testid={testId}\n role=\"dialog\"\n aria-modal=\"false\"\n aria-label={panelTitle}\n tabIndex={-1}\n onPointerDown={handleBringToFront}\n style={{\n ...style,\n ...inlineVars,\n }}\n {...rest}\n >\n <div\n className={header}\n onPointerDown={handleDragStart}\n data-testid=\"panel-header\"\n >\n <span className={title}>{panelTitle}</span>\n <div className={headerActions}>\n <span\n className={headerButton}\n role=\"button\"\n tabIndex={0}\n onClick={toggleCollapse}\n onKeyDown={handleCollapseKeyDown}\n aria-label={isCollapsed ? 'Expand panel' : 'Collapse panel'}\n >\n {isCollapsed ? (\n <ChevronDownIcon size=\"sm\" color=\"secondary\" decorative />\n ) : (\n <ChevronUpIcon size=\"sm\" color=\"secondary\" decorative />\n )}\n </span>\n {closable && onClose && (\n <span\n className={headerButton}\n role=\"button\"\n tabIndex={0}\n onClick={onClose}\n onKeyDown={handleCloseKeyDown}\n aria-label=\"Close panel\"\n >\n <CloseIcon size=\"sm\" color=\"secondary\" decorative />\n </span>\n )}\n </div>\n </div>\n <div className={isCollapsed ? collapsibleHidden : collapsibleVisible}>\n <ScrollArea\n direction=\"both\"\n scrollbarVisibility=\"auto\"\n scrollbarWidth={5}\n autoFill\n >\n {children}\n </ScrollArea>\n </div>\n {resizable && !isCollapsed && (\n <div\n className={resizeHandle}\n onPointerDown={handleResizeStart}\n data-testid=\"resize-handle\"\n />\n )}\n </div>\n );\n};\n\nFloatingPanel.displayName = 'FloatingPanel';\n"],"names":[],"mappings":";;;;;;;;;;;;;AA+CA;AAEA;AACE;AAEF;AAEO;AAIL;;AAGA;;;;;;AAOA;AACE;;;AAIF;AACE;;AACoC;;;;AAKtC;;AAGI;AACF;AAIF;;;;;;AASA;AAKF;AAEA;AAEA;;AA6BE;AACA;AACA;;;AAKA;AACA;AAEI;;AACA;AACF;;;AAOF;AACA;AAEI;;AACA;AACF;;;AAMF;AACA;AACE;;;AAEA;;AAGF;AAEI;;AAEE;;AAEJ;AAIF;AAEI;;;;AAIF;;;;AAOF;;AAGE;;;;;;;;;AASF;AAEA;AACE;AACF;;;AAKA;;AAGA;;AAGI;AACA;;;AAIA;AACA;;;AAIA;AACE;AACA;;;;AAIE;AACA;;AAEA;AACA;AACF;AACE;AACA;AACD;AACH;;AAGE;AACA;AACF;AAEA;AACA;AACF;;AAKF;;;AAII;AACA;AAEA;AACA;AACA;AACA;AAEA;AACE;AACA;AACA;AACA;;AAEF;;AAGE;AACA;AACF;AAEA;AACA;AACF;;AAKA;AACA;AACA;AACA;AACA;AACD;AAED;AAWM;AACA;;AA0DR;AAEA;;"}
|
|
@@ -1,16 +1,17 @@
|
|
|
1
|
-
import './../../../assets/src/components/shell/MenuBar/MenuBar.css.ts.vanilla-
|
|
1
|
+
import './../../../assets/src/components/shell/MenuBar/MenuBar.css.ts.vanilla-Dt3w4f-q.css';
|
|
2
2
|
import { createRuntimeFn } from '@vanilla-extract/recipes/createRuntimeFn';
|
|
3
3
|
|
|
4
4
|
var dropdown = 'MenuBar_dropdown__1h81mxv7';
|
|
5
|
+
var indicatorSlot = 'MenuBar_indicatorSlot__1h81mxva';
|
|
5
6
|
var menuBarRoot = createRuntimeFn({defaultClassName:'MenuBar_menuBarRoot__1h81mxv0',variantClassNames:{size:{sm:'MenuBar_menuBarRoot_size_sm__1h81mxv1',md:'MenuBar_menuBarRoot_size_md__1h81mxv2'}},defaultVariants:{size:'md'},compoundVariants:[]});
|
|
6
7
|
var menuContainer = 'MenuBar_menuContainer__1h81mxv3';
|
|
7
8
|
var menuItem = 'MenuBar_menuItem__1h81mxv8';
|
|
8
|
-
var separator = '
|
|
9
|
+
var separator = 'MenuBar_separator__1h81mxvb';
|
|
9
10
|
var shortcut = 'MenuBar_shortcut__1h81mxv9';
|
|
10
|
-
var subContainer = '
|
|
11
|
-
var subDropdown = '
|
|
12
|
-
var subTrigger = '
|
|
11
|
+
var subContainer = 'MenuBar_subContainer__1h81mxve';
|
|
12
|
+
var subDropdown = 'MenuBar_subDropdown__1h81mxvd';
|
|
13
|
+
var subTrigger = 'MenuBar_subTrigger__1h81mxvc MenuBar_menuItem__1h81mxv8';
|
|
13
14
|
var trigger = createRuntimeFn({defaultClassName:'MenuBar_trigger__1h81mxv4',variantClassNames:{active:{true:'MenuBar_trigger_active_true__1h81mxv5',false:'MenuBar_trigger_active_false__1h81mxv6'}},defaultVariants:{active:false},compoundVariants:[]});
|
|
14
15
|
|
|
15
|
-
export { dropdown, menuBarRoot, menuContainer, menuItem, separator, shortcut, subContainer, subDropdown, subTrigger, trigger };
|
|
16
|
+
export { dropdown, indicatorSlot, menuBarRoot, menuContainer, menuItem, separator, shortcut, subContainer, subDropdown, subTrigger, trigger };
|
|
16
17
|
//# sourceMappingURL=MenuBar.css.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MenuBar.css.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"MenuBar.css.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;"}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
3
|
-
import { useState, useRef, useMemo, useCallback, useEffect, useId, createContext
|
|
3
|
+
import { useState, useRef, useMemo, useCallback, useEffect, useContext, useId, createContext } from 'react';
|
|
4
|
+
import { CheckIcon } from '../../Icons/CheckIcon.js';
|
|
5
|
+
import { CircleIcon } from '../../Icons/CircleIcon.js';
|
|
4
6
|
import { cx } from '../../../utils/cx.js';
|
|
5
|
-
import { menuBarRoot, separator, subTrigger, subDropdown, subContainer, shortcut, menuItem, trigger, dropdown, menuContainer } from './MenuBar.css.js';
|
|
7
|
+
import { menuBarRoot, separator, subTrigger, subDropdown, subContainer, indicatorSlot, shortcut, menuItem, trigger, dropdown, menuContainer } from './MenuBar.css.js';
|
|
6
8
|
|
|
7
9
|
// --- Context ---
|
|
8
10
|
const MenuBarContext = /*#__PURE__*/ createContext({
|
|
@@ -16,7 +18,7 @@ const MenuBarContext = /*#__PURE__*/ createContext({
|
|
|
16
18
|
});
|
|
17
19
|
const useMenuBar = () => useContext(MenuBarContext);
|
|
18
20
|
const SUBMENU_CLOSE_DELAY = 150;
|
|
19
|
-
const MENU_ITEM_SELECTOR = '[role="menuitem"]:not(:disabled)';
|
|
21
|
+
const MENU_ITEM_SELECTOR = '[role="menuitem"]:not(:disabled), [role="menuitemcheckbox"]:not(:disabled), [role="menuitemradio"]:not(:disabled)';
|
|
20
22
|
const ChevronRight = () => (jsx("span", { style: {
|
|
21
23
|
fontSize: '8px',
|
|
22
24
|
lineHeight: 1,
|
|
@@ -33,6 +35,50 @@ const MenuBarItem = ({ onClick, shortcut: shortcutText, disabled = false, icon,
|
|
|
33
35
|
return (jsxs("button", { role: "menuitem", onClick: handleClick, disabled: disabled, className: cx(menuItem, className), style: style, "data-testid": testId, ref: ref, type: "button", tabIndex: -1, ...rest, children: [icon && jsx("span", { children: icon }), jsx("span", { children: children }), shortcutText && jsx("span", { className: shortcut, children: shortcutText })] }));
|
|
34
36
|
};
|
|
35
37
|
MenuBarItem.displayName = 'MenuBar.Item';
|
|
38
|
+
// --- Checkable items (checkbox / radio) ---
|
|
39
|
+
const MenuBarRadioContext =
|
|
40
|
+
/*#__PURE__*/ createContext(null);
|
|
41
|
+
const MenuBarCheckboxItem = ({ checked, defaultChecked = false, onCheckedChange, shortcut: shortcutText, indicator = jsx(CheckIcon, { size: "sm" }), closeOnClick = false, disabled = false, children, className, style, testId, ref, ...rest }) => {
|
|
42
|
+
const { setOpenMenuId } = useMenuBar();
|
|
43
|
+
const [internalChecked, setInternalChecked] = useState(defaultChecked);
|
|
44
|
+
const isControlled = checked !== undefined;
|
|
45
|
+
const isChecked = isControlled ? checked : internalChecked;
|
|
46
|
+
const handleClick = useCallback(() => {
|
|
47
|
+
const next = !isChecked;
|
|
48
|
+
if (!isControlled)
|
|
49
|
+
setInternalChecked(next);
|
|
50
|
+
onCheckedChange?.(next);
|
|
51
|
+
if (closeOnClick)
|
|
52
|
+
setOpenMenuId(null);
|
|
53
|
+
}, [isChecked, isControlled, onCheckedChange, closeOnClick, setOpenMenuId]);
|
|
54
|
+
return (jsxs("button", { role: "menuitemcheckbox", "aria-checked": isChecked, onClick: handleClick, disabled: disabled, className: cx(menuItem, className), style: style, "data-testid": testId, ref: ref, type: "button", tabIndex: -1, ...rest, children: [jsx("span", { className: indicatorSlot, "aria-hidden": "true", children: isChecked ? indicator : null }), jsx("span", { children: children }), shortcutText && jsx("span", { className: shortcut, children: shortcutText })] }));
|
|
55
|
+
};
|
|
56
|
+
MenuBarCheckboxItem.displayName = 'MenuBar.CheckboxItem';
|
|
57
|
+
const MenuBarRadioGroup = ({ value, defaultValue, onValueChange, children, className, style, testId, ref, ...rest }) => {
|
|
58
|
+
const [internalValue, setInternalValue] = useState(defaultValue);
|
|
59
|
+
const isControlled = value !== undefined;
|
|
60
|
+
const selectedValue = isControlled ? value : internalValue;
|
|
61
|
+
const setValue = useCallback((next) => {
|
|
62
|
+
if (!isControlled)
|
|
63
|
+
setInternalValue(next);
|
|
64
|
+
onValueChange?.(next);
|
|
65
|
+
}, [isControlled, onValueChange]);
|
|
66
|
+
const contextValue = useMemo(() => ({ value: selectedValue, setValue }), [selectedValue, setValue]);
|
|
67
|
+
return (jsx(MenuBarRadioContext.Provider, { value: contextValue, children: jsx("div", { role: "group", className: className, style: style, "data-testid": testId, ref: ref, ...rest, children: children }) }));
|
|
68
|
+
};
|
|
69
|
+
MenuBarRadioGroup.displayName = 'MenuBar.RadioGroup';
|
|
70
|
+
const MenuBarRadioItem = ({ value, shortcut: shortcutText, indicator = jsx(CircleIcon, { size: "sm" }), closeOnClick = false, disabled = false, children, className, style, testId, ref, ...rest }) => {
|
|
71
|
+
const { setOpenMenuId } = useMenuBar();
|
|
72
|
+
const group = useContext(MenuBarRadioContext);
|
|
73
|
+
const isChecked = group?.value === value;
|
|
74
|
+
const handleClick = useCallback(() => {
|
|
75
|
+
group?.setValue(value);
|
|
76
|
+
if (closeOnClick)
|
|
77
|
+
setOpenMenuId(null);
|
|
78
|
+
}, [group, value, closeOnClick, setOpenMenuId]);
|
|
79
|
+
return (jsxs("button", { role: "menuitemradio", "aria-checked": isChecked, onClick: handleClick, disabled: disabled, className: cx(menuItem, className), style: style, "data-testid": testId, ref: ref, type: "button", tabIndex: -1, ...rest, children: [jsx("span", { className: indicatorSlot, "aria-hidden": "true", children: isChecked ? indicator : null }), jsx("span", { children: children }), shortcutText && jsx("span", { className: shortcut, children: shortcutText })] }));
|
|
80
|
+
};
|
|
81
|
+
MenuBarRadioItem.displayName = 'MenuBar.RadioItem';
|
|
36
82
|
const MenuBarSub = ({ label, children, disabled = false, className, style, testId, ref, ...rest }) => {
|
|
37
83
|
const { menuOffset } = useMenuBar();
|
|
38
84
|
const [open, setOpen] = useState(false);
|
|
@@ -214,6 +260,9 @@ MenuBarRoot.displayName = 'MenuBar';
|
|
|
214
260
|
const MenuBar = /*#__PURE__*/ Object.assign(MenuBarRoot, {
|
|
215
261
|
Menu: MenuBarMenu,
|
|
216
262
|
Item: MenuBarItem,
|
|
263
|
+
CheckboxItem: MenuBarCheckboxItem,
|
|
264
|
+
RadioGroup: MenuBarRadioGroup,
|
|
265
|
+
RadioItem: MenuBarRadioItem,
|
|
217
266
|
Sub: MenuBarSub,
|
|
218
267
|
Separator: MenuBarSeparator,
|
|
219
268
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MenuBar.js","sources":["../../../../../../src/components/shell/MenuBar/MenuBar.tsx"],"sourcesContent":[null],"names":[],"mappings":";;;;;;AAsCA;AAEA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACD;AAED;AAEA;AAEA;AAEA;AAGM;AACA;AACD;AAML;AAEA;AAaE;AAEA;;AAEI;;;AAGJ;AAEA;AAkBF;AAEA;AAEA;AAWE;;AAEA;AAEA;AACE;AACE;AACA;;AAEF;;AACF;AAEA;AACE;;;;;AAMA;;AAC0B;AAC1B;;;AAoCJ;AAEA;AAEA;AAOE;AAUF;AAEA;AAEA;AAEA;AAWE;AACA;AAOA;AACA;AACA;;;AAIE;;AAEA;;AAGF;AACE;;;;AAIF;;;;;;AAOA;AAEI;;;;;;;;;AAWA;;;AAGE;;AAEJ;AAIF;AAEI;;AAOA;;AAEE;AACA;;AACK;;;AAIL;;AACK;;;AAGL;;AAEJ;;;AAMA;;AAEA;AACE;;;;;AAOF;AAEA;AACA;AACE;AACF;AACF;AAEA;;;;AAwCF;AAEA;AAEA;AAEA;;;;;;AAgBE;;AAGI;AACA;;;AAEO;AACL;;AAEJ;AAIF;;;AAIA;AACE;;AAGF;;;AAII;;AASA;;AAEE;AACA;;AAEA;AACE;;;AAEG;;;AAIL;AACA;AACE;;;AAGN;AAIF;;AAGI;;;;;;AAMD;;;;;;;;AASA;;AAmBL;AAEA;AAEA;AAEO;AACL;AACA;AACA;AACA;AACD;;"}
|
|
1
|
+
{"version":3,"file":"MenuBar.js","sources":["src/components/shell/MenuBar/MenuBar.tsx"],"sourcesContent":["'use client';\n\nimport React, {\n createContext,\n useContext,\n useState,\n useCallback,\n useRef,\n useEffect,\n useId,\n KeyboardEvent,\n useMemo,\n} from 'react';\n\nimport { CheckIcon } from '@/components/Icons/CheckIcon';\nimport { CircleIcon } from '@/components/Icons/CircleIcon';\nimport { cx } from '@/utils/cx';\n\nimport {\n menuBarRoot,\n menuContainer,\n trigger,\n dropdown,\n menuItem,\n shortcut,\n separator,\n subTrigger,\n subDropdown,\n subContainer,\n indicatorSlot,\n} from './MenuBar.css';\n\nimport type {\n MenuBarProps,\n MenuBarMenuProps,\n MenuBarItemProps,\n MenuBarSubProps,\n MenuBarSeparatorProps,\n MenuBarContextValue,\n MenuBarCheckboxItemProps,\n MenuBarRadioGroupProps,\n MenuBarRadioItemProps,\n MenuBarRadioGroupContextValue,\n} from './MenuBar.types';\n\n// --- Context ---\n\nconst MenuBarContext = /*#__PURE__*/ createContext<MenuBarContextValue>({\n size: 'md',\n menuOffset: 2,\n openMenuId: null,\n setOpenMenuId: () => {},\n registerMenu: () => {},\n unregisterMenu: () => {},\n menuIds: [],\n});\n\nconst useMenuBar = () => useContext(MenuBarContext);\n\nconst SUBMENU_CLOSE_DELAY = 150;\n\nconst MENU_ITEM_SELECTOR =\n '[role=\"menuitem\"]:not(:disabled), [role=\"menuitemcheckbox\"]:not(:disabled), [role=\"menuitemradio\"]:not(:disabled)';\n\nconst ChevronRight = () => (\n <span\n style={{\n fontSize: '8px',\n lineHeight: 1,\n }}\n >\n ▶\n </span>\n);\n\n// --- Sub-components ---\n\nconst MenuBarItem: React.FC<MenuBarItemProps> = ({\n onClick,\n shortcut: shortcutText,\n disabled = false,\n icon,\n children,\n\n className,\n style,\n testId,\n ref,\n ...rest\n}) => {\n const { setOpenMenuId } = useMenuBar();\n\n const handleClick = useCallback(() => {\n if (onClick) {\n onClick();\n setOpenMenuId(null);\n }\n }, [onClick, setOpenMenuId]);\n\n return (\n <button\n role=\"menuitem\"\n onClick={handleClick}\n disabled={disabled}\n className={cx(menuItem, className)}\n style={style}\n data-testid={testId}\n ref={ref}\n type=\"button\"\n tabIndex={-1}\n {...rest}\n >\n {icon && <span>{icon}</span>}\n <span>{children}</span>\n {shortcutText && <span className={shortcut}>{shortcutText}</span>}\n </button>\n );\n};\n\nMenuBarItem.displayName = 'MenuBar.Item';\n\n// --- Checkable items (checkbox / radio) ---\n\nconst MenuBarRadioContext =\n /*#__PURE__*/ createContext<MenuBarRadioGroupContextValue | null>(null);\n\nconst MenuBarCheckboxItem: React.FC<MenuBarCheckboxItemProps> = ({\n checked,\n defaultChecked = false,\n onCheckedChange,\n shortcut: shortcutText,\n indicator = <CheckIcon size=\"sm\" />,\n closeOnClick = false,\n disabled = false,\n children,\n\n className,\n style,\n testId,\n ref,\n ...rest\n}) => {\n const { setOpenMenuId } = useMenuBar();\n const [internalChecked, setInternalChecked] = useState(defaultChecked);\n const isControlled = checked !== undefined;\n const isChecked = isControlled ? checked : internalChecked;\n\n const handleClick = useCallback(() => {\n const next = !isChecked;\n if (!isControlled) setInternalChecked(next);\n onCheckedChange?.(next);\n if (closeOnClick) setOpenMenuId(null);\n }, [isChecked, isControlled, onCheckedChange, closeOnClick, setOpenMenuId]);\n\n return (\n <button\n role=\"menuitemcheckbox\"\n aria-checked={isChecked}\n onClick={handleClick}\n disabled={disabled}\n className={cx(menuItem, className)}\n style={style}\n data-testid={testId}\n ref={ref}\n type=\"button\"\n tabIndex={-1}\n {...rest}\n >\n <span className={indicatorSlot} aria-hidden=\"true\">\n {isChecked ? indicator : null}\n </span>\n <span>{children}</span>\n {shortcutText && <span className={shortcut}>{shortcutText}</span>}\n </button>\n );\n};\n\nMenuBarCheckboxItem.displayName = 'MenuBar.CheckboxItem';\n\nconst MenuBarRadioGroup: React.FC<MenuBarRadioGroupProps> = ({\n value,\n defaultValue,\n onValueChange,\n children,\n\n className,\n style,\n testId,\n ref,\n ...rest\n}) => {\n const [internalValue, setInternalValue] = useState<string | undefined>(\n defaultValue\n );\n const isControlled = value !== undefined;\n const selectedValue = isControlled ? value : internalValue;\n\n const setValue = useCallback(\n (next: string) => {\n if (!isControlled) setInternalValue(next);\n onValueChange?.(next);\n },\n [isControlled, onValueChange]\n );\n\n const contextValue = useMemo(\n () => ({ value: selectedValue, setValue }),\n [selectedValue, setValue]\n );\n\n return (\n <MenuBarRadioContext.Provider value={contextValue}>\n <div\n role=\"group\"\n className={className}\n style={style}\n data-testid={testId}\n ref={ref}\n {...rest}\n >\n {children}\n </div>\n </MenuBarRadioContext.Provider>\n );\n};\n\nMenuBarRadioGroup.displayName = 'MenuBar.RadioGroup';\n\nconst MenuBarRadioItem: React.FC<MenuBarRadioItemProps> = ({\n value,\n shortcut: shortcutText,\n indicator = <CircleIcon size=\"sm\" />,\n closeOnClick = false,\n disabled = false,\n children,\n\n className,\n style,\n testId,\n ref,\n ...rest\n}) => {\n const { setOpenMenuId } = useMenuBar();\n const group = useContext(MenuBarRadioContext);\n const isChecked = group?.value === value;\n\n const handleClick = useCallback(() => {\n group?.setValue(value);\n if (closeOnClick) setOpenMenuId(null);\n }, [group, value, closeOnClick, setOpenMenuId]);\n\n return (\n <button\n role=\"menuitemradio\"\n aria-checked={isChecked}\n onClick={handleClick}\n disabled={disabled}\n className={cx(menuItem, className)}\n style={style}\n data-testid={testId}\n ref={ref}\n type=\"button\"\n tabIndex={-1}\n {...rest}\n >\n <span className={indicatorSlot} aria-hidden=\"true\">\n {isChecked ? indicator : null}\n </span>\n <span>{children}</span>\n {shortcutText && <span className={shortcut}>{shortcutText}</span>}\n </button>\n );\n};\n\nMenuBarRadioItem.displayName = 'MenuBar.RadioItem';\n\nconst MenuBarSub: React.FC<MenuBarSubProps> = ({\n label,\n children,\n disabled = false,\n\n className,\n style,\n testId,\n ref,\n ...rest\n}) => {\n const { menuOffset } = useMenuBar();\n const [open, setOpen] = useState(false);\n const closeTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const handleEnter = useCallback(() => {\n if (closeTimer.current) {\n clearTimeout(closeTimer.current);\n closeTimer.current = null;\n }\n if (!disabled) setOpen(true);\n }, [disabled]);\n\n const handleLeave = useCallback(() => {\n closeTimer.current = setTimeout(() => {\n setOpen(false);\n }, SUBMENU_CLOSE_DELAY);\n }, []);\n\n useEffect(() => {\n return () => {\n if (closeTimer.current) clearTimeout(closeTimer.current);\n };\n }, []);\n\n return (\n <div\n ref={ref}\n className={cx(subContainer, className)}\n style={style}\n data-testid={testId}\n onMouseEnter={handleEnter}\n onMouseLeave={handleLeave}\n {...rest}\n >\n <button\n className={subTrigger}\n role=\"menuitem\"\n aria-haspopup=\"true\"\n aria-expanded={open}\n disabled={disabled}\n type=\"button\"\n tabIndex={-1}\n >\n <span>{label}</span>\n <ChevronRight />\n </button>\n {open && (\n <div\n className={subDropdown}\n role=\"menu\"\n style={{ left: `calc(100% + ${menuOffset}px)` }}\n >\n {children}\n </div>\n )}\n </div>\n );\n};\n\nMenuBarSub.displayName = 'MenuBar.Sub';\n\nconst MenuBarSeparator: React.FC<MenuBarSeparatorProps> = ({\n className,\n style,\n testId,\n ref,\n ...rest\n}) => {\n return (\n <div\n role=\"separator\"\n className={cx(separator, className)}\n style={style}\n data-testid={testId}\n ref={ref}\n {...rest}\n />\n );\n};\n\nMenuBarSeparator.displayName = 'MenuBar.Separator';\n\n// --- MenuBarMenu (top-level dropdown trigger) ---\n\nconst MenuBarMenu: React.FC<MenuBarMenuProps> = ({\n label,\n children,\n disabled = false,\n\n className,\n style,\n testId,\n ref,\n ...rest\n}) => {\n const menuId = useId();\n const {\n openMenuId,\n setOpenMenuId,\n registerMenu,\n unregisterMenu,\n menuOffset,\n } = useMenuBar();\n const triggerRef = useRef<HTMLButtonElement>(null);\n const dropdownRef = useRef<HTMLDivElement>(null);\n const isOpen = openMenuId === menuId;\n\n useEffect(() => {\n registerMenu(menuId);\n return () => {\n unregisterMenu(menuId);\n };\n }, [menuId, registerMenu, unregisterMenu]);\n\n const handleClick = useCallback(() => {\n if (disabled) return;\n setOpenMenuId(isOpen ? null : menuId);\n }, [disabled, isOpen, menuId, setOpenMenuId]);\n\n const handleMouseEnter = useCallback(() => {\n // Open on hover only when another menu is already open\n if (openMenuId !== null && openMenuId !== menuId && !disabled) {\n setOpenMenuId(menuId);\n }\n }, [openMenuId, menuId, disabled, setOpenMenuId]);\n\n const handleKeyDown = useCallback(\n (e: KeyboardEvent<HTMLButtonElement>) => {\n if (e.key === 'ArrowDown' || e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n setOpenMenuId(menuId);\n // Focus first item in dropdown after open\n setTimeout(() => {\n const firstItem = dropdownRef.current?.querySelector(\n MENU_ITEM_SELECTOR\n ) as HTMLElement | null;\n firstItem?.focus();\n }, 0);\n }\n if (e.key === 'Escape') {\n e.preventDefault();\n setOpenMenuId(null);\n triggerRef.current?.focus();\n }\n },\n [menuId, setOpenMenuId]\n );\n\n const handleDropdownKeyDown = useCallback(\n (e: KeyboardEvent<HTMLDivElement>) => {\n const items = Array.from(\n dropdownRef.current?.querySelectorAll<HTMLElement>(\n MENU_ITEM_SELECTOR\n ) ?? []\n );\n const currentIndex = items.indexOf(document.activeElement as HTMLElement);\n\n if (e.key === 'ArrowDown') {\n e.preventDefault();\n const next = currentIndex + 1 < items.length ? currentIndex + 1 : 0;\n items[next]?.focus();\n } else if (e.key === 'ArrowUp') {\n e.preventDefault();\n const prev =\n currentIndex - 1 >= 0 ? currentIndex - 1 : items.length - 1;\n items[prev]?.focus();\n } else if (e.key === 'Escape') {\n e.preventDefault();\n setOpenMenuId(null);\n triggerRef.current?.focus();\n }\n },\n [setOpenMenuId]\n );\n\n // Close dropdown when clicking outside\n useEffect(() => {\n if (!isOpen) return;\n\n const handleOutsideClick = (e: MouseEvent) => {\n const target = e.target as Node;\n if (\n !triggerRef.current?.contains(target) &&\n !dropdownRef.current?.contains(target)\n ) {\n setOpenMenuId(null);\n }\n };\n\n document.addEventListener('mousedown', handleOutsideClick);\n return () => {\n document.removeEventListener('mousedown', handleOutsideClick);\n };\n }, [isOpen, setOpenMenuId]);\n\n return (\n <div\n ref={ref}\n className={cx(menuContainer, className)}\n style={style}\n data-testid={testId}\n {...rest}\n >\n <button\n ref={triggerRef}\n className={trigger({ active: isOpen })}\n onClick={handleClick}\n onMouseEnter={handleMouseEnter}\n onKeyDown={handleKeyDown}\n disabled={disabled}\n type=\"button\"\n role=\"menuitem\"\n aria-haspopup=\"true\"\n aria-expanded={isOpen}\n tabIndex={-1}\n >\n {label}\n </button>\n {isOpen && (\n <div\n ref={dropdownRef}\n className={dropdown}\n role=\"menu\"\n aria-label={label}\n // Focusable programmatically so the open menu can receive focus and\n // handle arrow-key navigation across its items.\n tabIndex={-1}\n onKeyDown={handleDropdownKeyDown}\n style={{ top: `calc(100% + ${menuOffset}px)` }}\n >\n {children}\n </div>\n )}\n </div>\n );\n};\n\nMenuBarMenu.displayName = 'MenuBar.Menu';\n\n// --- Root Component ---\n\nconst MenuBarRoot: React.FC<MenuBarProps> = ({\n size = 'md',\n menuOffset = 2,\n children,\n\n className,\n style,\n testId,\n ref: externalRef,\n ...rest\n}) => {\n const [openMenuId, setOpenMenuId] = useState<string | null>(null);\n // Registered child menu ids, kept in state so the context value stays\n // reactive (previously a ref + forceUpdate, which read the ref during render).\n const [menuIds, setMenuIds] = useState<string[]>([]);\n const safeMenuOffset = Math.max(0, menuOffset);\n const barRef = useRef<HTMLDivElement>(null);\n const setBarRef = useMemo(\n () => (node: HTMLDivElement | null) => {\n barRef.current = node;\n if (typeof externalRef === 'function') {\n externalRef(node);\n } else if (externalRef && typeof externalRef === 'object') {\n externalRef.current = node;\n }\n },\n [externalRef]\n );\n\n const registerMenu = useCallback((id: string) => {\n setMenuIds(prev => (prev.includes(id) ? prev : [...prev, id]));\n }, []);\n\n const unregisterMenu = useCallback((id: string) => {\n setMenuIds(prev => prev.filter(m => m !== id));\n }, []);\n\n const handleBarKeyDown = useCallback(\n (e: KeyboardEvent<HTMLDivElement>) => {\n if (!barRef.current) return;\n\n const triggers = Array.from(\n barRef.current.querySelectorAll<HTMLElement>(\n ':scope > div > [role=\"menuitem\"]'\n )\n );\n const currentIndex = triggers.indexOf(\n document.activeElement as HTMLElement\n );\n\n if (e.key === 'ArrowRight') {\n e.preventDefault();\n const next = currentIndex + 1 < triggers.length ? currentIndex + 1 : 0;\n triggers[next]?.focus();\n // If menu is open, switch to next menu\n if (openMenuId !== null) {\n triggers[next]?.click();\n }\n } else if (e.key === 'ArrowLeft') {\n e.preventDefault();\n const prev =\n currentIndex - 1 >= 0 ? currentIndex - 1 : triggers.length - 1;\n triggers[prev]?.focus();\n if (openMenuId !== null) {\n triggers[prev]?.click();\n }\n }\n },\n [openMenuId]\n );\n\n const contextValue = useMemo(\n () => ({\n size,\n menuOffset: safeMenuOffset,\n openMenuId,\n setOpenMenuId,\n registerMenu,\n unregisterMenu,\n menuIds,\n }),\n [\n size,\n safeMenuOffset,\n openMenuId,\n setOpenMenuId,\n registerMenu,\n unregisterMenu,\n menuIds,\n ]\n );\n\n return (\n <MenuBarContext.Provider value={contextValue}>\n <div\n ref={setBarRef}\n className={cx(menuBarRoot({ size }), className)}\n style={style}\n data-testid={testId}\n role=\"menubar\"\n onKeyDown={handleBarKeyDown}\n tabIndex={0}\n {...rest}\n >\n {children}\n </div>\n </MenuBarContext.Provider>\n );\n};\n\nMenuBarRoot.displayName = 'MenuBar';\n\n// --- Compound Component ---\n\nexport const MenuBar = /*#__PURE__*/ Object.assign(MenuBarRoot, {\n Menu: MenuBarMenu,\n Item: MenuBarItem,\n CheckboxItem: MenuBarCheckboxItem,\n RadioGroup: MenuBarRadioGroup,\n RadioItem: MenuBarRadioItem,\n Sub: MenuBarSub,\n Separator: MenuBarSeparator,\n});\n"],"names":[],"mappings":";;;;;;;;AA6CA;AAEA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACD;AAED;AAEA;AAEA;AAGA;AAGM;AACA;AACD;AAML;AAEA;AAaE;AAEA;;AAEI;;;AAGJ;AAEA;AAkBF;AAEA;AAEA;AAEA;AACE;AAEF;AAgBE;;AAEA;;AAGA;AACE;AACA;;AACA;AACA;;AACF;AAEA;AAqBF;AAEA;AAEA;;AAeE;;AAGA;AAEI;;AACA;AACF;;AASF;AAcF;AAEA;AAEA;AAcE;AACA;AACA;AAEA;AACE;AACA;;;AAGF;AAqBF;AAEA;AAEA;AAWE;;AAEA;AAEA;AACE;AACE;AACA;;AAEF;;AACF;AAEA;AACE;;;;;AAMA;;AAC0B;AAC1B;;;AAoCJ;AAEA;AAEA;AAOE;AAUF;AAEA;AAEA;AAEA;AAWE;AACA;AAOA;AACA;AACA;;;AAIE;;AAEA;;AAGF;AACE;;;;AAIF;;;;;;AAOA;AAEI;;;;;;;;;AAWA;;;AAGE;;AAEJ;AAIF;AAEI;;AAOA;;AAEE;AACA;;AACK;;;AAIL;;AACK;;;AAGL;;AAEJ;;;AAMA;;AAEA;AACE;;;;;AAOF;AAEA;AACA;AACE;AACF;AACF;AAEA;;;;AAwCF;AAEA;AAEA;AAEA;;;;;;AAgBE;;AAGI;AACA;;;AAEO;AACL;;AAEJ;AAIF;;;AAIA;AACE;;AAGF;;;AAII;;AASA;;AAEE;AACA;;AAEA;AACE;;;AAEG;;;AAIL;AACA;AACE;;;AAGN;AAIF;;AAGI;;;;;;AAMD;;;;;;;;AASA;;AAmBL;AAEA;AAEA;AAEO;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACD;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StatusBar.js","sources":["
|
|
1
|
+
{"version":3,"file":"StatusBar.js","sources":["src/components/shell/StatusBar/StatusBar.tsx"],"sourcesContent":["'use client';\n\nimport React, { createContext, useContext, useMemo } from 'react';\n\nimport { cx } from '@/utils/cx';\n\nimport {\n statusBarRoot,\n statusBarSection,\n itemButton,\n itemSpan,\n badge,\n} from './StatusBar.css';\n\nimport type {\n StatusBarProps,\n StatusBarSectionProps,\n StatusBarItemProps,\n StatusBarContextValue,\n} from './StatusBar.types';\n\nconst StatusBarContext = /*#__PURE__*/ createContext<StatusBarContextValue>({\n size: 'sm',\n});\n\nconst useStatusBar = () => useContext(StatusBarContext);\n\n// --- Sub-components ---\n\nconst StatusBarSection = /*#__PURE__*/ React.memo<StatusBarSectionProps>(\n ({\n side = 'left',\n children,\n\n className,\n style,\n testId,\n ref,\n ...rest\n }) => {\n return (\n <div\n ref={ref}\n className={cx(statusBarSection({ side }), className)}\n style={style}\n data-testid={testId}\n {...rest}\n >\n {children}\n </div>\n );\n }\n);\n\nStatusBarSection.displayName = 'StatusBar.Section';\n\nconst StatusBarItem = /*#__PURE__*/ React.memo<StatusBarItemProps>(\n ({\n onClick,\n icon,\n children,\n title,\n badge: badgeProp,\n disabled = false,\n\n className,\n style,\n testId,\n ref,\n ...rest\n }) => {\n useStatusBar();\n\n const content = (\n <>\n {icon}\n {children && <span>{children}</span>}\n {badgeProp !== undefined && badgeProp !== false && (\n <span className={badge({ dot: badgeProp === true })}>\n {typeof badgeProp === 'number' ? badgeProp : null}\n </span>\n )}\n </>\n );\n\n if (onClick) {\n return (\n <button\n onClick={onClick}\n title={title}\n disabled={disabled}\n className={cx(itemButton, className)}\n style={style}\n data-testid={testId}\n ref={ref as React.Ref<HTMLButtonElement>}\n type=\"button\"\n {...rest}\n >\n {content}\n </button>\n );\n }\n\n return (\n <span\n title={title}\n className={cx(itemSpan, className)}\n style={style}\n data-testid={testId}\n ref={ref}\n {...rest}\n >\n {content}\n </span>\n );\n }\n);\n\nStatusBarItem.displayName = 'StatusBar.Item';\n\n// --- Root Component ---\n\nconst StatusBarRoot: React.FC<StatusBarProps> = ({\n size = 'sm',\n variant = 'default',\n children,\n\n className,\n style,\n testId,\n ref,\n ...rest\n}) => {\n const contextValue = useMemo(() => ({ size }), [size]);\n\n return (\n <StatusBarContext.Provider value={contextValue}>\n <div\n ref={ref}\n className={cx(statusBarRoot({ variant, size }), className)}\n style={style}\n data-testid={testId}\n role=\"status\"\n aria-live=\"polite\"\n {...rest}\n >\n {children}\n </div>\n </StatusBarContext.Provider>\n );\n};\n\nStatusBarRoot.displayName = 'StatusBar';\n\n// --- Compound Component ---\n\nexport const StatusBar = /*#__PURE__*/ Object.assign(StatusBarRoot, {\n Section: StatusBarSection,\n Item: StatusBarItem,\n});\n"],"names":[],"mappings":";;;;;;AAqBA;AACE;AACD;AAED;AAEA;AAEA;AAWI;AAWF;AAGF;AAEA;AAeI;;;AAeE;;AAiBF;AAYF;AAGF;AAEA;AAEA;AAWE;;AAiBF;AAEA;AAEA;AAEO;AACL;AACA;AACD;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Toolbar.js","sources":["
|
|
1
|
+
{"version":3,"file":"Toolbar.js","sources":["src/components/shell/Toolbar/Toolbar.tsx"],"sourcesContent":["'use client';\n\nimport React, {\n createContext,\n useContext,\n useRef,\n useCallback,\n useMemo,\n KeyboardEvent,\n} from 'react';\n\nimport { cx } from '@/utils/cx';\n\nimport {\n toolbarRoot,\n toolbarButton,\n toolbarGroup,\n toolbarSeparator,\n toolbarSpacer,\n} from './Toolbar.css';\n\nimport type {\n ToolbarProps,\n ToolbarButtonProps,\n ToolbarToggleProps,\n ToolbarGroupProps,\n ToolbarSeparatorProps,\n ToolbarSpacerProps,\n ToolbarContextValue,\n} from './Toolbar.types';\n\nconst ToolbarContext = /*#__PURE__*/ createContext<ToolbarContextValue>({\n orientation: 'horizontal',\n size: 'md',\n});\n\nconst useToolbar = () => useContext(ToolbarContext);\n\n// --- Roving tabindex helper ---\n\nconst FOCUSABLE_ITEM_SELECTOR =\n 'button:not(:disabled), [role=\"button\"]:not([aria-disabled=\"true\"])';\n\nfunction focusableItems(container: HTMLElement): HTMLElement[] {\n return Array.from(\n container.querySelectorAll<HTMLElement>(FOCUSABLE_ITEM_SELECTOR)\n );\n}\n\n// --- Sub-components ---\n\nconst ToolbarButton = /*#__PURE__*/ React.memo<ToolbarButtonProps>(\n ({\n onClick,\n icon,\n children,\n tooltip,\n variant = 'default',\n disabled = false,\n\n className,\n style,\n testId,\n ref,\n ...rest\n }) => {\n const { size } = useToolbar();\n\n return (\n <button\n onClick={onClick}\n disabled={disabled}\n className={cx(\n toolbarButton({ size, variant, active: false }),\n className\n )}\n style={style}\n data-testid={testId}\n ref={ref}\n title={tooltip}\n type=\"button\"\n tabIndex={-1}\n {...rest}\n >\n {icon}\n {!icon && children}\n </button>\n );\n }\n);\n\nToolbarButton.displayName = 'Toolbar.Button';\n\nconst ToolbarToggle = /*#__PURE__*/ React.memo<ToolbarToggleProps>(\n ({\n pressed,\n onPressedChange,\n icon,\n children,\n tooltip,\n disabled = false,\n\n className,\n style,\n testId,\n ref,\n ...rest\n }) => {\n const { size } = useToolbar();\n\n return (\n <button\n onClick={() => {\n onPressedChange(!pressed);\n }}\n disabled={disabled}\n className={cx(\n toolbarButton({ size, variant: 'default', active: pressed }),\n className\n )}\n style={style}\n data-testid={testId}\n ref={ref}\n title={tooltip}\n type=\"button\"\n tabIndex={-1}\n aria-pressed={pressed}\n {...rest}\n >\n {icon}\n {!icon && children}\n </button>\n );\n }\n);\n\nToolbarToggle.displayName = 'Toolbar.Toggle';\n\nconst ToolbarGroupComp = /*#__PURE__*/ React.memo<ToolbarGroupProps>(\n ({ children, className, style, testId, ref, ...rest }) => {\n const { orientation } = useToolbar();\n\n return (\n <div\n className={cx(toolbarGroup({ orientation }), className)}\n style={style}\n data-testid={testId}\n ref={ref}\n role=\"group\"\n {...rest}\n >\n {children}\n </div>\n );\n }\n);\n\nToolbarGroupComp.displayName = 'Toolbar.Group';\n\nconst ToolbarSeparatorComp = /*#__PURE__*/ React.memo<ToolbarSeparatorProps>(\n ({ className, style, testId, ref, ...rest }) => {\n const { orientation } = useToolbar();\n return (\n <div\n className={cx(toolbarSeparator({ orientation }), className)}\n style={style}\n data-testid={testId}\n ref={ref}\n role=\"separator\"\n aria-orientation={\n orientation === 'horizontal' ? 'vertical' : 'horizontal'\n }\n {...rest}\n />\n );\n }\n);\n\nToolbarSeparatorComp.displayName = 'Toolbar.Separator';\n\nconst ToolbarSpacerComp = /*#__PURE__*/ React.memo<ToolbarSpacerProps>(\n ({ className, style, testId, ref, ...rest }) => {\n return (\n <div\n className={cx(toolbarSpacer, className)}\n style={style}\n data-testid={testId}\n ref={ref}\n {...rest}\n />\n );\n }\n);\n\nToolbarSpacerComp.displayName = 'Toolbar.Spacer';\n\n// --- Root Component ---\n\nconst ToolbarRoot: React.FC<ToolbarProps> = ({\n orientation = 'horizontal',\n size = 'md',\n children,\n\n className,\n style,\n testId,\n ref: externalRef,\n ...rest\n}) => {\n const internalRef = useRef<HTMLDivElement>(null);\n\n const setToolbarRef = useMemo(\n () => (node: HTMLDivElement | null) => {\n internalRef.current = node;\n if (typeof externalRef === 'function') {\n externalRef(node);\n } else if (externalRef && typeof externalRef === 'object') {\n externalRef.current = node;\n }\n },\n [externalRef]\n );\n\n const handleKeyDown = useCallback(\n (e: KeyboardEvent<HTMLDivElement>) => {\n const container = internalRef.current;\n if (!container) return;\n\n const items = focusableItems(container);\n if (items.length === 0) return;\n\n const current = document.activeElement as HTMLElement;\n const currentIndex = items.indexOf(current);\n\n const nextKey = orientation === 'horizontal' ? 'ArrowRight' : 'ArrowDown';\n const prevKey = orientation === 'horizontal' ? 'ArrowLeft' : 'ArrowUp';\n\n if (e.key === nextKey) {\n e.preventDefault();\n const next = currentIndex + 1 < items.length ? currentIndex + 1 : 0;\n items[next]?.focus();\n } else if (e.key === prevKey) {\n e.preventDefault();\n const prev =\n currentIndex - 1 >= 0 ? currentIndex - 1 : items.length - 1;\n items[prev]?.focus();\n } else if (e.key === 'Home') {\n e.preventDefault();\n items[0]?.focus();\n } else if (e.key === 'End') {\n e.preventDefault();\n items[items.length - 1]?.focus();\n }\n },\n [orientation]\n );\n\n const handleFocus = useCallback(() => {\n const container = internalRef.current;\n if (!container) return;\n\n // If focus lands on the toolbar container itself, move to first button\n if (document.activeElement === container) {\n const items = focusableItems(container);\n items[0]?.focus();\n }\n }, []);\n\n const toolbarContextValue = useMemo(\n () => ({ orientation, size }),\n [orientation, size]\n );\n\n return (\n <ToolbarContext.Provider value={toolbarContextValue}>\n <div\n ref={setToolbarRef}\n className={cx(toolbarRoot({ orientation }), className)}\n style={style}\n data-testid={testId}\n role=\"toolbar\"\n aria-orientation={orientation}\n tabIndex={0}\n onKeyDown={handleKeyDown}\n onFocus={handleFocus}\n {...rest}\n >\n {children}\n </div>\n </ToolbarContext.Provider>\n );\n};\n\nToolbarRoot.displayName = 'Toolbar';\n\n// --- Compound Component ---\n\nexport const Toolbar = /*#__PURE__*/ Object.assign(ToolbarRoot, {\n Button: ToolbarButton,\n Toggle: ToolbarToggle,\n Group: ToolbarGroupComp,\n Separator: ToolbarSeparatorComp,\n Spacer: ToolbarSpacerComp,\n});\n"],"names":[],"mappings":";;;;;;AA+BA;AACE;AACA;AACD;AAED;AAEA;AAEA;AAGA;;AAIA;AAEA;AAEA;AAeI;AAEA;AAoBF;AAGF;AAEA;AAeI;AAEA;AAGM;AACF;AAmBN;AAGF;AAEA;AAEI;AAEA;AAYF;AAGF;AAEA;AAEI;;AAcF;AAGF;AAEA;;AAWE;AAGF;AAEA;AAEA;AAWE;;AAII;AACA;;;AAEO;AACL;;AAEJ;AAIF;AAEI;AACA;;AAEA;AACA;;AAEA;;AAGA;AACA;AAEA;;AAEE;AACA;;AACK;;;AAIL;;AACK;;AAEL;;AACK;;;;AAIT;AAIF;AACE;AACA;;;AAGA;AACE;AACA;;;;;AA2BN;AAEA;AAEA;AAEO;AACL;AACA;AACA;AACA;AACA;AACD;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"KeyboardContext.js","sources":["
|
|
1
|
+
{"version":3,"file":"KeyboardContext.js","sources":["src/context/KeyboardContext.tsx"],"sourcesContent":["'use client';\n\nimport React, {\n createContext,\n useContext,\n memo,\n useEffect,\n useCallback,\n} from 'react';\n\nimport { useKeyboard, isKeyPressed } from '@/hooks/useKeyboard';\n\nimport type { KeyboardState, AllKeys } from '@/hooks/useKeyboard';\n\nconst NEUTRAL_KEYBOARD_STATE: KeyboardState = {\n pressedKeys: [],\n modifiers: { control: false, shift: false, alt: false, meta: false },\n};\n\nconst KeyboardContext = /*#__PURE__*/ createContext<KeyboardState>(\n NEUTRAL_KEYBOARD_STATE\n);\n\nexport interface KeyboardContextProviderProps {\n children: React.ReactNode;\n}\n\nexport const KeyboardContextProvider = /*#__PURE__*/ memo(\n ({ children }: KeyboardContextProviderProps) => {\n const keyboardState = useKeyboard();\n return (\n <KeyboardContext.Provider value={keyboardState}>\n {children}\n </KeyboardContext.Provider>\n );\n }\n);\n\nexport const useKeyboardContext = (): KeyboardState =>\n useContext(KeyboardContext);\n\ntype KeyboardEffectMode = 'keydown' | 'keyup';\n\ntype KeyboardEffect = {\n key: AllKeys;\n actions: {\n [mode in KeyboardEffectMode]?: () => void;\n };\n};\n\nexport function useEffectsOnKeyboard(actions: KeyboardEffect[]): void {\n const keyboard = useKeyboardContext();\n\n const handleKeyDown = useCallback(() => {\n actions.forEach(action => {\n if (isKeyPressed(keyboard, action.key) && action.actions.keydown) {\n action.actions.keydown();\n }\n });\n }, [keyboard, actions]);\n\n const handleKeyUp = useCallback(() => {\n actions.forEach(action => {\n if (isKeyPressed(keyboard, action.key) && action.actions.keyup) {\n action.actions.keyup();\n }\n });\n }, [keyboard, actions]);\n\n useEffect(() => {\n handleKeyDown();\n handleKeyUp();\n }, [handleKeyDown, handleKeyUp]);\n}\n"],"names":[],"mappings":";;;;;;AAcA;AACE;AACA;;AAGF;AAQO;AAEH;AACA;AAKF;AAGK;AAYD;AACJ;AAEA;AACE;AACE;AACE;;AAEJ;AACF;AAEA;AACE;AACE;AACE;;AAEJ;AACF;;AAGE;AACA;AACF;AACF;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useBreakpoint.js","sources":["
|
|
1
|
+
{"version":3,"file":"useBreakpoint.js","sources":["src/hooks/useBreakpoint/useBreakpoint.ts"],"sourcesContent":["'use client';\n\nimport { useMemo } from 'react';\n\nimport { useMediaQuery } from '@/hooks/useMediaQuery';\nimport { BREAKPOINT_ORDER, breakpoints } from '@/theme/breakpoints';\n\nimport type { UseBreakpointReturn } from './useBreakpoint.types';\nimport type { Breakpoint } from '@/theme/breakpoints';\n\nconst SM_QUERY = `(min-width: ${breakpoints.sm}px)`;\nconst MD_QUERY = `(min-width: ${breakpoints.md}px)`;\nconst LG_QUERY = `(min-width: ${breakpoints.lg}px)`;\nconst XL_QUERY = `(min-width: ${breakpoints.xl}px)`;\n\nfunction resolveCurrent(map: Omit<UseBreakpointReturn, 'current'>): Breakpoint {\n for (let i = BREAKPOINT_ORDER.length - 1; i >= 0; i -= 1) {\n const name = BREAKPOINT_ORDER[i];\n if (name && map[name]) return name;\n }\n return 'xs';\n}\n\n/**\n * Reactive named-breakpoint matcher.\n *\n * Returns booleans for each named breakpoint plus the largest matching\n * `current`. Built on top of `useMediaQuery`. Breakpoint values come from\n * `@/theme/breakpoints` and match the values used by `Stack` and other\n * responsive layout components.\n *\n * @example\n * ```tsx\n * const { md, current } = useBreakpoint();\n * return md ? <Sidebar /> : <BottomSheet />;\n * ```\n */\nexport function useBreakpoint(): UseBreakpointReturn {\n const sm = useMediaQuery(SM_QUERY);\n const md = useMediaQuery(MD_QUERY);\n const lg = useMediaQuery(LG_QUERY);\n const xl = useMediaQuery(XL_QUERY);\n\n return useMemo(() => {\n const map = { xs: true, sm, md, lg, xl };\n return { ...map, current: resolveCurrent(map) };\n }, [sm, md, lg, xl]);\n}\n"],"names":[],"mappings":";;;;;AAUA;AACA;AACA;AACA;AAEA;AACE;AACE;AACA;AAAuB;;AAEzB;AACF;AAEA;;;;;;;;;;;;;AAaG;;AAED;AACA;AACA;AACA;;AAGE;;;AAGJ;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useClickOutside.js","sources":["
|
|
1
|
+
{"version":3,"file":"useClickOutside.js","sources":["src/hooks/useClickOutside/useClickOutside.ts"],"sourcesContent":["'use client';\n\nimport { useEffect } from 'react';\n\nimport { useLatest } from '@/hooks/useLatest';\n\nimport type { RefObject } from 'react';\n\nexport interface UseClickOutsideOptions {\n /**\n * When false, the listener is not attached. Useful for toggling without\n * unmount.\n * @default true\n */\n enabled?: boolean;\n /**\n * Mouse / pointer event to listen for.\n * @default 'mousedown'\n */\n event?: 'mousedown' | 'click' | 'pointerdown';\n}\n\ntype AnyHTMLRef = RefObject<HTMLElement | null>;\n\nfunction isOutside(target: EventTarget | null, refs: AnyHTMLRef[]): boolean {\n if (!(target instanceof Node)) return true;\n\n for (const ref of refs) {\n const node = ref.current;\n if (node && (node === target || node.contains(target))) {\n return false;\n }\n }\n return true;\n}\n\n/**\n * Fire a callback when a click occurs outside the referenced element(s).\n *\n * Accepts either a single ref or an array of refs. The handler fires only\n * when the click lands outside ALL provided refs — useful for popover +\n * trigger pairs where clicking either should be considered \"inside\".\n *\n * Defaults to `mousedown` so the handler runs before `click`. SSR-safe;\n * cleans up on unmount and when `enabled` flips to `false`.\n *\n * @example\n * ```tsx\n * const ref = useRef<HTMLDivElement>(null);\n * useClickOutside(ref, () => setOpen(false), { enabled: isOpen });\n *\n * return <div ref={ref}>...</div>;\n * ```\n */\nexport function useClickOutside<T extends HTMLElement = HTMLElement>(\n ref: RefObject<T | null> | Array<RefObject<HTMLElement | null>>,\n handler: (event: MouseEvent) => void,\n options: UseClickOutsideOptions = {}\n): void {\n const { enabled = true, event = 'mousedown' } = options;\n\n // Latest handler / ref(s), refreshed after each commit so the document\n // listener reads live values without re-subscribing and consumers may pass\n // inline arrays.\n const handlerRef = useLatest(handler);\n const refsRef = useLatest<AnyHTMLRef[]>(Array.isArray(ref) ? ref : [ref]);\n\n useEffect(() => {\n if (!enabled) return;\n if (typeof document === 'undefined') return;\n\n const listener = (e: MouseEvent) => {\n if (isOutside(e.target, refsRef.current)) {\n handlerRef.current(e);\n }\n };\n\n document.addEventListener(event, listener);\n return () => {\n document.removeEventListener(event, listener);\n };\n // handlerRef/refsRef are stable useLatest refs; listed to satisfy the rule.\n }, [enabled, event, handlerRef, refsRef]);\n}\n"],"names":[],"mappings":";;;;AAwBA;AACE;AAA+B;AAE/B;AACE;AACA;AACE;;;AAGJ;AACF;AAEA;;;;;;;;;;;;;;;;;AAiBG;AACG;;;;;AAUJ;;;AAIE;;;;AAGA;;AAEI;;AAEJ;AAEA;AACA;AACE;AACF;;;AAGJ;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useClipboard.js","sources":["
|
|
1
|
+
{"version":3,"file":"useClipboard.js","sources":["src/hooks/useClipboard/useClipboard.ts"],"sourcesContent":["'use client';\n\nimport { useCallback, useEffect, useRef, useState } from 'react';\n\nimport type {\n ClipboardStatus,\n UseClipboardOptions,\n UseClipboardReturn,\n} from './useClipboard.types';\n\nconst DEFAULT_TIMEOUT = 2000;\n\nasync function writeViaClipboardApi(text: string): Promise<boolean> {\n if (\n typeof navigator === 'undefined' ||\n typeof navigator.clipboard === 'undefined' ||\n typeof navigator.clipboard.writeText !== 'function'\n ) {\n return false;\n }\n await navigator.clipboard.writeText(text);\n return true;\n}\n\nfunction writeViaExecCommand(text: string): boolean {\n if (typeof document === 'undefined') return false;\n\n const textarea = document.createElement('textarea');\n textarea.value = text;\n textarea.setAttribute('readonly', '');\n textarea.style.position = 'absolute';\n textarea.style.left = '-9999px';\n textarea.style.top = '0';\n document.body.appendChild(textarea);\n textarea.select();\n\n let succeeded = false;\n try {\n // Legacy fallback used only when navigator.clipboard.writeText is\n // unavailable (the modern primary path). execCommand has no modern\n // synchronous equivalent for this fallback scenario.\n // eslint-disable-next-line @typescript-eslint/no-deprecated\n succeeded = document.execCommand('copy');\n } catch {\n succeeded = false;\n }\n document.body.removeChild(textarea);\n return succeeded;\n}\n\n/**\n * Copy text to the clipboard with a built-in timeout-driven feedback state.\n *\n * Uses `navigator.clipboard.writeText` when available; falls back to\n * `document.execCommand('copy')` only when the modern API is missing.\n * In SSR or restricted contexts where neither path is available, `copy`\n * resolves to `false` and `status` flips to `'error'` without throwing.\n *\n * @example\n * ```tsx\n * const { copy, copied } = useClipboard({ timeout: 1500 });\n *\n * return (\n * <Button onClick={() => copy('Hello!')}>\n * {copied ? 'Copied!' : 'Copy'}\n * </Button>\n * );\n * ```\n */\nexport function useClipboard(\n options: UseClipboardOptions = {}\n): UseClipboardReturn {\n const { timeout = DEFAULT_TIMEOUT } = options;\n\n const [status, setStatus] = useState<ClipboardStatus>('idle');\n const [error, setError] = useState<Error | null>(null);\n\n const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const timeoutRef = useRef(timeout);\n // Bumped on every `copy()` call. The async path captures its own value\n // and short-circuits on resolution if a newer call has started — that\n // prevents an older, slower `writeText` from overwriting the state of a\n // newer copy or from arming a stale timer.\n const requestSeqRef = useRef(0);\n\n useEffect(() => {\n timeoutRef.current = timeout;\n }, [timeout]);\n\n const clearTimer = useCallback(() => {\n if (timerRef.current !== null) {\n clearTimeout(timerRef.current);\n timerRef.current = null;\n }\n }, []);\n\n const reset = useCallback(() => {\n clearTimer();\n // Invalidate any in-flight copy so a late resolution does not flip\n // the status back from idle/error.\n requestSeqRef.current += 1;\n setStatus('idle');\n setError(null);\n }, [clearTimer]);\n\n const copy = useCallback(\n async (text: string): Promise<boolean> => {\n clearTimer();\n\n requestSeqRef.current += 1;\n const requestId = requestSeqRef.current;\n\n let succeeded = false;\n let copyError: Error | null = null;\n\n try {\n succeeded = await writeViaClipboardApi(text);\n } catch (err) {\n copyError = err instanceof Error ? err : new Error(String(err));\n }\n\n if (!succeeded && copyError === null) {\n try {\n succeeded = writeViaExecCommand(text);\n if (!succeeded) {\n copyError = new Error('Copy command was rejected by the browser.');\n }\n } catch (err) {\n copyError = err instanceof Error ? err : new Error(String(err));\n }\n }\n\n // A newer call (or `reset`) has started — drop this result on the\n // floor so it does not race the active state.\n if (requestId !== requestSeqRef.current) {\n return succeeded;\n }\n\n if (succeeded) {\n setError(null);\n setStatus('copied');\n timerRef.current = setTimeout(() => {\n timerRef.current = null;\n // Guard the timer too: another copy may have started during the\n // window and bumped the sequence. We still clear the timer ref,\n // but only flip back to idle if the timer belonged to the\n // current request.\n if (requestId === requestSeqRef.current) {\n setStatus('idle');\n }\n }, timeoutRef.current);\n return true;\n }\n\n setError(copyError ?? new Error('Clipboard API is not available.'));\n setStatus('error');\n return false;\n },\n [clearTimer]\n );\n\n useEffect(() => {\n return () => {\n if (timerRef.current !== null) {\n clearTimeout(timerRef.current);\n timerRef.current = null;\n }\n };\n }, []);\n\n return {\n status,\n copied: status === 'copied',\n error,\n copy,\n reset,\n };\n}\n"],"names":[],"mappings":";;;AAUA;AAEA;;AAGI;;AAGA;;;AAGF;AACF;AAEA;;AACuC;;AAGrC;AACA;AACA;AACA;AACA;AACA;;;AAIA;;;;;AAKE;;AACA;;;AAGF;AACA;AACF;AAEA;;;;;;;;;;;;;;;;;;AAkBG;AACG;AAGJ;;;AAKA;AACA;;;;;AAKA;;AAGE;AACF;AAEA;AACE;AACE;AACA;;;AAIJ;AACE;;;AAGA;;;AAGF;;AAII;AAEA;AACA;;;AAKA;AACE;;;AAEA;;AAGF;AACE;AACE;;AAEE;;;;AAGF;;;;;AAMJ;AACE;;;;;AAMA;AACE;;;;;AAKA;;;AAGF;AACA;;;;AAKF;AACF;;AAKA;AACE;AACE;AACA;;AAEJ;;;;;;;;;AAUJ;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useControlledState.js","sources":["
|
|
1
|
+
{"version":3,"file":"useControlledState.js","sources":["src/hooks/useControlledState/useControlledState.ts"],"sourcesContent":["'use client';\n\nimport { useCallback, useEffect, useRef, useState } from 'react';\n\nimport { devWarn } from '@/utils/devWarn';\n\nexport interface UseControlledStateOptions<T> {\n /** Controlled value — when defined, drives the returned value. */\n value?: T;\n /** Default value used when the hook is uncontrolled. */\n defaultValue?: T;\n /** Called when the consumer requests a change in either mode. */\n onChange?: (value: T) => void;\n /** Fallback used when both `value` and `defaultValue` are undefined. */\n fallback: T;\n}\n\n/**\n * Manage a value that may be either controlled (via prop) or uncontrolled\n * (managed internally with a default).\n *\n * Returns a `[value, setValue]` tuple just like `useState`. When\n * `options.value` is defined the state is read from there and `setValue`\n * becomes a pure side-effect callback that calls `onChange` only — internal\n * state is never mutated. When `options.value` is undefined the hook owns the\n * state and `setValue` updates it as well as calls `onChange`.\n *\n * Switching between controlled and uncontrolled within a component's lifetime\n * is a known pitfall (matches React's own warning for `<input value/defaultValue>`).\n * The hook emits a development-only warning when this happens.\n *\n * @example\n * ```ts\n * const [value, setValue] = useControlledState({\n * value: props.value,\n * defaultValue: props.defaultValue,\n * onChange: props.onChange,\n * fallback: '',\n * });\n * ```\n */\nexport function useControlledState<T>(\n options: UseControlledStateOptions<T>\n): [T, (next: T | ((prev: T) => T)) => void] {\n const { value, defaultValue, onChange, fallback } = options;\n\n const isControlled = value !== undefined;\n\n // Only `undefined` means \"no defaultValue given\" — `null` is a legal value\n // for generic T (e.g. `string | null`) and must not be replaced by fallback.\n const [internalValue, setInternalValue] = useState<T>(\n // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing\n defaultValue !== undefined ? defaultValue : fallback\n );\n\n // Stable refs to avoid stale closures and unnecessary identity changes.\n const onChangeRef = useRef(onChange);\n const internalValueRef = useRef(internalValue);\n const isControlledRef = useRef(isControlled);\n\n useEffect(() => {\n onChangeRef.current = onChange;\n }, [onChange]);\n\n useEffect(() => {\n internalValueRef.current = internalValue;\n }, [internalValue]);\n\n // Dev-only warning when controlled <-> uncontrolled flips after mount.\n // Mirrors React's behavior for <input value/defaultValue>.\n useEffect(() => {\n const wasControlled = isControlledRef.current;\n if (wasControlled !== isControlled) {\n devWarn(\n `[useControlledState] Component is changing from ${\n wasControlled ? 'controlled' : 'uncontrolled'\n } to ${isControlled ? 'controlled' : 'uncontrolled'}. ` +\n 'Components should not switch between controlled and uncontrolled ' +\n '(or vice versa) during their lifetime. Decide between using a ' +\n 'controlled or uncontrolled mode for the lifetime of the component.'\n );\n isControlledRef.current = isControlled;\n }\n }, [isControlled]);\n\n const setValue = useCallback(\n (next: T | ((prev: T) => T)) => {\n const prev = isControlledRef.current\n ? (value as T)\n : internalValueRef.current;\n const resolved =\n typeof next === 'function' ? (next as (p: T) => T)(prev) : next;\n\n if (!isControlledRef.current) {\n setInternalValue(resolved);\n internalValueRef.current = resolved;\n }\n onChangeRef.current?.(resolved);\n },\n [value]\n );\n\n const currentValue = isControlled ? (value as T) : internalValue;\n return [currentValue, setValue];\n}\n"],"names":[],"mappings":";;;;AAiBA;;;;;;;;;;;;;;;;;;;;;;;AAuBG;AACG;;AAKJ;;;AAIA;;;;AAMA;AACA;AACA;;AAGE;AACF;;AAGE;AACF;;;;AAKE;AACA;;;;AAOM;AAEJ;;AAEJ;AAEA;AAEI;AACE;AACA;AACF;AAGA;;AAEE;;AAEF;AACF;;AAKF;AACF;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useDebouncedCallback.js","sources":["
|
|
1
|
+
{"version":3,"file":"useDebouncedCallback.js","sources":["src/hooks/useDebounced/useDebouncedCallback.ts"],"sourcesContent":["'use client';\n\nimport { useCallback, useEffect, useMemo, useRef } from 'react';\n\nimport { useEventCallback } from '@/hooks/useEventCallback';\n\nimport type {\n DebouncedCallback,\n UseDebouncedCallbackOptions,\n} from './useDebounced.types';\n\n/**\n * Wrap a function so rapid calls collapse into one. By default the call\n * fires on the trailing edge — pass `leading: true` to also fire\n * immediately. `maxWait` forces a call even when calls keep coming.\n *\n * The wrapper identity is stable; passing fresh inline functions across\n * renders is safe and does not reset pending timers.\n *\n * @example\n * ```tsx\n * const onResize = useDebouncedCallback(measure, 150);\n * useEffect(() => {\n * window.addEventListener('resize', onResize);\n * return () => window.removeEventListener('resize', onResize);\n * }, [onResize]);\n * ```\n */\nexport function useDebouncedCallback<Args extends unknown[]>(\n fn: (...args: Args) => void,\n delay: number,\n options: UseDebouncedCallbackOptions = {}\n): DebouncedCallback<Args> {\n const { leading = false, maxWait } = options;\n\n // Stable wrapper that always invokes the current callback, refreshed via\n // useInsertionEffect so the debounced wrapper never goes stale.\n const stableFn = useEventCallback(fn);\n\n const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const maxTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const lastArgsRef = useRef<Args | null>(null);\n const lastInvokeRef = useRef<number>(0);\n\n const clearTimers = useCallback(() => {\n if (timerRef.current !== null) {\n clearTimeout(timerRef.current);\n timerRef.current = null;\n }\n if (maxTimerRef.current !== null) {\n clearTimeout(maxTimerRef.current);\n maxTimerRef.current = null;\n }\n }, []);\n\n const invoke = useCallback(() => {\n const args = lastArgsRef.current;\n lastArgsRef.current = null;\n lastInvokeRef.current = Date.now();\n clearTimers();\n if (args) stableFn(...args);\n }, [clearTimers, stableFn]);\n\n const debounced = useMemo<DebouncedCallback<Args>>(() => {\n const wrapper = (...args: Args): void => {\n lastArgsRef.current = args;\n // Date.now() runs only when the wrapper is invoked from an event/timer,\n // never during render, so it is not a render-purity concern here.\n // eslint-disable-next-line react-hooks/purity\n const now = Date.now();\n const isLeadingCall =\n leading && timerRef.current === null && maxTimerRef.current === null;\n\n if (isLeadingCall) {\n // Fire immediately, then suppress trailing for `delay` ms unless\n // additional calls happen — in that case the trailing handler will\n // pick them up.\n lastArgsRef.current = null;\n lastInvokeRef.current = now;\n stableFn(...args);\n }\n\n if (timerRef.current !== null) clearTimeout(timerRef.current);\n timerRef.current = setTimeout(() => {\n timerRef.current = null;\n if (lastArgsRef.current !== null) invoke();\n else if (maxTimerRef.current !== null) {\n clearTimeout(maxTimerRef.current);\n maxTimerRef.current = null;\n }\n }, delay);\n\n if (maxWait !== undefined && maxTimerRef.current === null) {\n maxTimerRef.current = setTimeout(() => {\n maxTimerRef.current = null;\n if (lastArgsRef.current !== null) invoke();\n }, maxWait);\n }\n };\n\n wrapper.cancel = () => {\n lastArgsRef.current = null;\n clearTimers();\n };\n wrapper.flush = () => {\n if (lastArgsRef.current !== null) invoke();\n else clearTimers();\n };\n\n return wrapper;\n }, [delay, leading, maxWait, invoke, clearTimers, stableFn]);\n\n // Cleanup on unmount.\n useEffect(() => clearTimers, [clearTimers]);\n\n return debounced;\n}\n"],"names":[],"mappings":";;;;AAWA;;;;;;;;;;;;;;;;AAgBG;AACG;;;;AASJ;AAEA;AACA;AACA;AACA;AAEA;AACE;AACE;AACA;;AAEF;AACE;AACA;;;AAIJ;AACE;AACA;AACA;AACA;AACA;AAAU;AACZ;AAEA;AACE;AACE;;;;AAIA;AACA;;;;;AAOE;AACA;AACA;;AAGF;AAA+B;AAC/B;AACE;AACA;AAAkC;AAC7B;AACH;AACA;;;;AAKF;AACE;AACA;AAAkC;;;AAGxC;AAEA;AACE;AACA;AACF;AACA;AACE;AAAkC;;AAC7B;AACP;AAEA;AACF;;;AAKA;AACF;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useDebouncedValue.js","sources":["
|
|
1
|
+
{"version":3,"file":"useDebouncedValue.js","sources":["src/hooks/useDebounced/useDebouncedValue.ts"],"sourcesContent":["'use client';\n\nimport { useEffect, useState } from 'react';\n\n/**\n * Returns a copy of `value` that updates only after `delay` ms have elapsed\n * without further changes (trailing-edge debounce).\n *\n * @deprecated Prefer React's `useDeferredValue` for the common case — keeping\n * an input responsive while an expensive derivation (filtering, search) lags\n * behind it. It needs no fixed delay, integrates with concurrent rendering,\n * and gives better interactivity on large lists. Reach for `useDebouncedCallback`\n * when you need to debounce a *side effect* (a network request, an analytics\n * ping) rather than a rendered value. This hook stays exported for now but\n * will not see further work.\n *\n * @example\n * ```tsx\n * // Preferred: defer the value that drives the expensive derivation.\n * const deferredQuery = useDeferredValue(query);\n * const results = useMemo(\n * () => filter(items, deferredQuery),\n * [items, deferredQuery]\n * );\n * ```\n */\nexport function useDebouncedValue<T>(value: T, delay: number): T {\n const [debounced, setDebounced] = useState<T>(value);\n\n useEffect(() => {\n if (delay <= 0) {\n // Synchronous passthrough when debouncing is disabled; this hook's whole\n // purpose is to mirror an external value into state.\n // eslint-disable-next-line react-hooks/set-state-in-effect\n setDebounced(value);\n return;\n }\n const id = setTimeout(() => {\n setDebounced(value);\n }, delay);\n return () => {\n clearTimeout(id);\n };\n }, [value, delay]);\n\n return debounced;\n}\n"],"names":[],"mappings":";;;AAIA;;;;;;;;;;;;;;;;;;;;;AAqBG;AACG;;;AAIF;;;;;;;AAOA;;;AAGA;;AAEA;AACF;AAEA;AACF;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useDisclosure.js","sources":["
|
|
1
|
+
{"version":3,"file":"useDisclosure.js","sources":["src/hooks/useDisclosure/useDisclosure.ts"],"sourcesContent":["'use client';\n\nimport { useCallback, useMemo } from 'react';\n\nimport { useControlledState } from '@/hooks/useControlledState';\n\nimport type {\n UseDisclosureOptions,\n UseDisclosureReturn,\n} from './useDisclosure.types';\n\n/**\n * Manage an open/closed state with stable `open`, `close`, `toggle` callbacks.\n *\n * Supports both controlled (driven by an `open` prop) and uncontrolled\n * (managed internally with `defaultOpen`) modes. Built on top of\n * `useControlledState` so the same controlled / uncontrolled rules apply —\n * including the development warning when switching modes mid-life.\n *\n * @example\n * ```tsx\n * const { isOpen, open, close, toggle } = useDisclosure();\n *\n * return (\n * <>\n * <Button onClick={open}>Open</Button>\n * <Dialog open={isOpen} onClose={close}>...</Dialog>\n * </>\n * );\n * ```\n */\nexport function useDisclosure(\n options: UseDisclosureOptions = {}\n): UseDisclosureReturn {\n const { defaultOpen, open: controlledOpen, onOpenChange } = options;\n\n const [isOpen, setOpenState] = useControlledState<boolean>({\n value: controlledOpen,\n defaultValue: defaultOpen,\n onChange: onOpenChange,\n fallback: false,\n });\n\n const setOpen = useCallback(\n (next: boolean) => {\n setOpenState(next);\n },\n [setOpenState]\n );\n\n const open = useCallback(() => {\n setOpenState(true);\n }, [setOpenState]);\n\n const close = useCallback(() => {\n setOpenState(false);\n }, [setOpenState]);\n\n const toggle = useCallback(() => {\n setOpenState(prev => !prev);\n }, [setOpenState]);\n\n return useMemo(\n () => ({ isOpen, open, close, toggle, setOpen }),\n [isOpen, open, close, toggle, setOpen]\n );\n}\n"],"names":[],"mappings":";;;;AAWA;;;;;;;;;;;;;;;;;;;AAmBG;AACG;;AAKJ;AACE;AACA;AACA;AACA;AACD;AAED;;AAGE;AAIF;;AAEA;AAEA;;AAEA;AAEA;;AAEA;AAEA;AAIF;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useEventCallback.js","sources":["
|
|
1
|
+
{"version":3,"file":"useEventCallback.js","sources":["src/hooks/useEventCallback/useEventCallback.ts"],"sourcesContent":["'use client';\n\nimport { useCallback, useInsertionEffect, useRef } from 'react';\n\n/**\n * Wrap a callback so its identity stays stable for the component lifetime\n * while it always invokes the most recently rendered version — the\n * `useEffectEvent` pattern, backed by `useInsertionEffect`.\n *\n * The stored callback is refreshed in `useInsertionEffect`, which commits\n * before any layout effect. So the freshness guarantee holds even for\n * consumers that read it during the same commit: a child's\n * `useLayoutEffect`, a synchronous external-store subscription, an\n * imperative handle. A passively updated ref (e.g. `useLatest`, which writes\n * in `useEffect`) would still be one render stale in those cases.\n *\n * Pass the result to memoized children, dependency arrays, or external\n * subscriptions without re-subscribing — the reference never changes, yet it\n * still closes over the latest props and state.\n *\n * The one rule: do not call it during render. Like an event handler it reads\n * through a ref, so a render-phase call has no meaningful \"latest\" value to\n * read. Use it in event handlers, effects, layout effects, timers, and\n * subscriptions only.\n *\n * @example\n * ```tsx\n * const handleChange = useEventCallback((value: string) => {\n * onChange?.(value, internalState);\n * });\n * useEffect(() => store.subscribe(handleChange), [handleChange]);\n * ```\n */\nexport function useEventCallback<Args extends unknown[], Return>(\n fn: (...args: Args) => Return\n): (...args: Args) => Return {\n const ref = useRef(fn);\n\n // Refresh the stored callback before layout effects run. useInsertionEffect\n // commits earlier than useEffect / useLayoutEffect, so children's layout\n // effects and synchronous subscriptions firing in the same commit already\n // observe the latest callback.\n useInsertionEffect(() => {\n ref.current = fn;\n }, [fn]);\n\n // Stable wrapper for the component lifetime. Reading `ref.current` happens\n // only when the wrapper is invoked from an event, effect, or timer — never\n // during render.\n return useCallback((...args: Args): Return => ref.current(...args), []);\n}\n"],"names":[],"mappings":";;;AAIA;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BG;AACG;AAGJ;;;;;;AAOE;AACF;;;;AAKA;AACF;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useFocusTrap.js","sources":["
|
|
1
|
+
{"version":3,"file":"useFocusTrap.js","sources":["src/hooks/useFocusTrap/useFocusTrap.ts"],"sourcesContent":["'use client';\n\nimport { useCallback } from 'react';\n\nimport type { KeyboardEvent, RefObject } from 'react';\n\nconst FOCUSABLE_SELECTOR = [\n 'a[href]',\n 'button:not([disabled])',\n 'input:not([disabled])',\n 'textarea:not([disabled])',\n 'select:not([disabled])',\n '[tabindex]:not([tabindex=\"-1\"])',\n].join(', ');\n\nexport interface UseFocusTrapOptions {\n /** Ref to the container element whose focusable descendants are trapped. */\n containerRef: RefObject<HTMLElement | null>;\n /**\n * When false, the trap is bypassed (Tab works normally). Useful for toggling\n * trap state without unmounting.\n * @default true\n */\n enabled?: boolean;\n}\n\n/**\n * Trap focus within a container element. Tab and Shift+Tab cycle through the\n * focusable descendants without escaping the container.\n *\n * Returns a keyboard event handler to attach to the container's `onKeyDown`.\n *\n * @example\n * ```tsx\n * const ref = useRef<HTMLDivElement>(null);\n * const handleKeyDown = useFocusTrap({ containerRef: ref, enabled: isOpen });\n *\n * return <div ref={ref} onKeyDown={handleKeyDown}>...</div>;\n * ```\n */\nexport function useFocusTrap({\n containerRef,\n enabled = true,\n}: UseFocusTrapOptions): (event: KeyboardEvent) => void {\n return useCallback(\n (e: KeyboardEvent) => {\n if (!enabled || e.key !== 'Tab') return;\n\n const container = containerRef.current;\n if (!container) return;\n\n const focusableElements = Array.from(\n container.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR)\n );\n\n if (focusableElements.length === 0) {\n e.preventDefault();\n return;\n }\n\n const firstElement = focusableElements[0] as HTMLElement;\n const lastElement = focusableElements[\n focusableElements.length - 1\n ] as HTMLElement;\n\n if (e.shiftKey) {\n if (document.activeElement === firstElement) {\n e.preventDefault();\n lastElement.focus();\n }\n } else {\n if (document.activeElement === lastElement) {\n e.preventDefault();\n firstElement.focus();\n }\n }\n },\n [enabled, containerRef]\n );\n}\n\nexport { FOCUSABLE_SELECTOR };\n"],"names":[],"mappings":";;;AAMA;;;;;;;AAOC;AAaD;;;;;;;;;;;;;AAaG;AACG;AAIJ;AAEI;;AAEA;AACA;;AAEA;AAIA;;;;AAKA;;AAKA;AACE;;;;;;AAKA;;;;;AAKJ;AAGJ;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useHotkey.js","sources":["
|
|
1
|
+
{"version":3,"file":"useHotkey.js","sources":["src/hooks/useHotkey/useHotkey.ts"],"sourcesContent":["'use client';\n\nimport { useEffect, useMemo, useRef } from 'react';\n\nimport { useLatest } from '@/hooks/useLatest';\nimport { getPlatform, parseShortcut } from '@/utils/platform';\n\nimport type { HotkeyTarget, UseHotkeyOptions } from './useHotkey.types';\nimport type { RefObject } from 'react';\n\nfunction isRefObject(value: unknown): value is RefObject<EventTarget | null> {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'current' in (value as Record<string, unknown>)\n );\n}\n\nfunction resolveTarget(target: HotkeyTarget | undefined): EventTarget | null {\n if (target === undefined) {\n return typeof window === 'undefined' ? null : window;\n }\n if (target === null) return null;\n if (isRefObject(target)) return target.current;\n return target;\n}\n\ninterface ParsedCombo {\n ctrl: boolean;\n meta: boolean;\n alt: boolean;\n shift: boolean;\n /** Lower-cased non-modifier key, e.g. \"s\", \"k\", \"escape\", \"arrowup\". */\n key: string;\n}\n\nconst MODIFIERS = new Set(['ctrl', 'cmd', 'meta', 'alt', 'option', 'shift']);\n\nconst KEY_ALIASES: Record<string, string> = {\n esc: 'escape',\n return: 'enter',\n space: ' ',\n spacebar: ' ',\n up: 'arrowup',\n down: 'arrowdown',\n left: 'arrowleft',\n right: 'arrowright',\n plus: '+',\n};\n\nfunction normalizeKey(key: string): string {\n const lower = key.toLowerCase();\n return KEY_ALIASES[lower] ?? lower;\n}\n\nfunction parseCombo(combo: string): ParsedCombo | null {\n const parts = parseShortcut(combo);\n if (parts.length === 0) return null;\n\n const platform = getPlatform();\n const isMac = platform === 'mac';\n\n let ctrl = false;\n let meta = false;\n let alt = false;\n let shift = false;\n let key: string | null = null;\n\n for (const raw of parts) {\n const lower = raw.toLowerCase();\n if (MODIFIERS.has(lower)) {\n switch (lower) {\n case 'ctrl':\n ctrl = true;\n break;\n case 'cmd':\n // Cmd auto-maps to Ctrl on non-Mac.\n if (isMac) {\n meta = true;\n } else {\n ctrl = true;\n }\n break;\n case 'meta':\n meta = true;\n break;\n case 'alt':\n case 'option':\n alt = true;\n break;\n case 'shift':\n shift = true;\n break;\n }\n } else {\n key = normalizeKey(raw);\n }\n }\n\n if (key === null) return null;\n return { ctrl, meta, alt, shift, key };\n}\n\nfunction isEditableTarget(target: EventTarget | null): boolean {\n if (!(target instanceof HTMLElement)) return false;\n if (target.isContentEditable) return true;\n const tag = target.tagName;\n if (tag === 'INPUT') {\n const type = (target as HTMLInputElement).type.toLowerCase();\n // Buttons and similar non-text inputs should not block hotkeys.\n const nonEditable = new Set([\n 'button',\n 'submit',\n 'reset',\n 'checkbox',\n 'radio',\n 'file',\n 'image',\n 'range',\n 'color',\n ]);\n return !nonEditable.has(type);\n }\n return tag === 'TEXTAREA' || tag === 'SELECT';\n}\n\nfunction matches(parsed: ParsedCombo, event: KeyboardEvent): boolean {\n if (event.ctrlKey !== parsed.ctrl) return false;\n if (event.metaKey !== parsed.meta) return false;\n if (event.altKey !== parsed.alt) return false;\n if (event.shiftKey !== parsed.shift) return false;\n\n const eventKey = event.key.toLowerCase();\n return eventKey === parsed.key;\n}\n\n/**\n * Bind a single keyboard combo to a callback.\n *\n * Combos use the same `+`-separated string format as `MenuBar`'s `shortcut`\n * prop: `\"Ctrl+S\"`, `\"Cmd+Shift+P\"`, `\"Escape\"`. Modifiers: `Ctrl`, `Cmd`\n * (= Meta), `Alt` / `Option`, `Shift`. `Cmd` automatically maps to `Ctrl` on\n * non-Mac platforms via `getPlatform()`.\n *\n * The handler reference is stable — passing a fresh inline function on every\n * render is safe and does not re-subscribe the underlying listener.\n *\n * @example\n * ```tsx\n * useHotkey('Ctrl+S', e => save(), { preventDefault: true });\n * useHotkey('Escape', () => setOpen(false));\n * useHotkey('Cmd+K', () => openCommandPalette()); // Cmd on Mac, Ctrl elsewhere\n * ```\n */\nexport function useHotkey(\n combo: string,\n handler: (event: KeyboardEvent) => void,\n options: UseHotkeyOptions = {}\n): void {\n const {\n enabled = true,\n enableInInputs = false,\n preventDefault = true,\n stopPropagation = false,\n target,\n } = options;\n\n // Latest handler / options / parsed combo, refreshed after each commit so\n // the document listener reads live values without re-subscribing.\n const handlerRef = useLatest(handler);\n const optionsRef = useLatest({\n enabled,\n enableInInputs,\n preventDefault,\n stopPropagation,\n });\n\n const parsed = useMemo(() => parseCombo(combo), [combo]);\n const parsedRef = useLatest(parsed);\n\n const attachedTargetRef = useRef<EventTarget | null>(null);\n const listenerRef = useRef<((event: Event) => void) | null>(null);\n\n // Runs every render so a ref whose `.current` becomes available after the\n // first render (conditional rendering, lazy children) is picked up. The\n // identity check on the resolved target keeps re-attaches limited to the\n // moments the underlying element actually changes.\n useEffect(() => {\n const desired = enabled && parsed !== null ? resolveTarget(target) : null;\n if (desired === attachedTargetRef.current) return;\n\n if (attachedTargetRef.current && listenerRef.current) {\n attachedTargetRef.current.removeEventListener(\n 'keydown',\n listenerRef.current\n );\n }\n attachedTargetRef.current = desired;\n if (desired === null) {\n listenerRef.current = null;\n return;\n }\n\n const listener = (event: Event) => {\n const ke = event as KeyboardEvent;\n const opts = optionsRef.current;\n const p = parsedRef.current;\n if (p === null || !opts.enabled) return;\n if (!matches(p, ke)) return;\n if (!opts.enableInInputs && isEditableTarget(ke.target)) return;\n\n if (opts.preventDefault) ke.preventDefault();\n handlerRef.current(ke);\n if (opts.stopPropagation) ke.stopPropagation();\n };\n listenerRef.current = listener;\n desired.addEventListener('keydown', listener);\n });\n\n useEffect(\n () => () => {\n if (attachedTargetRef.current && listenerRef.current) {\n attachedTargetRef.current.removeEventListener(\n 'keydown',\n listenerRef.current\n );\n }\n attachedTargetRef.current = null;\n listenerRef.current = null;\n },\n []\n );\n}\n"],"names":[],"mappings":";;;;;AAUA;AACE;AAEE;;AAGJ;AAEA;AACE;AACE;;;AAEmB;;;AAErB;AACF;AAWA;AAEA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAGF;AACE;AACA;AACF;AAEA;AACE;AACA;AAAwB;AAExB;AACA;;;;;;AAQA;AACE;AACA;;AAEI;;;AAGA;;;;;;;;;AAQA;;;AAGA;AACA;;;AAGA;;;;;;AAKF;;;;AAIc;;AAEpB;AAEA;AACE;AAAsC;;AACR;AAC9B;AACA;;;AAGE;;;;;;;;;;AAUC;AACD;;AAEF;AACF;AAEA;AACE;AAAmC;AACnC;AAAmC;AACnC;AAAiC;AACjC;AAAqC;;AAGrC;AACF;AAEA;;;;;;;;;;;;;;;;;AAiBG;AACG;;;;AAeJ;;;;;;AAMC;AAED;AACA;AAEA;AACA;;;;;;AAOE;AACA;;;;;AAQA;AACA;AACE;;;AAIF;;AAEE;AACA;AACA;;AACA;;;;;;AAIA;;;AAEF;AACA;AACA;AACF;AAEA;;;;AAQI;AACA;;AAIN;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useIntersectionObserver.js","sources":["
|
|
1
|
+
{"version":3,"file":"useIntersectionObserver.js","sources":["src/hooks/useIntersectionObserver/useIntersectionObserver.ts"],"sourcesContent":["'use client';\n\nimport { useCallback, useEffect, useRef, useState } from 'react';\n\nimport type {\n UseIntersectionObserverOptions,\n UseIntersectionObserverReturn,\n} from './useIntersectionObserver.types';\nimport type React from 'react';\n\n/**\n * Observe whether an element intersects a root (defaults to the viewport).\n * Returns a ref callback to attach to the observed element plus the latest\n * `IntersectionObserverEntry`. SSR-safe: returns a no-op ref and `null`\n * entry when `IntersectionObserver` is unavailable.\n *\n * @example\n * ```tsx\n * const { ref, isIntersecting } = useIntersectionObserver<HTMLDivElement>({\n * rootMargin: '200px',\n * });\n * useEffect(() => {\n * if (isIntersecting) loadMore();\n * }, [isIntersecting]);\n * return <div ref={ref} />;\n * ```\n */\nexport function useIntersectionObserver<T extends Element = Element>(\n options: UseIntersectionObserverOptions = {}\n): UseIntersectionObserverReturn<T> {\n const { root, rootMargin, threshold, enabled = true } = options;\n\n const [entry, setEntry] = useState<IntersectionObserverEntry | null>(null);\n const observerRef = useRef<IntersectionObserver | null>(null);\n const nodeRef = useRef<T | null>(null);\n\n const ref = useCallback<React.RefCallback<T>>(\n node => {\n if (typeof IntersectionObserver === 'undefined') {\n nodeRef.current = node;\n return;\n }\n\n if (observerRef.current) {\n observerRef.current.disconnect();\n observerRef.current = null;\n }\n\n nodeRef.current = node;\n if (!enabled || !node) {\n setEntry(null);\n return;\n }\n\n const observer = new IntersectionObserver(\n entries => {\n const next = entries[0];\n if (next) setEntry(next);\n },\n { root: root ?? null, rootMargin, threshold }\n );\n observer.observe(node);\n observerRef.current = observer;\n },\n [root, rootMargin, threshold, enabled]\n );\n\n // When toggled off via `enabled`, disconnect proactively so consumers don't\n // have to detach the ref to stop observing.\n useEffect(() => {\n if (enabled) return;\n if (observerRef.current) {\n observerRef.current.disconnect();\n observerRef.current = null;\n }\n // Sync state to the external observer being torn down when disabled.\n // eslint-disable-next-line react-hooks/set-state-in-effect\n setEntry(null);\n }, [enabled]);\n\n // Final cleanup on unmount.\n useEffect(\n () => () => {\n observerRef.current?.disconnect();\n observerRef.current = null;\n nodeRef.current = null;\n },\n []\n );\n\n return {\n ref,\n entry,\n isIntersecting: entry?.isIntersecting ?? false,\n };\n}\n"],"names":[],"mappings":";;;AAUA;;;;;;;;;;;;;;;;AAgBG;AACG;AAGJ;;AAGA;AACA;AAEA;AAEI;AACE;;;AAIF;AACE;AACA;;AAGF;AACA;;;;AAKA;AAEI;AACA;;AACF;AAGF;AACA;;;;;AAQF;;AACA;AACE;AACA;;;;;AAKJ;;AAGA;AAEI;AACA;AACA;;;;;AAQF;;AAEJ;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useIsMounted.js","sources":["
|
|
1
|
+
{"version":3,"file":"useIsMounted.js","sources":["src/hooks/useIsMounted/useIsMounted.ts"],"sourcesContent":["'use client';\n\nimport { useCallback, useEffect, useRef } from 'react';\n\n/**\n * Returns a stable getter that reports whether the component is still\n * mounted. Call it inside async continuations (promises, timers, fetches)\n * before committing state, so a resolution that lands after unmount becomes\n * a no-op instead of a \"set state on an unmounted component\" warning.\n *\n * The getter identity is stable for the component lifetime, so it is safe to\n * include in — or omit from — dependency arrays without churn. It reports\n * `false` until the mount effect commits and `false` again after unmount,\n * which also covers the double mount/unmount of React StrictMode.\n *\n * Prefer real cancellation (`AbortController`, effect cleanup) where the\n * async source supports it; reach for this only when the source cannot be\n * cancelled.\n *\n * @example\n * ```tsx\n * const isMounted = useIsMounted();\n * useEffect(() => {\n * void load().then(data => {\n * if (isMounted()) setData(data);\n * });\n * }, [isMounted]);\n * ```\n */\nexport function useIsMounted(): () => boolean {\n const mountedRef = useRef(false);\n\n useEffect(() => {\n mountedRef.current = true;\n return () => {\n mountedRef.current = false;\n };\n }, []);\n\n // Reading `mountedRef.current` happens only when the getter is invoked from\n // an async continuation or effect — never during render.\n return useCallback(() => mountedRef.current, []);\n}\n"],"names":[],"mappings":";;;AAIA;;;;;;;;;;;;;;;;;;;;;;;;AAwBG;;AAED;;AAGE;AACA;AACE;AACF;;;;;AAMJ;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useKeyboard.js","sources":["
|
|
1
|
+
{"version":3,"file":"useKeyboard.js","sources":["src/hooks/useKeyboard/useKeyboard.tsx"],"sourcesContent":["'use client';\n\n// src/hooks/useKeyboard.ts\nimport { useEffect, useState, useCallback, useMemo } from 'react';\n\nimport { updateState, mapInnerStateToState } from './utils';\n\nimport type { KeyboardState, KeyboardInnerState, AllKeys } from './types';\n\nexport type { ModifierKeys, KeyboardState, AllKeys } from './types';\nexport { isKeyPressed, isModifierKey } from './utils';\n\n/**\n * Hook for tracking keyboard state including modifier keys and pressed keys.\n *\n * Useful for components that need to respond to keyboard modifiers like\n * Ctrl+drag for snapping, Shift+drag for precision, etc.\n * @returns Current keyboard state\n *\n * @example\n * ```tsx\n * // Track all keys and modifiers\n * const keyboard = useKeyboard();\n *\n * const handleMouseMove = (e: MouseEvent) => {\n * if (keyboard.ctrl) {\n * // Snap to grid\n * } else if (keyboard.shift) {\n * // Precision mode\n * }\n * };\n *\n * // Track only specific keys\n * const keyboard = useKeyboard({\n * trackAllKeys: false,\n * trackedKeys: ['Space', 'Enter', 'Escape']\n * });\n *\n * // Check if specific key is pressed\n * const isSpacePressed = keyboard.pressedKeys.includes('Space');\n * ```\n */\nexport const useKeyboard = (): KeyboardState => {\n const [keyboardState, setKeyboardState] = useState<KeyboardInnerState>(\n new Set<AllKeys>()\n );\n\n const handleKeyDown = useCallback((event: KeyboardEvent) => {\n setKeyboardState(prevState => updateState(prevState, event));\n }, []);\n\n const handleKeyUp = useCallback((event: KeyboardEvent) => {\n setKeyboardState(prevState => updateState(prevState, event));\n }, []);\n\n useEffect(() => {\n window.addEventListener('keydown', handleKeyDown);\n window.addEventListener('keyup', handleKeyUp);\n\n return () => {\n window.removeEventListener('keydown', handleKeyDown);\n window.removeEventListener('keyup', handleKeyUp);\n };\n }, [handleKeyDown, handleKeyUp]);\n\n return useMemo(() => mapInnerStateToState(keyboardState), [keyboardState]);\n};\n"],"names":[],"mappings":";;;;;AAYA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BG;AACI;AACL;AAIA;AACE;;AAGF;AACE;;;AAIA;AACA;AAEA;AACE;AACA;AACF;AACF;AAEA;AACF;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","sources":["
|
|
1
|
+
{"version":3,"file":"utils.js","sources":["src/hooks/useKeyboard/utils.ts"],"sourcesContent":["import { devWarn } from '@/utils/devWarn';\n\nimport {\n KeyboardState,\n KeyboardInnerState,\n AllKeys,\n ModifierKeys,\n} from './types';\n\nexport const isModifierKey = (key: string): key is ModifierKeys => {\n return ['ctrl', 'shift', 'alt', 'meta', 'control'].includes(key);\n};\n\nconst KeypressUpdate = (\n prevState: KeyboardInnerState,\n key: AllKeys\n): KeyboardInnerState => {\n if (prevState.has(key)) {\n return prevState;\n }\n const newState = new Set(prevState);\n newState.add(key);\n return newState;\n};\n\nconst KeyReleaseUpdate = (\n prevState: KeyboardInnerState,\n key: AllKeys\n): KeyboardInnerState => {\n if (!prevState.has(key)) {\n return prevState;\n }\n const newState = new Set(prevState);\n newState.delete(key);\n return newState;\n};\n\nexport const updateState = (\n prevState: KeyboardInnerState,\n event: KeyboardEvent\n): KeyboardInnerState => {\n const key = event.key.toLowerCase();\n const eventType = event.type;\n switch (eventType) {\n case 'keydown':\n return KeypressUpdate(prevState, key);\n case 'keyup':\n return KeyReleaseUpdate(prevState, key);\n default:\n devWarn(`Unhandled event type: ${eventType}`);\n return prevState;\n }\n};\n\nexport const mapInnerStateToState = (\n innerState: KeyboardInnerState\n): KeyboardState => {\n const modifiers: KeyboardState['modifiers'] = {\n control: innerState.has('control') || innerState.has('ctrl'),\n shift: innerState.has('shift'),\n alt: innerState.has('alt'),\n meta: innerState.has('meta'),\n };\n const pressedKeys = Array.from(innerState).filter(key => !isModifierKey(key));\n return {\n pressedKeys,\n modifiers,\n };\n};\n\nexport const isKeyPressed = (\n keyboardState: KeyboardState,\n key: AllKeys\n): boolean => {\n if (isModifierKey(key)) {\n return keyboardState.modifiers[key];\n }\n return keyboardState.pressedKeys.includes(key);\n};\n"],"names":[],"mappings":";;AASO,MAAM,aAAa,GAAG,CAAC,GAAW,KAAyB;AAChE,IAAA,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC;AAClE;AAEA,MAAM,cAAc,GAAG,CACrB,SAA6B,EAC7B,GAAY,KACU;AACtB,IAAA,IAAI,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE;AACtB,QAAA,OAAO,SAAS;IAClB;AACA,IAAA,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC;AACnC,IAAA,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC;AACjB,IAAA,OAAO,QAAQ;AACjB,CAAC;AAED,MAAM,gBAAgB,GAAG,CACvB,SAA6B,EAC7B,GAAY,KACU;IACtB,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE;AACvB,QAAA,OAAO,SAAS;IAClB;AACA,IAAA,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC;AACnC,IAAA,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC;AACpB,IAAA,OAAO,QAAQ;AACjB,CAAC;MAEY,WAAW,GAAG,CACzB,SAA6B,EAC7B,KAAoB,KACE;IACtB,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,WAAW,EAAE;AACnC,IAAA,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI;IAC5B,QAAQ,SAAS;AACf,QAAA,KAAK,SAAS;AACZ,YAAA,OAAO,cAAc,CAAC,SAAS,EAAE,GAAG,CAAC;AACvC,QAAA,KAAK,OAAO;AACV,YAAA,OAAO,gBAAgB,CAAC,SAAS,EAAE,GAAG,CAAC;AACzC,QAAA;AACE,YAAA,OAAO,CAAC,CAAA,sBAAA,EAAyB,SAAS,CAAA,CAAE,CAAC;AAC7C,YAAA,OAAO,SAAS;;AAEtB;AAEO,MAAM,oBAAoB,GAAG,CAClC,UAA8B,KACb;AACjB,IAAA,MAAM,SAAS,GAA+B;AAC5C,QAAA,OAAO,EAAE,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC;AAC5D,QAAA,KAAK,EAAE,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC;AAC9B,QAAA,GAAG,EAAE,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC;AAC1B,QAAA,IAAI,EAAE,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC;KAC7B;IACD,MAAM,WAAW,GAAG,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;IAC7E,OAAO;QACL,WAAW;QACX,SAAS;KACV;AACH;MAEa,YAAY,GAAG,CAC1B,aAA4B,EAC5B,GAAY,KACD;AACX,IAAA,IAAI,aAAa,CAAC,GAAG,CAAC,EAAE;AACtB,QAAA,OAAO,aAAa,CAAC,SAAS,CAAC,GAAG,CAAC;IACrC;IACA,OAAO,aAAa,CAAC,WAAW,CAAC,QAAQ,CAAC,GAAG,CAAC;AAChD;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useLatest.js","sources":["
|
|
1
|
+
{"version":3,"file":"useLatest.js","sources":["src/hooks/useLatest/useLatest.ts"],"sourcesContent":["'use client';\n\nimport { useEffect, useRef } from 'react';\n\nimport type { RefObject } from 'react';\n\n/**\n * Keep a ref synced to the latest value of `value` after each render.\n *\n * Useful for reading \"live\" props/state inside event handlers, timers,\n * or external store subscriptions without re-attaching the listener on\n * every render. The ref is updated in `useEffect` so its value reflects\n * the most recently committed render.\n *\n * @example\n * ```ts\n * const latestOnChange = useLatest(onChange);\n * useEffect(() => {\n * const handler = (e: Event) => latestOnChange.current(e);\n * target.addEventListener('change', handler);\n * return () => target.removeEventListener('change', handler);\n * }, [target]);\n * ```\n */\nexport function useLatest<T>(value: T): RefObject<T> {\n const ref = useRef(value);\n useEffect(() => {\n ref.current = value;\n });\n return ref;\n}\n"],"names":[],"mappings":";;;AAMA;;;;;;;;;;;;;;;;;AAiBG;AACG;AACJ;;AAEE;AACF;AACA;AACF;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useListboxNav.js","sources":["
|
|
1
|
+
{"version":3,"file":"useListboxNav.js","sources":["src/hooks/useListboxNav/useListboxNav.ts"],"sourcesContent":["'use client';\n\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\n\nimport { useEventCallback } from '@/hooks/useEventCallback';\n\nimport type React from 'react';\n\nexport interface UseListboxNavOptions<T> {\n /** Items rendered in the listbox. The hook tracks navigation by index. */\n items: T[];\n\n /**\n * Whether a given item should be skipped during navigation and rejected by\n * `selectActive()`.\n */\n isItemDisabled?: (item: T, index: number) => boolean;\n\n /** Initial active index. Defaults to `-1` (no active item). */\n defaultActiveIndex?: number;\n\n /**\n * Wrap from last → first (and vice versa) on `next`/`prev`.\n * @default true\n */\n loop?: boolean;\n\n /**\n * Reset the active index when the items array changes by reference.\n * Useful for filtered listboxes where the previous index would be stale.\n * @default true\n */\n resetOnItemsChange?: boolean;\n\n /** Called when the active item is committed via `selectActive()`. */\n onSelect?: (item: T, index: number) => void;\n\n /** Called on `Escape`. */\n onEscape?: () => void;\n}\n\nexport interface UseListboxNavReturn {\n /** Currently active index, or `-1` when none. */\n activeIndex: number;\n /** Direct setter; the value is not validated. */\n setActiveIndex: (index: number) => void;\n /** Move to the next non-disabled item. */\n next: () => void;\n /** Move to the previous non-disabled item. */\n prev: () => void;\n /** Activate the first non-disabled item. */\n first: () => void;\n /** Activate the last non-disabled item. */\n last: () => void;\n /** Commit the active item via `onSelect`. */\n selectActive: () => void;\n /**\n * Single keyboard handler for ArrowUp/Down/Home/End/Enter/Escape. Returns\n * `true` when the event was handled (and `preventDefault` was called).\n */\n handleKeyDown: (event: KeyboardEvent | React.KeyboardEvent) => boolean;\n /** Snapshot of the navigable indices (non-disabled, in order). */\n navigableIndices: number[];\n}\n\n/**\n * Shared keyboard navigation for listbox-like surfaces (Select, MultiSelect,\n * Combobox, CommandPalette). Tracks an `activeIndex`, skips disabled items,\n * and exposes a single `handleKeyDown` covering Arrow / Home / End / Enter /\n * Escape semantics.\n *\n * The hook is purely logical — consumers render the list and bind the handler\n * to an input or the listbox container. Scroll-into-view is the consumer's\n * concern.\n *\n * @example\n * ```tsx\n * const { activeIndex, handleKeyDown } = useListboxNav({\n * items: filtered,\n * isItemDisabled: o => o.disabled ?? false,\n * onSelect: (item) => commit(item),\n * onEscape: () => close(),\n * });\n * ```\n */\nexport function useListboxNav<T>(\n options: UseListboxNavOptions<T>\n): UseListboxNavReturn {\n const {\n items,\n isItemDisabled,\n defaultActiveIndex = -1,\n loop = true,\n resetOnItemsChange = true,\n onSelect,\n onEscape,\n } = options;\n\n const [activeIndex, setActiveIndex] = useState(defaultActiveIndex);\n\n // Stable refs so handlers don't churn when callers pass inline closures.\n const itemsRef = useRef(items);\n const isItemDisabledRef = useRef(isItemDisabled);\n const onSelectRef = useRef(onSelect);\n const loopRef = useRef(loop);\n\n useEffect(() => {\n itemsRef.current = items;\n }, [items]);\n useEffect(() => {\n isItemDisabledRef.current = isItemDisabled;\n }, [isItemDisabled]);\n useEffect(() => {\n onSelectRef.current = onSelect;\n }, [onSelect]);\n useEffect(() => {\n loopRef.current = loop;\n }, [loop]);\n\n // Reset active index when items reference changes (e.g. filter).\n const prevItemsRef = useRef(items);\n useEffect(() => {\n if (!resetOnItemsChange) return;\n if (prevItemsRef.current !== items) {\n setActiveIndex(defaultActiveIndex);\n prevItemsRef.current = items;\n }\n }, [items, resetOnItemsChange, defaultActiveIndex]);\n\n const getNavigableIndices = useCallback((): number[] => {\n const list = itemsRef.current;\n const disabled = isItemDisabledRef.current;\n if (!disabled) return list.map((_, i) => i);\n const out: number[] = [];\n for (let i = 0; i < list.length; i += 1) {\n const item = list[i] as T;\n if (!disabled(item, i)) out.push(i);\n }\n return out;\n }, []);\n\n // Reactive navigable-index list for the returned value. Derived from the\n // live props rather than the latest-refs above, which exist only for reads\n // inside the event-time keyboard handlers.\n const navigableIndices = useMemo(() => {\n if (!isItemDisabled) return items.map((_, i) => i);\n const out: number[] = [];\n for (let i = 0; i < items.length; i += 1) {\n const item = items[i] as T;\n if (!isItemDisabled(item, i)) out.push(i);\n }\n return out;\n }, [items, isItemDisabled]);\n\n const move = useCallback(\n (direction: 'next' | 'prev' | 'first' | 'last') => {\n const navigable = getNavigableIndices();\n if (navigable.length === 0) {\n setActiveIndex(-1);\n return;\n }\n\n setActiveIndex(current => {\n const currentNavIdx = navigable.indexOf(current);\n switch (direction) {\n case 'first':\n return navigable[0] ?? -1;\n case 'last':\n return navigable[navigable.length - 1] ?? -1;\n case 'next': {\n if (currentNavIdx === -1) return navigable[0] ?? -1;\n const nextIdx = currentNavIdx + 1;\n if (nextIdx >= navigable.length) {\n return loopRef.current\n ? (navigable[0] ?? -1)\n : (navigable[navigable.length - 1] ?? -1);\n }\n return navigable[nextIdx] ?? -1;\n }\n case 'prev': {\n if (currentNavIdx === -1)\n return navigable[navigable.length - 1] ?? -1;\n const prevIdx = currentNavIdx - 1;\n if (prevIdx < 0) {\n return loopRef.current\n ? (navigable[navigable.length - 1] ?? -1)\n : (navigable[0] ?? -1);\n }\n return navigable[prevIdx] ?? -1;\n }\n }\n });\n },\n [getNavigableIndices]\n );\n\n const next = useCallback(() => {\n move('next');\n }, [move]);\n const prev = useCallback(() => {\n move('prev');\n }, [move]);\n const first = useCallback(() => {\n move('first');\n }, [move]);\n const last = useCallback(() => {\n move('last');\n }, [move]);\n\n const selectActive = useCallback(() => {\n const list = itemsRef.current;\n const disabled = isItemDisabledRef.current;\n setActiveIndex(current => {\n if (current < 0 || current >= list.length) return current;\n const item = list[current] as T;\n if (disabled?.(item, current)) return current;\n onSelectRef.current?.(item, current);\n return current;\n });\n }, []);\n\n // Escape handling via useEventCallback: stable identity (so handleKeyDown\n // does not churn when callers pass an inline onEscape) while always seeing\n // the latest onEscape and reporting whether it actually handled the key.\n const handleEscape = useEventCallback(\n (event: KeyboardEvent | React.KeyboardEvent): boolean => {\n if (!onEscape) return false;\n event.preventDefault();\n onEscape();\n return true;\n }\n );\n\n const handleKeyDown = useCallback(\n (event: KeyboardEvent | React.KeyboardEvent): boolean => {\n switch (event.key) {\n case 'ArrowDown':\n event.preventDefault();\n next();\n return true;\n case 'ArrowUp':\n event.preventDefault();\n prev();\n return true;\n case 'Home':\n event.preventDefault();\n first();\n return true;\n case 'End':\n event.preventDefault();\n last();\n return true;\n case 'Enter':\n event.preventDefault();\n selectActive();\n return true;\n case 'Escape':\n return handleEscape(event);\n default:\n return false;\n }\n },\n [next, prev, first, last, selectActive, handleEscape]\n );\n\n return {\n activeIndex,\n setActiveIndex,\n next,\n prev,\n first,\n last,\n selectActive,\n handleKeyDown,\n navigableIndices,\n };\n}\n"],"names":[],"mappings":";;;;AAiEA;;;;;;;;;;;;;;;;;;;AAmBG;AACG;;;;AAgBJ;AACA;AACA;AACA;;AAGE;AACF;;AAEE;AACF;;AAEE;AACF;;AAEE;AACF;;AAGA;;AAEE;;AACA;;AAEE;;;AAIJ;AACE;AACA;AACA;AAAe;;AAEf;AACE;AACA;AAAwB;;AAE1B;;;;;AAMF;AACE;AAAqB;;AAErB;AACE;AACA;AAA8B;;AAEhC;AACF;AAEA;AAEI;AACA;AACE;;;;;;AAOE;AACE;AACF;;;;AAG4B;AAC1B;AACA;;;AAGI;;AAEJ;;;;;AAKA;AACA;;AAEI;;;AAGJ;;;AAGN;AACF;AAIF;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AAEA;AACE;AACA;;;AAE6C;AAC3C;AACA;AAA+B;;AAE/B;AACF;;;;;AAMF;AAEI;AAAe;;AAEf;AACA;AACF;AAGF;AAEI;AACE;;AAEE;AACA;AACF;;AAEE;AACA;AACF;;AAEE;AACA;AACF;;AAEE;AACA;AACF;;AAEE;AACA;AACF;AACE;AACF;AACE;;AAEN;;;;;;;;;;;;AAeJ;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useMediaQuery.js","sources":["
|
|
1
|
+
{"version":3,"file":"useMediaQuery.js","sources":["src/hooks/useMediaQuery/useMediaQuery.ts"],"sourcesContent":["'use client';\n\nimport { useEffect, useState } from 'react';\n\nimport type { UseMediaQueryOptions } from './useMediaQuery.types';\n\nfunction getInitialMatch(query: string, fallback: boolean): boolean {\n if (\n typeof window === 'undefined' ||\n typeof window.matchMedia !== 'function'\n ) {\n return fallback;\n }\n return window.matchMedia(query).matches;\n}\n\n/**\n * Reactive media query matching.\n *\n * Returns `true` when the current viewport matches the query and updates as\n * the viewport changes. SSR-safe — returns `defaultMatches` when `matchMedia`\n * is unavailable. Uses the modern `addEventListener('change')` API with a\n * fallback to the legacy `addListener` for older browsers.\n *\n * @example\n * ```tsx\n * const isWide = useMediaQuery('(min-width: 768px)');\n * return isWide ? <DesktopLayout /> : <MobileLayout />;\n * ```\n */\nexport function useMediaQuery(\n query: string,\n options: UseMediaQueryOptions = {}\n): boolean {\n const { defaultMatches = false } = options;\n\n const [matches, setMatches] = useState<boolean>(() =>\n getInitialMatch(query, defaultMatches)\n );\n\n useEffect(() => {\n if (\n typeof window === 'undefined' ||\n typeof window.matchMedia !== 'function'\n ) {\n return;\n }\n\n const mql = window.matchMedia(query);\n // External-system sync: seed state from matchMedia on mount/query change.\n // eslint-disable-next-line react-hooks/set-state-in-effect\n setMatches(mql.matches);\n\n const handler = (event: MediaQueryListEvent): void => {\n setMatches(event.matches);\n };\n\n if (typeof mql.addEventListener === 'function') {\n mql.addEventListener('change', handler);\n return () => {\n mql.removeEventListener('change', handler);\n };\n }\n\n // Legacy Safari fallback, reached only when the modern addEventListener\n // API above is unavailable. addListener/removeListener are deprecated but\n // remain the only option on those older engines.\n // eslint-disable-next-line @typescript-eslint/no-deprecated\n mql.addListener(handler);\n return () => {\n // eslint-disable-next-line @typescript-eslint/no-deprecated\n mql.removeListener(handler);\n };\n }, [query]);\n\n return matches;\n}\n"],"names":[],"mappings":";;;AAMA;;AAGI;AAEA;;;AAGJ;AAEA;;;;;;;;;;;;;AAaG;;AAKD;AAEA;;;AAOI;;;;;;AAQF;AAEA;AACE;AACF;AAEA;AACE;AACA;AACE;AACF;;;;;;AAOF;AACA;;AAEE;AACF;AACF;AAEA;AACF;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useMergedRef.js","sources":["
|
|
1
|
+
{"version":3,"file":"useMergedRef.js","sources":["src/hooks/useMergedRef/useMergedRef.ts"],"sourcesContent":["'use client';\n\nimport { useCallback } from 'react';\n\nimport type { Ref, RefCallback } from 'react';\n\n/**\n * Merge multiple refs into a single callback ref. Each provided ref (object\n * ref, callback ref, or `null`/`undefined`) receives the node when the merged\n * ref fires.\n *\n * Useful when a component needs to keep an internal ref to a DOM node while\n * also forwarding the same node through an externally supplied `ref` prop\n * (ref-as-prop in React 19).\n *\n * @example\n * ```tsx\n * const internalRef = useRef<HTMLDivElement>(null);\n * const mergedRef = useMergedRef(internalRef, props.ref);\n *\n * return <div ref={mergedRef} />;\n * ```\n */\nexport function useMergedRef<T>(\n ...refs: Array<Ref<T> | undefined | null>\n): RefCallback<T> {\n // The spread of refs is intentional — the merged callback must pick up the\n // latest set of refs the consumer is passing in, so the dependency list is\n // dynamic by design (use-memo / exhaustive-deps cannot model that).\n return useCallback((node: T | null) => {\n for (const ref of refs) {\n if (!ref) continue;\n if (typeof ref === 'function') {\n ref(node);\n } else {\n // Assigning a node into an object ref's `.current` is the entire point\n // of a callback ref; it is not a forbidden mutation of hook arguments.\n // eslint-disable-next-line react-hooks/immutability\n (ref as { current: T | null }).current = node;\n }\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps, react-hooks/use-memo\n }, refs);\n}\n"],"names":[],"mappings":";;;AAMA;;;;;;;;;;;;;;;;AAgBG;AACG;;;;AAMJ;AACE;AACE;;AACA;;;;;;;AAMG;;;;;AAKT;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useNavigationHistory.js","sources":["
|
|
1
|
+
{"version":3,"file":"useNavigationHistory.js","sources":["src/hooks/useNavigationHistory/useNavigationHistory.ts"],"sourcesContent":["'use client';\n\nimport { useCallback, useMemo, useState, useSyncExternalStore } from 'react';\n\nimport type {\n NavigationHistoryApi,\n UseNavigationHistoryOptions,\n} from './useNavigationHistory.types';\n\n/**\n * Per-instance back/forward stack. Lives outside React so reads are stable\n * across concurrent renders and so non-React handlers can mutate without\n * scheduling extra renders for unrelated consumers.\n */\nclass NavigationHistoryStore<T> {\n #stack: T[];\n #index: number;\n #listeners = new Set<() => void>();\n\n constructor(initial: T | undefined) {\n if (initial !== undefined) {\n this.#stack = [initial];\n this.#index = 0;\n } else {\n this.#stack = [];\n this.#index = -1;\n }\n }\n\n subscribe = (listener: () => void): (() => void) => {\n this.#listeners.add(listener);\n return () => {\n this.#listeners.delete(listener);\n };\n };\n\n getCurrent = (): T | undefined => this.#stack[this.#index];\n getCanGoBack = (): boolean => this.#index > 0;\n getCanGoForward = (): boolean => this.#index < this.#stack.length - 1;\n\n #notify(): void {\n this.#listeners.forEach(l => {\n l();\n });\n }\n\n push(entry: T): void {\n if (this.#stack[this.#index] === entry) return;\n const next = this.#stack.slice(0, this.#index + 1);\n next.push(entry);\n this.#stack = next;\n this.#index = next.length - 1;\n this.#notify();\n }\n\n goBack(): T | undefined {\n if (this.#index <= 0) return undefined;\n this.#index -= 1;\n this.#notify();\n return this.#stack[this.#index];\n }\n\n goForward(): T | undefined {\n if (this.#index >= this.#stack.length - 1) return undefined;\n this.#index += 1;\n this.#notify();\n return this.#stack[this.#index];\n }\n\n reset(entry: T | undefined): void {\n if (entry !== undefined) {\n if (\n this.#stack.length === 1 &&\n this.#index === 0 &&\n this.#stack[0] === entry\n ) {\n return;\n }\n this.#stack = [entry];\n this.#index = 0;\n } else {\n if (this.#stack.length === 0) return;\n this.#stack = [];\n this.#index = -1;\n }\n this.#notify();\n }\n}\n\n/**\n * Back/forward history primitive. Tracks a stack of entries (`T`) with a\n * cursor, mirrors browser semantics (pushing truncates the forward branch),\n * and exposes the current entry plus `canGoBack` / `canGoForward` flags via\n * `useSyncExternalStore`.\n *\n * Useful any time a UI needs Back/Forward controls over a finite navigation\n * — folder browsers, wizard steps, drill-down inspectors. Generic over the\n * entry type, so it works with ids, paths, route objects, anything stable.\n *\n * @example\n * ```tsx\n * const nav = useNavigationHistory<string>({ initial: 'root' });\n *\n * <Toolbar>\n * <IconButton aria-label=\"Back\" disabled={!nav.canGoBack} onClick={nav.goBack}>\n * <ChevronLeftIcon />\n * </IconButton>\n * <IconButton aria-label=\"Forward\" disabled={!nav.canGoForward} onClick={nav.goForward}>\n * <ChevronRightIcon />\n * </IconButton>\n * </Toolbar>\n * ```\n */\nexport function useNavigationHistory<T = string>(\n options: UseNavigationHistoryOptions<T> = {}\n): NavigationHistoryApi<T> {\n const { initial, enabled = true } = options;\n\n // Lazy init via useState — the initial value is captured once on mount.\n // Subsequent changes to `initial` are ignored (use `reset()` to re-seed).\n const [store] = useState(() => new NavigationHistoryStore<T>(initial));\n\n const current = useSyncExternalStore(store.subscribe, store.getCurrent);\n const canGoBackRaw = useSyncExternalStore(\n store.subscribe,\n store.getCanGoBack\n );\n const canGoForwardRaw = useSyncExternalStore(\n store.subscribe,\n store.getCanGoForward\n );\n\n const canGoBack = enabled && canGoBackRaw;\n const canGoForward = enabled && canGoForwardRaw;\n\n const push = useCallback(\n (entry: T): void => {\n if (!enabled) return;\n store.push(entry);\n },\n [store, enabled]\n );\n\n const goBack = useCallback((): T | undefined => {\n if (!enabled) return undefined;\n return store.goBack();\n }, [store, enabled]);\n\n const goForward = useCallback((): T | undefined => {\n if (!enabled) return undefined;\n return store.goForward();\n }, [store, enabled]);\n\n const reset = useCallback(\n (entry?: T): void => {\n store.reset(entry);\n },\n [store]\n );\n\n return useMemo(\n () => ({\n current,\n canGoBack,\n canGoForward,\n push,\n goBack,\n goForward,\n reset,\n }),\n [current, canGoBack, canGoForward, push, goBack, goForward, reset]\n );\n}\n"],"names":[],"mappings":";;;;;AASA;;;;AAIG;AACH;AAKE;;;;;AAUA;AACE;AACA;AACE;AACF;AACF;AAEA;;AAEA;AAlBE;AACE;AACA;;;AAEA;AACA;;;AAqBJ;;;AAEE;AACA;AACA;;AAEA;;;AAIA;AAAsB;;AAEtB;;;;;AAK2C;;AAE3C;;;AAIF;AACE;AACE;;;;;AAOA;AACA;;;AAEA;;AACA;AACA;;AAEF;;AAEH;;AA9CG;AACE;AACF;AACF;AA6CF;;;;;;;;;;;;;;;;;;;;;;;AAuBG;AACG;;;;AAOJ;AAEA;AACA;AAIA;AAKA;AACA;AAEA;AAEI;;AACA;AACF;AAIF;AACE;AAAc;AACd;AACF;AAEA;AACE;AAAc;AACd;AACF;AAEA;AAEI;AACF;AAIF;;;;;;;;AASG;AAGL;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useResizeObserver.js","sources":["
|
|
1
|
+
{"version":3,"file":"useResizeObserver.js","sources":["src/hooks/useResizeObserver/useResizeObserver.ts"],"sourcesContent":["'use client';\n\nimport { useEffect, useRef } from 'react';\n\nimport { useEventCallback } from '@/hooks/useEventCallback';\n\nimport type { RefObject } from 'react';\n\nexport interface UseResizeObserverOptions {\n /**\n * When false, the observer is not attached. Toggling this from `false` to\n * `true` (re-)attaches the observer; toggling back disconnects it.\n * @default true\n */\n enabled?: boolean;\n}\n\n/**\n * Observe size changes on an element. SSR-safe; cleans up on unmount.\n *\n * The callback is wrapped with `useEventCallback` so consumers do not have\n * to memoize it — the underlying observer never re-subscribes when the\n * callback identity changes.\n *\n * The hook re-checks `ref.current` on every render so it picks up nodes that\n * mount later (e.g. through conditional rendering). When the observed node\n * has not changed the only cost is a single identity comparison.\n *\n * @example\n * ```tsx\n * const ref = useRef<HTMLDivElement>(null);\n * useResizeObserver(ref, entry => {\n * console.log(entry.contentRect.width);\n * });\n *\n * return <div ref={ref} />;\n * ```\n */\nexport function useResizeObserver<T extends HTMLElement>(\n ref: RefObject<T | null>,\n callback: (entry: ResizeObserverEntry) => void,\n options?: UseResizeObserverOptions\n): void {\n const { enabled = true } = options ?? {};\n\n const handleEntry = useEventCallback(callback);\n const observerRef = useRef<ResizeObserver | null>(null);\n const observedNodeRef = useRef<T | null>(null);\n\n // Runs on every render so a node that mounts after the first render\n // (conditional rendering, lazy children, suspense fallback flips, ...) is\n // picked up automatically. Short-circuits on the common case where the\n // observed node has not changed.\n useEffect(() => {\n if (typeof ResizeObserver === 'undefined') return;\n\n if (!enabled) {\n if (observerRef.current) {\n observerRef.current.disconnect();\n observerRef.current = null;\n observedNodeRef.current = null;\n }\n return;\n }\n\n const node = ref.current;\n if (node === observedNodeRef.current) return;\n\n if (observerRef.current) {\n observerRef.current.disconnect();\n observerRef.current = null;\n }\n observedNodeRef.current = node;\n if (!node) return;\n\n const observer = new ResizeObserver(entries => {\n for (const entry of entries) {\n handleEntry(entry);\n }\n });\n observer.observe(node);\n observerRef.current = observer;\n });\n\n // Final cleanup on unmount.\n useEffect(\n () => () => {\n observerRef.current?.disconnect();\n observerRef.current = null;\n observedNodeRef.current = null;\n },\n []\n );\n}\n"],"names":[],"mappings":";;;;AAiBA;;;;;;;;;;;;;;;;;;;;AAoBG;;;AAQD;AACA;AACA;;;;;;;;;AAUI;AACE;AACA;AACA;;;;AAKJ;AACA;;AAEA;AACE;AACA;;AAEF;AACA;;AAEA;AACE;;;AAGF;AACA;AACA;AACF;;AAGA;AAEI;AACA;AACA;;AAIN;;"}
|
|
@@ -3,7 +3,7 @@ import { useRef, useState, useLayoutEffect, useCallback, useMemo } from 'react';
|
|
|
3
3
|
import { vars } from '../../theme/contract.css.js';
|
|
4
4
|
import { darkThemeValues } from '../../theme/darkThemeValues.js';
|
|
5
5
|
import { lightThemeClass } from '../../theme/lightTheme.css.js';
|
|
6
|
-
import '
|
|
6
|
+
import '@vanilla-extract/css';
|
|
7
7
|
import './../../assets/src/theme/darkTheme.css.ts.vanilla-BHZ3ecRn.css';
|
|
8
8
|
import 'react/jsx-runtime';
|
|
9
9
|
import '../../utils/devWarn.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useTheme.js","sources":["
|
|
1
|
+
{"version":3,"file":"useTheme.js","sources":["src/hooks/useTheme/useTheme.ts"],"sourcesContent":["'use client';\n\nimport { useCallback, useLayoutEffect, useMemo, useRef, useState } from 'react';\n\nimport { darkThemeValues, lightThemeClass, vars } from '@/theme';\n\nimport type {\n ResolvedThemeValues,\n ThemeVariant,\n UseThemeReturn,\n} from './useTheme.types';\n\nconst VAR_REF_PATTERN = /^var\\((--[^,)]+)/;\n\n/**\n * Walk a dot-path on the theme contract and return the underlying CSS\n * custom property name (without the leading `var(`/trailing `)`).\n *\n * The contract emits hand-tuned, non-derivable names — `colors.accent.primary`\n * resolves to `--etui-color-accent-primary`, not `--etui-colors-accent-primary`.\n * Walking the existing `vars` object (whose leaves are already\n * `var(--etui-...)` references) is the only correct way to get the var name.\n *\n * Returns `''` for paths that don't resolve to a leaf reference.\n */\nfunction pathToCssVar(path: string): string {\n const segments = path.split('.');\n let node: unknown = vars;\n for (const segment of segments) {\n if (\n node !== null &&\n typeof node === 'object' &&\n segment in (node as Record<string, unknown>)\n ) {\n node = (node as Record<string, unknown>)[segment];\n } else {\n return '';\n }\n }\n if (typeof node !== 'string') return '';\n const match = VAR_REF_PATTERN.exec(node);\n return match?.[1] ?? '';\n}\n\nfunction pathToVarRef(path: string): string {\n const cssVar = pathToCssVar(path);\n return cssVar ? `var(${cssVar})` : '';\n}\n\n/**\n * Walk the dark-theme template alongside the contract, replacing each\n * leaf with the resolved CSS variable value read from `root`. Falls back\n * to the template value when a property is empty (e.g. before stylesheets\n * have applied during testing).\n */\nfunction resolveValues<T>(template: T, root: Element, contract: unknown): T {\n if (typeof template === 'string') {\n if (typeof contract === 'string') {\n const match = VAR_REF_PATTERN.exec(contract);\n const cssVar = match?.[1];\n if (cssVar) {\n const value = getComputedStyle(root).getPropertyValue(cssVar).trim();\n return (value || template) as T;\n }\n }\n return template;\n }\n if (template !== null && typeof template === 'object') {\n const out: Record<string, unknown> = {};\n for (const key of Object.keys(template)) {\n const childTemplate = (template as Record<string, unknown>)[key];\n const childContract = (contract as Record<string, unknown> | undefined)?.[\n key\n ];\n out[key] = resolveValues(childTemplate, root, childContract);\n }\n return out as T;\n }\n return template;\n}\n\nfunction readTemplateValue(template: unknown, path: string): string {\n const segments = path.split('.');\n let node: unknown = template;\n for (const segment of segments) {\n if (\n node !== null &&\n typeof node === 'object' &&\n segment in (node as Record<string, unknown>)\n ) {\n node = (node as Record<string, unknown>)[segment];\n } else {\n return '';\n }\n }\n return typeof node === 'string' ? node : '';\n}\n\n/**\n * Root-only detection: the variant is determined by whether the\n * light-theme class is applied to `document.documentElement` — the same\n * element from which `values` are resolved. Anything more permissive\n * (descendant scan, `<body>` check) would let the variant disagree with\n * the resolved values for a dark app that contains a single light\n * subtree.\n */\nfunction detectVariant(): ThemeVariant {\n if (typeof document === 'undefined') return 'dark';\n if (!lightThemeClass) return 'dark';\n if (document.documentElement.classList.contains(lightThemeClass)) {\n return 'light';\n }\n return 'dark';\n}\n\ninterface ThemeSnapshot {\n variant: ThemeVariant;\n values: ResolvedThemeValues;\n}\n\nconst FALLBACK_SNAPSHOT: ThemeSnapshot = {\n variant: 'dark',\n values: darkThemeValues,\n};\n\n/**\n * Read the current theme at runtime.\n *\n * Returns the resolved CSS variable snapshot from `:root`, the detected\n * theme variant, and helpers for reading individual tokens by path.\n * Components should prefer compile-time tokens via Vanilla Extract\n * (`vars.*` from `@/theme`) — reach for `useTheme` only when a runtime\n * read is genuinely required (canvas drawing, third-party libraries that\n * take colors as plain strings).\n *\n * **Scope:** the hook always reads from `:root` — both the variant and\n * the resolved values come from `document.documentElement`. A light- or\n * custom-themed subtree wrapped in `VanillaThemeProvider` will **not** be\n * reflected here; for that, components inside the subtree should style\n * via Vanilla Extract `vars.*` (which the browser resolves correctly per\n * scope) rather than runtime reads.\n *\n * **SSR:** returns the dark-theme defaults as a fallback. The hook\n * re-evaluates against the live DOM on mount via `useLayoutEffect`, so\n * the second render contains the real values before the browser paints.\n *\n * **Reactivity:** v0.8 reads once on mount. Toggling the root theme at\n * runtime is not reflected — remount the consuming subtree to pick up\n * the new theme. A `MutationObserver`-based subscription may be added\n * in a later release if consumer demand emerges.\n *\n * @example\n * ```tsx\n * const { values, variant, getVar, getToken } = useTheme();\n *\n * // Canvas drawing with theme-aware colors\n * ctx.strokeStyle = values.colors.accent.primary;\n *\n * // Conditional logic\n * if (variant === 'dark') loadDarkAssets();\n *\n * // Inline styles that follow theme changes automatically\n * <div style={{ color: getVar('colors.text.primary') }} />\n *\n * // One-off token resolution\n * const accent = getToken('colors.accent.primary');\n * ```\n */\nexport function useTheme(): UseThemeReturn {\n const tokenCacheRef = useRef<Map<string, string>>(new Map());\n const [snapshot, setSnapshot] = useState<ThemeSnapshot>(FALLBACK_SNAPSHOT);\n\n useLayoutEffect(() => {\n if (typeof document === 'undefined') return;\n const root = document.documentElement;\n const next: ThemeSnapshot = {\n variant: detectVariant(),\n values: resolveValues(darkThemeValues, root, vars),\n };\n tokenCacheRef.current.clear();\n // External-system sync: read resolved CSS custom properties from the DOM\n // into state once mounted (they are unavailable during SSR/first render).\n // eslint-disable-next-line react-hooks/set-state-in-effect\n setSnapshot(next);\n }, []);\n\n const getToken = useCallback((path: string): string => {\n const cache = tokenCacheRef.current;\n const cached = cache.get(path);\n if (cached !== undefined) return cached;\n\n if (typeof document === 'undefined') {\n const fallback = readTemplateValue(darkThemeValues, path);\n cache.set(path, fallback);\n return fallback;\n }\n\n const cssVar = pathToCssVar(path);\n if (!cssVar) {\n cache.set(path, '');\n return '';\n }\n\n const value = getComputedStyle(document.documentElement)\n .getPropertyValue(cssVar)\n .trim();\n const resolved = value || readTemplateValue(darkThemeValues, path);\n cache.set(path, resolved);\n return resolved;\n }, []);\n\n const getVar = useCallback((path: string): string => pathToVarRef(path), []);\n\n return useMemo(\n () => ({\n variant: snapshot.variant,\n values: snapshot.values,\n getToken,\n getVar,\n }),\n [snapshot, getToken, getVar]\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;;AAYA;AAEA;;;;;;;;;;AAUG;AACH;;;AAGE;;;;AAMI;;;AAEA;;;;AAG0B;;AAE9B;AACF;AAEA;AACE;;AAEF;AAEA;;;;;AAKG;AACH;AACE;AACE;;AAEE;;AAEE;AACA;;;AAGJ;;;;;AAKE;AACA;AAGA;;AAEF;;AAEF;AACF;AAEA;;;AAGE;;;;AAMI;;;AAEA;;;AAGJ;AACF;AAEA;;;;;;;AAOG;AACH;;AACuC;;AAGnC;;AAEF;AACF;AAOA;AACE;AACA;;AAGF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0CG;;;;;;;AAOC;AACA;;;;AAIA;;;;;;AAOF;AACE;;;AAE0B;AAE1B;;AAEE;AACA;;AAGF;;AAEE;AACA;;AAGF;;AAEG;;AAEH;AACA;;AAGF;AAEA;;;;;;AASF;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useThrottledCallback.js","sources":["
|
|
1
|
+
{"version":3,"file":"useThrottledCallback.js","sources":["src/hooks/useThrottledCallback/useThrottledCallback.ts"],"sourcesContent":["'use client';\n\nimport { useEffect, useMemo, useRef } from 'react';\n\nimport { useEventCallback } from '@/hooks/useEventCallback';\n\nimport type {\n ThrottledCallback,\n UseThrottledCallbackOptions,\n} from './useThrottledCallback.types';\n\n/**\n * Wrap a function so it fires at most once per `delay` ms. With both\n * `leading` and `trailing` enabled (the default), the first call fires\n * immediately and the most recent call inside the window fires at its\n * end. Set `leading: false` to defer the first call; set\n * `trailing: false` to drop calls inside the window.\n *\n * Typical use: drag handlers, scroll listeners, resize observers — places\n * where the input rate exceeds the work rate and you want to bound the\n * latter.\n *\n * The wrapper identity is stable across renders.\n *\n * @example\n * ```tsx\n * const onPointerMove = useThrottledCallback(updateGuide, 16);\n * ```\n */\nexport function useThrottledCallback<Args extends unknown[]>(\n fn: (...args: Args) => void,\n delay: number,\n options: UseThrottledCallbackOptions = {}\n): ThrottledCallback<Args> {\n const { leading = true, trailing = true } = options;\n\n // Stable wrapper that always invokes the current callback, refreshed via\n // useInsertionEffect so the throttled wrapper never goes stale.\n const stableFn = useEventCallback(fn);\n\n const lastCallRef = useRef<number>(0);\n const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const lastArgsRef = useRef<Args | null>(null);\n\n const throttled = useMemo<ThrottledCallback<Args>>(() => {\n const invoke = (now: number): void => {\n lastCallRef.current = now;\n const args = lastArgsRef.current;\n lastArgsRef.current = null;\n if (args) stableFn(...args);\n };\n\n const wrapper = (...args: Args): void => {\n // Date.now() runs only when the wrapper is invoked from an event/timer,\n // never during render, so it is not a render-purity concern here.\n // eslint-disable-next-line react-hooks/purity\n const now = Date.now();\n if (lastCallRef.current === 0 && !leading) {\n // Skip the leading fire by anchoring the window to \"now\".\n lastCallRef.current = now;\n }\n\n const elapsed = now - lastCallRef.current;\n lastArgsRef.current = args;\n\n if (elapsed >= delay) {\n if (timerRef.current !== null) {\n clearTimeout(timerRef.current);\n timerRef.current = null;\n }\n invoke(now);\n return;\n }\n\n if (trailing && timerRef.current === null) {\n timerRef.current = setTimeout(() => {\n timerRef.current = null;\n invoke(Date.now());\n }, delay - elapsed);\n }\n };\n\n wrapper.cancel = () => {\n if (timerRef.current !== null) {\n clearTimeout(timerRef.current);\n timerRef.current = null;\n }\n lastArgsRef.current = null;\n lastCallRef.current = 0;\n };\n\n return wrapper;\n }, [delay, leading, trailing, stableFn]);\n\n useEffect(\n () => () => {\n if (timerRef.current !== null) clearTimeout(timerRef.current);\n },\n []\n );\n\n return throttled;\n}\n"],"names":[],"mappings":";;;;AAWA;;;;;;;;;;;;;;;;;AAiBG;AACG;;;;AASJ;AAEA;AACA;AACA;AAEA;AACE;AACE;AACA;AACA;AACA;AAAU;AACZ;AAEA;;;;AAIE;;;AAGE;;AAGF;AACA;AAEA;AACE;AACE;AACA;;;;;;AAOF;AACE;AACA;AACF;;AAEJ;AAEA;AACE;AACE;AACA;;AAEF;AACA;AACF;AAEA;;AAGF;AAEI;AAA+B;;AAKnC;AACF;;"}
|