@dxos/react-ui-canvas 0.13.0 → 0.14.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/README.md +1 -1
- package/dist/lib/index.mjs +9044 -1135
- package/dist/lib/index.mjs.map +1 -1
- package/dist/types/src/components/About/About.d.ts +13 -0
- package/dist/types/src/components/About/About.d.ts.map +1 -0
- package/dist/types/src/components/About/About.stories.d.ts +7 -0
- package/dist/types/src/components/About/About.stories.d.ts.map +1 -0
- package/dist/types/src/components/About/index.d.ts +2 -0
- package/dist/types/src/components/About/index.d.ts.map +1 -0
- package/dist/types/src/components/Breadcrumbs/Breadcrumbs.d.ts.map +1 -1
- package/dist/types/src/components/ControlFrame/ControlFrame.d.ts +8 -2
- package/dist/types/src/components/ControlFrame/ControlFrame.d.ts.map +1 -1
- package/dist/types/src/components/Layers/Layers.d.ts +30 -0
- package/dist/types/src/components/Layers/Layers.d.ts.map +1 -0
- package/dist/types/src/components/Layers/Layers.stories.d.ts +11 -0
- package/dist/types/src/components/Layers/Layers.stories.d.ts.map +1 -0
- package/dist/types/src/components/Layers/index.d.ts +2 -0
- package/dist/types/src/components/Layers/index.d.ts.map +1 -0
- package/dist/types/src/components/Palette/Palette.d.ts +20 -4
- package/dist/types/src/components/Palette/Palette.d.ts.map +1 -1
- package/dist/types/src/components/Palette/Palette.stories.d.ts +22 -0
- package/dist/types/src/components/Palette/Palette.stories.d.ts.map +1 -0
- package/dist/types/src/components/PartEditor/PartEditor.d.ts.map +1 -1
- package/dist/types/src/components/Properties/AlignField.d.ts +23 -0
- package/dist/types/src/components/Properties/AlignField.d.ts.map +1 -0
- package/dist/types/src/components/Properties/ClassField.d.ts +23 -0
- package/dist/types/src/components/Properties/ClassField.d.ts.map +1 -0
- package/dist/types/src/components/Properties/FontField.d.ts +7 -0
- package/dist/types/src/components/Properties/FontField.d.ts.map +1 -0
- package/dist/types/src/components/Properties/LayerField.d.ts +17 -0
- package/dist/types/src/components/Properties/LayerField.d.ts.map +1 -0
- package/dist/types/src/components/Properties/Properties.d.ts +27 -3
- package/dist/types/src/components/Properties/Properties.d.ts.map +1 -1
- package/dist/types/src/components/Properties/Properties.stories.d.ts +20 -0
- package/dist/types/src/components/Properties/Properties.stories.d.ts.map +1 -0
- package/dist/types/src/components/Properties/SceneField.d.ts +8 -0
- package/dist/types/src/components/Properties/SceneField.d.ts.map +1 -0
- package/dist/types/src/components/Properties/StyleGrid.d.ts +15 -5
- package/dist/types/src/components/Properties/StyleGrid.d.ts.map +1 -1
- package/dist/types/src/components/Properties/StyleGrid.stories.d.ts +7 -0
- package/dist/types/src/components/Properties/StyleGrid.stories.d.ts.map +1 -0
- package/dist/types/src/components/SceneLayer/SceneLayer.d.ts +31 -7
- package/dist/types/src/components/SceneLayer/SceneLayer.d.ts.map +1 -1
- package/dist/types/src/components/SceneView/Dock.d.ts +22 -0
- package/dist/types/src/components/SceneView/Dock.d.ts.map +1 -0
- package/dist/types/src/components/SceneView/Dock.stories.d.ts +15 -0
- package/dist/types/src/components/SceneView/Dock.stories.d.ts.map +1 -0
- package/dist/types/src/components/SceneView/SceneView.d.ts +33 -7
- package/dist/types/src/components/SceneView/SceneView.d.ts.map +1 -1
- package/dist/types/src/components/SceneView/SceneView.stories.d.ts +16 -6
- package/dist/types/src/components/SceneView/SceneView.stories.d.ts.map +1 -1
- package/dist/types/src/components/SceneView/SceneViewContext.d.ts +36 -4
- package/dist/types/src/components/SceneView/SceneViewContext.d.ts.map +1 -1
- package/dist/types/src/components/SceneView/Scored.stories.d.ts +4 -4
- package/dist/types/src/components/SceneView/Scored.stories.d.ts.map +1 -1
- package/dist/types/src/components/SceneView/usePointerMachine.d.ts +3 -1
- package/dist/types/src/components/SceneView/usePointerMachine.d.ts.map +1 -1
- package/dist/types/src/components/SceneView/useSceneCamera.d.ts +2 -2
- package/dist/types/src/components/SceneView/useSceneCamera.d.ts.map +1 -1
- package/dist/types/src/components/SceneView/useSceneClipboard.d.ts.map +1 -1
- package/dist/types/src/components/SceneView/useSceneKeys.d.ts.map +1 -1
- package/dist/types/src/components/SceneView/useSceneNavigation.d.ts +7 -4
- package/dist/types/src/components/SceneView/useSceneNavigation.d.ts.map +1 -1
- package/dist/types/src/components/Toolbar/Toolbar.d.ts +2 -0
- package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +1 -1
- package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts.map +1 -1
- package/dist/types/src/components/index.d.ts +2 -0
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/hooks/useWheel.d.ts +5 -0
- package/dist/types/src/hooks/useWheel.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +4 -1
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/model/atoms.d.ts +21 -3
- package/dist/types/src/model/atoms.d.ts.map +1 -1
- package/dist/types/src/model/projection.d.ts.map +1 -1
- package/dist/types/src/model/projections/constrained.d.ts +2 -1
- package/dist/types/src/model/projections/constrained.d.ts.map +1 -1
- package/dist/types/src/model/projections/dynamic.d.ts.map +1 -1
- package/dist/types/src/model/projections/lattice.d.ts.map +1 -1
- package/dist/types/src/model/registry.d.ts +21 -3
- package/dist/types/src/model/registry.d.ts.map +1 -1
- package/dist/types/src/model/store.d.ts +10 -2
- package/dist/types/src/model/store.d.ts.map +1 -1
- package/dist/types/src/model/types.d.ts +699 -338
- package/dist/types/src/model/types.d.ts.map +1 -1
- package/dist/types/src/playwright/SceneManager.d.ts +1 -1
- package/dist/types/src/playwright/SceneManager.d.ts.map +1 -1
- package/dist/types/src/utils/builder.d.ts +22 -41
- package/dist/types/src/utils/builder.d.ts.map +1 -1
- package/dist/types/src/utils/camera.d.ts +19 -10
- package/dist/types/src/utils/camera.d.ts.map +1 -1
- package/dist/types/src/utils/clipboard.d.ts +4 -2
- package/dist/types/src/utils/clipboard.d.ts.map +1 -1
- package/dist/types/src/utils/diagram.d.ts.map +1 -1
- package/dist/types/src/utils/group.d.ts +14 -0
- package/dist/types/src/utils/group.d.ts.map +1 -0
- package/dist/types/src/utils/group.test.d.ts +2 -0
- package/dist/types/src/utils/group.test.d.ts.map +1 -0
- package/dist/types/src/utils/gutter-route.d.ts +7 -0
- package/dist/types/src/utils/gutter-route.d.ts.map +1 -1
- package/dist/types/src/utils/hit.d.ts +3 -20
- package/dist/types/src/utils/hit.d.ts.map +1 -1
- package/dist/types/src/utils/index.d.ts +3 -0
- package/dist/types/src/utils/index.d.ts.map +1 -1
- package/dist/types/src/utils/lattice.d.ts +5 -0
- package/dist/types/src/utils/lattice.d.ts.map +1 -1
- package/dist/types/src/utils/layers.d.ts +35 -0
- package/dist/types/src/utils/layers.d.ts.map +1 -0
- package/dist/types/src/utils/layers.test.d.ts +2 -0
- package/dist/types/src/utils/layers.test.d.ts.map +1 -0
- package/dist/types/src/utils/parts.d.ts +25 -9
- package/dist/types/src/utils/parts.d.ts.map +1 -1
- package/dist/types/src/utils/ports.d.ts +2 -0
- package/dist/types/src/utils/ports.d.ts.map +1 -1
- package/dist/types/src/utils/properties.d.ts +4 -3
- package/dist/types/src/utils/properties.d.ts.map +1 -1
- package/dist/types/src/utils/route.d.ts +5 -0
- package/dist/types/src/utils/route.d.ts.map +1 -1
- package/dist/types/src/utils/scenes.d.ts +14 -0
- package/dist/types/src/utils/scenes.d.ts.map +1 -0
- package/dist/types/src/utils/scenes.test.d.ts +2 -0
- package/dist/types/src/utils/scenes.test.d.ts.map +1 -0
- package/dist/types/src/utils/shapes.d.ts +23 -9
- package/dist/types/src/utils/shapes.d.ts.map +1 -1
- package/dist/types/src/utils/style.d.ts +29 -6
- package/dist/types/src/utils/style.d.ts.map +1 -1
- package/dist/types/src/utils/style.test.d.ts +2 -0
- package/dist/types/src/utils/style.test.d.ts.map +1 -0
- package/dist/types/src/utils/testing.d.ts +2 -2
- package/dist/types/src/utils/testing.d.ts.map +1 -1
- package/dist/types/src/utils/undo.d.ts +15 -4
- package/dist/types/src/utils/undo.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +20 -26
- package/src/components/About/About.stories.tsx +32 -0
- package/src/components/About/About.tsx +38 -0
- package/src/components/About/index.ts +5 -0
- package/src/components/Breadcrumbs/Breadcrumbs.tsx +17 -3
- package/src/components/ControlFrame/ControlFrame.tsx +138 -82
- package/src/components/Grid/Grid.stories.tsx +1 -1
- package/src/components/Layers/Layers.stories.tsx +131 -0
- package/src/components/Layers/Layers.tsx +216 -0
- package/src/components/Layers/index.ts +5 -0
- package/src/components/Palette/Palette.stories.tsx +135 -0
- package/src/components/Palette/Palette.tsx +230 -39
- package/src/components/PartEditor/PartEditor.tsx +4 -3
- package/src/components/Properties/AlignField.tsx +117 -0
- package/src/components/Properties/ClassField.tsx +44 -0
- package/src/components/Properties/FontField.tsx +73 -0
- package/src/components/Properties/LayerField.tsx +61 -0
- package/src/components/Properties/Properties.stories.tsx +113 -0
- package/src/components/Properties/Properties.tsx +240 -60
- package/src/components/Properties/SceneField.tsx +22 -0
- package/src/components/Properties/StyleGrid.stories.tsx +50 -0
- package/src/components/Properties/StyleGrid.tsx +54 -26
- package/src/components/SceneLayer/SceneLayer.tsx +497 -188
- package/src/components/SceneView/Constrained.stories.tsx +1 -1
- package/src/components/SceneView/Dock.stories.tsx +95 -0
- package/src/components/SceneView/Dock.tsx +146 -0
- package/src/components/SceneView/Dynamic.stories.tsx +1 -1
- package/src/components/SceneView/SceneView.stories.tsx +170 -18
- package/src/components/SceneView/SceneView.tsx +565 -151
- package/src/components/SceneView/SceneViewContext.ts +23 -2
- package/src/components/SceneView/Scored.stories.tsx +11 -11
- package/src/components/SceneView/usePointerMachine.ts +80 -20
- package/src/components/SceneView/useSceneCamera.ts +15 -8
- package/src/components/SceneView/useSceneClipboard.ts +4 -0
- package/src/components/SceneView/useSceneKeys.ts +6 -4
- package/src/components/SceneView/useSceneNavigation.ts +44 -33
- package/src/components/Toolbar/Toolbar.stories.tsx +14 -15
- package/src/components/Toolbar/Toolbar.tsx +15 -4
- package/src/components/index.ts +2 -0
- package/src/hooks/useWheel.ts +35 -0
- package/src/index.ts +4 -3
- package/src/model/atoms.ts +27 -3
- package/src/model/projection.ts +5 -0
- package/src/model/projections/constrained.test.ts +18 -10
- package/src/model/projections/constrained.ts +19 -8
- package/src/model/projections/dynamic.ts +5 -2
- package/src/model/projections/lattice.ts +15 -3
- package/src/model/registry.test.ts +14 -9
- package/src/model/registry.ts +39 -34
- package/src/model/store.ts +12 -2
- package/src/model/types.ts +124 -36
- package/src/playwright/SceneManager.ts +2 -2
- package/src/playwright/create.spec.ts +5 -3
- package/src/playwright/demo.spec.ts +2 -2
- package/src/playwright/lattice.spec.ts +4 -5
- package/src/playwright/scene.spec.ts +27 -25
- package/src/utils/builder.test.ts +46 -9
- package/src/utils/builder.ts +42 -18
- package/src/utils/camera.test.ts +22 -51
- package/src/utils/camera.ts +39 -32
- package/src/utils/clipboard.test.ts +23 -2
- package/src/utils/clipboard.ts +8 -4
- package/src/utils/diagram.test.ts +3 -1
- package/src/utils/diagram.ts +3 -25
- package/src/utils/group.test.ts +69 -0
- package/src/utils/group.ts +94 -0
- package/src/utils/gutter-route.test.ts +49 -0
- package/src/utils/gutter-route.ts +142 -16
- package/src/utils/hit.test.ts +4 -38
- package/src/utils/hit.ts +4 -26
- package/src/utils/index.ts +3 -0
- package/src/utils/lattice.ts +7 -1
- package/src/utils/layers.test.ts +139 -0
- package/src/utils/layers.ts +175 -0
- package/src/utils/parts.test.ts +66 -32
- package/src/utils/parts.ts +63 -41
- package/src/utils/ports.ts +7 -4
- package/src/utils/properties.test.ts +23 -6
- package/src/utils/properties.ts +66 -16
- package/src/utils/route.test.ts +51 -2
- package/src/utils/route.ts +133 -23
- package/src/utils/scenes.test.ts +38 -0
- package/src/utils/scenes.ts +54 -0
- package/src/utils/shapes.test.ts +33 -8
- package/src/utils/shapes.ts +51 -35
- package/src/utils/style.test.ts +54 -0
- package/src/utils/style.ts +153 -40
- package/src/utils/testing.test.ts +3 -3
- package/src/utils/testing.ts +21 -30
- package/src/utils/undo.test.ts +25 -2
- package/src/utils/undo.ts +50 -13
- package/dist/lib/chunk-Grid.mjs +0 -99
- package/dist/lib/chunk-Grid.mjs.map +0 -1
- package/dist/lib/scene.mjs +0 -6835
- package/dist/lib/scene.mjs.map +0 -1
- package/dist/lib/types.mjs +0 -16
- package/dist/lib/types.mjs.map +0 -1
- package/dist/types/src/archive/components/Canvas/Canvas.d.ts +0 -15
- package/dist/types/src/archive/components/Canvas/Canvas.d.ts.map +0 -1
- package/dist/types/src/archive/components/Canvas/Canvas.stories.d.ts +0 -18
- package/dist/types/src/archive/components/Canvas/Canvas.stories.d.ts.map +0 -1
- package/dist/types/src/archive/components/Canvas/index.d.ts +0 -2
- package/dist/types/src/archive/components/Canvas/index.d.ts.map +0 -1
- package/dist/types/src/archive/components/CellGrid/CellGrid.d.ts +0 -21
- package/dist/types/src/archive/components/CellGrid/CellGrid.d.ts.map +0 -1
- package/dist/types/src/archive/components/CellGrid/CellGrid.stories.d.ts +0 -21
- package/dist/types/src/archive/components/CellGrid/CellGrid.stories.d.ts.map +0 -1
- package/dist/types/src/archive/components/CellGrid/headers/Ruler.d.ts +0 -15
- package/dist/types/src/archive/components/CellGrid/headers/Ruler.d.ts.map +0 -1
- package/dist/types/src/archive/components/CellGrid/headers/TrackHeader.d.ts +0 -19
- package/dist/types/src/archive/components/CellGrid/headers/TrackHeader.d.ts.map +0 -1
- package/dist/types/src/archive/components/CellGrid/headers/index.d.ts +0 -3
- package/dist/types/src/archive/components/CellGrid/headers/index.d.ts.map +0 -1
- package/dist/types/src/archive/components/CellGrid/index.d.ts +0 -6
- package/dist/types/src/archive/components/CellGrid/index.d.ts.map +0 -1
- package/dist/types/src/archive/components/CellGrid/input/index.d.ts +0 -3
- package/dist/types/src/archive/components/CellGrid/input/index.d.ts.map +0 -1
- package/dist/types/src/archive/components/CellGrid/input/pointer.d.ts +0 -33
- package/dist/types/src/archive/components/CellGrid/input/pointer.d.ts.map +0 -1
- package/dist/types/src/archive/components/CellGrid/input/wheel.d.ts +0 -14
- package/dist/types/src/archive/components/CellGrid/input/wheel.d.ts.map +0 -1
- package/dist/types/src/archive/components/CellGrid/render/index.d.ts +0 -3
- package/dist/types/src/archive/components/CellGrid/render/index.d.ts.map +0 -1
- package/dist/types/src/archive/components/CellGrid/render/overlay-layer.d.ts +0 -21
- package/dist/types/src/archive/components/CellGrid/render/overlay-layer.d.ts.map +0 -1
- package/dist/types/src/archive/components/CellGrid/render/static-layer.d.ts +0 -36
- package/dist/types/src/archive/components/CellGrid/render/static-layer.d.ts.map +0 -1
- package/dist/types/src/archive/components/CellGrid/state/atoms.d.ts +0 -23
- package/dist/types/src/archive/components/CellGrid/state/atoms.d.ts.map +0 -1
- package/dist/types/src/archive/components/CellGrid/state/index.d.ts +0 -4
- package/dist/types/src/archive/components/CellGrid/state/index.d.ts.map +0 -1
- package/dist/types/src/archive/components/CellGrid/state/types.d.ts +0 -39
- package/dist/types/src/archive/components/CellGrid/state/types.d.ts.map +0 -1
- package/dist/types/src/archive/components/CellGrid/state/viewport.d.ts +0 -52
- package/dist/types/src/archive/components/CellGrid/state/viewport.d.ts.map +0 -1
- package/dist/types/src/archive/components/CellGrid/state/viewport.test.d.ts +0 -2
- package/dist/types/src/archive/components/CellGrid/state/viewport.test.d.ts.map +0 -1
- package/dist/types/src/archive/components/FPS.d.ts +0 -9
- package/dist/types/src/archive/components/FPS.d.ts.map +0 -1
- package/dist/types/src/archive/components/Grid/Grid.d.ts +0 -6
- package/dist/types/src/archive/components/Grid/Grid.d.ts.map +0 -1
- package/dist/types/src/archive/components/Grid/index.d.ts +0 -2
- package/dist/types/src/archive/components/Grid/index.d.ts.map +0 -1
- package/dist/types/src/archive/components/index.d.ts +0 -5
- package/dist/types/src/archive/components/index.d.ts.map +0 -1
- package/dist/types/src/archive/hooks/index.d.ts +0 -5
- package/dist/types/src/archive/hooks/index.d.ts.map +0 -1
- package/dist/types/src/archive/hooks/projection.d.ts +0 -60
- package/dist/types/src/archive/hooks/projection.d.ts.map +0 -1
- package/dist/types/src/archive/hooks/useCanvasContext.d.ts +0 -13
- package/dist/types/src/archive/hooks/useCanvasContext.d.ts.map +0 -1
- package/dist/types/src/archive/hooks/useDrag.d.ts +0 -6
- package/dist/types/src/archive/hooks/useDrag.d.ts.map +0 -1
- package/dist/types/src/archive/hooks/useWheel.d.ts +0 -8
- package/dist/types/src/archive/hooks/useWheel.d.ts.map +0 -1
- package/dist/types/src/archive/index.d.ts +0 -5
- package/dist/types/src/archive/index.d.ts.map +0 -1
- package/dist/types/src/archive/types.d.ts +0 -19
- package/dist/types/src/archive/types.d.ts.map +0 -1
- package/dist/types/src/archive/util/index.d.ts +0 -4
- package/dist/types/src/archive/util/index.d.ts.map +0 -1
- package/dist/types/src/archive/util/svg-path.d.ts +0 -4
- package/dist/types/src/archive/util/svg-path.d.ts.map +0 -1
- package/dist/types/src/archive/util/svg.d.ts +0 -32
- package/dist/types/src/archive/util/svg.d.ts.map +0 -1
- package/dist/types/src/archive/util/svg.stories.d.ts +0 -15
- package/dist/types/src/archive/util/svg.stories.d.ts.map +0 -1
- package/dist/types/src/archive/util/util.d.ts +0 -17
- package/dist/types/src/archive/util/util.d.ts.map +0 -1
- package/dist/types/src/scene.d.ts +0 -5
- package/dist/types/src/scene.d.ts.map +0 -1
- package/src/archive/components/Canvas/Canvas.stories.tsx +0 -111
- package/src/archive/components/Canvas/Canvas.tsx +0 -87
- package/src/archive/components/Canvas/index.ts +0 -5
- package/src/archive/components/CellGrid/CellGrid.stories.tsx +0 -239
- package/src/archive/components/CellGrid/CellGrid.tsx +0 -270
- package/src/archive/components/CellGrid/headers/Ruler.tsx +0 -71
- package/src/archive/components/CellGrid/headers/TrackHeader.tsx +0 -58
- package/src/archive/components/CellGrid/headers/index.ts +0 -6
- package/src/archive/components/CellGrid/index.ts +0 -9
- package/src/archive/components/CellGrid/input/index.ts +0 -6
- package/src/archive/components/CellGrid/input/pointer.ts +0 -236
- package/src/archive/components/CellGrid/input/wheel.ts +0 -68
- package/src/archive/components/CellGrid/render/index.ts +0 -6
- package/src/archive/components/CellGrid/render/overlay-layer.ts +0 -66
- package/src/archive/components/CellGrid/render/static-layer.ts +0 -112
- package/src/archive/components/CellGrid/state/atoms.ts +0 -43
- package/src/archive/components/CellGrid/state/index.ts +0 -7
- package/src/archive/components/CellGrid/state/types.ts +0 -40
- package/src/archive/components/CellGrid/state/viewport.test.ts +0 -50
- package/src/archive/components/CellGrid/state/viewport.ts +0 -94
- package/src/archive/components/FPS.tsx +0 -98
- package/src/archive/components/Grid/Grid.tsx +0 -17
- package/src/archive/components/Grid/index.ts +0 -5
- package/src/archive/components/index.ts +0 -8
- package/src/archive/hooks/index.ts +0 -8
- package/src/archive/hooks/projection.tsx +0 -156
- package/src/archive/hooks/useCanvasContext.ts +0 -32
- package/src/archive/hooks/useDrag.tsx +0 -96
- package/src/archive/hooks/useWheel.tsx +0 -80
- package/src/archive/index.ts +0 -8
- package/src/archive/types.ts +0 -14
- package/src/archive/util/index.ts +0 -7
- package/src/archive/util/svg-path.ts +0 -13
- package/src/archive/util/svg.stories.tsx +0 -46
- package/src/archive/util/svg.tsx +0 -128
- package/src/archive/util/util.ts +0 -50
- package/src/scene.ts +0 -9
|
@@ -87,7 +87,7 @@ const DefaultStory = () => {
|
|
|
87
87
|
};
|
|
88
88
|
|
|
89
89
|
const meta: Meta = {
|
|
90
|
-
title: 'ui/react-ui-canvas/
|
|
90
|
+
title: 'ui/react-ui-canvas/Constrained',
|
|
91
91
|
render: DefaultStory,
|
|
92
92
|
decorators: [withRegistry, withTheme(), withLayout({ layout: 'fullscreen' })],
|
|
93
93
|
};
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
6
|
+
import React, { useLayoutEffect, useMemo } from 'react';
|
|
7
|
+
import { expect, userEvent, waitFor, within } from 'storybook/test';
|
|
8
|
+
|
|
9
|
+
import { translations as formTranslations } from '@dxos/react-ui-form/translations';
|
|
10
|
+
import { withLayout, withRegistry, withTheme } from '@dxos/react-ui/testing';
|
|
11
|
+
import { translations as uiTranslations } from '@dxos/react-ui/translations';
|
|
12
|
+
|
|
13
|
+
import { useRegistry } from '../../hooks/index.ts';
|
|
14
|
+
import { createSceneViewAtoms } from '../../model/atoms.ts';
|
|
15
|
+
import { createMemoryStore } from '../../model/store.ts';
|
|
16
|
+
import { type Layer } from '../../model/types.ts';
|
|
17
|
+
import { SceneBuilder } from '../../utils/builder.ts';
|
|
18
|
+
import { between } from '../../utils/order.ts';
|
|
19
|
+
import { SceneView } from './SceneView.tsx';
|
|
20
|
+
|
|
21
|
+
const box = (x: number, y = 0) => ({ x, y, width: 256, height: 128 });
|
|
22
|
+
|
|
23
|
+
/** A scene with two layers and a shape on each. */
|
|
24
|
+
const createTree = () => {
|
|
25
|
+
const background: Layer = { id: 'background', name: 'Background', z: between() };
|
|
26
|
+
const diagram: Layer = { id: 'diagram', name: 'Diagram', z: between(background.z) };
|
|
27
|
+
const tree = SceneBuilder.scene('root', [
|
|
28
|
+
SceneBuilder.rect('a', box(0)).properties({ label: 'Rect', layer: 'diagram' }),
|
|
29
|
+
SceneBuilder.rect('b', box(400)).properties({ label: 'Other', layer: 'background' }),
|
|
30
|
+
]).build();
|
|
31
|
+
const [scene, ...rest] = tree.scenes;
|
|
32
|
+
return { root: tree.root, scenes: [{ ...scene, layers: { background, diagram } }, ...rest] };
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
type StoryArgs = {
|
|
36
|
+
/** The element selected, whose properties fill the dock's first section. */
|
|
37
|
+
select?: string;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/** The docked panels alone: the properties, layers and about sections, without the canvas they sit beside. */
|
|
41
|
+
const DefaultStory = ({ select }: StoryArgs) => {
|
|
42
|
+
const registry = useRegistry();
|
|
43
|
+
const { store, root } = useMemo(() => {
|
|
44
|
+
const tree = createTree();
|
|
45
|
+
return { store: createMemoryStore(tree.scenes), root: tree.root };
|
|
46
|
+
}, []);
|
|
47
|
+
const atoms = useMemo(() => createSceneViewAtoms(root), [root]);
|
|
48
|
+
useLayoutEffect(() => {
|
|
49
|
+
registry.set(atoms.selection, new Set(select ? [select] : []));
|
|
50
|
+
}, [registry, atoms, select]);
|
|
51
|
+
return (
|
|
52
|
+
<SceneView.Root store={store} root={root} atoms={atoms}>
|
|
53
|
+
<SceneView.Properties />
|
|
54
|
+
<SceneView.Layers />
|
|
55
|
+
<SceneView.About />
|
|
56
|
+
</SceneView.Root>
|
|
57
|
+
);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const meta: Meta<StoryArgs> = {
|
|
61
|
+
title: 'ui/react-ui-canvas/Dock',
|
|
62
|
+
render: DefaultStory,
|
|
63
|
+
decorators: [withRegistry, withTheme(), withLayout({ layout: 'fullscreen' })],
|
|
64
|
+
args: { select: 'a' },
|
|
65
|
+
parameters: { translations: [...uiTranslations, ...formTranslations] },
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export default meta;
|
|
69
|
+
|
|
70
|
+
type Story = StoryObj<StoryArgs>;
|
|
71
|
+
|
|
72
|
+
/** A shape selected: its properties above the scene's layers. */
|
|
73
|
+
export const Default: Story = {};
|
|
74
|
+
|
|
75
|
+
/** Nothing selected: the properties section says so rather than going away. */
|
|
76
|
+
export const Empty: Story = { args: { select: undefined } };
|
|
77
|
+
|
|
78
|
+
/** Each section's header collapses it. */
|
|
79
|
+
export const Test: Story = {
|
|
80
|
+
play: async ({ canvasElement }) => {
|
|
81
|
+
const canvas = within(canvasElement);
|
|
82
|
+
// 1. Both sections are open, the properties showing the selected shape's layer.
|
|
83
|
+
const dock = await canvas.findByTestId('scene-view-dock');
|
|
84
|
+
await waitFor(() => expect(within(dock).getByTestId('dock-section-layers')).toHaveAttribute('data-state', 'open'));
|
|
85
|
+
await expect(await within(dock).findByRole('combobox', { name: 'Layer' })).toHaveTextContent('Diagram');
|
|
86
|
+
// 2. The properties header collapses its section.
|
|
87
|
+
await userEvent.click(within(dock).getByRole('button', { name: 'Properties' }));
|
|
88
|
+
await waitFor(() =>
|
|
89
|
+
expect(within(dock).getByTestId('dock-section-properties')).toHaveAttribute('data-state', 'closed'),
|
|
90
|
+
);
|
|
91
|
+
// 3. The about section follows the layers, its figures those of the scene.
|
|
92
|
+
await expect(within(dock).getByTestId('about-nodes')).toHaveTextContent('2');
|
|
93
|
+
await expect(within(dock).getByTestId('about-layers')).toHaveTextContent('2');
|
|
94
|
+
},
|
|
95
|
+
};
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
//
|
|
6
|
+
// The dock: a column beside the canvas that panels move into when the view's panels are docked, one accordion
|
|
7
|
+
// section per panel. A panel registers its section and portals itself into the section's slot.
|
|
8
|
+
//
|
|
9
|
+
|
|
10
|
+
import React, {
|
|
11
|
+
type PropsWithChildren,
|
|
12
|
+
type ReactNode,
|
|
13
|
+
type SyntheticEvent,
|
|
14
|
+
createContext,
|
|
15
|
+
useCallback,
|
|
16
|
+
useContext,
|
|
17
|
+
useLayoutEffect,
|
|
18
|
+
useMemo,
|
|
19
|
+
useState,
|
|
20
|
+
} from 'react';
|
|
21
|
+
import { createPortal } from 'react-dom';
|
|
22
|
+
|
|
23
|
+
import * as Accordion from '@dxos/react-ui/Accordion';
|
|
24
|
+
import * as ScrollArea from '@dxos/react-ui/ScrollArea';
|
|
25
|
+
|
|
26
|
+
/** A panel's section in the dock. */
|
|
27
|
+
export type DockSection = {
|
|
28
|
+
id: string;
|
|
29
|
+
title: string;
|
|
30
|
+
icon: string;
|
|
31
|
+
/** Sections stack by it, lowest first. */
|
|
32
|
+
order: number;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
type DockContextValue = {
|
|
36
|
+
sections: readonly DockSection[];
|
|
37
|
+
slots: Readonly<Record<string, HTMLDivElement | null>>;
|
|
38
|
+
closed: ReadonlySet<string>;
|
|
39
|
+
register: (section: DockSection) => () => void;
|
|
40
|
+
setSlot: (id: string, element: HTMLDivElement | null) => void;
|
|
41
|
+
setClosed: (closed: ReadonlySet<string>) => void;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const DockContext = createContext<DockContextValue | undefined>(undefined);
|
|
45
|
+
|
|
46
|
+
/** Holds the docked sections and their slots for a view's `Dock` and its panels. */
|
|
47
|
+
export const DockProvider = ({ children }: PropsWithChildren) => {
|
|
48
|
+
const [sections, setSections] = useState<DockSection[]>([]);
|
|
49
|
+
const [slots, setSlots] = useState<Record<string, HTMLDivElement | null>>({});
|
|
50
|
+
// Closed rather than open, so a section that docks later starts open.
|
|
51
|
+
const [closed, setClosed] = useState<ReadonlySet<string>>(new Set());
|
|
52
|
+
const register = useCallback((section: DockSection) => {
|
|
53
|
+
setSections((current) =>
|
|
54
|
+
[...current.filter((entry) => entry.id !== section.id), section].sort((left, right) => left.order - right.order),
|
|
55
|
+
);
|
|
56
|
+
return () => setSections((current) => current.filter((entry) => entry.id !== section.id));
|
|
57
|
+
}, []);
|
|
58
|
+
const setSlot = useCallback(
|
|
59
|
+
(id: string, element: HTMLDivElement | null) =>
|
|
60
|
+
setSlots((current) => (current[id] === element ? current : { ...current, [id]: element })),
|
|
61
|
+
[],
|
|
62
|
+
);
|
|
63
|
+
const value = useMemo(
|
|
64
|
+
() => ({ sections, slots, closed, register, setSlot, setClosed }),
|
|
65
|
+
[sections, slots, closed, register, setSlot],
|
|
66
|
+
);
|
|
67
|
+
return <DockContext.Provider value={value}>{children}</DockContext.Provider>;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
/** A section's slot; its ref callback is stable, since a new one each render would detach and reattach (a loop). */
|
|
71
|
+
const DockSlot = ({ id, setSlot }: { id: string; setSlot: DockContextValue['setSlot'] }) => {
|
|
72
|
+
const ref = useCallback((element: HTMLDivElement | null) => setSlot(id, element), [id, setSlot]);
|
|
73
|
+
return <div ref={ref} />;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
/** Stops a docked panel's events at the dock: through the portal they would reach the canvas root (hover, menus). */
|
|
77
|
+
const stop = (event: SyntheticEvent) => event.stopPropagation();
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* The dock column: an accordion of the registered sections, any number open, each as tall as its panel, the column
|
|
81
|
+
* scrolling them together. Nothing (and no space) while no panel is docked.
|
|
82
|
+
*/
|
|
83
|
+
export const Dock = () => {
|
|
84
|
+
const dock = useContext(DockContext);
|
|
85
|
+
if (!dock || dock.sections.length === 0) {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
const { sections, closed, setSlot, setClosed } = dock;
|
|
89
|
+
return (
|
|
90
|
+
// The column is the scroll area itself: each section is as tall as its panel, and it scrolls them together.
|
|
91
|
+
<ScrollArea.Root
|
|
92
|
+
classNames='w-80 shrink-0 border-l border-separator-subtle bg-base-surface'
|
|
93
|
+
data-testid='scene-view-dock'
|
|
94
|
+
>
|
|
95
|
+
<ScrollArea.Viewport>
|
|
96
|
+
<Accordion.Root
|
|
97
|
+
border={false}
|
|
98
|
+
onPointerDown={stop}
|
|
99
|
+
onPointerMove={stop}
|
|
100
|
+
onPointerUp={stop}
|
|
101
|
+
onDoubleClick={stop}
|
|
102
|
+
onContextMenu={stop}
|
|
103
|
+
onKeyDown={stop}
|
|
104
|
+
value={sections.filter((section) => !closed.has(section.id)).map((section) => section.id)}
|
|
105
|
+
onValueChange={(open) =>
|
|
106
|
+
setClosed(new Set(sections.filter((section) => !open.includes(section.id)).map((section) => section.id)))
|
|
107
|
+
}
|
|
108
|
+
>
|
|
109
|
+
{sections.map((section) => (
|
|
110
|
+
<Accordion.Item key={section.id} value={section.id} data-testid={`dock-section-${section.id}`}>
|
|
111
|
+
{/* A distinct surface, so a section's header reads apart from its panel's own toolbar. */}
|
|
112
|
+
<Accordion.ItemTrigger icon={section.icon} classNames='bg-group-surface'>
|
|
113
|
+
{section.title}
|
|
114
|
+
</Accordion.ItemTrigger>
|
|
115
|
+
<Accordion.ItemContent>
|
|
116
|
+
<DockSlot id={section.id} setSlot={setSlot} />
|
|
117
|
+
</Accordion.ItemContent>
|
|
118
|
+
</Accordion.Item>
|
|
119
|
+
))}
|
|
120
|
+
</Accordion.Root>
|
|
121
|
+
</ScrollArea.Viewport>
|
|
122
|
+
</ScrollArea.Root>
|
|
123
|
+
);
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Registers a section while `docked`, and renders `panel` into it: docked, the panel's portal into the dock (once its
|
|
128
|
+
* slot mounts); floating, `null`, so the caller renders the panel where it floats.
|
|
129
|
+
*/
|
|
130
|
+
export const useDockSection = (section: DockSection, docked: boolean, panel: ReactNode): ReactNode | null => {
|
|
131
|
+
const dock = useContext(DockContext);
|
|
132
|
+
const { id, title, icon, order } = section;
|
|
133
|
+
useLayoutEffect(() => {
|
|
134
|
+
if (!dock || !docked) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
return dock.register({ id, title, icon, order });
|
|
138
|
+
// `register` is stable; the section re-registers only when it changes.
|
|
139
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
140
|
+
}, [dock?.register, docked, id, title, icon, order]);
|
|
141
|
+
if (!docked) {
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
const slot = dock?.slots[id];
|
|
145
|
+
return slot ? createPortal(panel, slot) : null;
|
|
146
|
+
};
|
|
@@ -126,7 +126,7 @@ const DefaultStory = () => {
|
|
|
126
126
|
};
|
|
127
127
|
|
|
128
128
|
const meta: Meta = {
|
|
129
|
-
title: 'ui/react-ui-canvas/
|
|
129
|
+
title: 'ui/react-ui-canvas/Dynamic',
|
|
130
130
|
render: DefaultStory,
|
|
131
131
|
decorators: [withRegistry, withTheme(), withLayout({ layout: 'fullscreen' })],
|
|
132
132
|
};
|
|
@@ -4,18 +4,20 @@
|
|
|
4
4
|
|
|
5
5
|
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
6
6
|
import React, { useMemo } from 'react';
|
|
7
|
+
import { expect, userEvent, waitFor, within } from 'storybook/test';
|
|
7
8
|
|
|
8
9
|
import { translations as formTranslations } from '@dxos/react-ui-form/translations';
|
|
9
10
|
import { withLayout, withRegistry, withTheme } from '@dxos/react-ui/testing';
|
|
10
11
|
import { translations as uiTranslations } from '@dxos/react-ui/translations';
|
|
11
12
|
|
|
13
|
+
import { type PanelMode } from '../../model/atoms.ts';
|
|
12
14
|
import { createLatticeProjection } from '../../model/projections/lattice.ts';
|
|
13
15
|
import { createMemoryStore } from '../../model/store.ts';
|
|
14
16
|
import { type Box, SceneBuilder } from '../../utils/builder.ts';
|
|
15
17
|
import { DEFAULT_LATTICE, cellBounds } from '../../utils/lattice.ts';
|
|
16
18
|
import { DEFAULT_SHAPE_SIZE } from '../../utils/shapes.ts';
|
|
17
19
|
import { TONES } from '../../utils/style.ts';
|
|
18
|
-
import {
|
|
20
|
+
import { createModelSceneTree, createSceneTree } from '../../utils/testing.ts';
|
|
19
21
|
import { SceneView } from './SceneView.tsx';
|
|
20
22
|
|
|
21
23
|
/**
|
|
@@ -26,9 +28,9 @@ import { SceneView } from './SceneView.tsx';
|
|
|
26
28
|
* 3. L / K / P pick the line, curve or spline link tool: every port shows; dropping onto empty canvas creates a
|
|
27
29
|
* rectangle and links to it. A selected spline shows a diamond per control point and a dot per span midpoint:
|
|
28
30
|
* drag a diamond to move a point, drag a dot to add one there, alt-click a diamond to remove one.
|
|
29
|
-
* 4. R / E /
|
|
30
|
-
* selection (nodes or links).
|
|
31
|
-
* 5. Double-click a
|
|
31
|
+
* 4. R / E / T / F then drag draws a rectangle, ellipse, text or frame (the UML class is plugin-uml's);
|
|
32
|
+
* Delete removes the selection (nodes or links).
|
|
33
|
+
* 5. Double-click a frame (or zoom until it fills the view) drills in; Escape, Up or the breadcrumb drills out.
|
|
32
34
|
* 6. G (or the Grid button) toggles the grid; with it off nothing snaps. The floating panel (top right) edits the
|
|
33
35
|
* selected element.
|
|
34
36
|
*/
|
|
@@ -36,7 +38,8 @@ type StoryArgs = {
|
|
|
36
38
|
depth: number;
|
|
37
39
|
liveDepth: number;
|
|
38
40
|
readonly?: boolean;
|
|
39
|
-
fixture?: 'elements' | '
|
|
41
|
+
fixture?: 'elements' | 'model' | 'square' | 'lattice' | 'scenes' | 'frame';
|
|
42
|
+
panels?: PanelMode;
|
|
40
43
|
};
|
|
41
44
|
|
|
42
45
|
type EditorProps = {
|
|
@@ -45,13 +48,15 @@ type EditorProps = {
|
|
|
45
48
|
liveDepth: number;
|
|
46
49
|
readonly?: boolean;
|
|
47
50
|
lattice?: boolean;
|
|
51
|
+
panels?: PanelMode;
|
|
48
52
|
};
|
|
49
53
|
|
|
50
|
-
const Editor = ({ store, root, liveDepth, readonly, lattice }: EditorProps) => (
|
|
54
|
+
const Editor = ({ store, root, liveDepth, readonly, lattice, panels }: EditorProps) => (
|
|
51
55
|
<SceneView.Root
|
|
52
56
|
store={store}
|
|
53
57
|
root={root}
|
|
54
58
|
readonly={readonly}
|
|
59
|
+
panels={panels}
|
|
55
60
|
createProjection={lattice ? createLatticeProjection : undefined}
|
|
56
61
|
>
|
|
57
62
|
<SceneView.Canvas liveDepth={liveDepth} />
|
|
@@ -60,6 +65,8 @@ const Editor = ({ store, root, liveDepth, readonly, lattice }: EditorProps) => (
|
|
|
60
65
|
<SceneView.Debug />
|
|
61
66
|
<SceneView.Palette />
|
|
62
67
|
<SceneView.Properties />
|
|
68
|
+
<SceneView.Layers />
|
|
69
|
+
<SceneView.About />
|
|
63
70
|
</SceneView.Root>
|
|
64
71
|
);
|
|
65
72
|
|
|
@@ -92,11 +99,11 @@ const createLatticeTree = () => {
|
|
|
92
99
|
...[box('a', at(-1, -1), 'A'), box('b', at(-1, 0), 'B'), box('c', at(-1, 1), 'C')].map((element) =>
|
|
93
100
|
element.properties({ style: { hue: 'neutral' } }),
|
|
94
101
|
),
|
|
95
|
-
box('d', at(0, 0), 'D').properties({ style: { hue: 'green', tone:
|
|
96
|
-
box('e', at(0, 1), 'E').properties({ style: { hue: 'green', tone:
|
|
102
|
+
box('d', at(0, 0), 'D').properties({ style: { hue: 'green', tone: 3 } }),
|
|
103
|
+
box('e', at(0, 1), 'E').properties({ style: { hue: 'green', tone: 1 } }),
|
|
97
104
|
// Unlabelled, so the scene shows its contents.
|
|
98
105
|
SceneBuilder.scene('f', [
|
|
99
|
-
// One hue at each of its tones,
|
|
106
|
+
// One hue at each of its tones: outline, then strongest to lightest.
|
|
100
107
|
...TONES.map((tone, index) =>
|
|
101
108
|
box(`f${index + 1}`, at(0, index - 1), `F${index + 1}`).properties({ style: { hue: 'blue', tone } }),
|
|
102
109
|
),
|
|
@@ -123,17 +130,105 @@ const createLatticeTree = () => {
|
|
|
123
130
|
.build();
|
|
124
131
|
};
|
|
125
132
|
|
|
126
|
-
|
|
133
|
+
/**
|
|
134
|
+
* Three levels, every shape 256×128: A, scene B, C; inside B, D, scene E, F, with two rectangles either side
|
|
135
|
+
* of E; inside E, X, Y, Z with a box above X, below Z and beside each; each level's shapes linked. Open the
|
|
136
|
+
* scenes to check that a shape is the same size at the same zoom on every level.
|
|
137
|
+
*/
|
|
138
|
+
const createScenesTree = () => {
|
|
139
|
+
const root = 'scene:root';
|
|
140
|
+
const size = { width: 256, height: 128 };
|
|
141
|
+
const at = (y: number, x = 0) => ({ x: x - size.width / 2, y: y - size.height / 2, ...size });
|
|
142
|
+
const rect = (id: string, y: number, x = 0) =>
|
|
143
|
+
SceneBuilder.rect(id, at(y, x)).properties({ label: id.toUpperCase() });
|
|
144
|
+
// An unlabelled box beside the column.
|
|
145
|
+
const box = (id: string, y: number, x: number) => SceneBuilder.rect(id, at(y, x));
|
|
146
|
+
const link = (from: string, to: string) => SceneBuilder.link('smart', from, to);
|
|
147
|
+
return SceneBuilder.scene(root, [
|
|
148
|
+
rect('a', -256),
|
|
149
|
+
SceneBuilder.scene('b', [
|
|
150
|
+
rect('d', -256),
|
|
151
|
+
// X, Y, Z down the middle, with a box above X, below Z and either side of each.
|
|
152
|
+
SceneBuilder.scene('e', [
|
|
153
|
+
box('n', -512, 0),
|
|
154
|
+
box('xw', -256, -384),
|
|
155
|
+
rect('x', -256),
|
|
156
|
+
box('xe', -256, 384),
|
|
157
|
+
rect('y', 0),
|
|
158
|
+
box('zw', 256, -384),
|
|
159
|
+
rect('z', 256),
|
|
160
|
+
box('ze', 256, 384),
|
|
161
|
+
box('s', 512, 0),
|
|
162
|
+
link('n', 'x'),
|
|
163
|
+
link('xw', 'x'),
|
|
164
|
+
link('x', 'xe'),
|
|
165
|
+
link('x', 'y'),
|
|
166
|
+
link('y', 'z'),
|
|
167
|
+
link('zw', 'z'),
|
|
168
|
+
link('z', 'ze'),
|
|
169
|
+
link('z', 's'),
|
|
170
|
+
])
|
|
171
|
+
.at(at(0))
|
|
172
|
+
.name('E'),
|
|
173
|
+
rect('f', 256),
|
|
174
|
+
// Two rectangles either side of E.
|
|
175
|
+
rect('g', 0, -768),
|
|
176
|
+
rect('h', 0, -384),
|
|
177
|
+
rect('i', 0, 384),
|
|
178
|
+
rect('j', 0, 768),
|
|
179
|
+
link('d', 'e'),
|
|
180
|
+
link('e', 'f'),
|
|
181
|
+
link('g', 'h'),
|
|
182
|
+
link('h', 'e'),
|
|
183
|
+
link('e', 'i'),
|
|
184
|
+
link('i', 'j'),
|
|
185
|
+
])
|
|
186
|
+
.at(at(0))
|
|
187
|
+
.name('B'),
|
|
188
|
+
rect('c', 256),
|
|
189
|
+
link('a', 'b'),
|
|
190
|
+
link('b', 'c'),
|
|
191
|
+
])
|
|
192
|
+
.name('root')
|
|
193
|
+
.build();
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
/** A labelled frame onto a nested scene of two linked shapes, beside a rectangle. */
|
|
197
|
+
const createFrameTree = () => {
|
|
198
|
+
const root = 'scene:root';
|
|
199
|
+
const size = { width: 256, height: 128 };
|
|
200
|
+
const at = (x: number, y = 0) => ({ x: x - size.width / 2, y: y - size.height / 2, ...size });
|
|
201
|
+
return SceneBuilder.scene(root, [
|
|
202
|
+
SceneBuilder.rect('a', at(-384)).properties({ label: 'A' }),
|
|
203
|
+
SceneBuilder.scene('frame', [
|
|
204
|
+
SceneBuilder.rect('b', at(-192)).properties({ label: 'B' }),
|
|
205
|
+
SceneBuilder.rect('c', at(192)).properties({ label: 'C' }),
|
|
206
|
+
SceneBuilder.link('smart', 'b', 'c'),
|
|
207
|
+
])
|
|
208
|
+
.name('Inner')
|
|
209
|
+
.at({ x: 0, y: -128, width: 512, height: 256 })
|
|
210
|
+
.properties({ label: 'Frame' }),
|
|
211
|
+
SceneBuilder.link('smart', 'a', 'frame'),
|
|
212
|
+
])
|
|
213
|
+
.name('root')
|
|
214
|
+
.build();
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
const DefaultStory = ({ depth, liveDepth, readonly, fixture, panels }: StoryArgs) => {
|
|
127
218
|
const { store, root } = useMemo(() => {
|
|
128
|
-
// The
|
|
219
|
+
// The model fixture is a fixed three levels, so `depth` does not apply to it.
|
|
129
220
|
const tree =
|
|
130
|
-
fixture === '
|
|
131
|
-
?
|
|
221
|
+
fixture === 'model'
|
|
222
|
+
? createModelSceneTree()
|
|
132
223
|
: fixture === 'square'
|
|
133
224
|
? createSquareTree()
|
|
134
225
|
: fixture === 'lattice'
|
|
135
226
|
? createLatticeTree()
|
|
136
|
-
:
|
|
227
|
+
: fixture === 'scenes'
|
|
228
|
+
? createScenesTree()
|
|
229
|
+
: fixture === 'frame'
|
|
230
|
+
? createFrameTree()
|
|
231
|
+
: createSceneTree(depth);
|
|
137
232
|
return { store: createMemoryStore(tree.scenes), root: tree.root };
|
|
138
233
|
}, [depth, fixture]);
|
|
139
234
|
|
|
@@ -146,17 +241,23 @@ const DefaultStory = ({ depth, liveDepth, readonly, fixture }: StoryArgs) => {
|
|
|
146
241
|
liveDepth={liveDepth}
|
|
147
242
|
readonly={readonly}
|
|
148
243
|
lattice={fixture === 'lattice'}
|
|
244
|
+
panels={panels}
|
|
149
245
|
/>
|
|
150
246
|
);
|
|
151
247
|
};
|
|
152
248
|
|
|
153
249
|
const meta: Meta<StoryArgs> = {
|
|
154
|
-
title: 'ui/react-ui-canvas/
|
|
250
|
+
title: 'ui/react-ui-canvas/SceneView',
|
|
155
251
|
render: DefaultStory,
|
|
156
252
|
decorators: [withRegistry, withTheme(), withLayout({ layout: 'fullscreen' })],
|
|
157
253
|
// The properties panel is a react-ui-form form; its strings (e.g. "Mixed") and its controls' come from their bundles.
|
|
158
254
|
parameters: { translations: [...uiTranslations, ...formTranslations] },
|
|
159
255
|
argTypes: {
|
|
256
|
+
panels: {
|
|
257
|
+
control: { type: 'inline-radio' },
|
|
258
|
+
options: ['docked', 'floating'],
|
|
259
|
+
description: 'Where the properties and layers panels sit',
|
|
260
|
+
},
|
|
160
261
|
depth: {
|
|
161
262
|
control: { type: 'range', min: 0, max: 5, step: 1 },
|
|
162
263
|
description: 'Levels of nested scenes in the fixture; 0 is an empty scene',
|
|
@@ -192,15 +293,66 @@ export const Readonly: Story = {
|
|
|
192
293
|
args: { depth: 1, liveDepth: 1, readonly: true },
|
|
193
294
|
};
|
|
194
295
|
|
|
195
|
-
/** A three-level class
|
|
196
|
-
export const
|
|
197
|
-
args: { depth: 0, liveDepth: 1, fixture: '
|
|
296
|
+
/** A three-level model, a box per class: drill into a subsystem's portal to open its own. */
|
|
297
|
+
export const Model: Story = {
|
|
298
|
+
args: { depth: 0, liveDepth: 1, fixture: 'model' },
|
|
198
299
|
};
|
|
199
300
|
|
|
200
301
|
/**
|
|
201
302
|
* Lattice mode (DESIGN §8b): shapes snap to whole cells of a 256x128 lattice with 128x64 gutters, span any number
|
|
202
303
|
* of cells, and may not overlap; a drag onto occupied cells previews in red and is refused.
|
|
203
304
|
*/
|
|
305
|
+
/** A, an empty scene B and C in a column: open B, draw in it, and compare sizes at 100% on both levels. */
|
|
306
|
+
export const Scenes: Story = {
|
|
307
|
+
args: { depth: 0, liveDepth: 1, fixture: 'scenes' },
|
|
308
|
+
};
|
|
309
|
+
|
|
204
310
|
export const Lattice: Story = {
|
|
205
311
|
args: { depth: 0, liveDepth: 1, fixture: 'lattice' },
|
|
206
312
|
};
|
|
313
|
+
|
|
314
|
+
/** The panels dock by default: one accordion section each beside the canvas; a section collapses to its header. */
|
|
315
|
+
export const Docked: Story = {
|
|
316
|
+
args: { depth: 0, liveDepth: 1, fixture: 'square' },
|
|
317
|
+
play: async ({ canvasElement }) => {
|
|
318
|
+
const canvas = within(canvasElement);
|
|
319
|
+
// 1. The dock holds the properties and layers sections, both open.
|
|
320
|
+
const dock = await canvas.findByTestId('scene-view-dock');
|
|
321
|
+
await waitFor(() => expect(within(dock).getByTestId('dock-section-layers')).toHaveAttribute('data-state', 'open'));
|
|
322
|
+
await expect(within(dock).getByTestId('dock-section-properties')).toHaveAttribute('data-state', 'open');
|
|
323
|
+
// 2. A section's header collapses it.
|
|
324
|
+
await userEvent.click(
|
|
325
|
+
within(within(dock).getByTestId('dock-section-properties')).getByRole('button', { name: 'Properties' }),
|
|
326
|
+
);
|
|
327
|
+
await waitFor(() =>
|
|
328
|
+
expect(within(dock).getByTestId('dock-section-properties')).toHaveAttribute('data-state', 'closed'),
|
|
329
|
+
);
|
|
330
|
+
},
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
/** Floating panels: no dock, the layers over the canvas until something is selected. */
|
|
334
|
+
export const Floating: Story = {
|
|
335
|
+
args: { depth: 0, liveDepth: 1, fixture: 'square', panels: 'floating' },
|
|
336
|
+
play: async ({ canvasElement }) => {
|
|
337
|
+
const canvas = within(canvasElement);
|
|
338
|
+
await expect(await canvas.findByTestId('layers')).toBeInTheDocument();
|
|
339
|
+
await expect(canvas.queryByTestId('scene-view-dock')).not.toBeInTheDocument();
|
|
340
|
+
// About lives only in the dock.
|
|
341
|
+
await expect(canvas.queryByTestId('about')).not.toBeInTheDocument();
|
|
342
|
+
},
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
/** A frame onto a nested scene: its open control drills into the child scene, and Up returns. */
|
|
346
|
+
export const Frame: Story = {
|
|
347
|
+
args: { depth: 0, liveDepth: 1, fixture: 'frame' },
|
|
348
|
+
play: async ({ canvasElement }) => {
|
|
349
|
+
const canvas = within(canvasElement);
|
|
350
|
+
// 1. The frame shows its label and an open control; the root has nowhere up to go.
|
|
351
|
+
await waitFor(() => expect(canvasElement.querySelector('[data-node-id="frame"]')).toHaveTextContent('Frame'));
|
|
352
|
+
await waitFor(() => expect(canvas.getByTestId('toolbar-up')).toBeDisabled());
|
|
353
|
+
// 2. Opening the frame drills into its scene.
|
|
354
|
+
await userEvent.click(await canvas.findByTestId('portal-open'));
|
|
355
|
+
await waitFor(() => expect(canvas.getByTestId('toolbar-up')).toBeEnabled());
|
|
356
|
+
await waitFor(() => expect(canvasElement.querySelector('[data-node-id="b"]')).not.toBeNull());
|
|
357
|
+
},
|
|
358
|
+
};
|