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":"TreeView.js","sources":["../../../../../../src/components/controls/TreeView/TreeView.tsx"],"sourcesContent":[null],"names":[],"mappings":";;;;;;;;;AAiBA;;;;;;;;;;;;;;;;;AAiBG;AACI;AAgCL;AAEA;;;;;;;;;;AAmBC;;;;;AAOG;AAEA;;AAEI;;AACK;AACL;;;AAEA;;;;;AAMJ;AACE;;AAGF;AACF;;;;;;;AAQC;;AAIH;;AAGM;;AAEF;AAGA;;;AAGI;AACA;AACE;;AAEE;;;;;;;;;;AAUJ;AACA;AACE;;AAEE;;;;;;;;;;;AAWF;AAEA;AACE;;AACK;;AAEL;AACA;AACE;;AAEE;;;;;;;;;;AAUN;AAEA;AACE;;AACK;;AAEL;;;;;;;AAOJ;;AAEE;;;;;;;;AAQA;;;;;;AAMF;AACE;;;;;;;AAOA;AACE;;AACK;AACL;;;;;;;;AAQF;;;;;AAKR;;;;;;;;;AAUC;;AAIH;AACE;AACE;;;;;AAOJ;;;AAIM;;;AAKN;AACE;;AAeF;AAeM;AACA;;AAgCR;AAEA;;"}
|
|
1
|
+
{"version":3,"file":"TreeView.js","sources":["src/components/controls/TreeView/TreeView.tsx"],"sourcesContent":["'use client';\n\nimport { assignInlineVars } from '@vanilla-extract/dynamic';\nimport React, { useCallback, useRef } from 'react';\n\nimport { cx } from '@/utils/cx';\n\nimport { TreeNodeComponent } from './TreeNode';\nimport {\n treeContainerStyle,\n emptyStateStyle,\n maxHeightVar,\n} from './TreeView.css';\nimport { useTreeState } from './useTreeState';\n\nimport type { TreeViewProps, TreeNodeData } from './TreeView.types';\n\n/**\n * A hierarchical collapsible tree for displaying and managing nested data.\n *\n * Supports multi-selection, keyboard navigation, inline renaming,\n * and custom node rendering. The most iconic editor panel — scene hierarchy,\n * file browser, layer stack.\n *\n * @example\n * ```tsx\n * const nodes = [\n * { id: '1', label: 'Root', children: [\n * { id: '1.1', label: 'Child A' },\n * { id: '1.2', label: 'Child B' },\n * ]},\n * ];\n * <TreeView nodes={nodes} onSelectionChange={setSelected} />\n * ```\n */\nexport const TreeView = ({\n nodes,\n expandedIds,\n defaultExpandedIds,\n selectedIds,\n defaultSelectedIds,\n selectionMode = 'single',\n renamable = false,\n size = 'md',\n indent = 16,\n showChevrons = true,\n showGuideLines = false,\n expandOnSelect = false,\n expandOnClick = false,\n maxHeight,\n renderNode,\n renderActions,\n emptyContent,\n dropTargetId,\n onExpandedChange,\n onSelectionChange,\n onNodeClick,\n onNodeDoubleClick,\n onNodeContextMenu,\n onNodeRename,\n onNodeDragOver,\n onNodeDragLeave,\n onNodeDrop,\n className,\n testId,\n ...rest\n}: TreeViewProps) => {\n const treeRef = useRef<HTMLDivElement>(null);\n\n const {\n expandedSet,\n selectedSet,\n focusedId,\n visibleNodes,\n toggleExpand,\n select,\n setFocused,\n selectAll,\n } = useTreeState({\n nodes,\n expandedIds,\n defaultExpandedIds,\n selectedIds,\n defaultSelectedIds,\n selectionMode,\n expandOnSelect,\n onExpandedChange,\n onSelectionChange,\n });\n\n // Handle node click with selection logic\n const handleNodeClick = useCallback(\n (node: TreeNodeData, event: React.MouseEvent) => {\n if (node.disabled) return;\n\n setFocused(node.id);\n\n if (selectionMode !== 'none') {\n if (event.ctrlKey || event.metaKey) {\n select(node.id, 'toggle');\n } else if (event.shiftKey) {\n select(node.id, 'range');\n } else {\n select(node.id, 'replace');\n }\n }\n\n // Toggle a parent node's expansion when its whole row is clicked. The\n // chevron stops propagation, so it never double-toggles.\n if (expandOnClick && node.children && node.children.length > 0) {\n toggleExpand(node.id);\n }\n\n onNodeClick?.(node, event);\n },\n [\n selectionMode,\n select,\n setFocused,\n onNodeClick,\n expandOnClick,\n toggleExpand,\n ]\n );\n\n // Keyboard navigation\n const handleKeyDown = useCallback(\n (event: React.KeyboardEvent) => {\n const currentIdx = focusedId\n ? visibleNodes.findIndex(vn => vn.node.id === focusedId)\n : -1;\n const currentNode =\n currentIdx >= 0 ? visibleNodes[currentIdx] : undefined;\n\n switch (event.key) {\n case 'ArrowDown': {\n event.preventDefault();\n const nextIdx = currentIdx + 1;\n if (nextIdx < visibleNodes.length) {\n const nextNode = visibleNodes[nextIdx];\n if (nextNode) {\n setFocused(nextNode.node.id);\n if (event.shiftKey && selectionMode === 'multiple') {\n select(nextNode.node.id, 'range');\n }\n }\n }\n break;\n }\n case 'ArrowUp': {\n event.preventDefault();\n const prevIdx = currentIdx - 1;\n if (prevIdx >= 0) {\n const prevNode = visibleNodes[prevIdx];\n if (prevNode) {\n setFocused(prevNode.node.id);\n if (event.shiftKey && selectionMode === 'multiple') {\n select(prevNode.node.id, 'range');\n }\n }\n }\n break;\n }\n case 'ArrowRight': {\n event.preventDefault();\n if (currentNode) {\n const hasChildren =\n currentNode.node.children && currentNode.node.children.length > 0;\n if (hasChildren && !expandedSet.has(currentNode.node.id)) {\n toggleExpand(currentNode.node.id);\n } else if (hasChildren && expandedSet.has(currentNode.node.id)) {\n // Move to first child\n const nextIdx = currentIdx + 1;\n if (nextIdx < visibleNodes.length) {\n const nextNode = visibleNodes[nextIdx];\n if (nextNode) {\n setFocused(nextNode.node.id);\n }\n }\n }\n }\n break;\n }\n case 'ArrowLeft': {\n event.preventDefault();\n if (currentNode) {\n const hasChildren =\n currentNode.node.children && currentNode.node.children.length > 0;\n if (hasChildren && expandedSet.has(currentNode.node.id)) {\n toggleExpand(currentNode.node.id);\n } else if (currentNode.parentId) {\n // Move to parent\n setFocused(currentNode.parentId);\n }\n }\n break;\n }\n case 'Home': {\n event.preventDefault();\n const firstNode = visibleNodes[0];\n if (firstNode) {\n setFocused(firstNode.node.id);\n }\n break;\n }\n case 'End': {\n event.preventDefault();\n const lastNode = visibleNodes[visibleNodes.length - 1];\n if (lastNode) {\n setFocused(lastNode.node.id);\n }\n break;\n }\n case 'Enter': {\n event.preventDefault();\n if (focusedId && selectionMode !== 'none') {\n select(focusedId, 'replace');\n }\n break;\n }\n case ' ': {\n event.preventDefault();\n if (focusedId) {\n if (selectionMode === 'multiple') {\n select(focusedId, 'toggle');\n } else if (selectionMode === 'single') {\n select(focusedId, 'replace');\n }\n }\n break;\n }\n case 'a': {\n if (event.ctrlKey || event.metaKey) {\n event.preventDefault();\n selectAll();\n }\n break;\n }\n }\n },\n [\n focusedId,\n visibleNodes,\n expandedSet,\n selectionMode,\n setFocused,\n select,\n toggleExpand,\n selectAll,\n ]\n );\n\n // Scroll focused node into view\n React.useEffect(() => {\n if (focusedId && treeRef.current) {\n const el = treeRef.current.querySelector(\n `#treenode-${CSS.escape(focusedId)}`\n );\n if (el && typeof el.scrollIntoView === 'function') {\n el.scrollIntoView({ block: 'nearest' });\n }\n }\n }, [focusedId]);\n\n const containerInlineVars = maxHeight\n ? assignInlineVars({\n [maxHeightVar]:\n typeof maxHeight === 'number' ? `${maxHeight}px` : maxHeight,\n })\n : undefined;\n\n if (nodes.length === 0) {\n return (\n <div\n ref={treeRef}\n className={cx(treeContainerStyle, className)}\n data-testid={testId}\n role=\"tree\"\n aria-label=\"Empty tree\"\n style={containerInlineVars}\n {...rest}\n >\n <div className={emptyStateStyle}>{emptyContent ?? 'No items'}</div>\n </div>\n );\n }\n\n return (\n <div\n ref={treeRef}\n className={cx(treeContainerStyle, className)}\n data-testid={testId}\n role=\"tree\"\n aria-label=\"Tree\"\n aria-multiselectable={selectionMode === 'multiple' ? true : undefined}\n aria-activedescendant={focusedId ? `treenode-${focusedId}` : undefined}\n tabIndex={0}\n onKeyDown={handleKeyDown}\n style={containerInlineVars}\n {...rest}\n >\n {visibleNodes.map((flatNode, idx) => {\n const { node, depth } = flatNode;\n return (\n <TreeNodeComponent\n key={node.id}\n node={node}\n depth={depth}\n size={size}\n indent={indent}\n isSelected={selectedSet.has(node.id)}\n isExpanded={expandedSet.has(node.id)}\n isFocused={focusedId === node.id}\n isFirst={idx === 0}\n isLast={idx === visibleNodes.length - 1}\n showChevrons={showChevrons}\n showGuideLines={showGuideLines}\n renamable={renamable}\n nodeId={node.id}\n isDropTarget={dropTargetId === node.id}\n renderNode={renderNode}\n renderActions={renderActions}\n onToggleExpand={toggleExpand}\n onClick={handleNodeClick}\n onDoubleClick={onNodeDoubleClick}\n onContextMenu={onNodeContextMenu}\n onRename={onNodeRename}\n onDragOver={onNodeDragOver}\n onDragLeave={onNodeDragLeave}\n onDrop={onNodeDrop}\n />\n );\n })}\n </div>\n );\n};\n\nTreeView.displayName = 'TreeView';\n"],"names":[],"mappings":";;;;;;;;;AAiBA;;;;;;;;;;;;;;;;;AAiBG;AACI;AAgCL;AAEA;;;;;;;;;;AAmBC;;;;;AAOG;AAEA;;AAEI;;AACK;AACL;;;AAEA;;;;;AAMJ;AACE;;AAGF;AACF;;;;;;;AAQC;;AAIH;;AAGM;;AAEF;AAGA;;;AAGI;AACA;AACE;;AAEE;;;;;;;;;;AAUJ;AACA;AACE;;AAEE;;;;;;;;;;;AAWF;AAEA;AACE;;AACK;;AAEL;AACA;AACE;;AAEE;;;;;;;;;;AAUN;AAEA;AACE;;AACK;;AAEL;;;;;;;AAOJ;;AAEE;;;;;;;;AAQA;;;;;;AAMF;AACE;;;;;;;AAOA;AACE;;AACK;AACL;;;;;;;;AAQF;;;;;AAKR;;;;;;;;;AAUC;;AAIH;AACE;AACE;;;;;AAOJ;;;AAIM;;;AAKN;AACE;;AAeF;AAeM;AACA;;AAgCR;AAEA;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useTreeState.js","sources":["
|
|
1
|
+
{"version":3,"file":"useTreeState.js","sources":["src/components/controls/TreeView/useTreeState.ts"],"sourcesContent":["'use client';\n\nimport { useState, useMemo, useCallback } from 'react';\n\nimport type { TreeNodeData, TreeSelectionMode } from './TreeView.types';\n\ninterface FlatNode {\n node: TreeNodeData;\n depth: number;\n parentId: string | null;\n}\n\ninterface UseTreeStateOptions {\n nodes: TreeNodeData[];\n expandedIds?: string[];\n defaultExpandedIds?: string[];\n selectedIds?: string[];\n defaultSelectedIds?: string[];\n selectionMode: TreeSelectionMode;\n expandOnSelect?: boolean;\n onExpandedChange?: (expandedIds: string[]) => void;\n onSelectionChange?: (selectedIds: string[]) => void;\n}\n\nexport interface UseTreeStateReturn {\n expandedSet: Set<string>;\n selectedSet: Set<string>;\n focusedId: string | null;\n visibleNodes: FlatNode[];\n toggleExpand: (id: string) => void;\n select: (id: string, mode: 'replace' | 'toggle' | 'range') => void;\n setFocused: (id: string | null) => void;\n selectAll: () => void;\n}\n\n/**\n * Flatten tree into a list of visible nodes, respecting expansion state.\n */\nfunction flattenTree(\n nodes: TreeNodeData[],\n expandedSet: Set<string>,\n depth: number = 0,\n parentId: string | null = null\n): FlatNode[] {\n const result: FlatNode[] = [];\n for (const node of nodes) {\n result.push({ node, depth, parentId });\n if (node.children && node.children.length > 0 && expandedSet.has(node.id)) {\n result.push(\n ...flattenTree(node.children, expandedSet, depth + 1, node.id)\n );\n }\n }\n return result;\n}\n\n/**\n * Collect all node IDs from a tree for \"expand all\" or \"select all\".\n */\nfunction collectAllIds(nodes: TreeNodeData[]): string[] {\n const ids: string[] = [];\n for (const node of nodes) {\n ids.push(node.id);\n if (node.children) {\n ids.push(...collectAllIds(node.children));\n }\n }\n return ids;\n}\n\nexport function useTreeState({\n nodes,\n expandedIds: controlledExpanded,\n defaultExpandedIds,\n selectedIds: controlledSelected,\n defaultSelectedIds,\n selectionMode,\n expandOnSelect = false,\n onExpandedChange,\n onSelectionChange,\n}: UseTreeStateOptions): UseTreeStateReturn {\n // Internal expansion state\n const [internalExpanded, setInternalExpanded] = useState<Set<string>>(\n () => new Set(defaultExpandedIds ?? [])\n );\n const expandedSet = useMemo(\n () => (controlledExpanded ? new Set(controlledExpanded) : internalExpanded),\n [controlledExpanded, internalExpanded]\n );\n\n // Internal selection state\n const [internalSelected, setInternalSelected] = useState<Set<string>>(\n () => new Set(defaultSelectedIds ?? [])\n );\n const selectedSet = useMemo(\n () => (controlledSelected ? new Set(controlledSelected) : internalSelected),\n [controlledSelected, internalSelected]\n );\n\n // Focused node\n const [focusedId, setFocused] = useState<string | null>(null);\n\n // Flatten visible nodes\n const visibleNodes = useMemo(\n () => flattenTree(nodes, expandedSet),\n [nodes, expandedSet]\n );\n\n // Toggle expansion\n const toggleExpand = useCallback(\n (id: string) => {\n const next = new Set(expandedSet);\n if (next.has(id)) {\n next.delete(id);\n } else {\n next.add(id);\n }\n if (controlledExpanded === undefined) {\n setInternalExpanded(next);\n }\n onExpandedChange?.([...next]);\n },\n [expandedSet, controlledExpanded, onExpandedChange]\n );\n\n // Selection\n const select = useCallback(\n (id: string, mode: 'replace' | 'toggle' | 'range') => {\n if (selectionMode === 'none') return;\n\n let next: Set<string>;\n\n if (selectionMode === 'single') {\n next = new Set([id]);\n } else {\n // multiple mode\n if (mode === 'replace') {\n next = new Set([id]);\n } else if (mode === 'toggle') {\n next = new Set(selectedSet);\n if (next.has(id)) {\n next.delete(id);\n } else {\n next.add(id);\n }\n } else {\n // range — select from last selected to this id\n const visibleIds = visibleNodes.map(vn => vn.node.id);\n const lastSelected = [...selectedSet].pop();\n if (!lastSelected) {\n next = new Set([id]);\n } else {\n const startIdx = visibleIds.indexOf(lastSelected);\n const endIdx = visibleIds.indexOf(id);\n if (startIdx === -1 || endIdx === -1) {\n next = new Set([id]);\n } else {\n const from = Math.min(startIdx, endIdx);\n const to = Math.max(startIdx, endIdx);\n next = new Set(selectedSet);\n for (let i = from; i <= to; i++) {\n const nodeId = visibleIds[i];\n if (nodeId !== undefined) {\n next.add(nodeId);\n }\n }\n }\n }\n }\n }\n\n if (controlledSelected === undefined) {\n setInternalSelected(next);\n }\n onSelectionChange?.([...next]);\n\n // Expand on select\n if (expandOnSelect && !expandedSet.has(id)) {\n // Check if node has children\n const flat = flattenTree(nodes, new Set(collectAllIds(nodes)));\n const targetNode = flat.find(fn => fn.node.id === id);\n if (targetNode?.node.children && targetNode.node.children.length > 0) {\n toggleExpand(id);\n }\n }\n },\n [\n selectionMode,\n selectedSet,\n controlledSelected,\n onSelectionChange,\n expandOnSelect,\n expandedSet,\n nodes,\n visibleNodes,\n toggleExpand,\n ]\n );\n\n // Select all visible nodes\n const selectAll = useCallback(() => {\n if (selectionMode !== 'multiple') return;\n const allIds = new Set(visibleNodes.map(vn => vn.node.id));\n if (controlledSelected === undefined) {\n setInternalSelected(allIds);\n }\n onSelectionChange?.([...allIds]);\n }, [selectionMode, visibleNodes, controlledSelected, onSelectionChange]);\n\n return {\n expandedSet,\n selectedSet,\n focusedId,\n visibleNodes,\n toggleExpand,\n select,\n setFocused,\n selectAll,\n };\n}\n"],"names":[],"mappings":";;;AAmCA;;AAEG;AACH;;AAOE;;;;;;AAQA;AACF;AAEA;;AAEG;AACH;;AAEE;AACE;AACA;;;;AAIF;AACF;AAEM;;AAYJ;AAGA;;AAMA;AAGA;;;;;;AAeA;AAEI;AACA;AACE;;;AAEA;;AAEF;;;AAGA;;;;;;AAUA;AAEA;;;;;AAIE;;;AAEO;AACL;AACA;AACE;;;AAEA;;;;;AAIF;;;;;;;;;;;;;;AAYI;AACA;AACE;AACA;AACE;;;;;;;AAQZ;;;AAGA;;;;AAKE;AACA;AACA;;;;AAIJ;;;;;;;;;;AAWC;;AAIH;;;;AAGE;;;AAGA;;;;;;;;;;;;AAaJ;;"}
|
|
@@ -94,6 +94,12 @@ import '../../Icons/GitBranchIcon.js';
|
|
|
94
94
|
import '../../Icons/BugIcon.js';
|
|
95
95
|
import '../../Icons/MinusIcon.js';
|
|
96
96
|
import '../../Icons/PinIcon.js';
|
|
97
|
+
import '../../Icons/OrbitIcon.js';
|
|
98
|
+
import '../../Icons/RotateIcon.js';
|
|
99
|
+
import '../../Icons/SpinIcon.js';
|
|
100
|
+
import '../../Icons/CameraIcon.js';
|
|
101
|
+
import '../../Icons/CubeIcon.js';
|
|
102
|
+
import '../../Icons/ZoomFitIcon.js';
|
|
97
103
|
import { vars } from '../../../theme/contract.css.js';
|
|
98
104
|
import { cx } from '../../../utils/cx.js';
|
|
99
105
|
import { axisInputStyle, axisColorVar, axisLabelRecipe, linkButtonRecipe, gapVar, vectorRowRecipe, vectorContainerStyle } from './VectorInput.css.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VectorInput.js","sources":["
|
|
1
|
+
{"version":3,"file":"VectorInput.js","sources":["src/components/controls/VectorInput/VectorInput.tsx"],"sourcesContent":["'use client';\n\nimport { assignInlineVars } from '@vanilla-extract/dynamic';\nimport React, { useState, useCallback, useRef } from 'react';\n\nimport { NumberInput } from '@/components/controls/NumberInput';\nimport { FormHelperText } from '@/components/form/FormHelperText';\nimport { FormLabel } from '@/components/form/FormLabel';\nimport { LinkIcon, UnlinkIcon } from '@/components/Icons';\nimport { vars } from '@/theme/contract.css';\nimport { cx } from '@/utils/cx';\n\nimport {\n vectorContainerStyle,\n vectorRowRecipe,\n axisInputStyle,\n axisLabelRecipe,\n axisColorVar,\n linkButtonRecipe,\n gapVar,\n} from './VectorInput.css';\n\nimport type { VectorInputProps } from './VectorInput.types';\n\n// --- Label presets ---\n\nconst LABEL_PRESETS: Record<string, string[]> = {\n xyz: ['X', 'Y', 'Z', 'W'],\n rgba: ['R', 'G', 'B', 'A'],\n uvw: ['U', 'V', 'W'],\n};\n\n// --- Color presets ---\n\nconst SPATIAL_COLORS = ['#EF4444', '#22C55E', '#3B82F6', '#A855F7'];\nconst COLOR_COLORS = ['#EF4444', '#22C55E', '#3B82F6', '#FFFFFF'];\n\nfunction getAxisColor(\n colorPreset: string,\n axisColors: string[] | undefined,\n axisIndex: number\n): string | null {\n if (colorPreset === 'custom' && axisColors) {\n return axisColors[axisIndex] ?? null;\n }\n if (colorPreset === 'spatial') {\n return SPATIAL_COLORS[axisIndex] ?? null;\n }\n if (colorPreset === 'color') {\n return COLOR_COLORS[axisIndex] ?? null;\n }\n // 'none' — will be resolved via theme\n return null;\n}\n\nfunction getAxisLabels(\n labelPreset: string,\n axisLabels: string[] | undefined,\n dimension: number\n): string[] {\n if (labelPreset === 'custom' && axisLabels) {\n return axisLabels;\n }\n const preset = LABEL_PRESETS[labelPreset];\n if (preset) {\n return preset.slice(0, dimension);\n }\n return Array.from({ length: dimension }, (_, i) => String(i));\n}\n\n/**\n * A grouped numeric input for Vec2, Vec3, and Vec4 vectors.\n *\n * Used for position, rotation, scale, color channels, UV coordinates,\n * bounding box extents, and more. Composes NumberInput with per-axis labels,\n * optional color coding, and linked/unlinked proportional editing.\n *\n * @example\n * ```tsx\n * <VectorInput\n * label=\"Position\"\n * value={[0, 0, 0]}\n * onChange={(val) => setPosition(val)}\n * unit=\"px\"\n * />\n * ```\n */\nexport const VectorInput = ({\n value: controlledValue,\n defaultValue,\n dimension = 3,\n labelPreset = 'xyz',\n axisLabels: customAxisLabels,\n colorPreset = 'spatial',\n axisColors: customAxisColors,\n min,\n max,\n step,\n precisionStep,\n largeStep,\n precision,\n unit,\n size = 'md',\n label,\n helperText,\n error = false,\n errorMessage,\n disabled = false,\n showLink = false,\n defaultLinked = false,\n linked: controlledLinked,\n onLinkedChange,\n direction = 'row',\n gap = 2,\n onChange,\n onChangeComplete,\n className,\n testId,\n ...rest\n}: VectorInputProps) => {\n // Internal value state (uncontrolled mode)\n const [internalValue, setInternalValue] = useState<number[]>(\n () => defaultValue ?? Array.from({ length: dimension }, () => 0)\n );\n const currentValue = controlledValue ?? internalValue;\n\n // Internal linked state (uncontrolled mode)\n const [internalLinked, setInternalLinked] = useState(defaultLinked);\n const isLinked = controlledLinked ?? internalLinked;\n\n // Ref to track value before linked changes for proportional math\n const prevValueRef = useRef<number[]>(currentValue);\n\n // Keep prevValueRef up to date when value changes externally\n React.useEffect(() => {\n prevValueRef.current = currentValue;\n }, [currentValue]);\n\n // Handle link toggle\n const handleLinkToggle = useCallback(() => {\n const newLinked = !isLinked;\n if (controlledLinked === undefined) {\n setInternalLinked(newLinked);\n }\n onLinkedChange?.(newLinked);\n }, [isLinked, controlledLinked, onLinkedChange]);\n\n // Handle axis value change\n const handleAxisChange = useCallback(\n (axisIndex: number, newAxisValue: number) => {\n const prevVector = prevValueRef.current;\n let nextValue: number[];\n\n if (isLinked) {\n const oldAxisValue = prevVector[axisIndex] ?? 0;\n if (oldAxisValue !== 0) {\n // Proportional scaling\n const ratio = newAxisValue / oldAxisValue;\n nextValue = prevVector.map((v, i) =>\n i === axisIndex ? newAxisValue : v * ratio\n );\n } else {\n // Additive delta when old value is 0\n const delta = newAxisValue - oldAxisValue;\n nextValue = prevVector.map((v, i) =>\n i === axisIndex ? newAxisValue : v + delta\n );\n }\n } else {\n nextValue = currentValue.map((v, i) =>\n i === axisIndex ? newAxisValue : v\n );\n }\n\n prevValueRef.current = nextValue;\n\n if (controlledValue === undefined) {\n setInternalValue(nextValue);\n }\n onChange?.(nextValue, axisIndex);\n },\n [isLinked, currentValue, controlledValue, onChange]\n );\n\n // Handle commit (blur/Enter on any NumberInput)\n const handleCommit = useCallback(() => {\n onChangeComplete?.(prevValueRef.current);\n }, [onChangeComplete]);\n\n // Resolve labels\n const labels = getAxisLabels(labelPreset, customAxisLabels, dimension);\n\n const iconSize = size === 'sm' ? 12 : size === 'md' ? 14 : 16;\n\n return (\n <div\n className={cx(vectorContainerStyle, className)}\n data-testid={testId}\n role=\"group\"\n aria-label={label ?? 'Vector input'}\n {...rest}\n >\n {label && <FormLabel disabled={disabled}>{label}</FormLabel>}\n\n <div\n className={vectorRowRecipe({ direction })}\n style={assignInlineVars({ [gapVar]: `${gap}px` })}\n >\n {Array.from({ length: dimension }, (_, i) => {\n const axisLabel = labels[i] ?? String(i);\n const axisValue = currentValue[i] ?? 0;\n const axisColor = getAxisColor(colorPreset, customAxisColors, i);\n // Fallback to theme secondary text color when no axis color\n const resolvedColor = axisColor ?? vars.colors.text.secondary;\n\n return (\n <div key={i} className={axisInputStyle}>\n <div\n className={axisLabelRecipe({ size })}\n style={assignInlineVars({ [axisColorVar]: resolvedColor })}\n >\n {axisLabel}\n </div>\n <NumberInput\n value={axisValue}\n onChange={(val: number) => {\n handleAxisChange(i, val);\n }}\n onBlur={handleCommit}\n min={min}\n max={max}\n step={step}\n precisionStep={precisionStep}\n largeStep={largeStep}\n precision={precision}\n unit={unit}\n size={size}\n disabled={disabled}\n showStepButtons={false}\n aria-label={`${axisLabel} axis`}\n />\n </div>\n );\n })}\n\n {showLink && (\n <button\n type=\"button\"\n className={linkButtonRecipe({ active: isLinked, size })}\n onClick={handleLinkToggle}\n disabled={disabled}\n aria-pressed={isLinked}\n aria-label={isLinked ? 'Unlink axes' : 'Link axes'}\n title={isLinked ? 'Unlink axes' : 'Link axes'}\n >\n {isLinked ? (\n <LinkIcon size={iconSize} color=\"currentColor\" decorative />\n ) : (\n <UnlinkIcon size={iconSize} color=\"currentColor\" decorative />\n )}\n </button>\n )}\n </div>\n\n {(error && errorMessage) || helperText ? (\n <FormHelperText error={error && !!errorMessage}>\n {error && errorMessage ? errorMessage : helperText}\n </FormHelperText>\n ) : null}\n </div>\n );\n};\n\nVectorInput.displayName = 'VectorInput';\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwBA;AAEA;;;AAGE;;AAGF;AAEA;AACA;AAEA;AAKE;AACE;;AAEF;AACE;;AAEF;AACE;;;AAGF;AACF;AAEA;AAKE;AACE;;AAEF;;;;;AAKF;AAEA;;;;;;;;;;;;;;;;AAgBG;;;AAmCD;AAGA;;;AAIA;;AAGA;;AAGA;AACE;AACF;;AAGA;AACE;AACA;;;AAGA;;;;AAME;AACA;;;AAIE;;AAEE;;;;;AAMA;;;;;;;AAWJ;AAEA;;;AAGA;;;AAMJ;AACE;AACF;;;;AAOA;;;;;;;AAgCgB;;AAiBV;AA4BR;AAEA;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DataTable.js","sources":["../../../../../../src/components/data/DataTable/DataTable.tsx"],"sourcesContent":[null],"names":[],"mappings":";;;;;;;;;;;;;;AAkDA;AACE;AACA;AACA;;AAGF;AACA;AAEA;;AAKoC;AAClC;AACA;AACF;AAEA;AAKE;;AACoC;;AAElC;AACF;AACF;AAEA;;AACe;AACb;AAAmC;AACnC;;;;AAEA;;;;AAGA;AACA;AACA;AACF;AAEA;;AAGwB;;AACC;AACvB;AACF;AAEA;;AAG2B;AACzB;AACF;AAOA;AAIE;;;AAGA;;AAEE;AACA;;AAEF;;AAEF;AAEA;;;;;;;;;;;;;;;;;;;;;;AAsBG;AACH;;AAuCE;;;AAGE;;AAED;AAED;AAEI;AACA;AACA;AACA;AACD;;;AAKH;AACA;AAEA;AACE;AAAgB;AAChB;AACA;AAAqC;AACrC;AACA;AAAa;;;AAGX;AACA;AACA;AACE;AACA;AACF;AACF;AACA;;AAGF;AAKA;AACE;AAAoB;AACpB;AAAsB;;;AAGpB;AAA+B;AACjC;AACA;;AAGF;AAEE;AACE;;AAEF;;AAGA;AACE;;AAEF;AAEF;;;;;;;;AAQU;;;AAGN;AACF;AAIF;AACA;AAEA;AAEI;;;;AAEA;AACE;;;;AAOF;;;;AAKE;AACE;AACA;AACA;;;;AAEA;;;AAGA;;AAEA;AACE;AAAoB;;AACf;;AAEP;AACF;;;AAIF;;AAEE;AACA;AAA0B;;AACrB;AACL;AACF;AACF;AAIF;;;;;;;;;;;AAUA;;;;;;AAMC;AAED;AAEA;;;;;;;;;AAYE;AACA;;AAED;AAED;AACA;AAEA;;;;AAII;;;AAGF;AACE;AACA;AACA;AACF;AACA;;AAGF;;AACwB;AACtB;AAEA;AACA;AAEI;;AAEE;;;AAUR;AAEA;AAOI;AACA;AACA;AACE;AACA;AACA;AACF;;AAEE;AACA;;AAEE;;AAC8B;AAC9B;AACF;AACF;AACA;AACA;AACF;AAIF;AAEI;;;;;;;;AAUJ;AAEI;;AACA;AACA;AACE;;AAEE;;AAEF;;AAEE;;AAEF;;;;AAIA;;AAEE;;AAEF;;AAEE;;AAEF;;AAEE;;;;;;AAKA;;;AAEA;;AAEE;;AAEF;;;;;;;;;AAOA;AACA;AACA;;;;;AAIF;;;AAGJ;;;;;;;;AASC;;;AAKC;;;AAIJ;AACE;AAEI;AACE;AACA;AACE;AACA;;AAER;AAOQ;;;AAGD;;;;AAWC;;AAKI;;;;;;;;AAqBF;AAEE;;AAEA;AACF;;AAMZ;AAEA;AAgBY;AACF;AAUV;;;AASY;;AAED;AAMD;AACA;AACE;AACA;AACE;;AAEE;AACA;AAGd;;;;;AAWA;AAQU;AACF;;AAGA;AAEE;;;AAGF;;AAYI;;AAWZ;;AAEE;AACE;;;AAGJ;;AAGE;AACF;AAEA;AACE;;AAEE;AACE;;;;AAIN;;AAGE;;;;;;;AAQD;;;;;AA0BH;;;AAIE;;AAcF;AAEA;AACE;;;;AAIA;;;AAIF;;AAGE;AACA;AACD;AAED;AAkCc;AACD;;;AAIwB;AACvB;AACE;;AAED;AACH;AAWd;AAEA;AAEO;;"}
|
|
1
|
+
{"version":3,"file":"DataTable.js","sources":["src/components/data/DataTable/DataTable.tsx"],"sourcesContent":["'use client';\n\nimport { useVirtualizer } from '@tanstack/react-virtual';\nimport { assignInlineVars } from '@vanilla-extract/dynamic';\nimport React, {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\n\nimport { ChevronDownIcon } from '@/components/Icons/ChevronDownIcon';\nimport { ChevronUpIcon } from '@/components/Icons/ChevronUpIcon';\nimport { SortIcon } from '@/components/Icons/SortIcon';\nimport { Checkbox } from '@/components/primitives/Checkbox';\nimport { useControlledState } from '@/hooks/useControlledState';\nimport { cx } from '@/utils/cx';\n\nimport {\n bodyStyle,\n cellRecipe,\n emptyStateStyle,\n gridTemplateColumnsVar,\n headerCellInnerStyle,\n headerCellRecipe,\n headerStyle,\n innerWrapperStyle,\n resizeHandleStyle,\n rootRecipe,\n rowRecipe,\n scrollContainerStyle,\n skeletonBarStyle,\n skeletonCellStyle,\n skeletonRowStyle,\n sortIndicatorRecipe,\n tableMinWidthVar,\n totalHeightVar,\n virtualBodyStyle,\n} from './DataTable.css';\n\nimport type {\n DataTableColumn,\n DataTableDensity,\n DataTableProps,\n DataTableSelectionState,\n DataTableSortDirection,\n DataTableSortState,\n} from './DataTable.types';\n\nconst DENSITY_ROW_HEIGHT: Record<DataTableDensity, number> = {\n comfortable: 40,\n compact: 32,\n dense: 24,\n};\n\nconst VIRTUALIZATION_THRESHOLD_DEFAULT = 100;\nconst SELECT_COLUMN_WIDTH = 40;\n\nfunction getRowKey<R>(\n rowKey: keyof R | ((row: R, rowIndex: number) => string),\n row: R,\n rowIndex: number\n): string {\n if (typeof rowKey === 'function') return rowKey(row, rowIndex);\n const value = row[rowKey];\n return typeof value === 'string' ? value : String(value);\n}\n\nfunction readCellValue<R>(\n column: DataTableColumn<R>,\n row: R,\n rowIndex: number\n): unknown {\n const accessor = column.accessor;\n if (typeof accessor === 'function') return accessor(row, rowIndex);\n if (typeof accessor === 'string')\n return (row as Record<string, unknown>)[accessor];\n return (row as Record<string, unknown>)[column.id];\n}\n\nfunction defaultCompare(a: unknown, b: unknown): number {\n if (a === b) return 0;\n if (a === null || a === undefined) return 1;\n if (b === null || b === undefined) return -1;\n if (typeof a === 'number' && typeof b === 'number') return a - b;\n if (a instanceof Date && b instanceof Date) return a.getTime() - b.getTime();\n if (typeof a === 'boolean' && typeof b === 'boolean')\n return a === b ? 0 : a ? 1 : -1;\n const sa = typeof a === 'string' ? a : JSON.stringify(a);\n const sb = typeof b === 'string' ? b : JSON.stringify(b);\n return sa.localeCompare(sb);\n}\n\nfunction nextSortDirection(\n current: DataTableSortDirection\n): DataTableSortDirection {\n if (current === null) return 'asc';\n if (current === 'asc') return 'desc';\n return null;\n}\n\nfunction resolveDimension(\n value: number | string | undefined\n): string | undefined {\n if (value === undefined) return undefined;\n return typeof value === 'number' ? `${value}px` : value;\n}\n\ninterface ColumnTrack {\n templateValue: string;\n minPx: number;\n}\n\nfunction getColumnTrack<R>(\n column: DataTableColumn<R>,\n override: number | undefined\n): ColumnTrack {\n if (override !== undefined) {\n return { templateValue: `${override}px`, minPx: override };\n }\n if (column.width !== undefined) {\n const resolved = resolveDimension(column.width) ?? '1fr';\n const minPx = typeof column.width === 'number' ? column.width : 0;\n return { templateValue: resolved, minPx };\n }\n const minWidth = column.minWidth ?? 80;\n return { templateValue: `minmax(${minWidth}px, 1fr)`, minPx: minWidth };\n}\n\n/**\n * Data-driven table with sortable columns, row selection, sticky header,\n * optional sticky first column, optional column resizing, and optional\n * row virtualization (auto-enabled above 100 rows).\n *\n * Uses a CSS grid layout under `role=\"grid\"` so columns line up across the\n * sticky header and individual virtualized rows without depending on real\n * `<table>` semantics.\n *\n * @example\n * ```tsx\n * <DataTable\n * rows={users}\n * rowKey=\"id\"\n * columns={[\n * { id: 'name', header: 'Name', accessor: 'name', sortable: true },\n * { id: 'role', header: 'Role', accessor: 'role' },\n * ]}\n * selectionMode=\"multiple\"\n * onSelectionChange={setSelected}\n * />\n * ```\n */\nfunction DataTableInner<R>(props: DataTableProps<R>): React.ReactElement {\n const {\n rows,\n columns,\n rowKey,\n density = 'comfortable',\n stickyHeader = true,\n sort,\n defaultSort,\n onSortChange,\n manualSort = false,\n selectionMode = false,\n selection,\n defaultSelection,\n onSelectionChange,\n isRowSelectable,\n onRowClick,\n onRowActivate,\n renderRow,\n emptyState,\n loading = false,\n loadingRowCount = 5,\n virtualized = 'auto',\n virtualizationThreshold = VIRTUALIZATION_THRESHOLD_DEFAULT,\n estimatedRowHeight,\n overscan = 8,\n height = 480,\n maxHeight,\n resizableColumns = false,\n onColumnResize,\n className,\n style,\n testId,\n ref,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n ...rest\n } = props;\n\n const [sortState, setSortState] = useControlledState<DataTableSortState>({\n value: sort ?? undefined,\n defaultValue: defaultSort ?? undefined,\n onChange: onSortChange,\n fallback: { columnId: null, direction: null },\n });\n\n const [selectionState, setSelectionState] =\n useControlledState<DataTableSelectionState>({\n value: selection,\n defaultValue: defaultSelection,\n onChange: onSelectionChange,\n fallback: [],\n });\n\n const [columnWidths, setColumnWidths] = useState<Record<string, number>>({});\n const [activeRowIndex, setActiveRowIndex] = useState<number | null>(null);\n\n const showSelectColumn = selectionMode === 'multiple';\n const selectedSet = useMemo(() => new Set(selectionState), [selectionState]);\n\n const sortedRows = useMemo(() => {\n if (manualSort) return rows;\n const { columnId, direction } = sortState;\n if (!columnId || direction === null) return rows;\n const column = columns.find(c => c.id === columnId);\n if (!column) return rows;\n const indexed = rows.map((row, idx) => ({ row, idx }));\n indexed.sort((a, b) => {\n const valueA = readCellValue(column, a.row, a.idx);\n const valueB = readCellValue(column, b.row, b.idx);\n const cmp = column.sortComparator\n ? column.sortComparator(valueA, valueB, a.row, b.row)\n : defaultCompare(valueA, valueB);\n return direction === 'asc' ? cmp : -cmp;\n });\n return indexed.map(item => item.row);\n }, [rows, sortState, columns, manualSort]);\n\n const rowKeys = useMemo(\n () => sortedRows.map((row, idx) => getRowKey(rowKey, row, idx)),\n [sortedRows, rowKey]\n );\n\n const selectableIndexes = useMemo(() => {\n if (!selectionMode) return [];\n if (!isRowSelectable) return sortedRows.map((_, idx) => idx);\n const result: number[] = [];\n sortedRows.forEach((row, idx) => {\n if (isRowSelectable(row, idx)) result.push(idx);\n });\n return result;\n }, [sortedRows, isRowSelectable, selectionMode]);\n\n const allSelectableSelected =\n selectableIndexes.length > 0 &&\n selectableIndexes.every(idx => {\n const k = rowKeys[idx];\n return k !== undefined && selectedSet.has(k);\n });\n const someSelectableSelected =\n !allSelectableSelected &&\n selectableIndexes.some(idx => {\n const k = rowKeys[idx];\n return k !== undefined && selectedSet.has(k);\n });\n\n const handleSortClick = useCallback(\n (column: DataTableColumn<R>) => {\n if (!column.sortable) return;\n setSortState(prev => {\n if (prev.columnId === column.id) {\n const next = nextSortDirection(prev.direction);\n return next === null\n ? { columnId: null, direction: null }\n : { columnId: column.id, direction: next };\n }\n return { columnId: column.id, direction: 'asc' };\n });\n },\n [setSortState]\n );\n\n const selectionAnchorRef = useRef<number | null>(null);\n const selectionShiftKeyRef = useRef(false);\n\n const toggleRowSelection = useCallback(\n (rowKeyValue: string, row: R, rowIdx: number, rangeMode = false) => {\n if (!selectionMode) return;\n if (isRowSelectable && !isRowSelectable(row, rowIdx)) return;\n if (selectionMode === 'single') {\n selectionAnchorRef.current = rowIdx;\n setSelectionState(prev =>\n prev[0] === rowKeyValue ? [] : [rowKeyValue]\n );\n return;\n }\n\n const anchor = selectionAnchorRef.current;\n if (rangeMode && anchor !== null && anchor !== rowIdx) {\n const [start, end] =\n anchor < rowIdx ? [anchor, rowIdx] : [rowIdx, anchor];\n const rangeKeys: string[] = [];\n for (let i = start; i <= end; i += 1) {\n const candidate = sortedRows[i];\n const candidateKey = rowKeys[i];\n if (candidate === undefined || candidateKey === undefined) continue;\n if (isRowSelectable && !isRowSelectable(candidate, i)) continue;\n rangeKeys.push(candidateKey);\n }\n setSelectionState(prev => {\n const set = new Set(prev);\n const shouldDeselect = set.has(rowKeyValue);\n for (const key of rangeKeys) {\n if (shouldDeselect) set.delete(key);\n else set.add(key);\n }\n return Array.from(set);\n });\n return;\n }\n\n selectionAnchorRef.current = rowIdx;\n setSelectionState(prev => {\n const set = new Set(prev);\n if (set.has(rowKeyValue)) set.delete(rowKeyValue);\n else set.add(rowKeyValue);\n return Array.from(set);\n });\n },\n [selectionMode, isRowSelectable, setSelectionState, sortedRows, rowKeys]\n );\n\n const toggleSelectAll = useCallback(() => {\n if (selectionMode !== 'multiple') return;\n if (allSelectableSelected) {\n setSelectionState([]);\n return;\n }\n const next = selectableIndexes\n .map(idx => rowKeys[idx])\n .filter((k): k is string => typeof k === 'string');\n setSelectionState(next);\n }, [\n allSelectableSelected,\n selectableIndexes,\n rowKeys,\n selectionMode,\n setSelectionState,\n ]);\n\n const scrollContainerRef = useRef<HTMLDivElement>(null);\n\n const shouldVirtualize =\n virtualized === true ||\n (virtualized === 'auto' && sortedRows.length > virtualizationThreshold);\n\n const rowHeightPx = estimatedRowHeight ?? DENSITY_ROW_HEIGHT[density];\n\n // TanStack Virtual's useVirtualizer returns functions that cannot be safely\n // memoized; the Compiler flags it as an incompatible library. Behavior is\n // correct here because the virtualizer is read imperatively per render.\n // eslint-disable-next-line react-hooks/incompatible-library\n const virtualizer = useVirtualizer({\n count: shouldVirtualize ? sortedRows.length : 0,\n getScrollElement: () => scrollContainerRef.current,\n estimateSize: () => rowHeightPx,\n overscan,\n });\n\n const virtualItems = shouldVirtualize ? virtualizer.getVirtualItems() : [];\n const totalSize = shouldVirtualize ? virtualizer.getTotalSize() : 0;\n\n const tracks = useMemo(() => {\n const items: string[] = [];\n let minWidth = 0;\n if (showSelectColumn) {\n items.push(`${SELECT_COLUMN_WIDTH}px`);\n minWidth += SELECT_COLUMN_WIDTH;\n }\n columns.forEach(column => {\n const t = getColumnTrack(column, columnWidths[column.id]);\n items.push(t.templateValue);\n minWidth += t.minPx;\n });\n return { template: items.join(' '), minWidth };\n }, [columns, columnWidths, showSelectColumn]);\n\n const renderSortIndicator = (column: DataTableColumn<R>) => {\n if (!column.sortable) return null;\n const active =\n sortState.columnId === column.id && sortState.direction !== null;\n const direction = active ? sortState.direction : null;\n const Icon =\n direction === 'asc'\n ? ChevronUpIcon\n : direction === 'desc'\n ? ChevronDownIcon\n : SortIcon;\n return (\n <span\n className={sortIndicatorRecipe({ active: active || undefined })}\n aria-hidden=\"true\"\n >\n <Icon size=\"sm\" />\n </span>\n );\n };\n\n const beginResize = useCallback(\n (\n columnId: string,\n startX: number,\n startWidth: number,\n column: DataTableColumn<R>\n ) => {\n const minWidth = column.minWidth ?? 40;\n const maxWidth = column.maxWidth ?? Infinity;\n const handleMove = (event: PointerEvent) => {\n const delta = event.clientX - startX;\n const next = Math.max(minWidth, Math.min(maxWidth, startWidth + delta));\n setColumnWidths(prev => ({ ...prev, [columnId]: next }));\n };\n const handleUp = () => {\n window.removeEventListener('pointermove', handleMove);\n window.removeEventListener('pointerup', handleUp);\n setColumnWidths(prev => {\n const finalWidth = prev[columnId];\n if (finalWidth !== undefined) onColumnResize?.(columnId, finalWidth);\n return prev;\n });\n };\n window.addEventListener('pointermove', handleMove);\n window.addEventListener('pointerup', handleUp);\n },\n [onColumnResize]\n );\n\n const focusRow = useCallback(\n (index: number) => {\n if (sortedRows.length === 0) return;\n const clamped = Math.max(0, Math.min(sortedRows.length - 1, index));\n setActiveRowIndex(clamped);\n if (shouldVirtualize) {\n virtualizer.scrollToIndex(clamped, { align: 'auto' });\n }\n },\n [sortedRows.length, shouldVirtualize, virtualizer]\n );\n\n const handleKeyDown = useCallback(\n (event: React.KeyboardEvent<HTMLDivElement>) => {\n if (sortedRows.length === 0) return;\n const current = activeRowIndex ?? -1;\n switch (event.key) {\n case 'ArrowDown':\n event.preventDefault();\n focusRow(current + 1);\n return;\n case 'ArrowUp':\n event.preventDefault();\n focusRow(current - 1);\n return;\n case 'Home':\n event.preventDefault();\n focusRow(0);\n return;\n case 'End':\n event.preventDefault();\n focusRow(sortedRows.length - 1);\n return;\n case 'PageDown':\n event.preventDefault();\n focusRow(current + 10);\n return;\n case 'PageUp':\n event.preventDefault();\n focusRow(current - 10);\n return;\n case 'Enter': {\n if (current < 0) return;\n event.preventDefault();\n const row = sortedRows[current];\n if (row === undefined) return;\n const k = rowKeys[current];\n if (selectionMode === 'single' && k !== undefined) {\n toggleRowSelection(k, row, current);\n }\n onRowActivate?.(row, current);\n return;\n }\n case ' ': {\n if (current < 0) return;\n if (selectionMode !== 'multiple') return;\n event.preventDefault();\n const row = sortedRows[current];\n const k = rowKeys[current];\n if (row === undefined || k === undefined) return;\n toggleRowSelection(k, row, current, event.shiftKey);\n return;\n }\n default:\n return;\n }\n },\n [\n activeRowIndex,\n focusRow,\n sortedRows,\n rowKeys,\n selectionMode,\n toggleRowSelection,\n onRowActivate,\n ]\n );\n\n useEffect(() => {\n if (activeRowIndex !== null && activeRowIndex >= sortedRows.length) {\n setActiveRowIndex(sortedRows.length === 0 ? null : sortedRows.length - 1);\n }\n }, [activeRowIndex, sortedRows.length]);\n\n const renderHeaderCell = (column: DataTableColumn<R>) => {\n const ariaSort: 'ascending' | 'descending' | 'none' | undefined =\n column.sortable\n ? sortState.columnId === column.id && sortState.direction === 'asc'\n ? 'ascending'\n : sortState.columnId === column.id && sortState.direction === 'desc'\n ? 'descending'\n : 'none'\n : undefined;\n return (\n <div\n key={column.id}\n role=\"columnheader\"\n data-column-id={column.id}\n className={cx(\n headerCellRecipe({\n align: column.align ?? 'left',\n sortable: column.sortable ? true : undefined,\n sticky: column.sticky ? true : undefined,\n }),\n column.className\n )}\n aria-sort={ariaSort}\n aria-hidden={column.ariaHidden ? true : undefined}\n tabIndex={column.sortable ? 0 : undefined}\n onClick={\n column.sortable\n ? () => {\n handleSortClick(column);\n }\n : undefined\n }\n onKeyDown={\n column.sortable\n ? event => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n handleSortClick(column);\n }\n }\n : undefined\n }\n >\n <span className={headerCellInnerStyle}>\n <span>{column.header}</span>\n {renderSortIndicator(column)}\n </span>\n {resizableColumns && !column.disableResize && (\n <span\n className={resizeHandleStyle}\n role=\"separator\"\n aria-orientation=\"vertical\"\n aria-label={`Resize column ${column.id}`}\n onPointerDown={event => {\n event.preventDefault();\n event.stopPropagation();\n const startWidth =\n columnWidths[column.id] ??\n event.currentTarget.parentElement?.getBoundingClientRect()\n .width ??\n 100;\n beginResize(column.id, event.clientX, startWidth, column);\n }}\n />\n )}\n </div>\n );\n };\n\n const renderHeader = () => (\n <div\n role=\"row\"\n aria-rowindex={1}\n className={headerStyle({ sticky: stickyHeader || undefined })}\n >\n {showSelectColumn && (\n <div\n role=\"columnheader\"\n className={headerCellRecipe({ selectColumn: true, sticky: true })}\n aria-label=\"Select all rows\"\n >\n <Checkbox\n checked={allSelectableSelected}\n indeterminate={someSelectableSelected}\n onChange={() => {\n toggleSelectAll();\n }}\n aria-label=\"Select all rows\"\n size=\"sm\"\n />\n </div>\n )}\n {columns.map(renderHeaderCell)}\n </div>\n );\n\n const renderCells = (row: R, rowIdx: number) =>\n columns.map(column => {\n const value = readCellValue(column, row, rowIdx);\n return (\n <div\n key={column.id}\n role=\"gridcell\"\n className={cx(\n cellRecipe({\n align: column.align ?? 'left',\n sticky: column.sticky ? true : undefined,\n }),\n column.className\n )}\n data-column-id={column.id}\n >\n {column.cell\n ? column.cell({ value, row, rowIndex: rowIdx, column })\n : value === null || value === undefined\n ? ''\n : typeof value === 'string'\n ? value\n : typeof value === 'number' || typeof value === 'boolean'\n ? String(value)\n : JSON.stringify(value)}\n </div>\n );\n });\n\n const renderRowElement = (\n row: R,\n rowIdx: number,\n options: { virtualized?: boolean; transformY?: number } = {}\n ) => {\n const k = rowKeys[rowIdx] ?? `__row_${rowIdx}__`;\n const isSelected = selectedSet.has(k);\n const interactive = selectionMode === 'single' || onRowClick !== undefined;\n\n const cells = (\n <>\n {showSelectColumn && (\n <div\n role=\"gridcell\"\n tabIndex={-1}\n className={cellRecipe({ selectColumn: true, sticky: true })}\n onClickCapture={event => {\n selectionShiftKeyRef.current = event.shiftKey;\n }}\n onClick={event => {\n event.stopPropagation();\n }}\n onKeyDown={event => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.stopPropagation();\n }\n }}\n >\n <Checkbox\n checked={isSelected}\n disabled={isRowSelectable ? !isRowSelectable(row, rowIdx) : false}\n onChange={() => {\n toggleRowSelection(\n k,\n row,\n rowIdx,\n selectionShiftKeyRef.current\n );\n selectionShiftKeyRef.current = false;\n }}\n aria-label={`Select row ${rowIdx + 1}`}\n size=\"sm\"\n />\n </div>\n )}\n {renderCells(row, rowIdx)}\n </>\n );\n\n const handleRowClick = (event: React.MouseEvent<HTMLDivElement>) => {\n onRowClick?.(row, rowIdx, event);\n if (selectionMode === 'single') {\n toggleRowSelection(k, row, rowIdx);\n }\n setActiveRowIndex(rowIdx);\n };\n\n const handleRowDoubleClick = () => {\n onRowActivate?.(row, rowIdx);\n };\n\n const handleRowKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n if (selectionMode === 'single') {\n toggleRowSelection(k, row, rowIdx);\n }\n setActiveRowIndex(rowIdx);\n }\n };\n\n const rowContent = renderRow\n ? renderRow({ row, rowIndex: rowIdx, selected: isSelected }, cells)\n : cells;\n\n const rowClassName = rowRecipe({\n virtualized: options.virtualized ? true : undefined,\n interactive: interactive ? true : undefined,\n selected: isSelected ? true : undefined,\n active: activeRowIndex === rowIdx ? true : undefined,\n });\n\n const rowStyle: React.CSSProperties | undefined =\n options.virtualized && options.transformY !== undefined\n ? { transform: `translateY(${options.transformY}px)` }\n : undefined;\n\n return (\n <div\n key={k}\n role=\"row\"\n aria-rowindex={rowIdx + 2}\n aria-selected={selectionMode ? isSelected : undefined}\n data-row-key={k}\n data-row-index={rowIdx}\n data-row-interactive={interactive ? 'true' : undefined}\n className={rowClassName}\n style={rowStyle}\n tabIndex={interactive ? -1 : undefined}\n onClick={interactive ? handleRowClick : undefined}\n onKeyDown={interactive ? handleRowKeyDown : undefined}\n onDoubleClick={onRowActivate ? handleRowDoubleClick : undefined}\n >\n {rowContent}\n </div>\n );\n };\n\n const renderSkeletonRows = () => {\n const rowsToRender = Array.from({ length: loadingRowCount }, (_, i) => i);\n const cellCount = columns.length + (showSelectColumn ? 1 : 0);\n return rowsToRender.map(idx => (\n <div\n key={`__skeleton_${idx}__`}\n className={skeletonRowStyle}\n aria-hidden=\"true\"\n >\n {Array.from({ length: cellCount }, (_, ci) => (\n <div key={ci} className={skeletonCellStyle}>\n <span className={skeletonBarStyle} />\n </div>\n ))}\n </div>\n ));\n };\n\n const containerStyle: React.CSSProperties = {\n height: resolveDimension(height),\n ...(maxHeight !== undefined\n ? { maxHeight: resolveDimension(maxHeight) }\n : {}),\n ...style,\n };\n\n const isEmpty = !loading && sortedRows.length === 0;\n const totalColumnsCount = columns.length + (showSelectColumn ? 1 : 0);\n\n const gridVars = assignInlineVars({\n [gridTemplateColumnsVar]: tracks.template,\n [tableMinWidthVar]: `${tracks.minWidth}px`,\n });\n\n return (\n <div\n ref={ref}\n role=\"grid\"\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n aria-rowcount={sortedRows.length + 1}\n aria-colcount={totalColumnsCount}\n aria-multiselectable={selectionMode === 'multiple' ? true : undefined}\n data-testid={testId}\n data-density={density}\n tabIndex={0}\n className={cx(rootRecipe({ density }), className)}\n style={{ ...containerStyle, ...gridVars }}\n onKeyDown={handleKeyDown}\n {...rest}\n >\n <div ref={scrollContainerRef} className={scrollContainerStyle}>\n <div className={innerWrapperStyle}>\n {renderHeader()}\n {loading ? (\n <div className={bodyStyle}>{renderSkeletonRows()}</div>\n ) : isEmpty ? (\n <div className={bodyStyle}>\n <div className={emptyStateStyle} role=\"row\">\n <div role=\"gridcell\" aria-colspan={totalColumnsCount}>\n {emptyState ?? 'No rows to display'}\n </div>\n </div>\n </div>\n ) : shouldVirtualize ? (\n <div\n className={virtualBodyStyle}\n style={assignInlineVars({\n [totalHeightVar]: `${totalSize}px`,\n })}\n >\n {virtualItems.map(item => {\n const row = sortedRows[item.index];\n if (row === undefined) return null;\n return renderRowElement(row, item.index, {\n virtualized: true,\n transformY: item.start,\n });\n })}\n </div>\n ) : (\n <div className={bodyStyle}>\n {sortedRows.map((row, rowIdx) => renderRowElement(row, rowIdx))}\n </div>\n )}\n </div>\n </div>\n </div>\n );\n}\n\nDataTableInner.displayName = 'DataTable';\n\nexport const DataTable = DataTableInner as <R>(\n props: DataTableProps<R> & { ref?: React.Ref<HTMLDivElement> }\n) => React.ReactElement;\n"],"names":[],"mappings":";;;;;;;;;;;;;;AAkDA;AACE;AACA;AACA;;AAGF;AACA;AAEA;;AAKoC;AAClC;AACA;AACF;AAEA;AAKE;;AACoC;;AAElC;AACF;AACF;AAEA;;AACe;AACb;AAAmC;AACnC;;;;AAEA;;;;AAGA;AACA;AACA;AACF;AAEA;;AAGwB;;AACC;AACvB;AACF;AAEA;;AAG2B;AACzB;AACF;AAOA;AAIE;;;AAGA;;AAEE;AACA;;AAEF;;AAEF;AAEA;;;;;;;;;;;;;;;;;;;;;;AAsBG;AACH;;AAuCE;;;AAGE;;AAED;AAED;AAEI;AACA;AACA;AACA;AACD;;;AAKH;AACA;AAEA;AACE;AAAgB;AAChB;AACA;AAAqC;AACrC;AACA;AAAa;;;AAGX;AACA;AACA;AACE;AACA;AACF;AACF;AACA;;AAGF;AAKA;AACE;AAAoB;AACpB;AAAsB;;;AAGpB;AAA+B;AACjC;AACA;;AAGF;AAEE;AACE;;AAEF;;AAGA;AACE;;AAEF;AAEF;;;;;;;;AAQU;;;AAGN;AACF;AAIF;AACA;AAEA;AAEI;;;;AAEA;AACE;;;;AAOF;;;;AAKE;AACE;AACA;AACA;;;;AAEA;;;AAGA;;AAEA;AACE;AAAoB;;AACf;;AAEP;AACF;;;AAIF;;AAEE;AACA;AAA0B;;AACrB;AACL;AACF;AACF;AAIF;;;;;;;;;;;AAUA;;;;;;AAMC;AAED;AAEA;;;;;;;;;AAYE;AACA;;AAED;AAED;AACA;AAEA;;;;AAII;;;AAGF;AACE;AACA;AACA;AACF;AACA;;AAGF;;AACwB;AACtB;AAEA;AACA;AAEI;;AAEE;;;AAUR;AAEA;AAOI;AACA;AACA;AACE;AACA;AACA;AACF;;AAEE;AACA;;AAEE;;AAC8B;AAC9B;AACF;AACF;AACA;AACA;AACF;AAIF;AAEI;;;;;;;;AAUJ;AAEI;;AACA;AACA;AACE;;AAEE;;AAEF;;AAEE;;AAEF;;;;AAIA;;AAEE;;AAEF;;AAEE;;AAEF;;AAEE;;;;;;AAKA;;;AAEA;;AAEE;;AAEF;;;;;;;;;AAOA;AACA;AACA;;;;;AAIF;;;AAGJ;;;;;;;;AASC;;;AAKC;;;AAIJ;AACE;AAEI;AACE;AACA;AACE;AACA;;AAER;AAOQ;;;AAGD;;;;AAWC;;AAKI;;;;;;;;AAqBF;AAEE;;AAEA;AACF;;AAMZ;AAEA;AAgBY;AACF;AAUV;;;AASY;;AAED;AAMD;AACA;AACE;AACA;AACE;;AAEE;AACA;AAGd;;;;;AAWA;AAQU;AACF;;AAGA;AAEE;;;AAGF;;AAYI;;AAWZ;;AAEE;AACE;;;AAGJ;;AAGE;AACF;AAEA;AACE;;AAEE;AACE;;;;AAIN;;AAGE;;;;;;;AAQD;;;;;AA0BH;;;AAIE;;AAcF;AAEA;AACE;;;;AAIA;;;AAIF;;AAGE;AACA;AACD;AAED;AAkCc;AACD;;;AAIwB;AACvB;AACE;;AAED;AACH;AAWd;AAEA;AAEO;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AssetBrowser.js","sources":["../../../../../../src/components/editor/AssetBrowser/AssetBrowser.tsx"],"sourcesContent":[null],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAoCA;AAMA;AACA;AAEA;;;;;AAKG;AACH;;;;AAOI;;AACA;AACA;;;;;AAGF;AACA;AACF;AAEA;AACE;;;;;AAuEA;;;;;AAKA;AAEA;AAEA;;;;AAmBE;;;AAGA;;;AAGA;;;;AAIA;;;;AAIA;;;;;AAKD;AAED;;;;AAIE;;;AAGD;AAED;;;;;;AAcC;;;AAIC;;AAEA;;;;;;;AAOD;;;;AAKC;;;;;;AAMD;;;;AAKC;AACD;AAED;;;;;;;;;AASC;;;;;;AAOD;AAEI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoCD;;;;;;;;;;;;;;;;;;AAmBC;;;;;;;;;;;;AAYD;AAGH;;;;;;;;;;;;;;;;AAiBG;;;;;;;;;;;;;;;;AAiBA;;;AAMH;AA2CF;AAEA;AAEO;AACL;AACA;AACD;;"}
|
|
1
|
+
{"version":3,"file":"AssetBrowser.js","sources":["src/components/editor/AssetBrowser/AssetBrowser.tsx"],"sourcesContent":["'use client';\n\nimport React, { useMemo, useRef, useState } from 'react';\n\nimport { cx } from '@/utils/cx';\n\nimport { body, root } from './AssetBrowser.css';\nimport { AssetBrowserAnnouncementLive } from './AssetBrowserAnnouncementLive';\nimport { AssetBrowserBreadcrumbs } from './AssetBrowserBreadcrumbs';\nimport {\n AssetBrowserChromeContext,\n AssetBrowserContext,\n AssetBrowserStoreContext,\n} from './AssetBrowserContext';\nimport { resolveLabels } from './assetBrowserLabels';\nimport { AssetBrowserSidebar } from './AssetBrowserSidebar';\nimport { AssetBrowserStatusBar } from './AssetBrowserStatusBar';\nimport { AssetBrowserStore } from './AssetBrowserStore';\nimport { AssetBrowserSurface } from './AssetBrowserSurface';\nimport { AssetBrowserToolbar } from './AssetBrowserToolbar';\nimport { getSlotKind, markSlot } from './slots';\nimport { useAssetBrowserHandle } from './useAssetBrowserHandle';\nimport { useAssetBrowserViewState } from './useAssetBrowserViewState';\nimport { useAssetDnd } from './useAssetDnd';\nimport { useAssetMutationKeyboard } from './useAssetMutationKeyboard';\nimport { useAssetMutations } from './useAssetMutations';\nimport { useAssetNavigation } from './useAssetNavigation';\nimport { useAssetSelectionController } from './useAssetSelectionController';\n\nimport type { AssetBrowserProps } from './AssetBrowser.types';\nimport type {\n AssetBrowserChromeValue,\n AssetBrowserContextValue,\n} from './AssetBrowserContext';\nimport type { ReactNode } from 'react';\n\nconst DEFAULT_MIME = 'application/x-entangle-asset';\n\ninterface SlotProps {\n children?: ReactNode;\n}\n\nconst ToolbarSlot = markSlot(() => null, 'toolbar');\nconst SidebarSlot = markSlot(() => null, 'sidebar');\n\n/**\n * Pull the optional `Toolbar` / `Sidebar` slot children out of `children`.\n * Returns through an explicit type so the slots stay `ReactNode` at the call\n * site (a `let x: ReactNode = null` assigned only inside the forEach callback\n * would otherwise stay flow-narrowed to `null`).\n */\nfunction extractSlots(children: ReactNode): {\n toolbarSlot: ReactNode;\n sidebarSlot: ReactNode;\n} {\n let toolbarSlot: ReactNode = null;\n let sidebarSlot: ReactNode = null;\n React.Children.forEach(children, child => {\n if (!React.isValidElement(child)) return;\n const kind = getSlotKind(child);\n const slotChildren = (child.props as SlotProps).children;\n if (kind === 'toolbar') toolbarSlot = slotChildren;\n else if (kind === 'sidebar') sidebarSlot = slotChildren;\n });\n return { toolbarSlot, sidebarSlot };\n}\n\nfunction AssetBrowserRoot(props: AssetBrowserProps): React.ReactElement {\n const {\n items,\n path = [],\n folderTree,\n currentFolderId,\n selectionMode = 'multiple',\n density = 'comfortable',\n showStatusBar = false,\n manualSearch = false,\n manualFilter = false,\n manualSort = false,\n filterableTypes: filterableTypesProp,\n loading = false,\n loadingItemCount = 12,\n emptyState,\n error,\n onErrorRetry,\n columns,\n marquee = true,\n history = false,\n renderThumbnail,\n renderItem,\n renderItemContextMenu,\n renderEmptyContextMenu,\n defaultItemActions = false,\n onItemRename,\n onItemsDelete,\n onCreateFolder,\n onItemsDuplicate,\n virtualized = 'auto',\n virtualizationThreshold = 200,\n overscanRows = 4,\n dragOutMimeType = DEFAULT_MIME,\n getDragData,\n onItemDragStart,\n onItemDragEnd,\n onItemsMove,\n onFilesImport,\n className,\n style,\n testId,\n children,\n ref,\n labels: labelsProp,\n 'aria-label': ariaLabel = 'Asset browser',\n // Controlled-state props — destructured so they don't leak onto the DOM\n // root via `...rest`. Consumed by the view-state / selection / navigation\n // hooks below.\n view: viewProp,\n defaultView,\n onViewChange,\n thumbnailSize: thumbnailSizeProp,\n defaultThumbnailSize,\n onThumbnailSizeChange,\n search: searchProp,\n defaultSearch,\n onSearchChange,\n filters: filtersProp,\n defaultFilters,\n onFiltersChange,\n sort: sortProp,\n defaultSort,\n onSortChange,\n selection: selectionProp,\n defaultSelection,\n onSelectionChange,\n onItemOpen,\n onNavigate,\n ...rest\n } = props;\n\n const rootRef = useRef<HTMLDivElement>(null);\n // Lazy `useState` initializer (not `useMemo`) so the per-instance store\n // identity is guaranteed stable for the component's lifetime — `useMemo` may\n // legally drop its cache, which would orphan every slice subscription. Mirrors\n // `useNavigationHistory`'s store-creation idiom.\n const [store] = useState(() => new AssetBrowserStore());\n\n const labels = useMemo(() => resolveLabels(labelsProp), [labelsProp]);\n\n const {\n view,\n setView,\n thumbnailSize,\n setThumbnailSize,\n thumbnailSizePx,\n search,\n setSearch,\n filters,\n setFilters,\n sort,\n setSort,\n filterableTypes,\n displayed,\n marqueeEnabled,\n } = useAssetBrowserViewState({\n items,\n selectionMode,\n marquee,\n view: viewProp,\n defaultView,\n onViewChange,\n thumbnailSize: thumbnailSizeProp,\n defaultThumbnailSize,\n onThumbnailSizeChange,\n search: searchProp,\n defaultSearch,\n onSearchChange,\n manualSearch,\n filters: filtersProp,\n defaultFilters,\n onFiltersChange,\n manualFilter,\n sort: sortProp,\n defaultSort,\n onSortChange,\n manualSort,\n filterableTypesProp,\n });\n\n const { selectionSet, handlers } = useAssetSelectionController({\n store,\n displayed,\n selectionMode,\n selection: selectionProp,\n defaultSelection,\n onSelectionChange,\n });\n\n const {\n navigate,\n activateItem,\n goBack,\n goForward,\n canGoBack,\n canGoForward,\n handleRootKeyDown,\n } = useAssetNavigation({\n history,\n path,\n currentFolderId,\n onNavigate,\n onItemOpen,\n });\n\n const dnd = useAssetDnd({\n store,\n mime: dragOutMimeType,\n items,\n selection: selectionSet,\n currentFolderId,\n getDragData,\n onItemDragStart,\n onItemDragEnd,\n onItemsMove,\n onFilesImport,\n });\n\n const mutations = useAssetMutations({\n store,\n displayed,\n selection: selectionSet,\n currentFolderId,\n onItemRename,\n onItemsDelete,\n onCreateFolder,\n onItemsDuplicate,\n });\n\n const mutationKeyboard = useAssetMutationKeyboard({\n store,\n mutations,\n selection: selectionSet,\n });\n\n useAssetBrowserHandle({\n ref,\n rootRef,\n store,\n items,\n displayed,\n selectAll: handlers.selectAll,\n clearSelection: handlers.clearSelection,\n beginRename: mutations.beginRename,\n });\n\n // Render-props are forwarded through the item context as-is. They are called\n // during render (by grid/list cells), so they can't be stabilized with a\n // render-phase ref write (react-hooks/refs) or `useLatest` (its ref updates\n // in an effect, one render late). Consumers that pass inline render-props and\n // care about per-cell render cost should memoize them, mirroring `TreeView`.\n const itemValue = useMemo<AssetBrowserContextValue>(\n () => ({\n displayedItems: displayed,\n thumbnailSizePx,\n density,\n selectionMode,\n marqueeEnabled,\n handleItemClick: handlers.handleItemClick,\n activateItem,\n selectAll: handlers.selectAll,\n clearSelection: handlers.clearSelection,\n contextMenuSelect: handlers.contextMenuSelect,\n selectByKeyboard: handlers.selectByKeyboard,\n toggleSelectId: handlers.toggleSelectId,\n setSelectionIds: handlers.setSelectionIds,\n commitMarquee: handlers.commitMarquee,\n path,\n folderTree,\n currentFolderId,\n navigate,\n renderThumbnail,\n renderItem,\n renderItemContextMenu,\n renderEmptyContextMenu,\n defaultItemActions,\n mutations,\n mutationKeyDown: mutationKeyboard.onKeyDown,\n loading,\n loadingItemCount,\n emptyState,\n error,\n onErrorRetry,\n columns,\n dnd,\n virtualized,\n virtualizationThreshold,\n overscanRows,\n labels,\n }),\n [\n displayed,\n thumbnailSizePx,\n density,\n selectionMode,\n marqueeEnabled,\n handlers,\n activateItem,\n path,\n folderTree,\n currentFolderId,\n navigate,\n renderThumbnail,\n renderItem,\n renderItemContextMenu,\n renderEmptyContextMenu,\n defaultItemActions,\n mutations,\n mutationKeyboard.onKeyDown,\n loading,\n loadingItemCount,\n emptyState,\n error,\n onErrorRetry,\n columns,\n dnd,\n virtualized,\n virtualizationThreshold,\n overscanRows,\n labels,\n ]\n );\n\n const chromeValue = useMemo<AssetBrowserChromeValue>(\n () => ({\n view,\n setView,\n search,\n setSearch,\n filters,\n setFilters,\n filterableTypes,\n sort,\n setSort,\n history,\n canGoBack,\n canGoForward,\n goBack,\n goForward,\n labels,\n }),\n [\n view,\n setView,\n search,\n setSearch,\n filters,\n setFilters,\n filterableTypes,\n sort,\n setSort,\n history,\n canGoBack,\n canGoForward,\n goBack,\n goForward,\n labels,\n ]\n );\n\n // Slot extraction.\n const { toolbarSlot, sidebarSlot } = extractSlots(children);\n\n return (\n <AssetBrowserStoreContext.Provider value={store}>\n <AssetBrowserChromeContext.Provider value={chromeValue}>\n <AssetBrowserContext.Provider value={itemValue}>\n {/* The root is a labelled region that hosts the asset-browser widget;\n its onKeyDown adds widget-level shortcuts (Backspace / Alt+ArrowUp\n → parent folder), so the handler on a non-interactive role is\n intentional. */}\n {/* eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions */}\n <div\n ref={rootRef}\n className={cx(root, className)}\n style={style}\n data-testid={testId}\n role=\"region\"\n tabIndex={-1}\n aria-label={ariaLabel}\n onKeyDown={handleRootKeyDown}\n {...rest}\n >\n {toolbarSlot ?? <AssetBrowserToolbar />}\n <AssetBrowserBreadcrumbs />\n <div className={body}>\n {sidebarSlot ?? <AssetBrowserSidebar />}\n <AssetBrowserSurface\n onDragOver={dnd.onSurfaceDragOver}\n onDragLeave={dnd.onSurfaceDragLeave}\n onDrop={dnd.onSurfaceDrop}\n />\n </div>\n {showStatusBar && (\n <AssetBrowserStatusBar\n view={view}\n thumbnailSize={thumbnailSize}\n setThumbnailSize={setThumbnailSize}\n />\n )}\n <AssetBrowserAnnouncementLive />\n </div>\n </AssetBrowserContext.Provider>\n </AssetBrowserChromeContext.Provider>\n </AssetBrowserStoreContext.Provider>\n );\n}\n\nAssetBrowserRoot.displayName = 'AssetBrowser';\n\nexport const AssetBrowser = /*#__PURE__*/ Object.assign(AssetBrowserRoot, {\n Toolbar: ToolbarSlot,\n Sidebar: SidebarSlot,\n});\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAoCA;AAMA;AACA;AAEA;;;;;AAKG;AACH;;;;AAOI;;AACA;AACA;;;;;AAGF;AACA;AACF;AAEA;AACE;;;;;AAuEA;;;;;AAKA;AAEA;AAEA;;;;AAmBE;;;AAGA;;;AAGA;;;;AAIA;;;;AAIA;;;;;AAKD;AAED;;;;AAIE;;;AAGD;AAED;;;;;;AAcC;;;AAIC;;AAEA;;;;;;;AAOD;;;;AAKC;;;;;;AAMD;;;;AAKC;AACD;AAED;;;;;;;;;AASC;;;;;;AAOD;AAEI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoCD;;;;;;;;;;;;;;;;;;AAmBC;;;;;;;;;;;;AAYD;AAGH;;;;;;;;;;;;;;;;AAiBG;;;;;;;;;;;;;;;;AAiBA;;;AAMH;AA2CF;AAEA;AAEO;AACL;AACA;AACD;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AssetBrowserAnnouncementLive.js","sources":["
|
|
1
|
+
{"version":3,"file":"AssetBrowserAnnouncementLive.js","sources":["src/components/editor/AssetBrowser/AssetBrowserAnnouncementLive.tsx"],"sourcesContent":["'use client';\n\nimport React from 'react';\n\nimport { srOnly } from './AssetBrowser.css';\nimport { useAssetBrowserAnnouncement } from './useAssetBrowserAnnouncement';\n\n/**\n * Screen-reader-only `aria-live=\"polite\"` region carrying the current\n * \"N items, M selected\" announcement. Subscribes to the displayed-count and\n * selection slices via {@link useAssetBrowserAnnouncement}.\n */\nexport function AssetBrowserAnnouncementLive(): React.ReactElement {\n const announcement = useAssetBrowserAnnouncement();\n return (\n <div className={srOnly} aria-live=\"polite\" role=\"status\">\n {announcement}\n </div>\n );\n}\n"],"names":[],"mappings":";;;;;AAOA;;;;AAIG;;AAED;AACA;AAKF;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AssetBrowserBreadcrumbs.js","sources":["
|
|
1
|
+
{"version":3,"file":"AssetBrowserBreadcrumbs.js","sources":["src/components/editor/AssetBrowser/AssetBrowserBreadcrumbs.tsx"],"sourcesContent":["'use client';\n\nimport React from 'react';\n\nimport {\n Breadcrumbs,\n BreadcrumbItem,\n} from '@/components/navigation/Breadcrumbs';\n\nimport { breadcrumbsBar } from './AssetBrowser.css';\nimport { useAssetBrowserContext } from './AssetBrowserContext';\n\nexport function AssetBrowserBreadcrumbs(): React.ReactElement | null {\n const ctx = useAssetBrowserContext();\n if (ctx.path.length === 0) return null;\n\n return (\n <div className={breadcrumbsBar}>\n <Breadcrumbs maxItems={4} itemsAfterCollapse={2} itemsBeforeCollapse={1}>\n {ctx.path.map((segment, index) => {\n const isLast = index === ctx.path.length - 1;\n return (\n <BreadcrumbItem\n key={segment.id}\n isCurrent={isLast}\n onClick={\n isLast\n ? undefined\n : () => {\n ctx.navigate(segment.id, 'breadcrumb');\n }\n }\n >\n {segment.name}\n </BreadcrumbItem>\n );\n })}\n </Breadcrumbs>\n </div>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;AAaE;AACA;AAA2B;AAE3B;;;AAWgB;;;;AASV;AAIR;;"}
|
|
@@ -92,6 +92,12 @@ import '../../Icons/GitBranchIcon.js';
|
|
|
92
92
|
import '../../Icons/BugIcon.js';
|
|
93
93
|
import '../../Icons/MinusIcon.js';
|
|
94
94
|
import '../../Icons/PinIcon.js';
|
|
95
|
+
import '../../Icons/OrbitIcon.js';
|
|
96
|
+
import '../../Icons/RotateIcon.js';
|
|
97
|
+
import '../../Icons/SpinIcon.js';
|
|
98
|
+
import '../../Icons/CameraIcon.js';
|
|
99
|
+
import '../../Icons/CubeIcon.js';
|
|
100
|
+
import '../../Icons/ZoomFitIcon.js';
|
|
95
101
|
import { Button } from '../../primitives/Button/Button.js';
|
|
96
102
|
import { emptyWrap, emptyText, loadingGrid, skeletonCell, cellWidthVar } from './AssetBrowser.css.js';
|
|
97
103
|
import { useAssetBrowserContext, useAssetBrowserChrome } from './AssetBrowserContext.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AssetBrowserContent.js","sources":["
|
|
1
|
+
{"version":3,"file":"AssetBrowserContent.js","sources":["src/components/editor/AssetBrowser/AssetBrowserContent.tsx"],"sourcesContent":["'use client';\n\nimport { assignInlineVars } from '@vanilla-extract/dynamic';\nimport React from 'react';\n\nimport { EmptyState } from '@/components/feedback/EmptyState';\nimport { Skeleton } from '@/components/feedback/Skeleton';\nimport { ErrorIcon } from '@/components/Icons';\nimport { Button } from '@/components/primitives/Button';\n\nimport {\n cellWidthVar,\n emptyText,\n emptyWrap,\n loadingGrid,\n skeletonCell,\n} from './AssetBrowser.css';\nimport {\n useAssetBrowserChrome,\n useAssetBrowserContext,\n} from './AssetBrowserContext';\nimport { cellWidth } from './assetBrowserGeometry';\nimport { AssetBrowserGrid } from './AssetBrowserGrid';\nimport { AssetBrowserList } from './AssetBrowserList';\n\n/**\n * Skeleton placeholder grid shown while `loading`. Renders `loadingItemCount`\n * cells sized to the current thumbnail scale (a square block + a label line)\n * so the loading state matches the real grid's geometry instead of a lone\n * spinner. Static (`animation=\"none\"`) — dozens of pulsing blocks are noise.\n */\nfunction LoadingGrid({\n count,\n thumbPx,\n}: {\n count: number;\n thumbPx: number;\n}): React.ReactElement {\n return (\n <div\n className={loadingGrid}\n style={assignInlineVars({ [cellWidthVar]: `${cellWidth(thumbPx)}px` })}\n role=\"presentation\"\n aria-hidden\n data-testid=\"asset-browser-loading\"\n >\n {Array.from({ length: Math.max(0, count) }, (_, i) => (\n <div key={i} className={skeletonCell}>\n <Skeleton width={thumbPx} height={thumbPx} animation=\"none\" />\n <Skeleton shape=\"line\" width=\"80%\" animation=\"none\" />\n </div>\n ))}\n </div>\n );\n}\n\n/**\n * Resolves the main content area: error → error state, loading → skeleton grid,\n * empty → empty state, otherwise the grid or list view depending on\n * `chrome.view`.\n */\nexport function AssetBrowserContent(): React.ReactElement {\n const {\n loading,\n loadingItemCount,\n thumbnailSizePx,\n displayedItems,\n emptyState,\n error,\n onErrorRetry,\n labels,\n } = useAssetBrowserContext();\n const { view } = useAssetBrowserChrome();\n\n // `null` / `undefined` / `false` → no error; `true` → built-in message;\n // any other node → custom replacement.\n if (error !== undefined && error !== null && error !== false) {\n return (\n <div className={emptyWrap}>\n {error === true ? (\n <EmptyState\n icon={<ErrorIcon />}\n title={labels.errorTitle}\n description={labels.errorDescription}\n action={\n onErrorRetry ? (\n <Button size=\"sm\" variant=\"default\" onClick={onErrorRetry}>\n {labels.retry}\n </Button>\n ) : undefined\n }\n />\n ) : (\n error\n )}\n </div>\n );\n }\n if (loading) {\n return view === 'grid' ? (\n <LoadingGrid count={loadingItemCount} thumbPx={thumbnailSizePx} />\n ) : (\n <div className={emptyWrap}>\n <EmptyState loading title={labels.loading} />\n </div>\n );\n }\n if (displayedItems.length === 0) {\n return (\n <div className={emptyWrap}>\n {emptyState ?? <span className={emptyText}>{labels.empty}</span>}\n </div>\n );\n }\n return view === 'grid' ? <AssetBrowserGrid /> : <AssetBrowserList />;\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyBA;;;;;AAKG;AACH;;AAuBA;AAEA;;;;AAIG;;;AAYD;;;AAIA;AACE;;;AAsBA;;AAQF;AACE;;AAMF;AACF;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AssetBrowserContext.js","sources":["
|
|
1
|
+
{"version":3,"file":"AssetBrowserContext.js","sources":["src/components/editor/AssetBrowser/AssetBrowserContext.ts"],"sourcesContent":["'use client';\n\nimport { createContext, useContext, useSyncExternalStore } from 'react';\n\nimport type {\n AssetFilterState,\n AssetItem,\n AssetItemActions,\n AssetItemState,\n AssetNavigationSource,\n AssetPathSegment,\n AssetSelectionReason,\n AssetSortState,\n AssetView,\n} from './AssetBrowser.types';\nimport type { AssetBrowserLabels } from './assetBrowserLabels';\nimport type {\n AssetBrowserStore,\n DragState,\n MarqueeState,\n} from './AssetBrowserStore';\nimport type { AssetMutationsApi } from './useAssetMutations';\nimport type { TreeNodeData } from '@/components/controls/TreeView';\nimport type {\n DataTableColumn,\n DataTableDensity,\n} from '@/components/data/DataTable';\nimport type { ReactNode } from 'react';\nimport type React from 'react';\n\n// ── Store context (hot path) ─────────────────────────────────────────────\n\nexport const AssetBrowserStoreContext =\n /*#__PURE__*/ createContext<AssetBrowserStore | null>(null);\n\nexport function useAssetBrowserStore(): AssetBrowserStore {\n const store = useContext(AssetBrowserStoreContext);\n if (!store) {\n throw new Error(\n 'AssetBrowser slice hooks must be used inside an <AssetBrowser> subtree.'\n );\n }\n return store;\n}\n\nexport function useAssetFocus(): string | null {\n const store = useAssetBrowserStore();\n return useSyncExternalStore(store.subscribeFocus, store.getFocusedId);\n}\n\n/**\n * Per-id focus slice: `true` only for the roving-focus item. Returns a boolean\n * so arrow-key navigation re-renders just the two affected cells, not the whole\n * grid.\n */\nexport function useAssetFocused(id: string): boolean {\n const store = useAssetBrowserStore();\n return useSyncExternalStore(\n store.subscribeFocus,\n () => store.getFocusedId() === id\n );\n}\n\n/**\n * Per-id rename slice: `true` only for the item whose label is being edited\n * inline. Returns a boolean so entering/leaving rename re-renders only that\n * cell.\n */\nexport function useAssetEditing(id: string): boolean {\n const store = useAssetBrowserStore();\n return useSyncExternalStore(\n store.subscribeEditing,\n () => store.getEditingId() === id\n );\n}\n\nexport function useAssetMarquee(): MarqueeState {\n const store = useAssetBrowserStore();\n return useSyncExternalStore(store.subscribeMarquee, store.getMarquee);\n}\n\nexport function useAssetDrag(): DragState {\n const store = useAssetBrowserStore();\n return useSyncExternalStore(store.subscribeDrag, store.getDrag);\n}\n\n/** Per-id drop-target slice: `true` while an active drag hovers this folder. */\nexport function useAssetDropTarget(id: string): boolean {\n const store = useAssetBrowserStore();\n return useSyncExternalStore(store.subscribeDrag, () => {\n const drag = store.getDrag();\n return drag.active && drag.dropTargetId === id;\n });\n}\n\n/** The full selection set (whole-set consumers: list view, status bar). */\nexport function useAssetSelection(): ReadonlySet<string> {\n const store = useAssetBrowserStore();\n return useSyncExternalStore(store.subscribeSelection, store.getSelection);\n}\n\n/**\n * Per-id selection slice. Returns a boolean so selecting an item re-renders\n * only the cells whose state actually flipped.\n */\nexport function useAssetSelected(id: string): boolean {\n const store = useAssetBrowserStore();\n return useSyncExternalStore(store.subscribeSelection, () =>\n store.getSelection().has(id)\n );\n}\n\n// ── Item context (data + interaction; consumed by every grid cell) ──────────\n\nexport interface AssetDndContext {\n internalEnabled: boolean;\n externalEnabled: boolean;\n onItemDragStart: (item: AssetItem, event: React.DragEvent) => void;\n onItemDragEnd: (item: AssetItem, event: React.DragEvent) => void;\n onFolderDragOver: (folderId: string, event: React.DragEvent) => void;\n onFolderDragLeave: (folderId: string, event: React.DragEvent) => void;\n onFolderDrop: (folderId: string, event: React.DragEvent) => void;\n onSurfaceDragOver: (event: React.DragEvent) => void;\n onSurfaceDragLeave: (event: React.DragEvent) => void;\n onSurfaceDrop: (event: React.DragEvent) => void;\n}\n\nexport interface AssetBrowserContextValue {\n // data\n displayedItems: readonly AssetItem[];\n\n // layout / mode (read by cells)\n thumbnailSizePx: number;\n density: DataTableDensity;\n selectionMode: 'single' | 'multiple' | false;\n /** Whether rubber-band marquee selection is active (multiple + `marquee`). */\n marqueeEnabled: boolean;\n\n // interaction handlers\n handleItemClick: (\n item: AssetItem,\n index: number,\n event: React.MouseEvent\n ) => void;\n activateItem: (item: AssetItem) => void;\n selectAll: () => void;\n clearSelection: () => void;\n contextMenuSelect: (item: AssetItem) => void;\n /** Select-or-extend driven by keyboard navigation over displayed order. */\n selectByKeyboard: (index: number, extend: boolean) => void;\n /** Toggle a single id (Space / Ctrl-click parity). */\n toggleSelectId: (id: string) => void;\n /** Replace selection with a raw id list (DataTable bridge). */\n setSelectionIds: (ids: string[], reason: AssetSelectionReason) => void;\n /** Commit a marquee result (additive merges with current selection). */\n commitMarquee: (ids: string[], additive: boolean) => void;\n\n // navigation\n path: readonly AssetPathSegment[];\n folderTree?: readonly TreeNodeData[];\n currentFolderId?: string;\n navigate: (folderId: string, source: AssetNavigationSource) => void;\n\n // render props\n renderThumbnail?: (\n item: AssetItem,\n ctx: { size: number; selected: boolean }\n ) => ReactNode;\n renderItem?: (item: AssetItem, state: AssetItemState) => ReactNode;\n renderItemContextMenu?: (\n items: AssetItem[],\n actions: AssetItemActions\n ) => ReactNode;\n renderEmptyContextMenu?: () => ReactNode;\n /** Auto-build default Rename/Duplicate/Delete/New-folder menu entries. */\n defaultItemActions: boolean;\n\n // mutations (inline rename, delete, create-folder, duplicate)\n mutations: AssetMutationsApi;\n /**\n * Mutation keyboard handler (F2 / Delete / Ctrl+D). Attached by each view to\n * its own scroller so the shortcuts work in both grid and list without\n * relying on event bubbling to the root.\n */\n mutationKeyDown: (event: React.KeyboardEvent) => void;\n\n // states\n loading: boolean;\n loadingItemCount: number;\n emptyState?: ReactNode;\n error?: ReactNode;\n onErrorRetry?: () => void;\n columns?: readonly DataTableColumn<AssetItem>[];\n\n // drag & drop\n dnd: AssetDndContext;\n\n // virtualization\n virtualized: boolean | 'auto';\n virtualizationThreshold: number;\n overscanRows: number;\n\n // i18n (resolved: defaults merged with the consumer's overrides)\n labels: AssetBrowserLabels;\n}\n\nexport const AssetBrowserContext =\n /*#__PURE__*/ createContext<AssetBrowserContextValue | null>(null);\n\nexport function useAssetBrowserContext(): AssetBrowserContextValue {\n const value = useContext(AssetBrowserContext);\n if (!value) {\n throw new Error(\n 'AssetBrowser subcomponents must be used inside an <AssetBrowser>.'\n );\n }\n return value;\n}\n\n// ── Chrome context (view / search / filter / sort / history) ────────────────\n// Split from the item context so typing in the search box (a per-keystroke\n// update) re-renders only the toolbar, not every grid cell.\n\nexport interface AssetBrowserChromeValue {\n view: AssetView;\n setView: (view: AssetView) => void;\n\n search: string;\n setSearch: (query: string) => void;\n filters: AssetFilterState;\n setFilters: (filters: AssetFilterState) => void;\n filterableTypes: string[];\n sort: AssetSortState;\n setSort: (sort: AssetSortState) => void;\n\n // history (only meaningful when the `history` prop is enabled)\n history: boolean;\n canGoBack: boolean;\n canGoForward: boolean;\n goBack: () => void;\n goForward: () => void;\n\n // i18n (resolved: defaults merged with the consumer's overrides)\n labels: AssetBrowserLabels;\n}\n\nexport const AssetBrowserChromeContext =\n /*#__PURE__*/ createContext<AssetBrowserChromeValue | null>(null);\n\nexport function useAssetBrowserChrome(): AssetBrowserChromeValue {\n const value = useContext(AssetBrowserChromeContext);\n if (!value) {\n throw new Error(\n 'AssetBrowser chrome subcomponents must be used inside an <AssetBrowser>.'\n );\n }\n return value;\n}\n"],"names":[],"mappings":";;;AA8BA;;AAGE;;AAGA;;AAEE;;AAIF;AACF;AAOA;;;;AAIG;AACG;AACJ;AACA;AAIF;AAEA;;;;AAIG;AACG;AACJ;AACA;AAIF;;AAGE;;AAEF;;AAGE;;AAEF;AAEA;AACM;AACJ;AACA;AACE;;AAEF;AACF;AAEA;;AAEE;;AAEF;AAEA;;;AAGG;AACG;AACJ;AACA;AAGF;;AAiGE;;AAGA;;AAEE;;AAIF;AACF;;AA8BE;;AAGA;;AAEE;;AAIF;AACF;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AssetBrowserContextMenu.js","sources":["
|
|
1
|
+
{"version":3,"file":"AssetBrowserContextMenu.js","sources":["src/components/editor/AssetBrowser/AssetBrowserContextMenu.tsx"],"sourcesContent":["'use client';\n\nimport React, { useRef, useState } from 'react';\n\nimport { ContextMenu } from '@/components/navigation/ContextMenu';\n\nimport { contextMenuTrigger } from './AssetBrowser.css';\nimport {\n useAssetBrowserContext,\n useAssetSelection,\n} from './AssetBrowserContext';\nimport { DefaultEmptyMenu, DefaultItemMenu } from './AssetBrowserDefaultMenus';\n\nimport type { AssetItem, AssetItemActions } from './AssetBrowser.types';\nimport type { AssetBrowserContextValue } from './AssetBrowserContext';\n\n/** Whether any item-level menu can be shown (custom render-prop or auto-built). */\nfunction hasItemMenu(ctx: AssetBrowserContextValue): boolean {\n if (ctx.renderItemContextMenu) return true;\n return (\n ctx.defaultItemActions &&\n (ctx.mutations.canRename ||\n ctx.mutations.canDelete ||\n ctx.mutations.canDuplicate)\n );\n}\n\n/** Whether any empty-area menu can be shown. */\nfunction hasEmptyMenu(ctx: AssetBrowserContextValue): boolean {\n if (ctx.renderEmptyContextMenu) return true;\n return ctx.defaultItemActions && ctx.mutations.canCreateFolder;\n}\n\n/**\n * Item context-menu body for the right-clicked item. Resolves the acted-on\n * items (the whole selection when the target is part of it, otherwise just the\n * target), builds the mutation `actions` bound to them, and renders either the\n * consumer's `renderItemContextMenu` or the auto-built default menu. Rendered\n * only while the menu is open (base-ui unmounts closed popups).\n */\nfunction ItemMenuBody({ targetId }: { targetId: string }): React.ReactElement {\n const ctx = useAssetBrowserContext();\n const selection = useAssetSelection();\n const target = ctx.displayedItems.find(it => it.id === targetId);\n const items: AssetItem[] = target\n ? selection.has(target.id)\n ? ctx.displayedItems.filter(it => selection.has(it.id))\n : [target]\n : [];\n\n const actions: AssetItemActions = {\n items,\n rename: () => {\n const first = items[0];\n if (first) ctx.mutations.beginRename(first.id);\n },\n delete: () => {\n ctx.mutations.deleteItems(items);\n },\n duplicate: () => {\n ctx.mutations.duplicateItems(items);\n },\n };\n\n if (ctx.renderItemContextMenu) {\n return <>{ctx.renderItemContextMenu(items, actions)}</>;\n }\n return <DefaultItemMenu actions={actions} />;\n}\n\n/** Empty-area context-menu body (consumer render-prop or the default). */\nfunction EmptyMenuBody(): React.ReactElement {\n const ctx = useAssetBrowserContext();\n if (ctx.renderEmptyContextMenu) return <>{ctx.renderEmptyContextMenu()}</>;\n return <DefaultEmptyMenu />;\n}\n\n/**\n * Single, view-agnostic context-menu mechanism for the whole content surface.\n *\n * One `ContextMenu` wraps both the grid and the list. On right-click it walks up\n * from the event target to the nearest item element — a grid cell\n * (`[data-asset-id]`) or a list row (`[data-row-key]`, set by DataTable to the\n * row's id) — to decide whether an **item** was hit (show the item menu + select\n * it) or **empty space** (show the empty-area menu). The same code path runs\n * regardless of view, so grid and list share identical right-click behaviour\n * with no per-cell / per-row wrapping and no DataTable changes.\n *\n * Open state is controlled so a right-click that resolves to no body (e.g. blank\n * space when only an item menu is configured) never opens an empty popup.\n */\nexport function AssetBrowserContextMenuLayer({\n children,\n}: {\n children: React.ReactNode;\n}): React.ReactElement {\n const ctx = useAssetBrowserContext();\n const [open, setOpen] = useState(false);\n const [targetId, setTargetId] = useState<string | null>(null);\n // Synchronous mirror of the right-clicked target, read by `onOpenChange`\n // (which fires in the same event, before the `setTargetId` render flushes).\n const targetIdRef = useRef<string | null>(null);\n\n const itemEnabled = hasItemMenu(ctx);\n const emptyEnabled = hasEmptyMenu(ctx);\n\n // No menus configured → render the surface untouched.\n if (!itemEnabled && !emptyEnabled) return <>{children}</>;\n\n const handleContextMenuCapture = (event: React.MouseEvent): void => {\n // Grid cells carry `data-asset-id`; DataTable list rows carry `data-row-key`\n // (the asset id, since `rowKey=\"id\"`). Match either to find the hit item.\n const el =\n event.target instanceof Element\n ? event.target.closest('[data-asset-id], [data-row-key]')\n : null;\n const id =\n el?.getAttribute('data-asset-id') ??\n el?.getAttribute('data-row-key') ??\n null;\n targetIdRef.current = id;\n setTargetId(id);\n if (id) {\n const item = ctx.displayedItems.find(it => it.id === id);\n if (item) ctx.contextMenuSelect(item);\n }\n };\n\n const showItemBody = targetId !== null && itemEnabled;\n\n return (\n <ContextMenu\n open={open}\n onOpenChange={next => {\n // Only allow opening when the resolved target has a body to show.\n if (next) {\n const willShow = targetIdRef.current ? itemEnabled : emptyEnabled;\n setOpen(willShow);\n } else {\n setOpen(false);\n }\n }}\n >\n <ContextMenu.Trigger\n render={\n <div\n className={contextMenuTrigger}\n onContextMenuCapture={handleContextMenuCapture}\n >\n {children}\n </div>\n }\n />\n <ContextMenu.Content>\n {showItemBody ? (\n <ItemMenuBody targetId={targetId} />\n ) : emptyEnabled ? (\n <EmptyMenuBody />\n ) : null}\n </ContextMenu.Content>\n </ContextMenu>\n );\n}\n"],"names":[],"mappings":";;;;;;;;AAgBA;AACA;;AACiC;;AAG7B;;AAEE;AAEN;AAEA;AACA;;AACkC;;AAElC;AAEA;;;;;;AAMG;AACH;AACE;AACA;AACA;;;AAGI;;;AAIJ;;;AAGI;AACA;;;;AAGA;;;AAGA;;;AAIJ;;;AAGA;AACF;AAEA;AACA;AACE;;AACgC;;AAElC;AAEA;;;;;;;;;;;;;AAaG;AACG;AAKJ;;;;;AAKA;AAEA;AACA;;AAGA;;AAEA;;;AAGE;;;AAIA;AAEE;AACA;AACF;;;AAGE;AACA;AAAU;;AAEd;AAEA;AAEA;;;AAMQ;;;;;;;AA0BV;;"}
|
|
@@ -87,6 +87,12 @@ import '../../Icons/GitBranchIcon.js';
|
|
|
87
87
|
import '../../Icons/BugIcon.js';
|
|
88
88
|
import '../../Icons/MinusIcon.js';
|
|
89
89
|
import '../../Icons/PinIcon.js';
|
|
90
|
+
import '../../Icons/OrbitIcon.js';
|
|
91
|
+
import '../../Icons/RotateIcon.js';
|
|
92
|
+
import '../../Icons/SpinIcon.js';
|
|
93
|
+
import '../../Icons/CameraIcon.js';
|
|
94
|
+
import '../../Icons/CubeIcon.js';
|
|
95
|
+
import '../../Icons/ZoomFitIcon.js';
|
|
90
96
|
import { Menu } from '../../navigation/Menu/Menu.js';
|
|
91
97
|
import { useAssetBrowserContext } from './AssetBrowserContext.js';
|
|
92
98
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AssetBrowserDefaultMenus.js","sources":["
|
|
1
|
+
{"version":3,"file":"AssetBrowserDefaultMenus.js","sources":["src/components/editor/AssetBrowser/AssetBrowserDefaultMenus.tsx"],"sourcesContent":["'use client';\n\nimport React from 'react';\n\nimport { CopyIcon, EditIcon, FolderIcon, TrashIcon } from '@/components/Icons';\nimport { Menu } from '@/components/navigation/Menu';\n\nimport { useAssetBrowserContext } from './AssetBrowserContext';\n\nimport type { AssetItemActions } from './AssetBrowser.types';\n\n/**\n * Auto-generated item context-menu body used when `defaultItemActions` is set\n * and the consumer hasn't supplied `renderItemContextMenu`. Renders only the\n * entries whose mutation callback is present. Rename targets a single item.\n */\nexport function DefaultItemMenu({\n actions,\n}: {\n actions: AssetItemActions;\n}): React.ReactElement | null {\n const { mutations, labels } = useAssetBrowserContext();\n const single = actions.items.length === 1;\n\n const entries: React.ReactNode[] = [];\n if (mutations.canRename) {\n entries.push(\n <Menu.Item\n key=\"rename\"\n icon={<EditIcon />}\n shortcut=\"F2\"\n disabled={!single}\n onClick={actions.rename}\n >\n {labels.rename}\n </Menu.Item>\n );\n }\n if (mutations.canDuplicate) {\n entries.push(\n <Menu.Item\n key=\"duplicate\"\n icon={<CopyIcon />}\n onClick={actions.duplicate}\n >\n {labels.duplicate}\n </Menu.Item>\n );\n }\n if (mutations.canDelete) {\n if (entries.length > 0) entries.push(<Menu.Separator key=\"sep\" />);\n entries.push(\n <Menu.Item key=\"delete\" icon={<TrashIcon />} onClick={actions.delete}>\n {labels.delete}\n </Menu.Item>\n );\n }\n\n if (entries.length === 0) return null;\n return <>{entries}</>;\n}\n\n/**\n * Auto-generated empty-area context-menu body used when `defaultItemActions` is\n * set and the consumer hasn't supplied `renderEmptyContextMenu`. Currently just\n * the New-folder entry (when `onCreateFolder` is present).\n */\nexport function DefaultEmptyMenu(): React.ReactElement | null {\n const { mutations, labels } = useAssetBrowserContext();\n if (!mutations.canCreateFolder) return null;\n return (\n <Menu.Item icon={<FolderIcon />} onClick={mutations.createFolder}>\n {labels.newFolder}\n </Menu.Item>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAWA;;;;AAIG;AACG;;;;AASJ;AACE;;AAYF;;;AAWA;AACE;;;;AAQF;AAA0B;;AAE5B;AAEA;;;;AAIG;;;;AAG+B;;AAMlC;;"}
|
|
@@ -6,7 +6,7 @@ import '../../../utils/devWarn.js';
|
|
|
6
6
|
import { useLatest } from '../../../hooks/useLatest/useLatest.js';
|
|
7
7
|
import '../../../hooks/useBreakpoint/useBreakpoint.js';
|
|
8
8
|
import './../../../assets/src/theme/lightTheme.css.ts.vanilla-Ct6f9Iym.css';
|
|
9
|
-
import '
|
|
9
|
+
import '@vanilla-extract/css';
|
|
10
10
|
import './../../../assets/src/theme/darkTheme.css.ts.vanilla-BHZ3ecRn.css';
|
|
11
11
|
import './../../../assets/src/theme/globalScrollbars.css.ts.vanilla-BAJwnUEJ.css';
|
|
12
12
|
import '../../../hooks/useNavigationHistory/useNavigationHistory.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AssetBrowserGrid.js","sources":["
|
|
1
|
+
{"version":3,"file":"AssetBrowserGrid.js","sources":["src/components/editor/AssetBrowser/AssetBrowserGrid.tsx"],"sourcesContent":["'use client';\n\nimport { assignInlineVars } from '@vanilla-extract/dynamic';\nimport React, {\n useCallback,\n useEffect,\n useId,\n useLayoutEffect,\n useRef,\n} from 'react';\n\nimport { useLatest } from '@/hooks';\n\nimport {\n columnsVar,\n gridGapVar,\n gridScroller,\n gridSizer,\n gridWindow,\n gridFlow,\n cellWidthVar,\n marquee as marqueeClass,\n marqueeHVar,\n marqueeWVar,\n marqueeXVar,\n marqueeYVar,\n offsetYVar,\n totalHeightVar,\n} from './AssetBrowser.css';\nimport {\n useAssetBrowserContext,\n useAssetBrowserStore,\n useAssetMarquee,\n} from './AssetBrowserContext';\nimport { GRID_GAP, cellDomId, cellWidth } from './assetBrowserGeometry';\nimport { AssetBrowserGridItem } from './AssetBrowserGridItem';\nimport { useAssetGridKeyboardNav } from './useAssetGridKeyboardNav';\nimport { useAssetGridVirtualizer } from './useAssetGridVirtualizer';\nimport { useAssetMarqueeGesture } from './useAssetMarqueeGesture';\n\nimport type { AssetGridWindow } from './useAssetGridVirtualizer';\n\nfunction MarqueeLayer(): React.ReactElement | null {\n const marquee = useAssetMarquee();\n if (!marquee.active || !marquee.rect) return null;\n return (\n <div\n className={marqueeClass}\n style={assignInlineVars({\n [marqueeXVar]: `${marquee.rect.x}px`,\n [marqueeYVar]: `${marquee.rect.y}px`,\n [marqueeWVar]: `${marquee.rect.width}px`,\n [marqueeHVar]: `${marquee.rect.height}px`,\n })}\n />\n );\n}\n\nexport function AssetBrowserGrid(): React.ReactElement {\n const ctx = useAssetBrowserContext();\n const store = useAssetBrowserStore();\n const scrollerRef = useRef<HTMLDivElement>(null);\n const gridId = useId();\n\n const items = ctx.displayedItems;\n const count = items.length;\n const thumbPx = ctx.thumbnailSizePx;\n\n // Mirror the store's roving focus onto the grid's `aria-activedescendant`\n // imperatively, so a screen reader announces the focused cell without us\n // re-rendering the whole grid on every arrow-key (the per-id focus slices keep\n // re-renders to the two affected cells). The matching cell `id` is built from\n // the same `gridId` in AssetBrowserGridItem via `cellDomId`.\n useEffect(() => {\n const el = scrollerRef.current;\n if (!el) return;\n const apply = (): void => {\n const focusedId = store.getFocusedId();\n if (focusedId === null) el.removeAttribute('aria-activedescendant');\n else\n el.setAttribute('aria-activedescendant', cellDomId(gridId, focusedId));\n };\n apply();\n return store.subscribeFocus(apply);\n }, [store, gridId]);\n\n const windowed =\n ctx.virtualized === true ||\n (ctx.virtualized === 'auto' && count > ctx.virtualizationThreshold);\n\n const win = useAssetGridVirtualizer({\n containerRef: scrollerRef,\n count,\n thumbPx,\n overscanRows: ctx.overscanRows,\n enabled: windowed,\n });\n\n const scrollIndexIntoView = useCallback(\n (index: number, w: AssetGridWindow): void => {\n const el = scrollerRef.current;\n if (!el) return;\n const row = Math.floor(index / w.columns);\n const top = row * w.rowHeight;\n const bottom = top + w.rowHeight;\n if (top < el.scrollTop) el.scrollTop = top;\n else if (bottom > el.scrollTop + el.clientHeight)\n el.scrollTop = bottom - el.clientHeight;\n },\n []\n );\n\n // Bridge the imperative `scrollToItem` handle through the store so it works\n // even when the target row is outside the virtualization window.\n const winRef = useLatest(win);\n useLayoutEffect(\n () =>\n store.registerScrollToIndex(index => {\n scrollIndexIntoView(index, winRef.current);\n }),\n [store, winRef, scrollIndexIntoView]\n );\n\n const keyboard = useAssetGridKeyboardNav({\n store,\n items,\n win,\n scrollerRef,\n selectionMode: ctx.selectionMode,\n selectAll: ctx.selectAll,\n clearSelection: ctx.clearSelection,\n activateItem: ctx.activateItem,\n toggleSelectId: ctx.toggleSelectId,\n selectByKeyboard: ctx.selectByKeyboard,\n scrollIndexIntoView,\n });\n\n const marquee = useAssetMarqueeGesture({\n scrollerRef,\n enabled: ctx.marqueeEnabled,\n items,\n columns: win.columns,\n thumbPx,\n commitMarquee: ctx.commitMarquee,\n });\n\n const sharedVars = assignInlineVars({\n [columnsVar]: String(win.columns),\n [gridGapVar]: `${GRID_GAP}px`,\n [cellWidthVar]: `${cellWidth(thumbPx)}px`,\n });\n\n // Grid keydown = 2D navigation + the shared mutation shortcuts (F2 / Delete /\n // Ctrl+D). Both are attached here, on the element that receives the event, so\n // the shortcuts fire without depending on bubbling to the root. The key sets\n // are disjoint, so order doesn't matter.\n const mutationKeyDown = ctx.mutationKeyDown;\n const onGridKeyDown = useCallback(\n (event: React.KeyboardEvent): void => {\n keyboard.onKeyDown(event);\n mutationKeyDown(event);\n },\n [keyboard, mutationKeyDown]\n );\n\n const rendered: React.ReactNode[] = [];\n for (let i = win.startIndex; i < win.endIndex; i += 1) {\n const item = items[i];\n if (!item) continue;\n rendered.push(\n <AssetBrowserGridItem\n key={item.id}\n item={item}\n index={i}\n gridId={gridId}\n columns={win.columns}\n />\n );\n }\n\n return (\n <div\n ref={scrollerRef}\n className={gridScroller}\n role=\"grid\"\n aria-label={ctx.labels.gridAria}\n aria-multiselectable={ctx.selectionMode === 'multiple'}\n aria-rowcount={win.totalRows}\n aria-colcount={win.columns}\n tabIndex={0}\n onKeyDown={onGridKeyDown}\n onPointerDown={marquee.onPointerDown}\n onPointerMove={marquee.onPointerMove}\n onPointerUp={marquee.onPointerUp}\n onPointerCancel={marquee.onPointerCancel}\n >\n {windowed ? (\n <div\n className={gridSizer}\n style={assignInlineVars({\n [totalHeightVar]: `${win.totalHeight}px`,\n })}\n >\n <MarqueeLayer />\n <div\n className={gridWindow}\n style={{\n ...sharedVars,\n ...assignInlineVars({ [offsetYVar]: `${win.offsetY}px` }),\n }}\n role=\"presentation\"\n >\n {rendered}\n </div>\n </div>\n ) : (\n <>\n <MarqueeLayer />\n <div className={gridFlow} style={sharedVars} role=\"presentation\">\n {rendered}\n </div>\n </>\n )}\n </div>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;AA0CA;AACE;;AACsC;;;;;;;AAYxC;;AAGE;AACA;AACA;AACA;AAEA;AACA;AACA;;;;;;;AAQE;AACA;;;AAEE;;AACwB;;AAEtB;AACJ;AACA;AACA;AACF;AAEA;AAEE;;AAGA;;;;AAIA;AACD;;AAIG;AACA;;AACA;AACA;AACA;AACA;AAAwB;;;;;;AAS5B;;AAIM;;;;;;;;;;;;;;AAiBL;;;;;;;;AASA;;;AAIC;;AAED;;;;;AAMD;AACA;AAEI;;AAEF;;AAKF;AACE;AACA;;AACA;;AAWF;AAoBU;;AAOE;AACA;AACD;AAgBb;;"}
|
|
@@ -89,6 +89,12 @@ 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 { Icon } from '../../primitives/Icon/Icon.js';
|
|
93
99
|
import { cellLabel, thumbSizeVar, cellHeightVar, cellWidthVar, cellRecipe, thumb, thumbImage } from './AssetBrowser.css.js';
|
|
94
100
|
import { useAssetBrowserContext, useAssetSelected, useAssetFocused, useAssetDropTarget, useAssetEditing } from './AssetBrowserContext.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AssetBrowserGridItem.js","sources":["
|
|
1
|
+
{"version":3,"file":"AssetBrowserGridItem.js","sources":["src/components/editor/AssetBrowser/AssetBrowserGridItem.tsx"],"sourcesContent":["'use client';\n\nimport { assignInlineVars } from '@vanilla-extract/dynamic';\nimport React, { useState } from 'react';\n\nimport {\n CodeIcon,\n EyeDropperIcon,\n FileTextIcon,\n FolderIcon,\n GridIcon,\n PlayIcon,\n} from '@/components/Icons';\nimport { Icon } from '@/components/primitives/Icon';\n\nimport {\n cellLabel,\n cellRecipe,\n cellWidthVar,\n cellHeightVar,\n thumb,\n thumbImage,\n thumbSizeVar,\n} from './AssetBrowser.css';\nimport {\n useAssetBrowserContext,\n useAssetDropTarget,\n useAssetEditing,\n useAssetFocused,\n useAssetSelected,\n} from './AssetBrowserContext';\nimport { cellDomId, cellHeight, cellWidth } from './assetBrowserGeometry';\nimport { AssetBrowserRenameField } from './AssetBrowserRenameField';\n\nimport type { AssetItem, AssetType } from './AssetBrowser.types';\n\nexport interface AssetBrowserGridItemProps {\n item: AssetItem;\n index: number;\n /** Grid `useId` base — builds the cell `id` for `aria-activedescendant`. */\n gridId: string;\n /** Current column count — derives 1-based `aria-rowindex`/`aria-colindex`. */\n columns: number;\n}\n\n/**\n * Type-derived fallback glyph for files without a thumbnail or explicit `icon`.\n * Only the asset types whose fallback is actually seen (image/model usually\n * carry a thumbnail) get a distinct icon; the rest share the generic file icon.\n */\nfunction typeFallbackIcon(\n assetType: AssetType | undefined\n): React.ReactElement {\n switch (assetType) {\n case 'material':\n return <EyeDropperIcon />;\n case 'audio':\n case 'video':\n return <PlayIcon />;\n case 'scene':\n return <GridIcon />;\n case 'text':\n return <CodeIcon />;\n default:\n return <FileTextIcon />;\n }\n}\n\nfunction FallbackIcon({ item }: { item: AssetItem }): React.ReactElement {\n if (item.icon) return <>{item.icon}</>;\n return (\n <Icon size=\"lg\" color=\"muted\" decorative>\n {item.kind === 'folder' ? (\n <FolderIcon />\n ) : (\n typeFallbackIcon(item.assetType)\n )}\n </Icon>\n );\n}\n\nfunction Thumbnail({\n item,\n thumbPx,\n selected,\n}: {\n item: AssetItem;\n thumbPx: number;\n selected: boolean;\n}): React.ReactElement {\n const { renderThumbnail } = useAssetBrowserContext();\n const custom = renderThumbnail?.(item, { size: thumbPx, selected });\n // Broken / expired / CORS-failed thumbnail URLs fall back to the type icon\n // instead of the browser's broken-image glyph. The error flag resets when the\n // source changes via the adjust-state-during-render pattern (a previous-prop\n // sentinel in state — component-patterns.md §14), so no effect is needed.\n const [errored, setErrored] = useState(false);\n const [errorUrl, setErrorUrl] = useState(item.thumbnailUrl);\n if (item.thumbnailUrl !== errorUrl) {\n setErrorUrl(item.thumbnailUrl);\n setErrored(false);\n }\n const showImg = !custom && item.thumbnailUrl !== undefined && !errored;\n return (\n <div className={thumb}>\n {custom ??\n (showImg ? (\n <img\n className={thumbImage}\n src={item.thumbnailUrl}\n alt=\"\"\n loading=\"lazy\"\n decoding=\"async\"\n draggable={false}\n onError={() => {\n setErrored(true);\n }}\n />\n ) : (\n <FallbackIcon item={item} />\n ))}\n </div>\n );\n}\n\nfunction GridItemImpl({\n item,\n index,\n gridId,\n columns,\n}: AssetBrowserGridItemProps): React.ReactElement {\n const ctx = useAssetBrowserContext();\n const selected = useAssetSelected(item.id);\n const focused = useAssetFocused(item.id);\n const dropActive = useAssetDropTarget(item.id);\n const editing = useAssetEditing(item.id);\n\n const isDropTarget = item.kind === 'folder' && dropActive;\n const thumbPx = ctx.thumbnailSizePx;\n // 1-based grid position for assistive tech, computed from display index so it\n // reflects true totals even though only the virtualization window is mounted.\n const cols = Math.max(1, columns);\n const ariaRowIndex = Math.floor(index / cols) + 1;\n const ariaColIndex = (index % cols) + 1;\n\n const isDraggable = item.draggable !== false && item.disabled !== true;\n const isFolderTarget =\n item.kind === 'folder' &&\n item.droppable !== false &&\n ctx.dnd.internalEnabled;\n\n const inner = ctx.renderItem ? (\n ctx.renderItem(item, {\n selected,\n focused,\n index,\n view: 'grid',\n dropTarget: isDropTarget,\n })\n ) : (\n <>\n <Thumbnail item={item} thumbPx={thumbPx} selected={selected} />\n {editing ? (\n <AssetBrowserRenameField item={item} />\n ) : (\n <span className={cellLabel}>{item.name}</span>\n )}\n </>\n );\n\n const cell = (\n // Keyboard interaction is centralized at the grid level: cells use roving\n // tabIndex and AssetBrowserGrid's onKeyDown drives Enter/Space/arrow nav,\n // so a redundant per-cell key handler is intentionally omitted.\n // eslint-disable-next-line jsx-a11y/click-events-have-key-events\n <div\n role=\"gridcell\"\n id={cellDomId(gridId, item.id)}\n data-asset-id={item.id}\n aria-selected={selected}\n aria-label={item.name}\n aria-rowindex={ariaRowIndex}\n aria-colindex={ariaColIndex}\n title={item.name}\n tabIndex={focused ? 0 : -1}\n className={cellRecipe({\n selected,\n dropTarget: isDropTarget,\n disabled: item.disabled === true,\n })}\n style={assignInlineVars({\n [cellWidthVar]: `${cellWidth(thumbPx)}px`,\n [cellHeightVar]: `${cellHeight(thumbPx)}px`,\n [thumbSizeVar]: `${thumbPx}px`,\n })}\n // While renaming, the cell is non-interactive for selection/open/drag —\n // clicks on the thumbnail above the field must not reselect or navigate.\n draggable={isDraggable && !editing}\n onClick={\n editing\n ? undefined\n : e => {\n ctx.handleItemClick(item, index, e);\n }\n }\n onDoubleClick={\n editing\n ? undefined\n : () => {\n ctx.activateItem(item);\n }\n }\n onDragStart={\n isDraggable && !editing\n ? e => {\n ctx.dnd.onItemDragStart(item, e);\n }\n : undefined\n }\n onDragEnd={\n isDraggable\n ? e => {\n ctx.dnd.onItemDragEnd(item, e);\n }\n : undefined\n }\n onDragOver={\n isFolderTarget\n ? e => {\n ctx.dnd.onFolderDragOver(item.id, e);\n }\n : undefined\n }\n onDragLeave={\n isFolderTarget\n ? e => {\n ctx.dnd.onFolderDragLeave(item.id, e);\n }\n : undefined\n }\n onDrop={\n isFolderTarget\n ? e => {\n ctx.dnd.onFolderDrop(item.id, e);\n }\n : undefined\n }\n >\n {inner}\n </div>\n );\n\n // The right-click menu and roving-focus marker `data-asset-id` are handled by\n // the shared AssetBrowserContextMenuLayer at the surface level — the cell is a\n // plain grid child with no per-cell ContextMenu wrapper.\n return cell;\n}\n\nexport const AssetBrowserGridItem = /*#__PURE__*/ React.memo(GridItemImpl);\nAssetBrowserGridItem.displayName = 'AssetBrowserGridItem';\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6CA;;;;AAIG;AACH;;AAII;;AAEA;AACA;;AAEA;;AAEA;;AAEA;;;AAGJ;AAEA;;AACiB;AACf;AASF;AAEA;AASE;AACA;;;;;;AAMA;AACA;AACE;;;AAGF;AACA;AAGM;;AAUI;AAOZ;AAEA;AAME;;;;;;AAOA;;;;AAIA;;AAGA;AACA;;AAGE;AAEF;;;;AAKI;AACA;;AAaJ;;;;;;;AAiBM;AACA;AACD;;;AAIC;;;;;AAOE;;;;AAOA;;AAEI;AACF;;;;AAOF;;;;AAOA;;;;AAOA;;;;AAOA;;;;AAOA;;;;AAUR;AACF;AAEO;AACP;;"}
|
|
@@ -89,6 +89,12 @@ 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 { Icon } from '../../primitives/Icon/Icon.js';
|
|
93
99
|
import { listScroller, nameCell } from './AssetBrowser.css.js';
|
|
94
100
|
import { useAssetBrowserContext, useAssetBrowserChrome, useAssetSelection, useAssetEditing } from './AssetBrowserContext.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AssetBrowserList.js","sources":["
|
|
1
|
+
{"version":3,"file":"AssetBrowserList.js","sources":["src/components/editor/AssetBrowser/AssetBrowserList.tsx"],"sourcesContent":["'use client';\n\nimport React, { useMemo } from 'react';\n\nimport { DataTable } from '@/components/data/DataTable';\nimport { FileTextIcon, FolderIcon } from '@/components/Icons';\nimport { Icon } from '@/components/primitives/Icon';\n\nimport { listScroller, nameCell } from './AssetBrowser.css';\nimport {\n useAssetBrowserChrome,\n useAssetBrowserContext,\n useAssetEditing,\n useAssetSelection,\n} from './AssetBrowserContext';\nimport { formatBytes, formatDate } from './assetBrowserFormat';\nimport { AssetBrowserRenameField } from './AssetBrowserRenameField';\n\nimport type { AssetItem } from './AssetBrowser.types';\nimport type { AssetBrowserLabels } from './assetBrowserLabels';\nimport type {\n DataTableColumn,\n DataTableSortState,\n} from '@/components/data/DataTable';\n\n/**\n * Name cell — swaps to the inline rename field when this row is the rename\n * target. A component (not an inline `cell` fn) so the `useAssetEditing` hook is\n * valid; this is the same shared rename mechanism the grid uses.\n */\nfunction ListNameCell({ item }: { item: AssetItem }): React.ReactElement {\n const editing = useAssetEditing(item.id);\n if (editing) return <AssetBrowserRenameField item={item} />;\n return (\n <span className={nameCell}>\n <Icon size=\"sm\" color=\"muted\" decorative>\n {item.kind === 'folder' ? <FolderIcon /> : <FileTextIcon />}\n </Icon>\n <span>{item.name}</span>\n </span>\n );\n}\n\n/**\n * Default name/type/size/modified columns, built from the resolved labels so\n * the headers and the folder \"Type\" value follow the `labels` prop.\n */\nfunction buildDefaultColumns(\n labels: AssetBrowserLabels\n): readonly DataTableColumn<AssetItem>[] {\n return [\n {\n id: 'name',\n header: labels.sortFields.name,\n accessor: 'name',\n sortable: true,\n sticky: true,\n minWidth: 160,\n cell: ({ row }) => <ListNameCell item={row} />,\n },\n {\n id: 'type',\n header: labels.sortFields.type,\n sortable: true,\n width: 120,\n accessor: row =>\n row.kind === 'folder' ? labels.folder : (row.assetType ?? ''),\n },\n {\n id: 'size',\n header: labels.sortFields.size,\n sortable: true,\n align: 'right',\n width: 100,\n accessor: 'size',\n cell: ({ row }) => (row.kind === 'folder' ? '—' : formatBytes(row.size)),\n },\n {\n id: 'modified',\n header: labels.sortFields.modified,\n sortable: true,\n align: 'right',\n width: 140,\n accessor: 'modifiedAt',\n cell: ({ row }) => formatDate(row.modifiedAt),\n },\n ];\n}\n\nexport function AssetBrowserList(): React.ReactElement {\n const ctx = useAssetBrowserContext();\n const chrome = useAssetBrowserChrome();\n const selectionSet = useAssetSelection();\n\n const defaultColumns = useMemo(\n () => buildDefaultColumns(ctx.labels),\n [ctx.labels]\n );\n const columns = ctx.columns ?? defaultColumns;\n\n // We own sorting (rows arrive already shaped); DataTable only reflects the\n // indicator and reports the next desired sort.\n const sort: DataTableSortState = {\n columnId: chrome.sort.field,\n direction: chrome.sort.direction,\n };\n\n const selection = useMemo(() => Array.from(selectionSet), [selectionSet]);\n\n return (\n // The mutation shortcuts (F2 / Delete / Ctrl+D) attach to this wrapper;\n // DataTable's own keydowns bubble up to it. F2 targets the sole selected row\n // (the list has no store roving-focus). Static-element handler is intentional.\n // eslint-disable-next-line jsx-a11y/no-static-element-interactions\n <div className={listScroller} onKeyDown={ctx.mutationKeyDown}>\n <DataTable<AssetItem>\n rows={ctx.displayedItems}\n columns={columns}\n rowKey=\"id\"\n density={ctx.density}\n height=\"100%\"\n sort={sort}\n manualSort\n onSortChange={next => {\n if (!next.columnId || !next.direction) {\n chrome.setSort({ field: 'name', direction: 'asc' });\n return;\n }\n chrome.setSort({ field: next.columnId, direction: next.direction });\n }}\n selectionMode={ctx.selectionMode}\n selection={selection}\n onSelectionChange={ids => {\n ctx.setSelectionIds(ids, 'click');\n }}\n isRowSelectable={row =>\n row.selectable !== false && row.disabled !== true\n }\n onRowActivate={row => {\n ctx.activateItem(row);\n }}\n emptyState={ctx.emptyState}\n loading={ctx.loading}\n aria-label=\"Assets\"\n />\n </div>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyBA;;;;AAIG;AACH;;AAEE;AAAa;;AASf;AAEA;;;AAGG;AACH;;AAII;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACD;AACD;AACE;AACA;AACA;AACA;;AAGD;AACD;AACE;AACA;AACA;AACA;AACA;AACA;;AAED;AACD;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACD;;AAEL;;AAGE;AACA;AACA;;AAMA;;;AAIA;AACE;AACA;;AAGF;;;;;;;;AAkBU;;;AAGF;AACF;AAIE;;AAMA;AACF;AAOR;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AssetBrowserRenameField.js","sources":["
|
|
1
|
+
{"version":3,"file":"AssetBrowserRenameField.js","sources":["src/components/editor/AssetBrowser/AssetBrowserRenameField.tsx"],"sourcesContent":["'use client';\n\nimport React, { useCallback, useLayoutEffect, useRef, useState } from 'react';\n\nimport { Input } from '@/components/primitives/Input';\n\nimport { renameField } from './AssetBrowser.css';\nimport { useAssetBrowserContext } from './AssetBrowserContext';\n\nimport type { AssetItem } from './AssetBrowser.types';\n\nexport interface AssetBrowserRenameFieldProps {\n item: AssetItem;\n}\n\n/**\n * Inline label editor shown in place of a grid cell's name while the cell is\n * the rename target. Owns only the transient text buffer; the \"is editing\" flag\n * lives in the store. Commits on Enter / blur (changed value), cancels on\n * Escape, and stops key propagation so the grid's roving navigation doesn't\n * steal keystrokes.\n */\nexport function AssetBrowserRenameField({\n item,\n}: AssetBrowserRenameFieldProps): React.ReactElement {\n const { mutations, labels } = useAssetBrowserContext();\n const [value, setValue] = useState(item.name);\n const inputRef = useRef<HTMLInputElement>(null);\n // Guard against the blur fired by our own Enter/Escape commit double-firing.\n const committedRef = useRef(false);\n\n // Focus + select the basename once, after the field mounts.\n useLayoutEffect(() => {\n const el = inputRef.current;\n if (!el) return;\n el.focus();\n el.select();\n }, []);\n\n const commit = useCallback((): void => {\n if (committedRef.current) return;\n committedRef.current = true;\n void mutations.commitRename(item, value).then(accepted => {\n // Rejected name → reopen for another attempt.\n if (!accepted) committedRef.current = false;\n });\n }, [mutations, item, value]);\n\n const cancel = useCallback((): void => {\n committedRef.current = true;\n mutations.cancelRename();\n }, [mutations]);\n\n return (\n <Input\n ref={inputRef}\n className={renameField}\n size=\"sm\"\n value={value}\n aria-label={`${labels.rename} ${item.name}`}\n onChange={setValue}\n onKeyDown={e => {\n // Keep arrows / Space / type-ahead inside the field, away from the grid.\n e.stopPropagation();\n if (e.key === 'Enter') {\n e.preventDefault();\n commit();\n } else if (e.key === 'Escape') {\n e.preventDefault();\n cancel();\n }\n }}\n onBlur={commit}\n onClick={e => {\n // A click on the field shouldn't reselect / navigate the cell.\n e.stopPropagation();\n }}\n onDoubleClick={e => {\n e.stopPropagation();\n }}\n />\n );\n}\n"],"names":[],"mappings":";;;;;;;AAeA;;;;;;AAMG;AACG;;AAIJ;AACA;;AAEA;;;AAIE;AACA;;;;;AAKF;;;AAEE;AACA;;AAEE;AAAe;AACjB;;AAGF;AACE;;AAEF;AAEA;;;AAWM;;AAEE;;AACK;;AAEL;;;;;AAOJ;;;AAMN;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AssetBrowserSidebar.js","sources":["
|
|
1
|
+
{"version":3,"file":"AssetBrowserSidebar.js","sources":["src/components/editor/AssetBrowser/AssetBrowserSidebar.tsx"],"sourcesContent":["'use client';\n\nimport React from 'react';\n\nimport { TreeView } from '@/components/controls/TreeView';\n\nimport { sidebar } from './AssetBrowser.css';\nimport { useAssetBrowserContext } from './AssetBrowserContext';\n\nimport type { TreeNodeData } from '@/components/controls/TreeView';\n\nexport function AssetBrowserSidebar(): React.ReactElement | null {\n const ctx = useAssetBrowserContext();\n if (!ctx.folderTree) return null;\n\n return (\n <div className={sidebar}>\n <TreeView\n nodes={ctx.folderTree as TreeNodeData[]}\n selectionMode=\"single\"\n selectedIds={ctx.currentFolderId ? [ctx.currentFolderId] : []}\n onSelectionChange={ids => {\n const id = ids[0];\n if (id) ctx.navigate(id, 'sidebar');\n }}\n />\n </div>\n );\n}\n"],"names":[],"mappings":";;;;;;;AAYE;;AACqB;AAErB;AAOQ;AACA;AAAQ;;AAKlB;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AssetBrowserStatusBar.js","sources":["
|
|
1
|
+
{"version":3,"file":"AssetBrowserStatusBar.js","sources":["src/components/editor/AssetBrowser/AssetBrowserStatusBar.tsx"],"sourcesContent":["'use client';\n\nimport React from 'react';\n\nimport {\n SegmentedControl,\n SegmentedControlItem,\n} from '@/components/navigation/SegmentedControl';\n\nimport { statusBar, statusSpacer } from './AssetBrowser.css';\nimport { useAssetBrowserContext } from './AssetBrowserContext';\nimport { useAssetBrowserAnnouncement } from './useAssetBrowserAnnouncement';\n\nimport type { AssetThumbnailSize, AssetView } from './AssetBrowser.types';\n\nexport interface AssetBrowserStatusBarProps {\n /** Current view — drives whether the thumbnail-size control is rendered. */\n view: AssetView;\n thumbnailSize: AssetThumbnailSize;\n setThumbnailSize: (size: AssetThumbnailSize) => void;\n}\n\n/**\n * Optional footer bar showing the live announcement and (in grid view) a\n * thumbnail-size segmented control. The announcement is sourced from the\n * `useAssetBrowserAnnouncement` slice hook, so the bar updates without the\n * root recomputing it.\n */\nexport function AssetBrowserStatusBar({\n view,\n thumbnailSize,\n setThumbnailSize,\n}: AssetBrowserStatusBarProps): React.ReactElement {\n const announcement = useAssetBrowserAnnouncement();\n const { labels } = useAssetBrowserContext();\n return (\n <div className={statusBar}>\n <span>{announcement}</span>\n <span className={statusSpacer} />\n {view === 'grid' && (\n <SegmentedControl\n value={typeof thumbnailSize === 'string' ? thumbnailSize : 'md'}\n onChange={value => {\n setThumbnailSize(value as AssetThumbnailSize);\n }}\n size=\"sm\"\n aria-label={labels.thumbnailSizeAria}\n >\n <SegmentedControlItem value=\"sm\">S</SegmentedControlItem>\n <SegmentedControlItem value=\"md\">M</SegmentedControlItem>\n <SegmentedControlItem value=\"lg\">L</SegmentedControlItem>\n </SegmentedControl>\n )}\n </div>\n );\n}\n"],"names":[],"mappings":";;;;;;;;AAsBA;;;;;AAKG;AACG;AAKJ;AACA;AACA;;AASQ;AAWV;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AssetBrowserStore.js","sources":["../../../../../../src/components/editor/AssetBrowser/AssetBrowserStore.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AssetBrowserStore.js","sources":["src/components/editor/AssetBrowser/AssetBrowserStore.ts"],"sourcesContent":["/** Screen-space rectangle (px, relative to the grid scroll container). */\nexport interface MarqueeRect {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\nexport interface MarqueeState {\n active: boolean;\n rect: MarqueeRect | null;\n /** Whether the marquee adds to (Ctrl/Cmd) or replaces the selection. */\n additive: boolean;\n}\n\nexport interface DragState {\n active: boolean;\n draggingIds: string[];\n /** Folder id currently hovered as a drop target, or `'root'`/null for the surface. */\n dropTargetId: string | null;\n /** An external OS-file drag is hovering the surface. */\n externalOver: boolean;\n}\n\nconst EMPTY_SELECTION: ReadonlySet<string> = new Set<string>();\n\nconst INITIAL_MARQUEE: MarqueeState = {\n active: false,\n rect: null,\n additive: false,\n};\nconst INITIAL_DRAG: DragState = {\n active: false,\n draggingIds: [],\n dropTargetId: null,\n externalOver: false,\n};\n\nfunction sameDrag(a: DragState, b: DragState): boolean {\n return (\n a.active === b.active &&\n a.dropTargetId === b.dropTargetId &&\n a.externalOver === b.externalOver &&\n a.draggingIds.length === b.draggingIds.length &&\n a.draggingIds.every((id, i) => id === b.draggingIds[i])\n );\n}\n\nfunction sameMarquee(a: MarqueeState, b: MarqueeState): boolean {\n if (a.active !== b.active || a.additive !== b.additive) return false;\n if (a.rect === b.rect) return true;\n if (!a.rect || !b.rect) return false;\n return (\n a.rect.x === b.rect.x &&\n a.rect.y === b.rect.y &&\n a.rect.width === b.rect.width &&\n a.rect.height === b.rect.height\n );\n}\n\n/**\n * Per-instance hot-path store for AssetBrowser. Holds only state that updates\n * faster than a click — roving focus, the marquee rectangle, and drag state.\n *\n * View, search, filters, and sort are low-frequency and live in React state\n * (via `useControlledState`), not here. Selection is owned by React state too\n * (for the controlled API) but mirrored into this store so each grid item can\n * subscribe to its own \"am I selected?\" slice via `useAssetSelected(id)` rather\n * than re-rendering every cell on every selection change. Each slice exposes\n * its own `subscribe`/`get` pair for `useSyncExternalStore` so a marquee drag\n * does not re-render the toolbar.\n */\nexport class AssetBrowserStore {\n #focusedId: string | null = null;\n #editingId: string | null = null;\n #marquee: MarqueeState = INITIAL_MARQUEE;\n #drag: DragState = INITIAL_DRAG;\n #selection: ReadonlySet<string> = EMPTY_SELECTION;\n\n #focusListeners = new Set<() => void>();\n #editingListeners = new Set<() => void>();\n #marqueeListeners = new Set<() => void>();\n #dragListeners = new Set<() => void>();\n #selectionListeners = new Set<() => void>();\n\n /** Set by the grid so the imperative `scrollToItem` works under windowing. */\n #scrollToIndex: ((index: number) => void) | null = null;\n\n // ── Reads (arrow fns so they pass straight to useSyncExternalStore) ──\n getFocusedId = (): string | null => this.#focusedId;\n getEditingId = (): string | null => this.#editingId;\n getMarquee = (): MarqueeState => this.#marquee;\n getDrag = (): DragState => this.#drag;\n getSelection = (): ReadonlySet<string> => this.#selection;\n\n // ── Subscriptions ──\n subscribeFocus = (cb: () => void): (() => void) => {\n this.#focusListeners.add(cb);\n return () => {\n this.#focusListeners.delete(cb);\n };\n };\n subscribeEditing = (cb: () => void): (() => void) => {\n this.#editingListeners.add(cb);\n return () => {\n this.#editingListeners.delete(cb);\n };\n };\n subscribeSelection = (cb: () => void): (() => void) => {\n this.#selectionListeners.add(cb);\n return () => {\n this.#selectionListeners.delete(cb);\n };\n };\n subscribeMarquee = (cb: () => void): (() => void) => {\n this.#marqueeListeners.add(cb);\n return () => {\n this.#marqueeListeners.delete(cb);\n };\n };\n subscribeDrag = (cb: () => void): (() => void) => {\n this.#dragListeners.add(cb);\n return () => {\n this.#dragListeners.delete(cb);\n };\n };\n\n // ── Mutations (no-op when unchanged to keep snapshots stable) ──\n setFocusedId(next: string | null): void {\n if (next === this.#focusedId) return;\n this.#focusedId = next;\n this.#focusListeners.forEach(cb => {\n cb();\n });\n }\n\n /**\n * Id of the item whose label is being renamed inline, or `null`. Lives here\n * (not in React state) so rename can be triggered from outside the cell —\n * the F2 keyboard handler, a context-menu action, or the imperative\n * `beginRename` handle — and only the affected cell re-renders via its own\n * `useAssetEditing(id)` slice.\n */\n setEditingId(next: string | null): void {\n if (next === this.#editingId) return;\n this.#editingId = next;\n this.#editingListeners.forEach(cb => {\n cb();\n });\n }\n\n setMarquee(next: MarqueeState): void {\n if (sameMarquee(this.#marquee, next)) return;\n this.#marquee = next;\n this.#marqueeListeners.forEach(cb => {\n cb();\n });\n }\n\n clearMarquee(): void {\n this.setMarquee(INITIAL_MARQUEE);\n }\n\n setDrag(next: DragState): void {\n if (sameDrag(this.#drag, next)) return;\n this.#drag = next;\n this.#dragListeners.forEach(cb => {\n cb();\n });\n }\n\n clearDrag(): void {\n this.setDrag(INITIAL_DRAG);\n }\n\n /**\n * Mirror the React-owned selection. Callers pass a memoized Set, so a cheap\n * reference check keeps the snapshot stable when nothing changed.\n */\n setSelection(next: ReadonlySet<string>): void {\n if (next === this.#selection) return;\n this.#selection = next;\n this.#selectionListeners.forEach(cb => {\n cb();\n });\n }\n\n // ── Imperative scroll bridge ──\n registerScrollToIndex(fn: (index: number) => void): () => void {\n this.#scrollToIndex = fn;\n return () => {\n if (this.#scrollToIndex === fn) this.#scrollToIndex = null;\n };\n }\n\n /** Returns true when a grid was mounted to handle the scroll. */\n scrollToIndex(index: number): boolean {\n if (!this.#scrollToIndex) return false;\n this.#scrollToIndex(index);\n return true;\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -87,6 +87,12 @@ import '../../Icons/GitBranchIcon.js';
|
|
|
87
87
|
import '../../Icons/BugIcon.js';
|
|
88
88
|
import '../../Icons/MinusIcon.js';
|
|
89
89
|
import '../../Icons/PinIcon.js';
|
|
90
|
+
import '../../Icons/OrbitIcon.js';
|
|
91
|
+
import '../../Icons/RotateIcon.js';
|
|
92
|
+
import '../../Icons/SpinIcon.js';
|
|
93
|
+
import '../../Icons/CameraIcon.js';
|
|
94
|
+
import '../../Icons/CubeIcon.js';
|
|
95
|
+
import '../../Icons/ZoomFitIcon.js';
|
|
90
96
|
import { Icon } from '../../primitives/Icon/Icon.js';
|
|
91
97
|
import { main, importOverlay } from './AssetBrowser.css.js';
|
|
92
98
|
import { AssetBrowserContent } from './AssetBrowserContent.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AssetBrowserSurface.js","sources":["
|
|
1
|
+
{"version":3,"file":"AssetBrowserSurface.js","sources":["src/components/editor/AssetBrowser/AssetBrowserSurface.tsx"],"sourcesContent":["'use client';\n\nimport React from 'react';\n\nimport { CloudUploadIcon } from '@/components/Icons';\nimport { Icon } from '@/components/primitives/Icon';\n\nimport { importOverlay, main } from './AssetBrowser.css';\nimport { AssetBrowserContent } from './AssetBrowserContent';\nimport { useAssetBrowserContext, useAssetDrag } from './AssetBrowserContext';\nimport { AssetBrowserContextMenuLayer } from './AssetBrowserContextMenu';\n\nfunction ImportOverlay(): React.ReactElement | null {\n const drag = useAssetDrag();\n const { labels } = useAssetBrowserContext();\n if (!drag.externalOver) return null;\n return (\n <div className={importOverlay}>\n <Icon size=\"lg\" decorative>\n <CloudUploadIcon />\n </Icon>\n <span>{labels.importOverlay}</span>\n </div>\n );\n}\n\nexport interface AssetBrowserSurfaceProps {\n onDragOver: (event: React.DragEvent) => void;\n onDragLeave: (event: React.DragEvent) => void;\n onDrop: (event: React.DragEvent) => void;\n}\n\n/**\n * The scrollable content surface (grid/list/empty/loading/error) plus the\n * file-import overlay. Right-click menus (item + empty area) are handled by the\n * single view-agnostic {@link AssetBrowserContextMenuLayer}, which detects the\n * target from `data-asset-id` — so grid cells and list rows behave identically.\n */\nexport function AssetBrowserSurface({\n onDragOver,\n onDragLeave,\n onDrop,\n}: AssetBrowserSurfaceProps): React.ReactElement {\n return (\n <AssetBrowserContextMenuLayer>\n <div\n className={main}\n onDragOver={onDragOver}\n onDragLeave={onDragLeave}\n onDrop={onDrop}\n >\n <AssetBrowserContent />\n <ImportOverlay />\n </div>\n </AssetBrowserContextMenuLayer>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAYA;AACE;AACA;;AACwB;;AAS1B;AAQA;;;;;AAKG;AACG;AAKJ;AAaF;;"}
|
|
@@ -87,6 +87,12 @@ import '../../Icons/GitBranchIcon.js';
|
|
|
87
87
|
import '../../Icons/BugIcon.js';
|
|
88
88
|
import '../../Icons/MinusIcon.js';
|
|
89
89
|
import '../../Icons/PinIcon.js';
|
|
90
|
+
import '../../Icons/OrbitIcon.js';
|
|
91
|
+
import '../../Icons/RotateIcon.js';
|
|
92
|
+
import '../../Icons/SpinIcon.js';
|
|
93
|
+
import '../../Icons/CameraIcon.js';
|
|
94
|
+
import '../../Icons/CubeIcon.js';
|
|
95
|
+
import '../../Icons/ZoomFitIcon.js';
|
|
90
96
|
import { Menu } from '../../navigation/Menu/Menu.js';
|
|
91
97
|
import { SegmentedControl } from '../../navigation/SegmentedControl/SegmentedControl.js';
|
|
92
98
|
import { SegmentedControlItem } from '../../navigation/SegmentedControl/SegmentedControlItem.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AssetBrowserToolbar.js","sources":["
|
|
1
|
+
{"version":3,"file":"AssetBrowserToolbar.js","sources":["src/components/editor/AssetBrowser/AssetBrowserToolbar.tsx"],"sourcesContent":["'use client';\n\nimport React from 'react';\n\nimport {\n ChevronLeftIcon,\n ChevronRightIcon,\n FilterIcon,\n GridIcon,\n ListIcon,\n SearchIcon,\n SortIcon,\n} from '@/components/Icons';\nimport { Menu } from '@/components/navigation/Menu';\nimport {\n SegmentedControl,\n SegmentedControlItem,\n} from '@/components/navigation/SegmentedControl';\nimport { Icon } from '@/components/primitives/Icon';\nimport { IconButton } from '@/components/primitives/IconButton';\nimport { Input } from '@/components/primitives/Input';\n\nimport { searchField, toolbar, toolbarSpacer } from './AssetBrowser.css';\nimport { useAssetBrowserChrome } from './AssetBrowserContext';\n\nimport type { AssetSortField, AssetView } from './AssetBrowser.types';\n\nconst SORT_FIELDS: AssetSortField[] = ['name', 'type', 'size', 'modified'];\n\nexport function AssetBrowserToolbar(): React.ReactElement {\n const chrome = useAssetBrowserChrome();\n const { labels } = chrome;\n\n const toggleType = (type: string, checked: boolean): void => {\n const current = new Set(chrome.filters.types ?? []);\n if (checked) current.add(type);\n else current.delete(type);\n chrome.setFilters({ types: Array.from(current) });\n };\n\n return (\n <div className={toolbar}>\n {chrome.history && (\n <>\n <IconButton\n aria-label={labels.back}\n size=\"sm\"\n disabled={!chrome.canGoBack}\n onClick={chrome.goBack}\n >\n <ChevronLeftIcon size=\"sm\" decorative />\n </IconButton>\n <IconButton\n aria-label={labels.forward}\n size=\"sm\"\n disabled={!chrome.canGoForward}\n onClick={chrome.goForward}\n >\n <ChevronRightIcon size=\"sm\" decorative />\n </IconButton>\n </>\n )}\n\n <SegmentedControl\n value={chrome.view}\n onChange={value => {\n chrome.setView(value as AssetView);\n }}\n size=\"sm\"\n aria-label={labels.viewModeAria}\n >\n <SegmentedControlItem\n value=\"grid\"\n icon={<GridIcon />}\n tooltip={labels.gridViewTooltip}\n />\n <SegmentedControlItem\n value=\"list\"\n icon={<ListIcon />}\n tooltip={labels.listViewTooltip}\n />\n </SegmentedControl>\n\n <Input\n className={searchField}\n type=\"search\"\n size=\"sm\"\n placeholder={labels.searchPlaceholder}\n value={chrome.search}\n onChange={value => {\n chrome.setSearch(value);\n }}\n startIcon={\n <Icon size=\"sm\" decorative>\n <SearchIcon />\n </Icon>\n }\n />\n\n <span className={toolbarSpacer} />\n\n <Menu>\n <Menu.Trigger>\n <Icon size=\"sm\" decorative>\n <SortIcon />\n </Icon>\n {labels.sort}\n </Menu.Trigger>\n <Menu.Content>\n <Menu.RadioGroup\n value={chrome.sort.field}\n onValueChange={field => {\n chrome.setSort({ field, direction: chrome.sort.direction });\n }}\n >\n {SORT_FIELDS.map(field => (\n <Menu.RadioItem key={field} value={field}>\n {labels.sortFields[field]}\n </Menu.RadioItem>\n ))}\n </Menu.RadioGroup>\n <Menu.Separator />\n <Menu.RadioGroup\n value={chrome.sort.direction}\n onValueChange={direction => {\n chrome.setSort({\n field: chrome.sort.field,\n direction: direction === 'desc' ? 'desc' : 'asc',\n });\n }}\n >\n <Menu.RadioItem value=\"asc\">{labels.sortAscending}</Menu.RadioItem>\n <Menu.RadioItem value=\"desc\">\n {labels.sortDescending}\n </Menu.RadioItem>\n </Menu.RadioGroup>\n </Menu.Content>\n </Menu>\n\n {chrome.filterableTypes.length > 0 && (\n <Menu>\n <Menu.Trigger>\n <Icon size=\"sm\" decorative>\n <FilterIcon />\n </Icon>\n {labels.filter}\n </Menu.Trigger>\n <Menu.Content>\n {chrome.filterableTypes.map(type => (\n <Menu.CheckboxItem\n key={type}\n checked={(chrome.filters.types ?? []).includes(type)}\n onCheckedChange={checked => {\n toggleType(type, checked);\n }}\n >\n {type}\n </Menu.CheckboxItem>\n ))}\n </Menu.Content>\n </Menu>\n )}\n </div>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2BA;;AAGE;AACA;AAEA;AACE;AACA;AAAa;;AACR;AACL;AACF;;AA4BQ;AACF;AAuBE;AACF;AAqBM;;;AAcE;;AAED;;AAyBG;AACF;AAUhB;;"}
|