entangle-ui 0.10.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +72 -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/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/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.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.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.map +1 -1
- 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.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.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 +1 -1
- 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.js.map +1 -1
- package/dist/esm/components/shell/FloatingPanel/FloatingPanel.js.map +1 -1
- 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.js +3 -0
- package/dist/esm/styles.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/editor/PropertyInspector/PropertyInspector.types.d.ts +49 -3
- package/dist/types/components/primitives/Button/Button.d.ts +1 -1
- package/dist/types/components/primitives/IconButton/IconButton.d.ts +1 -1
- 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":"useNodeGraphEdgeInteraction.js","sources":["
|
|
1
|
+
{"version":3,"file":"useNodeGraphEdgeInteraction.js","sources":["src/components/editor/NodeGraph/useNodeGraphEdgeInteraction.ts"],"sourcesContent":["'use client';\n\nimport { useCallback, useEffect, useRef } from 'react';\n\nimport { worldToScreen as screenFromWorld } from '@/components/primitives/viewport';\nimport { useLatest } from '@/hooks';\n\nimport { findEdgeAtPoint, resolveEdgeEndpoints } from './nodeGraphMath';\n\nimport type { NodeGraphStore } from './NodeGraphStore';\nimport type { Point2D } from '@/components/primitives/canvas/canvas.types';\nimport type { ViewportTransform } from '@/components/primitives/viewport';\nimport type React from 'react';\n\n/**\n * Pointer proximity (screen px) for hovering / clicking / right-clicking an\n * edge. Divided by the current zoom to get the world-space threshold. Also\n * consumed by the parent for marquee-click edge selection and hit-testing.\n */\nexport const EDGE_HIT_PX = 6;\n\n/**\n * Pointer proximity (screen px) to an edge endpoint that starts a\n * reconnect / detach grab instead of selecting the edge.\n */\nconst EDGE_ENDPOINT_GRAB_PX = 16;\n\ninterface UseNodeGraphEdgeInteractionOptions {\n store: NodeGraphStore;\n getTransform: () => ViewportTransform;\n screenToWorldLocal: (clientX: number, clientY: number) => Point2D;\n localScreenPoint: (clientX: number, clientY: number) => Point2D;\n onEdgeReconnectStart: (\n edgeId: string,\n end: 'source' | 'target',\n clientX: number,\n clientY: number\n ) => void;\n disabled?: boolean;\n}\n\ninterface UseNodeGraphEdgeInteractionReturn {\n /** Attach to the root surface's `onPointerMove` — RAF-coalesced edge hover. */\n onRootPointerMove: (event: React.PointerEvent<HTMLDivElement>) => void;\n /** Attach to `onPointerLeave` — clears hover + cancels any pending hit-test. */\n onRootPointerLeave: () => void;\n /**\n * Attach to `onPointerDownCapture` — pre-empts the Viewport's marquee / pan\n * when the pointer lands near an edge endpoint, handing off to the\n * connection controller in reconnect mode.\n */\n onRootPointerDownCapture: (event: React.PointerEvent<HTMLDivElement>) => void;\n}\n\n/**\n * Edge hover + endpoint-grab interactions for the `<NodeGraph>` root surface.\n * Edges live on a canvas layer (no DOM to receive pointer events), so both\n * hovering an edge and grabbing its endpoint to reconnect are resolved by a\n * Bézier hit-test against the pointer position. Hover is RAF-coalesced to one\n * hit-test per frame; the endpoint grab runs in the capture phase so it wins\n * over the Viewport's own marquee / pan before they begin.\n */\nexport function useNodeGraphEdgeInteraction(\n options: UseNodeGraphEdgeInteractionOptions\n): UseNodeGraphEdgeInteractionReturn {\n const {\n store,\n getTransform,\n screenToWorldLocal,\n localScreenPoint,\n onEdgeReconnectStart,\n disabled,\n } = options;\n\n const disabledRef = useLatest(disabled);\n const edgeHoverRaf = useRef(0);\n const edgeHoverPoint = useRef<Point2D | null>(null);\n\n const clearEdgeHover = useCallback((): void => {\n const hover = store.getHover();\n if (hover.hoveredEdgeId !== null) {\n store.setHover({ ...hover, hoveredEdgeId: null });\n }\n }, [store]);\n\n const onRootPointerMove = useCallback(\n (event: React.PointerEvent<HTMLDivElement>): void => {\n if (disabledRef.current) return;\n if (store.getInteraction().kind !== 'idle') return;\n const el = event.target as Element | null;\n if (el?.closest('[data-node-id]')) {\n clearEdgeHover();\n return;\n }\n edgeHoverPoint.current = { x: event.clientX, y: event.clientY };\n if (edgeHoverRaf.current !== 0) return;\n edgeHoverRaf.current = requestAnimationFrame(() => {\n edgeHoverRaf.current = 0;\n const point = edgeHoverPoint.current;\n edgeHoverPoint.current = null;\n if (!point || store.getInteraction().kind !== 'idle') return;\n const zoom = getTransform().zoom || 1;\n const world = screenToWorldLocal(point.x, point.y);\n const edgeId = findEdgeAtPoint(\n world,\n store.getData().edges,\n store.getNodeById,\n store.getPortPosition,\n EDGE_HIT_PX / zoom\n );\n const hover = store.getHover();\n if (hover.hoveredEdgeId !== edgeId) {\n store.setHover({ ...hover, hoveredEdgeId: edgeId });\n }\n });\n },\n [store, disabledRef, getTransform, screenToWorldLocal, clearEdgeHover]\n );\n\n const onRootPointerLeave = useCallback((): void => {\n if (edgeHoverRaf.current !== 0) {\n cancelAnimationFrame(edgeHoverRaf.current);\n edgeHoverRaf.current = 0;\n }\n edgeHoverPoint.current = null;\n clearEdgeHover();\n }, [clearEdgeHover]);\n\n const onRootPointerDownCapture = useCallback(\n (event: React.PointerEvent<HTMLDivElement>): void => {\n if (disabledRef.current) return;\n if (event.button !== 0) return;\n const el = event.target as Element | null;\n if (el?.closest('[data-node-id]')) return;\n const transform = getTransform();\n const zoom = transform.zoom || 1;\n const world = screenToWorldLocal(event.clientX, event.clientY);\n const edges = store.getData().edges;\n const edgeId = findEdgeAtPoint(\n world,\n edges,\n store.getNodeById,\n store.getPortPosition,\n EDGE_HIT_PX / zoom\n );\n if (!edgeId) return;\n const edge = edges.find(e => e.id === edgeId);\n if (!edge) return;\n const ends = resolveEdgeEndpoints(\n edge,\n store.getNodeById,\n store.getPortPosition\n );\n if (!ends) return;\n const srcScreen = screenFromWorld(ends.source, transform);\n const tgtScreen = screenFromWorld(ends.target, transform);\n const local = localScreenPoint(event.clientX, event.clientY);\n const dSrc = Math.hypot(local.x - srcScreen.x, local.y - srcScreen.y);\n const dTgt = Math.hypot(local.x - tgtScreen.x, local.y - tgtScreen.y);\n if (Math.min(dSrc, dTgt) > EDGE_ENDPOINT_GRAB_PX) return;\n event.stopPropagation();\n event.preventDefault();\n const end = dSrc <= dTgt ? 'source' : 'target';\n onEdgeReconnectStart(edgeId, end, event.clientX, event.clientY);\n },\n [\n disabledRef,\n getTransform,\n screenToWorldLocal,\n localScreenPoint,\n store,\n onEdgeReconnectStart,\n ]\n );\n\n // Cancel a pending hover hit-test if the component unmounts mid-frame.\n useEffect(() => {\n return () => {\n if (edgeHoverRaf.current !== 0) {\n cancelAnimationFrame(edgeHoverRaf.current);\n }\n };\n }, []);\n\n return { onRootPointerMove, onRootPointerLeave, onRootPointerDownCapture };\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AAcA;;;;AAIG;AACI;AAEP;;;AAGG;AACH;AA6BA;;;;;;;AAOG;AACG;AAGJ;AASA;AACA;AACA;AAEA;AACE;AACA;AACE;;AAEJ;AAEA;;;AAGI;;AACA;AACA;AACE;;;AAGF;AACA;;AACA;AACE;AACA;AACA;;;;AAGA;;AAQA;AACA;AACE;;AAEJ;AACF;AAIF;AACE;AACE;AACA;;AAEF;AACA;AACF;AAEA;;;AAGI;;AACA;AACA;;AACA;AACA;AACA;;;AASA;;AACA;AACA;;AACA;AAKA;;;;AAGA;;;;;;;AAMA;AACA;AACF;;;;;;;AAQC;;;AAKD;AACE;AACE;;AAEJ;;AAGF;AACF;;"}
|
|
@@ -9,7 +9,7 @@ import '../../../utils/devWarn.js';
|
|
|
9
9
|
import { useLatest } from '../../../hooks/useLatest/useLatest.js';
|
|
10
10
|
import '../../../hooks/useBreakpoint/useBreakpoint.js';
|
|
11
11
|
import './../../../assets/src/theme/lightTheme.css.ts.vanilla-Ct6f9Iym.css';
|
|
12
|
-
import '
|
|
12
|
+
import '@vanilla-extract/css';
|
|
13
13
|
import './../../../assets/src/theme/darkTheme.css.ts.vanilla-BHZ3ecRn.css';
|
|
14
14
|
import 'react/jsx-runtime';
|
|
15
15
|
import './../../../assets/src/theme/globalScrollbars.css.ts.vanilla-BAJwnUEJ.css';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useNodeGraphGroupDrag.js","sources":["../../../../../../src/components/editor/NodeGraph/useNodeGraphGroupDrag.ts"],"sourcesContent":[null],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;AAkCA;AAiEA;;;;;;;;;;;;AAYG;AACG;;AAeJ;AACA;AACA;AACA;AACA;AAEA;AAEA;;;;;AAKG;;;;AAKG;AACE;AACA;;;;AAIF;AACE;;;AAEA;;AACA;AAAwC;;;AAG5C;AACF;;;;AAQE;AACE;;AACA;AAA8C;;AAEhD;AACF;AAIF;;;;;;AAOA;AACA;AAEA;AACE;AACA;;AACA;AACA;AACF;AAEA;AACE;AACE;AACA;;;;AAKJ;AACE;AACA;AACF;;AAGE;AACE;AACA;AACF;AACF;AAEA;AAEI;AACA;;AACA;AACE;AACA;AACF;AACF;;;;AAOF;AAEI;AACA;;AACA;;;AAGA;AACA;AACE;AACA;;;AAIF;AACE;;;AAEE;;AAEF;;AAOA;AACE;AACA;;;AAGA;;AAED;;;;;;AAKD;;AASA;AACE;;;;AAIA;;AAED;;AAEL;;;;;;;AAQC;AAGH;AACE;AACA;AACA;;AAEF;;AAOI;;;;;AAIA;AACA;AACA;AAEA;;AAEE;;;AAEA;;;;;AAKF;;;AAGI;;;;;;AAOJ;AACA;AACA;;AAEE;;AAEE;AACE;AACA;;;;;;AAON;;AAEA;AACA;AACE;AACA;AACD;;AAGC;;;AAGA;;;AAGA;AACA;;;AAGA;;AAEA;;AAEJ;;;;;;;;;AAUC;;AASC;;;;;;AAKA;;AAEA;AACA;AACE;AACA;AACD;;AAGC;;;;;;AAMA;;;;AAIA;AACA;AACA;;;;;AAKA;;;AAGA;;AAEA;AACD;AACH;;;;;;;;AASC;;AAQC;AACA;;;AAEA;;;AAIA;AAEA;;;AAII;;;;AAIA;AACE;AACA;AAAY;;AAEV;AACA;AACE;AACA;AACA;AACD;;AAEL;AACA;;AAEE;AACE;AACA;AAAY;;AAEV;;;AAGJ;AACA;;;;;;;;AAOC;AACL;;AAGE;;AAGA;;AAMG;AACA;AAGH;;;;;;AAKF;;AAEE;AACA;AACA;AACD;;AAGH;AACF;;;;;;;AAQC;;;;;;AAQL;;"}
|
|
1
|
+
{"version":3,"file":"useNodeGraphGroupDrag.js","sources":["src/components/editor/NodeGraph/useNodeGraphGroupDrag.ts"],"sourcesContent":["'use client';\n\nimport { useCallback, useEffect, useRef } from 'react';\n\nimport { screenToWorld as worldFromScreen } from '@/components/primitives/viewport';\nimport { useLatest } from '@/hooks';\n\nimport {\n applyGroupResize,\n getNodeBox,\n rectContains,\n rectsIntersect,\n snapDelta,\n toggleSelected,\n} from './nodeGraphMath';\nimport { useDragGesture } from './useDragGesture';\n\nimport type {\n NodeGraphGroup,\n NodeGraphNode,\n NodeGraphSelection,\n} from './NodeGraph.types';\nimport type {\n NodeGraphGroupResizeHandle,\n NodeGraphInteractionState,\n NodeGraphStore,\n} from './NodeGraphStore';\nimport type { Point2D } from '@/components/primitives/canvas/canvas.types';\nimport type {\n ViewportTransform,\n WorldRect,\n} from '@/components/primitives/viewport';\nimport type React from 'react';\n\nconst DRAG_START_THRESHOLD_PX = 3;\n\n/**\n * Pending interaction state buffered between pointermove and the next rAF\n * flush. Carries either a `drag-groups` or `resize-group` payload — they\n * share the same lifecycle, so the buffer keeps the latest of either.\n */\ntype PendingGroupInteraction = Extract<\n NodeGraphInteractionState,\n { kind: 'drag-groups' } | { kind: 'resize-group' }\n>;\n\ninterface UseNodeGraphGroupDragOptions {\n store: NodeGraphStore;\n getTransform: () => ViewportTransform;\n localScreenPoint: (clientX: number, clientY: number) => Point2D;\n snapToGrid: number | false;\n disabled?: boolean;\n defaultNodeSize: { width: number; height: number };\n emitGroupsChange: (next: NodeGraphGroup[]) => void;\n emitNodesChange: (next: NodeGraphNode[]) => void;\n emitSelectionChange: (next: NodeGraphSelection) => void;\n}\n\ninterface UseNodeGraphGroupDragReturn {\n onGroupBodyPointerDown: (\n event: React.PointerEvent<HTMLDivElement>,\n group: NodeGraphGroup\n ) => void;\n onGroupHandlePointerDown: (\n event: React.PointerEvent<HTMLDivElement>,\n group: NodeGraphGroup,\n handle: NodeGraphGroupResizeHandle\n ) => void;\n onGroupBodyPointerUp: (\n event: React.PointerEvent<HTMLDivElement>,\n group: NodeGraphGroup\n ) => void;\n}\n\ninterface GroupDragSession {\n kind: 'drag' | 'resize';\n pointerId: number;\n clickedGroupId: string;\n /** Handle being dragged (only for kind === 'resize'). */\n handle: NodeGraphGroupResizeHandle | null;\n startScreen: Point2D;\n startWorld: Point2D;\n selectionAtStart: NodeGraphSelection;\n /** Snapshot of bounds keyed by group id (for drag). */\n groupStartBounds: Map<string, WorldRect>;\n /**\n * Nodes fully contained inside one of the dragged groups at gesture\n * start — they ride along with the group(s). Set so the per-node\n * subscriber selector can do O(1) membership checks during the drag.\n */\n containedNodeIds: ReadonlySet<string>;\n /** Start positions of the contained nodes, keyed by node id. */\n nodeStartPositions: Map<string, Point2D>;\n /** Bounds of the resized group at start (for resize). */\n resizeStartBounds: WorldRect | null;\n additive: boolean;\n didDrag: boolean;\n}\n\n/**\n * Pointer-driven controller for group bodies and resize handles. Three\n * gestures share the same session state machine:\n *\n * - **drag**: pointerdown on a group body → snap-aware translation that\n * carries fully-contained nodes along, refused on overlap with other\n * groups, click-selection fallback when below the drag threshold.\n * - **resize**: pointerdown on a handle → eight-direction resize via\n * {@link applyGroupResize}, refused on overlap.\n * - **click**: pointerup before the drag threshold → selection toggle.\n *\n * Document listeners are attached only while a gesture is in flight.\n */\nexport function useNodeGraphGroupDrag(\n options: UseNodeGraphGroupDragOptions\n): UseNodeGraphGroupDragReturn {\n const {\n store,\n getTransform,\n localScreenPoint,\n snapToGrid,\n disabled,\n defaultNodeSize,\n emitGroupsChange,\n emitNodesChange,\n emitSelectionChange,\n } = options;\n\n const snapToGridRef = useLatest(snapToGrid);\n const disabledRef = useLatest(disabled);\n const emitGroupsChangeRef = useLatest(emitGroupsChange);\n const emitNodesChangeRef = useLatest(emitNodesChange);\n const emitSelectionChangeRef = useLatest(emitSelectionChange);\n\n const groupSessionRef = useRef<GroupDragSession | null>(null);\n\n /**\n * True when applying `delta` to the dragged groups (`session.groupStartBounds`)\n * would put any of them on top of a non-dragged group. Used by the\n * pointermove handler for live visual feedback and by pointerup to\n * refuse the commit.\n */\n const groupDragWouldOverlap = useCallback(\n (groupStartBounds: Map<string, WorldRect>, delta: Point2D): boolean => {\n const currentGroups = store.getData().groups;\n for (const [draggedId, start] of groupStartBounds) {\n const next: WorldRect = {\n x: start.x + delta.x,\n y: start.y + delta.y,\n width: start.width,\n height: start.height,\n };\n for (const other of currentGroups) {\n if (groupStartBounds.has(other.id)) continue;\n // Skip self defensively — already covered by the line above.\n if (other.id === draggedId) continue;\n if (rectsIntersect(next, other.bounds)) return true;\n }\n }\n return false;\n },\n [store]\n );\n\n /** True when the resized bounds would intersect any non-resized group. */\n const groupResizeWouldOverlap = useCallback(\n (groupId: string, nextBounds: WorldRect): boolean => {\n const currentGroups = store.getData().groups;\n for (const other of currentGroups) {\n if (other.id === groupId) continue;\n if (rectsIntersect(nextBounds, other.bounds)) return true;\n }\n return false;\n },\n [store]\n );\n\n const gesture = useDragGesture();\n\n // ── rAF-throttled interaction commits ──\n //\n // See `useNodeGraphNodeDrag` for the rationale — same coalescing pattern\n // applied to the group drag + resize move stream. Buffer the latest\n // interaction payload in a ref; an rAF callback drains it once per frame.\n const pendingInteractionRef = useRef<PendingGroupInteraction | null>(null);\n const rafRef = useRef<number>(0);\n\n const commitPending = useCallback((): void => {\n const pending = pendingInteractionRef.current;\n if (!pending) return;\n pendingInteractionRef.current = null;\n store.setInteraction(pending);\n }, [store]);\n\n const cancelRaf = useCallback((): void => {\n if (rafRef.current !== 0) {\n cancelAnimationFrame(rafRef.current);\n rafRef.current = 0;\n }\n }, []);\n\n /** Synchronous flush — cancels any pending rAF and commits immediately. */\n const flushPending = useCallback((): void => {\n cancelRaf();\n commitPending();\n }, [cancelRaf, commitPending]);\n\n useEffect(() => {\n return () => {\n cancelRaf();\n pendingInteractionRef.current = null;\n };\n }, [cancelRaf]);\n\n const scheduleCommit = useCallback(\n (next: PendingGroupInteraction): void => {\n pendingInteractionRef.current = next;\n if (rafRef.current !== 0) return;\n rafRef.current = requestAnimationFrame(() => {\n rafRef.current = 0;\n commitPending();\n });\n },\n [commitPending]\n );\n\n // Shared move handler for both the drag-groups and resize-group gestures.\n // Reads the active session from the ref and pushes the live interaction\n // delta into the store; the threshold-to-start gate lives here for drags.\n const handleGroupMove = useCallback(\n (e: PointerEvent): void => {\n const session = groupSessionRef.current;\n if (session?.pointerId !== e.pointerId) return;\n const current = localScreenPoint(e.clientX, e.clientY);\n const dx = current.x - session.startScreen.x;\n const dy = current.y - session.startScreen.y;\n const transform = getTransform();\n const worldDelta = {\n x: dx / transform.zoom,\n y: dy / transform.zoom,\n };\n const snapped = snapDelta(worldDelta, snapToGridRef.current);\n\n if (session.kind === 'drag') {\n if (!session.didDrag) {\n if (Math.hypot(dx, dy) < DRAG_START_THRESHOLD_PX) return;\n session.didDrag = true;\n }\n const ids: ReadonlySet<string> = new Set(\n session.groupStartBounds.keys()\n );\n const blocked = groupDragWouldOverlap(\n session.groupStartBounds,\n snapped\n );\n scheduleCommit({\n kind: 'drag-groups',\n groupIds: ids,\n containedNodeIds: session.containedNodeIds,\n startWorld: session.startWorld,\n delta: snapped,\n blocked,\n });\n } else if (session.resizeStartBounds && session.handle) {\n // `session.kind` is narrowed to 'resize' here (the `'drag'` branch\n // above is the only other kind); we still guard the optional\n // resize fields before using them.\n const nextBounds = applyGroupResize(\n session.resizeStartBounds,\n session.handle,\n snapped\n );\n const blocked = groupResizeWouldOverlap(\n session.clickedGroupId,\n nextBounds\n );\n scheduleCommit({\n kind: 'resize-group',\n groupId: session.clickedGroupId,\n handle: session.handle,\n startBounds: session.resizeStartBounds,\n delta: snapped,\n blocked,\n });\n }\n },\n [\n localScreenPoint,\n getTransform,\n snapToGridRef,\n groupDragWouldOverlap,\n groupResizeWouldOverlap,\n scheduleCommit,\n ]\n );\n\n const handleGroupCancel = useCallback((): void => {\n cancelRaf();\n pendingInteractionRef.current = null;\n groupSessionRef.current = null;\n store.setInteraction({ kind: 'idle' });\n }, [store, cancelRaf]);\n\n const onGroupBodyPointerDown = useCallback(\n (\n event: React.PointerEvent<HTMLDivElement>,\n group: NodeGraphGroup\n ): void => {\n if (event.button !== 0) return;\n event.stopPropagation();\n if (disabledRef.current) return;\n\n const additive = event.shiftKey || event.metaKey || event.ctrlKey;\n const currentSelection = store.getSelection();\n const alreadySelected = currentSelection.groups.includes(group.id);\n\n let dragIds: string[];\n if (alreadySelected) {\n dragIds = currentSelection.groups;\n } else if (!additive) {\n dragIds = [group.id];\n } else {\n dragIds = [...currentSelection.groups, group.id];\n }\n\n const startBounds = new Map<string, WorldRect>();\n for (const g of store.getData().groups) {\n if (dragIds.includes(g.id)) {\n startBounds.set(g.id, { ...g.bounds });\n }\n }\n\n // Snapshot nodes that are fully inside any of the dragged groups —\n // they ride along with the gesture so the visual grouping stays\n // consistent.\n const containedSet = new Set<string>();\n const nodeStartPositions = new Map<string, Point2D>();\n const measuredFor = store.getMeasuredSize;\n for (const n of store.getData().nodes) {\n const nb = getNodeBox(n, measuredFor(n.id), defaultNodeSize);\n for (const startRect of startBounds.values()) {\n if (rectContains(startRect, nb)) {\n containedSet.add(n.id);\n nodeStartPositions.set(n.id, { ...n.position });\n break;\n }\n }\n }\n const containedNodeIds: ReadonlySet<string> = containedSet;\n\n const startScreen = localScreenPoint(event.clientX, event.clientY);\n const startWorld = worldFromScreen(startScreen, getTransform());\n const pointerId = event.pointerId;\n gesture.begin(event, {\n onMove: handleGroupMove,\n onCancel: handleGroupCancel,\n });\n\n groupSessionRef.current = {\n kind: 'drag',\n pointerId,\n clickedGroupId: group.id,\n handle: null,\n startScreen,\n startWorld,\n selectionAtStart: currentSelection,\n groupStartBounds: startBounds,\n containedNodeIds,\n nodeStartPositions,\n resizeStartBounds: null,\n additive,\n didDrag: false,\n };\n },\n [\n disabledRef,\n defaultNodeSize,\n store,\n localScreenPoint,\n getTransform,\n gesture,\n handleGroupMove,\n handleGroupCancel,\n ]\n );\n\n const onGroupHandlePointerDown = useCallback(\n (\n event: React.PointerEvent<HTMLDivElement>,\n group: NodeGraphGroup,\n handle: NodeGraphGroupResizeHandle\n ): void => {\n if (event.button !== 0) return;\n event.stopPropagation();\n event.preventDefault();\n if (disabledRef.current) return;\n\n const startScreen = localScreenPoint(event.clientX, event.clientY);\n const startWorld = worldFromScreen(startScreen, getTransform());\n const pointerId = event.pointerId;\n gesture.begin(event, {\n onMove: handleGroupMove,\n onCancel: handleGroupCancel,\n });\n\n groupSessionRef.current = {\n kind: 'resize',\n pointerId,\n clickedGroupId: group.id,\n handle,\n startScreen,\n startWorld,\n selectionAtStart: store.getSelection(),\n groupStartBounds: new Map(),\n containedNodeIds: new Set<string>(),\n nodeStartPositions: new Map(),\n resizeStartBounds: { ...group.bounds },\n additive: false,\n didDrag: true,\n };\n // Immediately register the resize interaction so the canvas + overlay\n // start tracking from delta=0 at the click point.\n store.setInteraction({\n kind: 'resize-group',\n groupId: group.id,\n handle,\n startBounds: { ...group.bounds },\n delta: { x: 0, y: 0 },\n blocked: false,\n });\n },\n [\n disabledRef,\n localScreenPoint,\n getTransform,\n store,\n gesture,\n handleGroupMove,\n handleGroupCancel,\n ]\n );\n\n const onGroupBodyPointerUp = useCallback(\n (\n event: React.PointerEvent<HTMLDivElement>,\n group: NodeGraphGroup\n ): void => {\n const session = groupSessionRef.current;\n if (session?.pointerId !== event.pointerId) return;\n event.stopPropagation();\n gesture.finish(event);\n\n // Drain the rAF buffer so `store.getInteraction()` below reflects the\n // last pointermove rather than the previous frame's commit.\n flushPending();\n\n const interaction = store.getInteraction();\n\n if (session.kind === 'drag' && session.didDrag) {\n if (interaction.kind === 'drag-groups' && !interaction.blocked) {\n const delta = interaction.delta;\n // Apply the delta to both groups and any nodes that were\n // contained at gesture start, in a single set of state writes\n // so consumers get one atomic onGroupsChange + onNodesChange.\n const nextGroups = store.getData().groups.map(g => {\n const start = session.groupStartBounds.get(g.id);\n if (!start) return g;\n return {\n ...g,\n bounds: {\n ...start,\n x: start.x + delta.x,\n y: start.y + delta.y,\n },\n };\n });\n emitGroupsChangeRef.current(nextGroups);\n if (session.nodeStartPositions.size > 0) {\n const nextNodes = store.getData().nodes.map(n => {\n const start = session.nodeStartPositions.get(n.id);\n if (!start) return n;\n return {\n ...n,\n position: { x: start.x + delta.x, y: start.y + delta.y },\n };\n });\n emitNodesChangeRef.current(nextNodes);\n }\n }\n // If `blocked`, drop the gesture without committing — the overlay\n // already snaps back to the start position when we clear the\n // interaction state below.\n store.setInteraction({ kind: 'idle' });\n } else if (session.kind === 'resize') {\n if (\n interaction.kind === 'resize-group' &&\n !interaction.blocked &&\n session.resizeStartBounds &&\n session.handle\n ) {\n const next = applyGroupResize(\n session.resizeStartBounds,\n session.handle,\n interaction.delta\n );\n const nextGroups = store\n .getData()\n .groups.map(g =>\n g.id === session.clickedGroupId ? { ...g, bounds: next } : g\n );\n emitGroupsChangeRef.current(nextGroups);\n }\n store.setInteraction({ kind: 'idle' });\n } else {\n // Plain click on group body → selection\n const current = session.selectionAtStart;\n emitSelectionChangeRef.current({\n nodes: session.additive ? current.nodes : [],\n edges: session.additive ? current.edges : [],\n groups: toggleSelected(current.groups, group.id, session.additive),\n });\n }\n\n groupSessionRef.current = null;\n },\n [\n store,\n emitGroupsChangeRef,\n emitNodesChangeRef,\n emitSelectionChangeRef,\n gesture,\n flushPending,\n ]\n );\n\n return {\n onGroupBodyPointerDown,\n onGroupHandlePointerDown,\n onGroupBodyPointerUp,\n };\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;AAkCA;AAiEA;;;;;;;;;;;;AAYG;AACG;;AAeJ;AACA;AACA;AACA;AACA;AAEA;AAEA;;;;;AAKG;;;;AAKG;AACE;AACA;;;;AAIF;AACE;;;AAEA;;AACA;AAAwC;;;AAG5C;AACF;;;;AAQE;AACE;;AACA;AAA8C;;AAEhD;AACF;AAIF;;;;;;AAOA;AACA;AAEA;AACE;AACA;;AACA;AACA;AACF;AAEA;AACE;AACE;AACA;;;;AAKJ;AACE;AACA;AACF;;AAGE;AACE;AACA;AACF;AACF;AAEA;AAEI;AACA;;AACA;AACE;AACA;AACF;AACF;;;;AAOF;AAEI;AACA;;AACA;;;AAGA;AACA;AACE;AACA;;;AAIF;AACE;;;AAEE;;AAEF;;AAOA;AACE;AACA;;;AAGA;;AAED;;;;;;AAKD;;AASA;AACE;;;;AAIA;;AAED;;AAEL;;;;;;;AAQC;AAGH;AACE;AACA;AACA;;AAEF;;AAOI;;;;;AAIA;AACA;AACA;AAEA;;AAEE;;;AAEA;;;;;AAKF;;;AAGI;;;;;;AAOJ;AACA;AACA;;AAEE;;AAEE;AACE;AACA;;;;;;AAON;;AAEA;AACA;AACE;AACA;AACD;;AAGC;;;AAGA;;;AAGA;AACA;;;AAGA;;AAEA;;AAEJ;;;;;;;;;AAUC;;AASC;;;;;;AAKA;;AAEA;AACA;AACE;AACA;AACD;;AAGC;;;;;;AAMA;;;;AAIA;AACA;AACA;;;;;AAKA;;;AAGA;;AAEA;AACD;AACH;;;;;;;;AASC;;AAQC;AACA;;;AAEA;;;AAIA;AAEA;;;AAII;;;;AAIA;AACE;AACA;AAAY;;AAEV;AACA;AACE;AACA;AACA;AACD;;AAEL;AACA;;AAEE;AACE;AACA;AAAY;;AAEV;;;AAGJ;AACA;;;;;;;;AAOC;AACL;;AAGE;;AAGA;;AAMG;AACA;AAGH;;;;;;AAKF;;AAEE;AACA;AACA;AACD;;AAGH;AACF;;;;;;;AAQC;;;;;;AAQL;;"}
|
|
@@ -4,7 +4,7 @@ import '../../../utils/devWarn.js';
|
|
|
4
4
|
import { useLatest } from '../../../hooks/useLatest/useLatest.js';
|
|
5
5
|
import '../../../hooks/useBreakpoint/useBreakpoint.js';
|
|
6
6
|
import './../../../assets/src/theme/lightTheme.css.ts.vanilla-Ct6f9Iym.css';
|
|
7
|
-
import '
|
|
7
|
+
import '@vanilla-extract/css';
|
|
8
8
|
import './../../../assets/src/theme/darkTheme.css.ts.vanilla-BHZ3ecRn.css';
|
|
9
9
|
import 'react/jsx-runtime';
|
|
10
10
|
import './../../../assets/src/theme/globalScrollbars.css.ts.vanilla-BAJwnUEJ.css';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useNodeGraphKeyboard.js","sources":["
|
|
1
|
+
{"version":3,"file":"useNodeGraphKeyboard.js","sources":["src/components/editor/NodeGraph/useNodeGraphKeyboard.ts"],"sourcesContent":["'use client';\n\nimport { useCallback } from 'react';\n\nimport { useLatest } from '@/hooks';\n\nimport type { NodeGraphNode, NodeGraphSelection } from './NodeGraph.types';\nimport type { NodeGraphStore } from './NodeGraphStore';\nimport type React from 'react';\n\ninterface UseNodeGraphKeyboardOptions {\n store: NodeGraphStore;\n emitNodesChange: (next: NodeGraphNode[]) => void;\n emitSelectionChange: (next: NodeGraphSelection) => void;\n onDelete?: (selection: NodeGraphSelection) => void;\n onActivate?: (node: NodeGraphNode) => void;\n snapToGrid: number | false;\n /** When true, all keyboard handling is skipped. */\n disabled?: boolean;\n}\n\ninterface UseNodeGraphKeyboardReturn {\n onKeyDown: (event: React.KeyboardEvent<HTMLDivElement>) => void;\n}\n\nconst NUDGE_STEP_DEFAULT = 1;\nconst NUDGE_STEP_SHIFT = 10;\n\n/**\n * Keyboard interaction model for `<NodeGraph>`:\n *\n * | Keys | Action |\n * | ------------------- | -------------------------------------------- |\n * | Arrow keys | Nudge selected nodes by 1 grid step (or 1 unit) |\n * | Shift + Arrow keys | Nudge by 10× the base step |\n * | Delete / Backspace | Emit `onDelete(selection)` (non-empty) |\n * | Enter | Emit `onActivate(node)` when exactly one node selected |\n * | Cmd/Ctrl + A | Select all nodes |\n * | Escape | Clear selection (or cancel active gesture) |\n */\nexport function useNodeGraphKeyboard(\n options: UseNodeGraphKeyboardOptions\n): UseNodeGraphKeyboardReturn {\n const {\n store,\n emitNodesChange,\n emitSelectionChange,\n onDelete,\n onActivate,\n snapToGrid,\n disabled,\n } = options;\n\n const emitNodesChangeRef = useLatest(emitNodesChange);\n const emitSelectionChangeRef = useLatest(emitSelectionChange);\n const onDeleteRef = useLatest(onDelete);\n const onActivateRef = useLatest(onActivate);\n const snapToGridRef = useLatest(snapToGrid);\n const disabledRef = useLatest(disabled);\n\n const nudgeSelected = useCallback(\n (dx: number, dy: number): boolean => {\n const data = store.getData();\n const selection = store.getSelection();\n if (selection.nodes.length === 0) return false;\n const selectedIds = new Set(selection.nodes);\n const next = data.nodes.map(node => {\n if (!selectedIds.has(node.id)) return node;\n return {\n ...node,\n position: { x: node.position.x + dx, y: node.position.y + dy },\n };\n });\n emitNodesChangeRef.current(next);\n return true;\n },\n [store, emitNodesChangeRef]\n );\n\n const onKeyDown = useCallback(\n (event: React.KeyboardEvent<HTMLDivElement>): void => {\n if (disabledRef.current) return;\n // Don't steal input focus — if the event originated in an editable\n // element inside a node body, leave it alone.\n const target = event.target as HTMLElement | null;\n if (\n target &&\n (target.isContentEditable ||\n target.tagName === 'INPUT' ||\n target.tagName === 'TEXTAREA' ||\n target.tagName === 'SELECT')\n ) {\n return;\n }\n\n const grid = snapToGridRef.current;\n const baseStep = grid === false || grid <= 0 ? NUDGE_STEP_DEFAULT : grid;\n const step = event.shiftKey ? baseStep * NUDGE_STEP_SHIFT : baseStep;\n\n // Dispatch on `event.code` so character keys are matched by physical\n // location (e.g. `KeyA` for Ctrl+A), not by the printed character\n // — which would otherwise differ on non-QWERTY / non-Latin layouts.\n // For special keys (`ArrowUp`, `Delete`, `Backspace`, `Enter`,\n // `Escape`) `event.code` equals `event.key`, so existing cases keep\n // working unchanged.\n switch (event.code) {\n case 'ArrowUp':\n if (nudgeSelected(0, -step)) event.preventDefault();\n return;\n case 'ArrowDown':\n if (nudgeSelected(0, step)) event.preventDefault();\n return;\n case 'ArrowLeft':\n if (nudgeSelected(-step, 0)) event.preventDefault();\n return;\n case 'ArrowRight':\n if (nudgeSelected(step, 0)) event.preventDefault();\n return;\n case 'Delete':\n case 'Backspace': {\n const selection = store.getSelection();\n if (\n selection.nodes.length === 0 &&\n selection.edges.length === 0 &&\n selection.groups.length === 0\n ) {\n return;\n }\n onDeleteRef.current?.(selection);\n event.preventDefault();\n return;\n }\n case 'Enter': {\n const selection = store.getSelection();\n if (selection.nodes.length !== 1) return;\n const id = selection.nodes[0];\n const node = id != null ? store.getNodeById(id) : undefined;\n if (node) {\n onActivateRef.current?.(node);\n event.preventDefault();\n }\n return;\n }\n // `event.code` is layout-independent — matches the physical \"A\"\n // key regardless of the user's keyboard layout (Polish, Cyrillic,\n // Dvorak, etc., would otherwise fire a different `event.key`).\n case 'KeyA': {\n if (!(event.metaKey || event.ctrlKey)) return;\n const data = store.getData();\n emitSelectionChangeRef.current({\n nodes: data.nodes.map(n => n.id),\n edges: [],\n groups: [],\n });\n event.preventDefault();\n return;\n }\n case 'Escape': {\n // Cancel an in-flight connection first; otherwise clear selection.\n const interaction = store.getInteraction();\n if (interaction.kind !== 'idle') {\n store.setInteraction({ kind: 'idle' });\n event.preventDefault();\n return;\n }\n const selection = store.getSelection();\n if (\n selection.nodes.length === 0 &&\n selection.edges.length === 0 &&\n selection.groups.length === 0\n ) {\n return;\n }\n emitSelectionChangeRef.current({ nodes: [], edges: [], groups: [] });\n event.preventDefault();\n return;\n }\n default:\n return;\n }\n },\n [\n store,\n nudgeSelected,\n onDeleteRef,\n onActivateRef,\n emitSelectionChangeRef,\n snapToGridRef,\n disabledRef,\n ]\n );\n\n return { onKeyDown };\n}\n"],"names":[],"mappings":";;;;;;;;;;;;AAyBA;AACA;AAEA;;;;;;;;;;;AAWG;AACG;AAGJ;AAUA;AACA;AACA;AACA;AACA;AACA;;AAII;AACA;AACA;AAAkC;;;;AAGD;;AAE7B;;;AAGJ;AACA;AACA;AACF;AAIF;;;;;AAKI;AACA;;;;AAKI;;;AAKJ;AACA;AACA;;;;;;;AAQA;AACE;AACE;;;AAEF;AACE;;;AAEF;AACE;;;AAEF;AACE;;;AAEF;;AAEE;AACA;AAEE;AACA;;;AAIF;;;;;AAKA;AACA;;;AAEA;;AAEE;;;;;;;;;;;AAUF;;AAEE;AACA;AACA;AACD;;;;;;AAMD;AACA;;;;;AAKA;AACA;AAEE;AACA;;;AAIF;;;;AAIF;;;AAGJ;;;;;;;;AASC;;AAIL;;"}
|
|
@@ -9,7 +9,7 @@ import '../../../utils/devWarn.js';
|
|
|
9
9
|
import { useLatest } from '../../../hooks/useLatest/useLatest.js';
|
|
10
10
|
import '../../../hooks/useBreakpoint/useBreakpoint.js';
|
|
11
11
|
import './../../../assets/src/theme/lightTheme.css.ts.vanilla-Ct6f9Iym.css';
|
|
12
|
-
import '
|
|
12
|
+
import '@vanilla-extract/css';
|
|
13
13
|
import './../../../assets/src/theme/darkTheme.css.ts.vanilla-BHZ3ecRn.css';
|
|
14
14
|
import 'react/jsx-runtime';
|
|
15
15
|
import './../../../assets/src/theme/globalScrollbars.css.ts.vanilla-BAJwnUEJ.css';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useNodeGraphNodeDrag.js","sources":["../../../../../../src/components/editor/NodeGraph/useNodeGraphNodeDrag.ts"],"sourcesContent":[null],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;AAgBA;AA2CA;;;;;;;;;AASG;AACG;AAGJ;AAUA;AACA;AACA;AACA;AAEA;AACA;;;;;;;;;;AAWA;AACA;AAEA;AACE;AACA;;AACA;;AAEE;;;;AAID;AACH;AAEA;AACE;AACE;AACA;;;;AAKJ;AACE;AACA;AACF;;;;AAKE;AACE;AACA;AACF;AACF;;AAII;AAAwB;;;;;;;;;AAQxB;AACA;AACA;;;AAIA;;AAEE;;;;;;AAKA;;;;;AAKF;;;AAGI;;;AAIJ;;AAEA;AAEA;AACE;AACA;;AACA;;;AAGA;;;;AAGE;;;AAGE;;;AAGF;;AAEF;AACA;AACE;AACA;;;AAGF;;;;;;;AASE;;AAEF;;AACA;AACE;AACA;AACF;AACF;AAEA;AACE;;AAEE;AACA;AACA;;;AAGH;;;;;;AAOC;AACA;;AAEA;;AAEJ;;;;;;;;;AAUC;;AAKC;AACA;;;AAEA;;;;AAKA;AAEA;;AAEE;AACA;AACE;AACA;AACE;AACA;AAAY;;AAEV;;;AAGJ;AACA;;;;;;AAKF;AACE;;AAEE;AACA;AACA;AACD;;;AAIL;AACF;AAIF;AACF;;"}
|
|
1
|
+
{"version":3,"file":"useNodeGraphNodeDrag.js","sources":["src/components/editor/NodeGraph/useNodeGraphNodeDrag.ts"],"sourcesContent":["'use client';\n\nimport { useCallback, useEffect, useRef } from 'react';\n\nimport { screenToWorld as worldFromScreen } from '@/components/primitives/viewport';\nimport { useLatest } from '@/hooks';\n\nimport { snapDelta, toggleSelected } from './nodeGraphMath';\nimport { useDragGesture } from './useDragGesture';\n\nimport type { NodeGraphNode, NodeGraphSelection } from './NodeGraph.types';\nimport type { NodeGraphStore } from './NodeGraphStore';\nimport type { Point2D } from '@/components/primitives/canvas/canvas.types';\nimport type { ViewportTransform } from '@/components/primitives/viewport';\nimport type React from 'react';\n\nconst DRAG_START_THRESHOLD_PX = 3;\n\n/** Pending drag state buffered between pointermove and the next rAF flush. */\ninterface PendingDragMove {\n ids: ReadonlySet<string>;\n startWorld: Point2D;\n delta: Point2D;\n}\n\ninterface UseNodeGraphNodeDragOptions {\n store: NodeGraphStore;\n getTransform: () => ViewportTransform;\n localScreenPoint: (clientX: number, clientY: number) => Point2D;\n snapToGrid: number | false;\n disabled?: boolean;\n emitNodesChange: (next: NodeGraphNode[]) => void;\n emitSelectionChange: (next: NodeGraphSelection) => void;\n}\n\ninterface UseNodeGraphNodeDragReturn {\n onNodeBodyPointerDown: (\n event: React.PointerEvent<HTMLDivElement>,\n node: NodeGraphNode\n ) => void;\n onNodeBodyPointerUp: (\n event: React.PointerEvent<HTMLDivElement>,\n node: NodeGraphNode\n ) => void;\n}\n\ninterface DragSession {\n pointerId: number;\n clickedNodeId: string;\n startScreen: Point2D;\n startWorld: Point2D;\n /** Snapshot of selection at gesture start (for shift-toggle reset). */\n selectionAtStart: NodeGraphSelection;\n /** Snapshot of node positions at gesture start. */\n nodeStartPositions: Map<string, Point2D>;\n additive: boolean;\n didDrag: boolean;\n}\n\n/**\n * Pointer-driven drag + click-select controller for node bodies. Owns the\n * gesture lifecycle (`pointerdown` → document `pointermove` → `pointerup`\n * or `pointercancel`), commits position updates through the\n * controlled-state emitter on success, and falls back to plain click\n * selection when the pointer moves less than the drag threshold.\n *\n * Document listeners are attached only while a gesture is in flight, so\n * pointermove cost is zero between drags.\n */\nexport function useNodeGraphNodeDrag(\n options: UseNodeGraphNodeDragOptions\n): UseNodeGraphNodeDragReturn {\n const {\n store,\n getTransform,\n localScreenPoint,\n snapToGrid,\n disabled,\n emitNodesChange,\n emitSelectionChange,\n } = options;\n\n const snapToGridRef = useLatest(snapToGrid);\n const disabledRef = useLatest(disabled);\n const emitNodesChangeRef = useLatest(emitNodesChange);\n const emitSelectionChangeRef = useLatest(emitSelectionChange);\n\n const dragRef = useRef<DragSession | null>(null);\n const gesture = useDragGesture();\n\n // ── rAF-throttled interaction commits ──\n //\n // Native pointermove fires at the display's refresh rate (up to several\n // hundred Hz on high-refresh monitors) — without throttling, every event\n // triggers a full notification cycle through every NodeGraphStore\n // subscriber (one per node + one per edge label). Buffering the latest\n // delta in a ref and flushing once per frame caps the commit rate at\n // requestAnimationFrame, which already matches the refresh rate the\n // canvas layers are drawing at.\n const pendingMoveRef = useRef<PendingDragMove | null>(null);\n const rafRef = useRef<number>(0);\n\n const commitPending = useCallback((): void => {\n const pending = pendingMoveRef.current;\n if (!pending) return;\n pendingMoveRef.current = null;\n store.setInteraction({\n kind: 'drag-nodes',\n nodeIds: pending.ids,\n startWorld: pending.startWorld,\n delta: pending.delta,\n });\n }, [store]);\n\n const cancelRaf = useCallback((): void => {\n if (rafRef.current !== 0) {\n cancelAnimationFrame(rafRef.current);\n rafRef.current = 0;\n }\n }, []);\n\n /** Synchronous flush — cancels any pending rAF and commits immediately. */\n const flushPending = useCallback((): void => {\n cancelRaf();\n commitPending();\n }, [cancelRaf, commitPending]);\n\n // Cancel any in-flight rAF on unmount so a teardown mid-drag doesn't fire\n // a commit into a torn-down store.\n useEffect(() => {\n return () => {\n cancelRaf();\n pendingMoveRef.current = null;\n };\n }, [cancelRaf]);\n\n const onNodeBodyPointerDown = useCallback(\n (event: React.PointerEvent<HTMLDivElement>, node: NodeGraphNode): void => {\n if (event.button !== 0) return; // only primary button\n // Always stop propagation for clicks on a node body so the Viewport\n // beneath us doesn't fire a marquee. This applies even when disabled\n // or non-interactive — the click is consumed by the node hit target.\n event.stopPropagation();\n if (disabledRef.current) return;\n if (node.selectable === false && node.draggable === false) return;\n\n const additive = event.shiftKey || event.metaKey || event.ctrlKey;\n const currentSelection = store.getSelection();\n const alreadySelected = currentSelection.nodes.includes(node.id);\n\n // Decide which node ids should drag together. If clicked node is in\n // the selection, drag all selected nodes; otherwise just this one.\n let dragIds: string[];\n if (alreadySelected) {\n dragIds = currentSelection.nodes;\n } else if (!additive) {\n // Pre-emptively select just this node so the drag matches what the\n // user sees as \"selected\" mid-gesture. The actual selection emit\n // happens in pointerup if no drag occurred.\n dragIds = [node.id];\n } else {\n dragIds = [...currentSelection.nodes, node.id];\n }\n\n const positions = new Map<string, Point2D>();\n for (const n of store.getData().nodes) {\n if (dragIds.includes(n.id)) {\n positions.set(n.id, { ...n.position });\n }\n }\n\n const startScreen = localScreenPoint(event.clientX, event.clientY);\n const startWorld = worldFromScreen(startScreen, getTransform());\n const pointerId = event.pointerId;\n\n const handleMove = (e: PointerEvent): void => {\n const session = dragRef.current;\n if (session?.pointerId !== pointerId) return;\n const current = localScreenPoint(e.clientX, e.clientY);\n const dx = current.x - session.startScreen.x;\n const dy = current.y - session.startScreen.y;\n if (!session.didDrag) {\n if (Math.hypot(dx, dy) < DRAG_START_THRESHOLD_PX) return;\n const clicked = store.getNodeById(session.clickedNodeId);\n if (clicked?.draggable === false) {\n // Not draggable — promote to a no-op drag flag so pointerup falls\n // through to click selection logic.\n session.didDrag = false;\n return;\n }\n session.didDrag = true;\n }\n const transform = getTransform();\n const worldDelta = {\n x: dx / transform.zoom,\n y: dy / transform.zoom,\n };\n const snapped = snapDelta(worldDelta, snapToGridRef.current);\n const ids: ReadonlySet<string> = new Set(\n session.nodeStartPositions.keys()\n );\n // Buffer the latest delta; the rAF callback below picks the most\n // recent value when the frame fires, so any intermediate moves\n // between frames are coalesced into one commit.\n pendingMoveRef.current = {\n ids,\n startWorld: session.startWorld,\n delta: snapped,\n };\n if (rafRef.current !== 0) return;\n rafRef.current = requestAnimationFrame(() => {\n rafRef.current = 0;\n commitPending();\n });\n };\n\n gesture.begin(event, {\n onMove: handleMove,\n onCancel: () => {\n cancelRaf();\n pendingMoveRef.current = null;\n dragRef.current = null;\n store.setInteraction({ kind: 'idle' });\n },\n });\n\n dragRef.current = {\n pointerId,\n clickedNodeId: node.id,\n startScreen,\n startWorld,\n selectionAtStart: currentSelection,\n nodeStartPositions: positions,\n additive,\n didDrag: false,\n };\n },\n [\n store,\n getTransform,\n localScreenPoint,\n snapToGridRef,\n disabledRef,\n gesture,\n cancelRaf,\n commitPending,\n ]\n );\n\n const onNodeBodyPointerUp = useCallback(\n (event: React.PointerEvent<HTMLDivElement>, node: NodeGraphNode): void => {\n const session = dragRef.current;\n if (session?.pointerId !== event.pointerId) return;\n event.stopPropagation();\n gesture.finish(event);\n\n // Drain any deferred pointermove update before reading from the store —\n // otherwise the commit would use the previous frame's delta and drop\n // the final sub-frame movement before pointerup.\n flushPending();\n\n if (session.didDrag) {\n // Commit positions from store's interaction delta.\n const interaction = store.getInteraction();\n if (interaction.kind === 'drag-nodes') {\n const delta = interaction.delta;\n const nextNodes = store.getData().nodes.map(n => {\n const start = session.nodeStartPositions.get(n.id);\n if (!start) return n;\n return {\n ...n,\n position: { x: start.x + delta.x, y: start.y + delta.y },\n };\n });\n emitNodesChangeRef.current(nextNodes);\n }\n store.setInteraction({ kind: 'idle' });\n } else {\n // Plain click — handle selection semantics.\n if (node.selectable !== false) {\n const current = session.selectionAtStart;\n emitSelectionChangeRef.current({\n nodes: toggleSelected(current.nodes, node.id, session.additive),\n edges: session.additive ? current.edges : [],\n groups: session.additive ? current.groups : [],\n });\n }\n }\n\n dragRef.current = null;\n },\n [store, emitNodesChangeRef, emitSelectionChangeRef, gesture, flushPending]\n );\n\n return { onNodeBodyPointerDown, onNodeBodyPointerUp };\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;AAgBA;AA2CA;;;;;;;;;AASG;AACG;AAGJ;AAUA;AACA;AACA;AACA;AAEA;AACA;;;;;;;;;;AAWA;AACA;AAEA;AACE;AACA;;AACA;;AAEE;;;;AAID;AACH;AAEA;AACE;AACE;AACA;;;;AAKJ;AACE;AACA;AACF;;;;AAKE;AACE;AACA;AACF;AACF;;AAII;AAAwB;;;;;;;;;AAQxB;AACA;AACA;;;AAIA;;AAEE;;;;;;AAKA;;;;;AAKF;;;AAGI;;;AAIJ;;AAEA;AAEA;AACE;AACA;;AACA;;;AAGA;;;;AAGE;;;AAGE;;;AAGF;;AAEF;AACA;AACE;AACA;;;AAGF;;;;;;;AASE;;AAEF;;AACA;AACE;AACA;AACF;AACF;AAEA;AACE;;AAEE;AACA;AACA;;;AAGH;;;;;;AAOC;AACA;;AAEA;;AAEJ;;;;;;;;;AAUC;;AAKC;AACA;;;AAEA;;;;AAKA;AAEA;;AAEE;AACA;AACE;AACA;AACE;AACA;AAAY;;AAEV;;;AAGJ;AACA;;;;;;AAKF;AACE;;AAEE;AACA;AACA;AACD;;;AAIL;AACF;AAIF;AACF;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useStoreSlice.js","sources":["
|
|
1
|
+
{"version":3,"file":"useStoreSlice.js","sources":["src/components/editor/NodeGraph/useStoreSlice.ts"],"sourcesContent":["'use client';\n\nimport { useCallback, useRef, useSyncExternalStore } from 'react';\n\ninterface SliceCache<TSource, TSelected> {\n source: TSource;\n selected: TSelected;\n}\n\n/**\n * Subscribe to a slice of an external store, returning a memoized selection.\n *\n * Avoids the re-render storm caused by calling `useSyncExternalStore` with a\n * raw slice getter when the slice mutates at high frequency (e.g. drag\n * deltas at 60–120 Hz) but each subscriber only cares about a per-id payload.\n *\n * The selector is invoked only when the underlying source reference changes;\n * if the freshly computed selection compares equal to the previous one via\n * `isEqual`, the previous selection reference is preserved so downstream\n * `Object.is` checks (including React's reconciler) treat it as unchanged.\n *\n * Inspired by `use-sync-external-store/with-selector`, kept local to avoid\n * pulling in an extra peer dependency for a single call site.\n */\nexport function useStoreSlice<TSource, TSelected>(\n subscribe: (cb: () => void) => () => void,\n getSnapshot: () => TSource,\n selector: (snapshot: TSource) => TSelected,\n isEqual: (a: TSelected, b: TSelected) => boolean = Object.is\n): TSelected {\n const cacheRef = useRef<SliceCache<TSource, TSelected> | null>(null);\n const selectorRef = useRef(selector);\n const isEqualRef = useRef(isEqual);\n // Keep these refs pointed at the latest selector + equality fn so the\n // snapshot getter below always uses current values without forcing a\n // re-subscribe. This mirrors `use-sync-external-store/with-selector`, which\n // deliberately writes these refs during render rather than in an effect.\n // eslint-disable-next-line react-hooks/refs -- intentional with-selector latest refs\n selectorRef.current = selector;\n // eslint-disable-next-line react-hooks/refs -- intentional with-selector latest refs\n isEqualRef.current = isEqual;\n\n const getSelectedSnapshot = useCallback((): TSelected => {\n const next = getSnapshot();\n const cache = cacheRef.current;\n if (cache && Object.is(cache.source, next)) {\n return cache.selected;\n }\n const selected = selectorRef.current(next);\n if (cache && isEqualRef.current(cache.selected, selected)) {\n cacheRef.current = { source: next, selected: cache.selected };\n return cache.selected;\n }\n cacheRef.current = { source: next, selected };\n return selected;\n }, [getSnapshot]);\n\n return useSyncExternalStore(subscribe, getSelectedSnapshot);\n}\n"],"names":[],"mappings":";;;AASA;;;;;;;;;;;;;;AAcG;AACG;AAMJ;AACA;AACA;;;;;;AAMA;;AAEA;AAEA;AACE;AACA;AACA;;;;AAIA;AACE;;;;AAIF;AACF;AAEA;AACF;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PropertyGroup.js","sources":["
|
|
1
|
+
{"version":3,"file":"PropertyGroup.js","sources":["src/components/editor/PropertyInspector/PropertyGroup.tsx"],"sourcesContent":["'use client';\n\nimport { assignInlineVars } from '@vanilla-extract/dynamic';\nimport React from 'react';\n\nimport { vars } from '@/theme/contract.css';\nimport { cx } from '@/utils/cx';\n\nimport {\n groupRoot,\n groupDivider,\n groupTitle,\n groupLine,\n indentVar,\n} from './PropertyGroup.css';\n\nimport type { PropertyGroupProps } from './PropertyInspector.types';\n\n// --- Component ---\n\nexport const PropertyGroup: React.FC<PropertyGroupProps> = ({\n title,\n children,\n indent = 0,\n disabled = false,\n\n className,\n style,\n testId,\n ref,\n ...rest\n}) => {\n return (\n <div\n ref={ref}\n className={cx(groupRoot, className)}\n style={{\n ...style,\n ...assignInlineVars({\n [indentVar]: `calc(${indent} * ${vars.spacing.xl})`,\n }),\n opacity: disabled ? 0.5 : 1,\n pointerEvents: disabled ? 'none' : 'auto',\n }}\n data-testid={testId}\n {...rest}\n >\n {title != null && (\n <div className={groupDivider}>\n <span className={groupLine} />\n <span className={groupTitle}>{title}</span>\n <span className={groupLine} />\n </div>\n )}\n {children}\n </div>\n );\n};\n\nPropertyGroup.displayName = 'PropertyGroup';\n"],"names":[],"mappings":";;;;;;;AAkBA;AAEO;AAYL;AAKM;AACA;;;;;AAKD;AAcP;AAEA;;"}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import './../../../assets/src/components/editor/PropertyInspector/PropertyPanel.css.ts.vanilla-
|
|
1
|
+
import './../../../assets/src/components/editor/PropertyInspector/PropertyPanel.css.ts.vanilla-Dpr_k5I3.css';
|
|
2
2
|
|
|
3
3
|
var contentBottomSpacingVar = 'var(--contentBottomSpacingVar__151bru71)';
|
|
4
4
|
var contentTopSpacingVar = 'var(--contentTopSpacingVar__151bru70)';
|
|
5
5
|
var panelContent = 'PropertyPanel_panelContent__151bru76';
|
|
6
|
-
var
|
|
6
|
+
var panelContentScroll = 'PropertyPanel_panelContentScroll__151bru77';
|
|
7
|
+
var panelFooter = 'PropertyPanel_panelFooter__151bru78';
|
|
7
8
|
var panelHeader = 'PropertyPanel_panelHeader__151bru73';
|
|
8
9
|
var panelRoot = 'PropertyPanel_panelRoot__151bru72';
|
|
9
10
|
var searchInputBase = 'PropertyPanel_searchInputBase__151bru75';
|
|
10
11
|
var searchWrapper = 'PropertyPanel_searchWrapper__151bru74';
|
|
11
12
|
|
|
12
|
-
export { contentBottomSpacingVar, contentTopSpacingVar, panelContent, panelFooter, panelHeader, panelRoot, searchInputBase, searchWrapper };
|
|
13
|
+
export { contentBottomSpacingVar, contentTopSpacingVar, panelContent, panelContentScroll, panelFooter, panelHeader, panelRoot, searchInputBase, searchWrapper };
|
|
13
14
|
//# sourceMappingURL=PropertyPanel.css.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PropertyPanel.css.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"PropertyPanel.css.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;"}
|
|
@@ -4,7 +4,7 @@ import { assignInlineVars } from '@vanilla-extract/dynamic';
|
|
|
4
4
|
import { useState, useCallback, useDeferredValue, useMemo, createContext, useContext } from 'react';
|
|
5
5
|
import { ScrollArea } from '../../layout/ScrollArea/ScrollArea.js';
|
|
6
6
|
import { cx } from '../../../utils/cx.js';
|
|
7
|
-
import { contentBottomSpacingVar, contentTopSpacingVar, panelContent, panelHeader, searchWrapper, searchInputBase, panelFooter, panelRoot } from './PropertyPanel.css.js';
|
|
7
|
+
import { contentBottomSpacingVar, contentTopSpacingVar, panelContent, panelHeader, searchWrapper, searchInputBase, panelContentScroll, panelFooter, panelRoot } from './PropertyPanel.css.js';
|
|
8
8
|
|
|
9
9
|
// --- Context ---
|
|
10
10
|
const PropertyPanelContext =
|
|
@@ -34,7 +34,7 @@ const SEARCH_SIZE_MAP = {
|
|
|
34
34
|
},
|
|
35
35
|
};
|
|
36
36
|
// --- Component ---
|
|
37
|
-
const PropertyPanel = ({ children, header, footer, size = 'md', maxHeight, searchable = false, searchPlaceholder = 'Search properties...', onSearchChange, contentTopSpacing, contentBottomSpacing, className, style, testId, ref, ...rest }) => {
|
|
37
|
+
const PropertyPanel = ({ children, header, footer, size = 'md', maxHeight, fillHeight = false, density = 'normal', searchable = false, searchPlaceholder = 'Search properties...', onSearchChange, contentTopSpacing, contentBottomSpacing, className, style, testId, ref, ...rest }) => {
|
|
38
38
|
const [searchQuery, setSearchQuery] = useState('');
|
|
39
39
|
const handleSearchChange = useCallback((e) => {
|
|
40
40
|
const query = e.target.value;
|
|
@@ -47,8 +47,9 @@ const PropertyPanel = ({ children, header, footer, size = 'md', maxHeight, searc
|
|
|
47
47
|
const deferredSearchQuery = useDeferredValue(searchQuery);
|
|
48
48
|
const contextValue = useMemo(() => ({
|
|
49
49
|
size,
|
|
50
|
+
density,
|
|
50
51
|
searchQuery: deferredSearchQuery.toLowerCase(),
|
|
51
|
-
}), [size, deferredSearchQuery]);
|
|
52
|
+
}), [size, density, deferredSearchQuery]);
|
|
52
53
|
const hasHeader = header != null || searchable;
|
|
53
54
|
const hasFooter = footer != null;
|
|
54
55
|
const sizeConfig = SEARCH_SIZE_MAP[size];
|
|
@@ -65,7 +66,7 @@ const PropertyPanel = ({ children, header, footer, size = 'md', maxHeight, searc
|
|
|
65
66
|
height: `${sizeConfig.height}px`,
|
|
66
67
|
fontSize: sizeConfig.fontSize,
|
|
67
68
|
padding: `0 ${sizeConfig.padding}`,
|
|
68
|
-
} }) }))] })), maxHeight != null ? (jsx(ScrollArea, { maxHeight: maxHeight, direction: "vertical", scrollbarVisibility: "auto", fadeMask: true, children: content })) : (content), hasFooter && jsx("div", { className: panelFooter, children: footer })] }) }));
|
|
69
|
+
} }) }))] })), maxHeight != null ? (jsx(ScrollArea, { maxHeight: maxHeight, direction: "vertical", scrollbarVisibility: "auto", fadeMask: true, children: content })) : fillHeight ? (jsx(ScrollArea, { direction: "vertical", scrollbarVisibility: "auto", fadeMask: true, style: { flex: 1, minHeight: 0 }, children: jsx("div", { className: cx(panelContent, panelContentScroll), style: contentStyle, children: children }) })) : (content), hasFooter && jsx("div", { className: panelFooter, children: footer })] }) }));
|
|
69
70
|
};
|
|
70
71
|
PropertyPanel.displayName = 'PropertyPanel';
|
|
71
72
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PropertyPanel.js","sources":["
|
|
1
|
+
{"version":3,"file":"PropertyPanel.js","sources":["src/components/editor/PropertyInspector/PropertyPanel.tsx"],"sourcesContent":["'use client';\n\nimport { assignInlineVars } from '@vanilla-extract/dynamic';\nimport React, {\n createContext,\n useCallback,\n useContext,\n useDeferredValue,\n useMemo,\n useState,\n} from 'react';\n\nimport { ScrollArea } from '@/components/layout/ScrollArea';\nimport { cx } from '@/utils/cx';\n\nimport {\n panelRoot,\n panelHeader,\n searchWrapper,\n searchInputBase,\n panelContent,\n panelContentScroll,\n panelFooter,\n contentTopSpacingVar,\n contentBottomSpacingVar,\n} from './PropertyPanel.css';\n\nimport type {\n PropertyInspectorSize,\n PropertyPanelContextValue,\n PropertyPanelProps,\n} from './PropertyInspector.types';\n\n// --- Context ---\n\nconst PropertyPanelContext =\n /*#__PURE__*/ createContext<PropertyPanelContextValue | null>(null);\n\n/**\n * Returns the PropertyPanel context value, or null if not inside a PropertyPanel.\n * Child components use this to inherit size and search query from the panel.\n */\nexport function usePropertyPanelContext(): PropertyPanelContextValue | null {\n return useContext(PropertyPanelContext);\n}\n\nexport { PropertyPanelContext };\n\n// --- Size map ---\n\ninterface SearchInputSizeConfig {\n height: number;\n fontSize: string;\n padding: string;\n}\n\nconst SEARCH_SIZE_MAP: Record<PropertyInspectorSize, SearchInputSizeConfig> = {\n sm: {\n height: 20,\n fontSize: 'var(--etui-font-size-md)',\n padding: 'var(--etui-spacing-sm)',\n },\n md: {\n height: 24,\n fontSize: 'var(--etui-font-size-md)',\n padding: 'var(--etui-spacing-md)',\n },\n lg: {\n height: 28,\n fontSize: 'var(--etui-font-size-lg)',\n padding: 'var(--etui-spacing-md)',\n },\n};\n\n// --- Component ---\n\nexport const PropertyPanel: React.FC<PropertyPanelProps> = ({\n children,\n header,\n footer,\n size = 'md',\n maxHeight,\n fillHeight = false,\n density = 'normal',\n searchable = false,\n searchPlaceholder = 'Search properties...',\n onSearchChange,\n contentTopSpacing,\n contentBottomSpacing,\n\n className,\n style,\n testId,\n ref,\n ...rest\n}) => {\n const [searchQuery, setSearchQuery] = useState('');\n\n const handleSearchChange = useCallback(\n (e: React.ChangeEvent<HTMLInputElement>) => {\n const query = e.target.value;\n setSearchQuery(query);\n onSearchChange?.(query);\n },\n [onSearchChange]\n );\n\n // Defer the query handed to descendants for filtering so the search input\n // stays responsive while large property lists re-filter. The immediate\n // `searchQuery` still drives the input value.\n const deferredSearchQuery = useDeferredValue(searchQuery);\n\n const contextValue = useMemo<PropertyPanelContextValue>(\n () => ({\n size,\n density,\n searchQuery: deferredSearchQuery.toLowerCase(),\n }),\n [size, density, deferredSearchQuery]\n );\n\n const hasHeader = header != null || searchable;\n const hasFooter = footer != null;\n\n const sizeConfig = SEARCH_SIZE_MAP[size];\n\n const contentStyle = assignInlineVars({\n [contentTopSpacingVar]:\n contentTopSpacing != null\n ? `${contentTopSpacing}px`\n : 'var(--etui-spacing-sm)',\n [contentBottomSpacingVar]:\n contentBottomSpacing != null\n ? `${contentBottomSpacing}px`\n : 'var(--etui-spacing-md)',\n });\n\n const content = (\n <div className={panelContent} style={contentStyle}>\n {children}\n </div>\n );\n\n return (\n <PropertyPanelContext.Provider value={contextValue}>\n <div\n ref={ref}\n role=\"region\"\n aria-label=\"Properties\"\n className={cx(panelRoot, className)}\n style={style}\n data-testid={testId}\n {...rest}\n >\n {hasHeader && (\n <div className={panelHeader}>\n {header}\n {searchable && (\n <div className={searchWrapper}>\n <input\n type=\"search\"\n role=\"searchbox\"\n aria-label=\"Search properties\"\n placeholder={searchPlaceholder}\n value={searchQuery}\n onChange={handleSearchChange}\n className={searchInputBase}\n style={{\n height: `${sizeConfig.height}px`,\n fontSize: sizeConfig.fontSize,\n padding: `0 ${sizeConfig.padding}`,\n }}\n />\n </div>\n )}\n </div>\n )}\n\n {maxHeight != null ? (\n <ScrollArea\n maxHeight={maxHeight}\n direction=\"vertical\"\n scrollbarVisibility=\"auto\"\n fadeMask\n >\n {content}\n </ScrollArea>\n ) : fillHeight ? (\n <ScrollArea\n direction=\"vertical\"\n scrollbarVisibility=\"auto\"\n fadeMask\n style={{ flex: 1, minHeight: 0 }}\n >\n <div\n className={cx(panelContent, panelContentScroll)}\n style={contentStyle}\n >\n {children}\n </div>\n </ScrollArea>\n ) : (\n content\n )}\n\n {hasFooter && <div className={panelFooter}>{footer}</div>}\n </div>\n </PropertyPanelContext.Provider>\n );\n};\n\nPropertyPanel.displayName = 'PropertyPanel';\n"],"names":[],"mappings":";;;;;;;;AAiCA;AAEA;AACE;AAEF;;;AAGG;;AAED;AACF;AAYA;AACE;AACE;AACA;AACA;AACD;AACD;AACE;AACA;AACA;AACD;AACD;AACE;AACA;AACA;AACD;;AAGH;AAEO;;AAsBL;AAEI;;AAEA;AACF;;;;AAOF;AAEA;;;AAII;;AAKJ;AACA;AAEA;;AAGE;;AAGI;AACJ;;AAGI;AACL;AAED;AAMA;AAyBkB;;AAEA;;AAuCpB;AAEA;;"}
|
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
import './../../../assets/src/components/editor/PropertyInspector/PropertyRow.css.ts.vanilla-
|
|
1
|
+
import './../../../assets/src/components/editor/PropertyInspector/PropertyRow.css.ts.vanilla-pq1Uhd2a.css';
|
|
2
2
|
|
|
3
3
|
var RESET_BUTTON_CLASS = 'property-row-reset';
|
|
4
4
|
var controlRatioVar = 'var(--controlRatioVar__1ds48411)';
|
|
5
|
-
var fullWidthControl = '
|
|
5
|
+
var fullWidthControl = 'PropertyRow_fullWidthControl__1ds48418';
|
|
6
6
|
var fullWidthLabel = 'PropertyRow_fullWidthLabel__1ds48414';
|
|
7
|
-
var
|
|
8
|
-
var
|
|
9
|
-
var
|
|
7
|
+
var labelTooltipTarget = 'PropertyRow_labelTooltipTarget__1ds48415';
|
|
8
|
+
var modifiedDot = 'PropertyRow_modifiedDot__1ds48416';
|
|
9
|
+
var resetButton = 'PropertyRow_resetButton__1ds48419';
|
|
10
|
+
var rowControl = 'PropertyRow_rowControl__1ds48417';
|
|
10
11
|
var rowLabel = 'PropertyRow_rowLabel__1ds48413';
|
|
11
12
|
var rowRoot = 'PropertyRow_rowRoot__1ds48412';
|
|
12
13
|
var splitRatioVar = 'var(--splitRatioVar__1ds48410)';
|
|
13
14
|
|
|
14
|
-
export { RESET_BUTTON_CLASS, controlRatioVar, fullWidthControl, fullWidthLabel, modifiedDot, resetButton, rowControl, rowLabel, rowRoot, splitRatioVar };
|
|
15
|
+
export { RESET_BUTTON_CLASS, controlRatioVar, fullWidthControl, fullWidthLabel, labelTooltipTarget, modifiedDot, resetButton, rowControl, rowLabel, rowRoot, splitRatioVar };
|
|
15
16
|
//# sourceMappingURL=PropertyRow.css.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PropertyRow.css.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"PropertyRow.css.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;"}
|
|
@@ -89,10 +89,16 @@ import '../../Icons/GitBranchIcon.js';
|
|
|
89
89
|
import '../../Icons/BugIcon.js';
|
|
90
90
|
import '../../Icons/MinusIcon.js';
|
|
91
91
|
import '../../Icons/PinIcon.js';
|
|
92
|
+
import '../../Icons/OrbitIcon.js';
|
|
93
|
+
import '../../Icons/RotateIcon.js';
|
|
94
|
+
import '../../Icons/SpinIcon.js';
|
|
95
|
+
import '../../Icons/CameraIcon.js';
|
|
96
|
+
import '../../Icons/CubeIcon.js';
|
|
97
|
+
import '../../Icons/ZoomFitIcon.js';
|
|
92
98
|
import { Tooltip } from '../../primitives/Tooltip/Tooltip.js';
|
|
93
99
|
import { cx } from '../../../utils/cx.js';
|
|
94
100
|
import { usePropertyPanelContext } from './PropertyPanel.js';
|
|
95
|
-
import { modifiedDot, controlRatioVar, splitRatioVar, fullWidthLabel, fullWidthControl, rowLabel, rowControl, resetButton, RESET_BUTTON_CLASS, rowRoot } from './PropertyRow.css.js';
|
|
101
|
+
import { modifiedDot, labelTooltipTarget, controlRatioVar, splitRatioVar, fullWidthLabel, fullWidthControl, rowLabel, rowControl, resetButton, RESET_BUTTON_CLASS, rowRoot } from './PropertyRow.css.js';
|
|
96
102
|
|
|
97
103
|
const ROW_SIZE_MAP = {
|
|
98
104
|
sm: {
|
|
@@ -111,18 +117,25 @@ const ROW_SIZE_MAP = {
|
|
|
111
117
|
resetIconSize: 14,
|
|
112
118
|
},
|
|
113
119
|
};
|
|
120
|
+
const DENSITY_MAP = {
|
|
121
|
+
compact: { minHeightDelta: -4, paddingBlock: 0 },
|
|
122
|
+
normal: { minHeightDelta: 0, paddingBlock: 2 },
|
|
123
|
+
spacious: { minHeightDelta: 6, paddingBlock: 5 },
|
|
124
|
+
};
|
|
114
125
|
// --- Component ---
|
|
115
126
|
const PropertyRow = ({ label, tooltip, children, fullWidth = false, splitRatio = [40, 60], modified = false, disabled = false, visible = true, size: sizeProp, action, onLabelContextMenu, onReset, className, style, testId, ref, ...rest }) => {
|
|
116
127
|
const panelCtx = usePropertyPanelContext();
|
|
117
128
|
const size = sizeProp ?? panelCtx?.size ?? 'md';
|
|
118
129
|
const sizeConfig = ROW_SIZE_MAP[size];
|
|
130
|
+
const densityConfig = DENSITY_MAP[panelCtx?.density ?? 'normal'];
|
|
131
|
+
const rowMinHeight = sizeConfig.minHeight + densityConfig.minHeightDelta;
|
|
119
132
|
const handleResetClick = useCallback((e) => {
|
|
120
133
|
e.stopPropagation();
|
|
121
134
|
onReset?.();
|
|
122
135
|
}, [onReset]);
|
|
123
136
|
const modifiedDotEl = modified ? (jsx("span", { className: modifiedDot, "aria-label": "Modified", "data-testid": "modified-dot" })) : null;
|
|
124
137
|
const labelText = jsx("span", { children: label });
|
|
125
|
-
const labelElement = tooltip ? (jsx(Tooltip, { title: tooltip, children: jsxs("span", {
|
|
138
|
+
const labelElement = tooltip ? (jsx(Tooltip, { title: tooltip, children: jsxs("span", { className: labelTooltipTarget, children: [modifiedDotEl, labelText] }) })) : (jsxs(Fragment, { children: [modifiedDotEl, labelText] }));
|
|
126
139
|
const inlineVars = assignInlineVars({
|
|
127
140
|
[splitRatioVar]: `${splitRatio[0]}%`,
|
|
128
141
|
[controlRatioVar]: `${splitRatio[1]}%`,
|
|
@@ -133,7 +146,9 @@ const PropertyRow = ({ label, tooltip, children, fullWidth = false, splitRatio =
|
|
|
133
146
|
display: visible ? 'flex' : 'none',
|
|
134
147
|
flexDirection: fullWidth ? 'column' : 'row',
|
|
135
148
|
alignItems: fullWidth ? 'stretch' : 'center',
|
|
136
|
-
minHeight: `${
|
|
149
|
+
minHeight: `${rowMinHeight}px`,
|
|
150
|
+
paddingTop: `${densityConfig.paddingBlock}px`,
|
|
151
|
+
paddingBottom: `${densityConfig.paddingBlock}px`,
|
|
137
152
|
opacity: disabled ? 0.5 : 1,
|
|
138
153
|
pointerEvents: disabled ? 'none' : 'auto',
|
|
139
154
|
}, "data-testid": testId, ...rest, children: [fullWidth ? (jsxs(Fragment, { children: [jsx("div", { className: fullWidthLabel, onContextMenu: onLabelContextMenu, style: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PropertyRow.js","sources":["
|
|
1
|
+
{"version":3,"file":"PropertyRow.js","sources":["src/components/editor/PropertyInspector/PropertyRow.tsx"],"sourcesContent":["'use client';\n\nimport { assignInlineVars } from '@vanilla-extract/dynamic';\nimport React, { useCallback } from 'react';\n\nimport { UndoIcon } from '@/components/Icons';\nimport { Tooltip } from '@/components/primitives/Tooltip';\nimport { cx } from '@/utils/cx';\n\nimport { usePropertyPanelContext } from './PropertyPanel';\nimport {\n RESET_BUTTON_CLASS,\n splitRatioVar,\n controlRatioVar,\n rowRoot,\n rowLabel,\n fullWidthLabel,\n labelTooltipTarget,\n modifiedDot,\n rowControl,\n fullWidthControl,\n resetButton,\n} from './PropertyRow.css';\n\nimport type {\n PropertyDensity,\n PropertyInspectorSize,\n PropertyRowProps,\n} from './PropertyInspector.types';\n\n// --- Size maps ---\n\ninterface RowSizeConfig {\n minHeight: number;\n fontSize: string;\n resetIconSize: number;\n}\n\nconst ROW_SIZE_MAP: Record<PropertyInspectorSize, RowSizeConfig> = {\n sm: {\n minHeight: 22,\n fontSize: 'var(--etui-font-size-md)',\n resetIconSize: 10,\n },\n md: {\n minHeight: 26,\n fontSize: 'var(--etui-font-size-md)',\n resetIconSize: 12,\n },\n lg: {\n minHeight: 30,\n fontSize: 'var(--etui-font-size-lg)',\n resetIconSize: 14,\n },\n};\n\n// --- Density maps ---\n\ninterface DensityConfig {\n /** Adjustment applied to the size-based min-height (px). */\n minHeightDelta: number;\n /** Vertical padding above/below the row content (px). */\n paddingBlock: number;\n}\n\nconst DENSITY_MAP: Record<PropertyDensity, DensityConfig> = {\n compact: { minHeightDelta: -4, paddingBlock: 0 },\n normal: { minHeightDelta: 0, paddingBlock: 2 },\n spacious: { minHeightDelta: 6, paddingBlock: 5 },\n};\n\n// --- Component ---\n\nexport const PropertyRow: React.FC<PropertyRowProps> = ({\n label,\n tooltip,\n children,\n fullWidth = false,\n splitRatio = [40, 60],\n modified = false,\n disabled = false,\n visible = true,\n size: sizeProp,\n action,\n onLabelContextMenu,\n onReset,\n\n className,\n style,\n testId,\n ref,\n ...rest\n}) => {\n const panelCtx = usePropertyPanelContext();\n const size = sizeProp ?? panelCtx?.size ?? 'md';\n const sizeConfig = ROW_SIZE_MAP[size];\n const densityConfig = DENSITY_MAP[panelCtx?.density ?? 'normal'];\n const rowMinHeight = sizeConfig.minHeight + densityConfig.minHeightDelta;\n\n const handleResetClick = useCallback(\n (e: React.MouseEvent) => {\n e.stopPropagation();\n onReset?.();\n },\n [onReset]\n );\n\n const modifiedDotEl = modified ? (\n <span\n className={modifiedDot}\n aria-label=\"Modified\"\n data-testid=\"modified-dot\"\n />\n ) : null;\n\n const labelText = <span>{label}</span>;\n\n const labelElement = tooltip ? (\n <Tooltip title={tooltip}>\n <span className={labelTooltipTarget}>\n {modifiedDotEl}\n {labelText}\n </span>\n </Tooltip>\n ) : (\n <>\n {modifiedDotEl}\n {labelText}\n </>\n );\n\n const inlineVars = assignInlineVars({\n [splitRatioVar]: `${splitRatio[0]}%`,\n [controlRatioVar]: `${splitRatio[1]}%`,\n });\n\n return (\n <div\n ref={ref}\n className={cx(rowRoot, className)}\n style={{\n ...style,\n ...inlineVars,\n display: visible ? 'flex' : 'none',\n flexDirection: fullWidth ? 'column' : 'row',\n alignItems: fullWidth ? 'stretch' : 'center',\n minHeight: `${rowMinHeight}px`,\n paddingTop: `${densityConfig.paddingBlock}px`,\n paddingBottom: `${densityConfig.paddingBlock}px`,\n opacity: disabled ? 0.5 : 1,\n pointerEvents: disabled ? 'none' : 'auto',\n }}\n data-testid={testId}\n {...rest}\n >\n {fullWidth ? (\n <>\n <div\n className={fullWidthLabel}\n onContextMenu={onLabelContextMenu}\n style={{\n fontSize: sizeConfig.fontSize,\n fontWeight: modified\n ? 'var(--etui-font-weight-medium)'\n : 'var(--etui-font-weight-normal)',\n }}\n >\n {labelElement}\n </div>\n <div className={fullWidthControl}>{children}</div>\n </>\n ) : (\n <>\n <div\n className={rowLabel}\n onContextMenu={onLabelContextMenu}\n style={{\n fontSize: sizeConfig.fontSize,\n fontWeight: modified\n ? 'var(--etui-font-weight-medium)'\n : 'var(--etui-font-weight-normal)',\n }}\n >\n {labelElement}\n </div>\n <div className={rowControl}>{children}</div>\n </>\n )}\n\n {action}\n\n {onReset && (\n <span\n className={cx(RESET_BUTTON_CLASS, resetButton)}\n role=\"button\"\n tabIndex={0}\n aria-label=\"Reset to default\"\n onClick={handleResetClick}\n onKeyDown={(e: React.KeyboardEvent) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n onReset();\n }\n }}\n data-testid=\"reset-button\"\n >\n <UndoIcon size={sizeConfig.resetIconSize} decorative />\n </span>\n )}\n </div>\n );\n};\n\nPropertyRow.displayName = 'PropertyRow';\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsCA;AACE;AACE;AACA;AACA;AACD;AACD;AACE;AACA;AACA;AACD;AACD;AACE;AACA;AACA;AACD;;AAYH;;;;;AAMA;AAEO;AAoBL;;AAEA;;;AAIA;;;AAIE;;AAYF;AAEA;;;;AAiBC;AAED;AAKM;AACA;;;;;AAKA;AACA;;;;;AAcM;AACE;AACA;;;AAcF;AACE;AACA;;AAmBJ;;AAEE;;AAEJ;AAQV;AAEA;;"}
|
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
import './../../../assets/src/components/editor/PropertyInspector/PropertySection.css.ts.vanilla-
|
|
1
|
+
import './../../../assets/src/components/editor/PropertyInspector/PropertySection.css.ts.vanilla-6MWWAqyd.css';
|
|
2
2
|
import { createRuntimeFn } from '@vanilla-extract/recipes/createRuntimeFn';
|
|
3
3
|
|
|
4
|
-
var
|
|
5
|
-
var
|
|
6
|
-
var
|
|
7
|
-
var
|
|
8
|
-
var
|
|
9
|
-
var iconArea = '
|
|
10
|
-
var
|
|
4
|
+
var chevron = 'PropertySection_chevron__1xiu0w6f';
|
|
5
|
+
var chevronExpanded = 'PropertySection_chevronExpanded__1xiu0w6g';
|
|
6
|
+
var contentInner = 'PropertySection_contentInner__1xiu0w6n';
|
|
7
|
+
var contentWrapper = createRuntimeFn({defaultClassName:'PropertySection_contentWrapper__1xiu0w6k',variantClassNames:{expanded:{true:'PropertySection_contentWrapper_expanded_true__1xiu0w6l',false:'PropertySection_contentWrapper_expanded_false__1xiu0w6m'}},defaultVariants:{expanded:false},compoundVariants:[]});
|
|
8
|
+
var headerControls = 'PropertySection_headerControls__1xiu0w6j';
|
|
9
|
+
var iconArea = 'PropertySection_iconArea__1xiu0w6h';
|
|
10
|
+
var sectionHeader = createRuntimeFn({defaultClassName:'PropertySection_sectionHeader__1xiu0w61',variantClassNames:{size:{sm:'PropertySection_sectionHeader_size_sm__1xiu0w62',md:'PropertySection_sectionHeader_size_md__1xiu0w63',lg:'PropertySection_sectionHeader_size_lg__1xiu0w64'},disabled:{true:'PropertySection_sectionHeader_disabled_true__1xiu0w65',false:'PropertySection_sectionHeader_disabled_false__1xiu0w66'},expanded:{true:'PropertySection_sectionHeader_expanded_true__1xiu0w67',false:'PropertySection_sectionHeader_expanded_false__1xiu0w68'}},defaultVariants:{size:'md',disabled:false,expanded:false},compoundVariants:[]});
|
|
11
|
+
var sectionLabel = 'PropertySection_sectionLabel__1xiu0w6i';
|
|
11
12
|
var sectionRoot = 'PropertySection_sectionRoot__1xiu0w60';
|
|
12
|
-
var sectionTrigger = createRuntimeFn({defaultClassName:'
|
|
13
|
+
var sectionTrigger = createRuntimeFn({defaultClassName:'PropertySection_sectionTrigger__1xiu0w69',variantClassNames:{size:{sm:'PropertySection_sectionTrigger_size_sm__1xiu0w6a',md:'PropertySection_sectionTrigger_size_md__1xiu0w6b',lg:'PropertySection_sectionTrigger_size_lg__1xiu0w6c'},disabled:{true:'PropertySection_sectionTrigger_disabled_true__1xiu0w6d',false:'PropertySection_sectionTrigger_disabled_false__1xiu0w6e'}},defaultVariants:{size:'md',disabled:false},compoundVariants:[]});
|
|
13
14
|
|
|
14
|
-
export {
|
|
15
|
+
export { chevron, chevronExpanded, contentInner, contentWrapper, headerControls, iconArea, sectionHeader, sectionLabel, sectionRoot, sectionTrigger };
|
|
15
16
|
//# sourceMappingURL=PropertySection.css.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PropertySection.css.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"PropertySection.css.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;"}
|
|
@@ -88,9 +88,16 @@ import '../../Icons/GitBranchIcon.js';
|
|
|
88
88
|
import '../../Icons/BugIcon.js';
|
|
89
89
|
import '../../Icons/MinusIcon.js';
|
|
90
90
|
import '../../Icons/PinIcon.js';
|
|
91
|
+
import '../../Icons/OrbitIcon.js';
|
|
92
|
+
import '../../Icons/RotateIcon.js';
|
|
93
|
+
import '../../Icons/SpinIcon.js';
|
|
94
|
+
import '../../Icons/CameraIcon.js';
|
|
95
|
+
import '../../Icons/CubeIcon.js';
|
|
96
|
+
import '../../Icons/ZoomFitIcon.js';
|
|
97
|
+
import { Switch } from '../../primitives/Switch/Switch.js';
|
|
91
98
|
import { cx } from '../../../utils/cx.js';
|
|
92
99
|
import { usePropertyPanelContext } from './PropertyPanel.js';
|
|
93
|
-
import { chevronExpanded, chevron, iconArea, sectionLabel,
|
|
100
|
+
import { chevronExpanded, chevron, iconArea, sectionLabel, sectionTrigger, headerControls, sectionHeader, contentInner, contentWrapper, sectionRoot } from './PropertySection.css.js';
|
|
94
101
|
|
|
95
102
|
const TRIGGER_SIZE_MAP = {
|
|
96
103
|
sm: { chevronSize: 10 },
|
|
@@ -98,7 +105,7 @@ const TRIGGER_SIZE_MAP = {
|
|
|
98
105
|
lg: { chevronSize: 14 },
|
|
99
106
|
};
|
|
100
107
|
// --- Component ---
|
|
101
|
-
const PropertySection = ({ title, icon, actions, expanded: expandedProp, defaultExpanded = true, onExpandedChange, keepMounted = false, disabled = false, size: sizeProp, indicator, onContextMenu, children, className, style, testId, ref, ...rest }) => {
|
|
108
|
+
const PropertySection = ({ title, icon, actions, expanded: expandedProp, defaultExpanded = true, onExpandedChange, keepMounted = false, disabled = false, checkable = false, checked: checkedProp, defaultChecked = true, onCheckedChange, checkLabel, size: sizeProp, indicator, onContextMenu, children, className, style, testId, ref, ...rest }) => {
|
|
102
109
|
const autoId = useId();
|
|
103
110
|
const triggerId = `property-section-${autoId}-trigger`;
|
|
104
111
|
const contentId = `property-section-${autoId}-content`;
|
|
@@ -108,6 +115,11 @@ const PropertySection = ({ title, icon, actions, expanded: expandedProp, default
|
|
|
108
115
|
const [internalExpanded, setInternalExpanded] = useState(defaultExpanded);
|
|
109
116
|
const isControlled = expandedProp !== undefined;
|
|
110
117
|
const resolvedExpanded = isControlled ? expandedProp : internalExpanded;
|
|
118
|
+
// Enable/disable toggle state (controlled or uncontrolled), mirroring the
|
|
119
|
+
// expanded-state bridge above. Independent of the collapse state.
|
|
120
|
+
const [internalChecked, setInternalChecked] = useState(defaultChecked);
|
|
121
|
+
const isCheckedControlled = checkedProp !== undefined;
|
|
122
|
+
const resolvedChecked = isCheckedControlled ? checkedProp : internalChecked;
|
|
111
123
|
const handleToggle = useCallback(() => {
|
|
112
124
|
if (disabled)
|
|
113
125
|
return;
|
|
@@ -117,30 +129,26 @@ const PropertySection = ({ title, icon, actions, expanded: expandedProp, default
|
|
|
117
129
|
}
|
|
118
130
|
onExpandedChange?.(nextExpanded);
|
|
119
131
|
}, [disabled, resolvedExpanded, isControlled, onExpandedChange]);
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
// Prevent keyboard activation on nested action controls from bubbling up
|
|
124
|
-
// and toggling the section trigger.
|
|
125
|
-
const handleActionsKeyDown = useCallback((e) => {
|
|
126
|
-
if (e.key === 'Enter' || e.key === ' ') {
|
|
127
|
-
e.stopPropagation();
|
|
132
|
+
const handleCheckedChange = useCallback((next) => {
|
|
133
|
+
if (!isCheckedControlled) {
|
|
134
|
+
setInternalChecked(next);
|
|
128
135
|
}
|
|
129
|
-
|
|
136
|
+
onCheckedChange?.(next);
|
|
137
|
+
}, [isCheckedControlled, onCheckedChange]);
|
|
138
|
+
// When the section is checkable and switched off, the body is dimmed and made
|
|
139
|
+
// non-interactive but stays mounted (so its state is preserved).
|
|
140
|
+
const isBodyDisabled = checkable && !resolvedChecked;
|
|
130
141
|
const showIndicator = indicator !== null;
|
|
131
|
-
return (jsxs("div", { ref: ref, className: cx(sectionRoot, className), style: style, "data-testid": testId, ...rest, children: [jsxs("
|
|
142
|
+
return (jsxs("div", { ref: ref, className: cx(sectionRoot, className), style: style, "data-testid": testId, ...rest, children: [jsxs("div", { className: sectionHeader({
|
|
132
143
|
size,
|
|
133
144
|
disabled,
|
|
134
145
|
expanded: resolvedExpanded,
|
|
135
|
-
}), children: [showIndicator && (jsx("span", { className: cx(chevron, resolvedExpanded && chevronExpanded), style: {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
// interactive, so it intentionally has no role.
|
|
142
|
-
// eslint-disable-next-line jsx-a11y/no-static-element-interactions
|
|
143
|
-
jsx("span", { className: actionsArea, onClick: handleActionsClick, onKeyDown: handleActionsKeyDown, children: actions }))] }), (resolvedExpanded || keepMounted) && (jsx("div", { className: contentWrapper({ expanded: resolvedExpanded }), role: "region", id: contentId, "aria-labelledby": triggerId, hidden: !resolvedExpanded || undefined, children: jsx("div", { className: contentInner, children: children }) }))] }));
|
|
146
|
+
}), children: [jsxs("button", { type: "button", id: triggerId, "aria-expanded": resolvedExpanded, "aria-controls": contentId, "aria-disabled": disabled || undefined, disabled: disabled, onClick: handleToggle, onContextMenu: onContextMenu, className: sectionTrigger({ size, disabled }), children: [showIndicator && (jsx("span", { className: cx(chevron, resolvedExpanded && chevronExpanded), style: {
|
|
147
|
+
width: `${sizeConfig.chevronSize}px`,
|
|
148
|
+
height: `${sizeConfig.chevronSize}px`,
|
|
149
|
+
}, children: indicator ?? (jsx(ChevronRightIcon, { size: sizeConfig.chevronSize, decorative: true })) })), icon && jsx("span", { className: iconArea, children: icon }), jsx("span", { className: sectionLabel, children: title })] }), (checkable || actions) && (jsxs("span", { className: headerControls, children: [checkable && (jsx(Switch, { size: size, checked: resolvedChecked, onChange: handleCheckedChange, disabled: disabled, "aria-label": checkLabel ?? title, testId: "section-toggle" })), actions] }))] }), (resolvedExpanded || keepMounted) && (jsx("div", { className: contentWrapper({ expanded: resolvedExpanded }), role: "region", id: contentId, "aria-labelledby": triggerId, hidden: !resolvedExpanded || undefined, children: jsx("div", { className: contentInner, style: isBodyDisabled
|
|
150
|
+
? { opacity: 0.5, pointerEvents: 'none' }
|
|
151
|
+
: undefined, "aria-disabled": isBodyDisabled || undefined, children: children }) }))] }));
|
|
144
152
|
};
|
|
145
153
|
PropertySection.displayName = 'PropertySection';
|
|
146
154
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PropertySection.js","sources":["
|
|
1
|
+
{"version":3,"file":"PropertySection.js","sources":["src/components/editor/PropertyInspector/PropertySection.tsx"],"sourcesContent":["'use client';\n\nimport React, { useCallback, useId, useState } from 'react';\n\nimport { ChevronRightIcon } from '@/components/Icons';\nimport { Switch } from '@/components/primitives/Switch';\nimport { cx } from '@/utils/cx';\n\nimport { usePropertyPanelContext } from './PropertyPanel';\nimport {\n sectionRoot,\n sectionHeader,\n sectionTrigger,\n chevron,\n chevronExpanded,\n iconArea,\n sectionLabel,\n headerControls,\n contentWrapper,\n contentInner,\n} from './PropertySection.css';\n\nimport type {\n PropertyInspectorSize,\n PropertySectionProps,\n} from './PropertyInspector.types';\n\n// --- Size maps ---\n\ninterface TriggerSizeConfig {\n chevronSize: number;\n}\n\nconst TRIGGER_SIZE_MAP: Record<PropertyInspectorSize, TriggerSizeConfig> = {\n sm: { chevronSize: 10 },\n md: { chevronSize: 12 },\n lg: { chevronSize: 14 },\n};\n\n// --- Component ---\n\nexport const PropertySection: React.FC<PropertySectionProps> = ({\n title,\n icon,\n actions,\n expanded: expandedProp,\n defaultExpanded = true,\n onExpandedChange,\n keepMounted = false,\n disabled = false,\n checkable = false,\n checked: checkedProp,\n defaultChecked = true,\n onCheckedChange,\n checkLabel,\n size: sizeProp,\n indicator,\n onContextMenu,\n children,\n\n className,\n style,\n testId,\n ref,\n ...rest\n}) => {\n const autoId = useId();\n const triggerId = `property-section-${autoId}-trigger`;\n const contentId = `property-section-${autoId}-content`;\n\n const panelCtx = usePropertyPanelContext();\n const size = sizeProp ?? panelCtx?.size ?? 'md';\n const sizeConfig = TRIGGER_SIZE_MAP[size];\n\n const [internalExpanded, setInternalExpanded] = useState(defaultExpanded);\n const isControlled = expandedProp !== undefined;\n const resolvedExpanded = isControlled ? expandedProp : internalExpanded;\n\n // Enable/disable toggle state (controlled or uncontrolled), mirroring the\n // expanded-state bridge above. Independent of the collapse state.\n const [internalChecked, setInternalChecked] = useState(defaultChecked);\n const isCheckedControlled = checkedProp !== undefined;\n const resolvedChecked = isCheckedControlled ? checkedProp : internalChecked;\n\n const handleToggle = useCallback(() => {\n if (disabled) return;\n\n const nextExpanded = !resolvedExpanded;\n\n if (!isControlled) {\n setInternalExpanded(nextExpanded);\n }\n\n onExpandedChange?.(nextExpanded);\n }, [disabled, resolvedExpanded, isControlled, onExpandedChange]);\n\n const handleCheckedChange = useCallback(\n (next: boolean) => {\n if (!isCheckedControlled) {\n setInternalChecked(next);\n }\n onCheckedChange?.(next);\n },\n [isCheckedControlled, onCheckedChange]\n );\n\n // When the section is checkable and switched off, the body is dimmed and made\n // non-interactive but stays mounted (so its state is preserved).\n const isBodyDisabled = checkable && !resolvedChecked;\n\n const showIndicator = indicator !== null;\n\n return (\n <div\n ref={ref}\n className={cx(sectionRoot, className)}\n style={style}\n data-testid={testId}\n {...rest}\n >\n <div\n className={sectionHeader({\n size,\n disabled,\n expanded: resolvedExpanded,\n })}\n >\n <button\n type=\"button\"\n id={triggerId}\n aria-expanded={resolvedExpanded}\n aria-controls={contentId}\n aria-disabled={disabled || undefined}\n disabled={disabled}\n onClick={handleToggle}\n onContextMenu={onContextMenu}\n className={sectionTrigger({ size, disabled })}\n >\n {showIndicator && (\n <span\n className={cx(chevron, resolvedExpanded && chevronExpanded)}\n style={{\n width: `${sizeConfig.chevronSize}px`,\n height: `${sizeConfig.chevronSize}px`,\n }}\n >\n {indicator ?? (\n <ChevronRightIcon size={sizeConfig.chevronSize} decorative />\n )}\n </span>\n )}\n {icon && <span className={iconArea}>{icon}</span>}\n <span className={sectionLabel}>{title}</span>\n </button>\n {(checkable || actions) && (\n <span className={headerControls}>\n {checkable && (\n <Switch\n size={size}\n checked={resolvedChecked}\n onChange={handleCheckedChange}\n disabled={disabled}\n aria-label={checkLabel ?? title}\n testId=\"section-toggle\"\n />\n )}\n {actions}\n </span>\n )}\n </div>\n\n {(resolvedExpanded || keepMounted) && (\n <div\n className={contentWrapper({ expanded: resolvedExpanded })}\n role=\"region\"\n id={contentId}\n aria-labelledby={triggerId}\n hidden={!resolvedExpanded || undefined}\n >\n <div\n className={contentInner}\n style={\n isBodyDisabled\n ? { opacity: 0.5, pointerEvents: 'none' }\n : undefined\n }\n aria-disabled={isBodyDisabled || undefined}\n >\n {children}\n </div>\n </div>\n )}\n </div>\n );\n};\n\nPropertySection.displayName = 'PropertySection';\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiCA;AACE;AACA;AACA;;AAGF;AAEO;AAyBL;AACA;AACA;AAEA;;AAEA;;AAGA;;;;;AAMA;;AAGA;AACE;;AAEA;;;;AAMA;;AAGF;;;;AAKI;AACF;;;AAMF;AAEA;;;;AAcQ;;AAkBM;AACA;AACD;;AAwCC;AAUhB;AAEA;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"usePropertyUndo.js","sources":["
|
|
1
|
+
{"version":3,"file":"usePropertyUndo.js","sources":["src/components/editor/PropertyInspector/usePropertyUndo.ts"],"sourcesContent":["'use client';\n\nimport { useCallback, useReducer } from 'react';\n\nimport type {\n PropertyUndoEntry,\n UsePropertyUndoOptions,\n UsePropertyUndoReturn,\n} from './PropertyInspector.types';\n\n// --- Reducer ---\n\ninterface UndoState<T> {\n undoStack: PropertyUndoEntry<T>[];\n redoStack: PropertyUndoEntry<T>[];\n}\n\ntype UndoAction<T> =\n | { type: 'RECORD'; entry: PropertyUndoEntry<T>; maxHistory: number }\n | { type: 'UNDO' }\n | { type: 'REDO' }\n | { type: 'CLEAR' };\n\nfunction undoReducer<T>(\n state: UndoState<T>,\n action: UndoAction<T>\n): UndoState<T> {\n switch (action.type) {\n case 'RECORD': {\n const newStack = [...state.undoStack, action.entry];\n if (newStack.length > action.maxHistory) {\n newStack.shift();\n }\n return {\n undoStack: newStack,\n redoStack: [],\n };\n }\n case 'UNDO': {\n if (state.undoStack.length === 0) return state;\n const entry = state.undoStack[\n state.undoStack.length - 1\n ] as PropertyUndoEntry<T>;\n return {\n undoStack: state.undoStack.slice(0, -1),\n redoStack: [...state.redoStack, entry],\n };\n }\n case 'REDO': {\n if (state.redoStack.length === 0) return state;\n const entry = state.redoStack[\n state.redoStack.length - 1\n ] as PropertyUndoEntry<T>;\n return {\n undoStack: [...state.undoStack, entry],\n redoStack: state.redoStack.slice(0, -1),\n };\n }\n case 'CLEAR':\n return { undoStack: [], redoStack: [] };\n default:\n return state;\n }\n}\n\n// --- Hook ---\n\n/**\n * Lightweight undo/redo stack for property changes.\n * Records entries with property identifiers and values for precise undo control.\n */\nexport function usePropertyUndo<T = unknown>(\n options?: UsePropertyUndoOptions\n): UsePropertyUndoReturn<T> {\n const maxHistory = options?.maxHistory ?? 50;\n\n const [state, dispatch] = useReducer(undoReducer<T>, {\n undoStack: [],\n redoStack: [],\n });\n\n const record = useCallback(\n (entry: Omit<PropertyUndoEntry<T>, 'timestamp'>) => {\n dispatch({\n type: 'RECORD',\n entry: { ...entry, timestamp: Date.now() },\n maxHistory,\n });\n },\n [maxHistory]\n );\n\n const undo = useCallback((): PropertyUndoEntry<T> | null => {\n if (state.undoStack.length === 0) return null;\n const entry = state.undoStack[\n state.undoStack.length - 1\n ] as PropertyUndoEntry<T>;\n dispatch({ type: 'UNDO' });\n return entry;\n }, [state.undoStack]);\n\n const redo = useCallback((): PropertyUndoEntry<T> | null => {\n if (state.redoStack.length === 0) return null;\n const entry = state.redoStack[\n state.redoStack.length - 1\n ] as PropertyUndoEntry<T>;\n dispatch({ type: 'REDO' });\n return entry;\n }, [state.redoStack]);\n\n const clear = useCallback(() => {\n dispatch({ type: 'CLEAR' });\n }, []);\n\n return {\n record,\n undo,\n redo,\n canUndo: state.undoStack.length > 0,\n canRedo: state.redoStack.length > 0,\n history: state.undoStack,\n clear,\n };\n}\n"],"names":[],"mappings":";;;AAuBA;AAIE;;AAEI;;;;;AAKE;AACA;;;;AAIF;AAAkC;AAClC;;;;;;;AASA;AAAkC;AAClC;;;;;;AAQF;;AAEA;AACE;;AAEN;AAEA;AAEA;;;AAGG;AACG;AAGJ;;AAGE;AACA;AACD;AAED;AAEI;AACE;;;AAGD;AACH;AAIF;AACE;AAAkC;AAClC;AAGA;AACA;AACF;AAEA;AACE;AAAkC;AAClC;AAGA;AACA;AACF;AAEA;AACE;;;;;;AAOA;AACA;;;;AAIJ;;"}
|
|
@@ -7,7 +7,7 @@ import { useLatest } from '../../../hooks/useLatest/useLatest.js';
|
|
|
7
7
|
import { useResizeObserver } from '../../../hooks/useResizeObserver/useResizeObserver.js';
|
|
8
8
|
import '../../../hooks/useBreakpoint/useBreakpoint.js';
|
|
9
9
|
import './../../../assets/src/theme/lightTheme.css.ts.vanilla-Ct6f9Iym.css';
|
|
10
|
-
import '
|
|
10
|
+
import '@vanilla-extract/css';
|
|
11
11
|
import './../../../assets/src/theme/darkTheme.css.ts.vanilla-BHZ3ecRn.css';
|
|
12
12
|
import './../../../assets/src/theme/globalScrollbars.css.ts.vanilla-BAJwnUEJ.css';
|
|
13
13
|
import '../../../hooks/useNavigationHistory/useNavigationHistory.js';
|