@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
package/src/model/registry.ts
CHANGED
|
@@ -16,19 +16,15 @@
|
|
|
16
16
|
import type * as Schema from 'effect/Schema';
|
|
17
17
|
import { type ComponentType } from 'react';
|
|
18
18
|
|
|
19
|
-
import {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
PortalNodeView,
|
|
25
|
-
} from '../components/SceneLayer/SceneLayer.tsx';
|
|
26
|
-
import { type PartEditing } from '../utils/parts.ts';
|
|
27
|
-
import { DEFAULT_SIZES, createNode } from '../utils/shapes.ts';
|
|
19
|
+
import { type FormFieldMap } from '@dxos/react-ui-form';
|
|
20
|
+
|
|
21
|
+
import { BoxNodeView, EllipseNodeView, FrameNodeView, NoteNodeView } from '../components/SceneLayer/SceneLayer.tsx';
|
|
22
|
+
import { type PartEditing, type PartField } from '../utils/parts.ts';
|
|
23
|
+
import { NOMINAL_SIZES, createNode } from '../utils/shapes.ts';
|
|
28
24
|
import { type SceneStore } from './store.ts';
|
|
29
25
|
import {
|
|
30
|
-
ClassNode,
|
|
31
26
|
EllipseNode,
|
|
27
|
+
FrameNode,
|
|
32
28
|
type LinkType,
|
|
33
29
|
type Node,
|
|
34
30
|
type NodeBase,
|
|
@@ -36,7 +32,6 @@ import {
|
|
|
36
32
|
NoteNode,
|
|
37
33
|
type Point,
|
|
38
34
|
type Port,
|
|
39
|
-
PortalNode,
|
|
40
35
|
RectNode,
|
|
41
36
|
type Scene,
|
|
42
37
|
type Size,
|
|
@@ -58,6 +53,8 @@ export type NodeViewProps = {
|
|
|
58
53
|
opening?: boolean;
|
|
59
54
|
/** The text part of this node being edited in place, with the editor's callbacks. */
|
|
60
55
|
editing?: PartEditing;
|
|
56
|
+
/** The node's embedded content takes input (`SceneViewAtoms.active`); otherwise it should be inert. */
|
|
57
|
+
active?: boolean;
|
|
61
58
|
/** Opens the node (drills into a portal); absent where the layer is read-only. */
|
|
62
59
|
onOpen?: () => void;
|
|
63
60
|
};
|
|
@@ -77,6 +74,7 @@ export type NodeDef = {
|
|
|
77
74
|
component: ComponentType<NodeViewProps>;
|
|
78
75
|
/** A new node of the type with its default content, for the palette tool and drop-on-canvas. */
|
|
79
76
|
create: (props: CreateProps) => Node;
|
|
77
|
+
/** Size when created, in nominal units: one unit is the drawing's major grid cell. */
|
|
80
78
|
defaultSize: Size;
|
|
81
79
|
/** Explicit port layout; absent, the type gets `portsPerSide` ports spread along each side. */
|
|
82
80
|
ports?: (node: Node) => readonly Port[];
|
|
@@ -85,8 +83,22 @@ export type NodeDef = {
|
|
|
85
83
|
resizable?: boolean;
|
|
86
84
|
minSize?: Size;
|
|
87
85
|
maxSize?: Size;
|
|
88
|
-
/** Double-click opens the node (a
|
|
86
|
+
/** Double-click opens the node (a frame drills in; a text node edits, later). */
|
|
89
87
|
openable?: boolean;
|
|
88
|
+
/**
|
|
89
|
+
* The host's own way to open this node, when it has one (a frame showing an object rather than a scene):
|
|
90
|
+
* opening calls it instead of drilling in, and auto-drill passes the node by.
|
|
91
|
+
*/
|
|
92
|
+
hostOpen?: (node: Node) => (() => void) | undefined;
|
|
93
|
+
/**
|
|
94
|
+
* Controls floating above the node, flush with its right edge and at screen size, so they neither cover the node's
|
|
95
|
+
* content nor scale with the camera; renders nothing when it returns `null`.
|
|
96
|
+
*/
|
|
97
|
+
toolbar?: ComponentType<NodeViewProps>;
|
|
98
|
+
/** The text properties edited in place, in order; the first is the node's main text. */
|
|
99
|
+
parts?: readonly PartField[];
|
|
100
|
+
/** Properties-panel renderers for this type's fields, by path (e.g. a list as lines). */
|
|
101
|
+
fields?: FormFieldMap;
|
|
90
102
|
};
|
|
91
103
|
|
|
92
104
|
/**
|
|
@@ -148,11 +160,12 @@ const MIN_SIZE: Size = { width: 64, height: 32 };
|
|
|
148
160
|
|
|
149
161
|
/**
|
|
150
162
|
* A framed shape with a centred, editable label: resizable, with ports spread along every side. The
|
|
151
|
-
* built-in rectangle and
|
|
163
|
+
* built-in rectangle and frame are both boxes; a host type can extend it too.
|
|
152
164
|
*/
|
|
153
165
|
export const boxPrototype: NodeDefSpec = {
|
|
154
166
|
component: BoxNodeView,
|
|
155
|
-
|
|
167
|
+
parts: [{ field: 'label' }],
|
|
168
|
+
defaultSize: NOMINAL_SIZES.rect,
|
|
156
169
|
resizable: true,
|
|
157
170
|
minSize: MIN_SIZE,
|
|
158
171
|
};
|
|
@@ -175,43 +188,35 @@ export const defaultNodeTypes: Readonly<Record<NodeType, NodeDefSpec>> = {
|
|
|
175
188
|
schema: EllipseNode,
|
|
176
189
|
component: EllipseNodeView,
|
|
177
190
|
create: (props) => createNode({ type: 'ellipse', ...props }),
|
|
178
|
-
defaultSize:
|
|
191
|
+
defaultSize: NOMINAL_SIZES.ellipse,
|
|
179
192
|
// Only the side centres of the frame lie on the curve.
|
|
180
193
|
portsPerSide: 1,
|
|
194
|
+
parts: [{ field: 'label' }],
|
|
181
195
|
resizable: true,
|
|
182
196
|
minSize: MIN_SIZE,
|
|
183
197
|
},
|
|
184
|
-
|
|
185
|
-
name: 'Class',
|
|
186
|
-
icon: 'ph--rows--regular',
|
|
187
|
-
key: 'C',
|
|
188
|
-
schema: ClassNode,
|
|
189
|
-
component: ClassNodeView,
|
|
190
|
-
create: (props) => createNode({ type: 'class', ...props }),
|
|
191
|
-
defaultSize: DEFAULT_SIZES.class,
|
|
192
|
-
resizable: true,
|
|
193
|
-
minSize: { width: 128, height: 96 },
|
|
194
|
-
},
|
|
198
|
+
|
|
195
199
|
note: {
|
|
196
200
|
name: 'Note',
|
|
197
201
|
icon: 'ph--text-t--regular',
|
|
198
202
|
key: 'T',
|
|
199
203
|
schema: NoteNode,
|
|
200
204
|
component: NoteNodeView,
|
|
205
|
+
parts: [{ field: 'text', multiline: true }],
|
|
201
206
|
create: (props) => createNode({ type: 'note', ...props }),
|
|
202
|
-
defaultSize:
|
|
207
|
+
defaultSize: NOMINAL_SIZES.note,
|
|
203
208
|
resizable: true,
|
|
204
209
|
minSize: MIN_SIZE,
|
|
205
210
|
},
|
|
206
|
-
|
|
211
|
+
frame: {
|
|
207
212
|
extends: 'box',
|
|
208
|
-
name: '
|
|
213
|
+
name: 'Frame',
|
|
209
214
|
icon: 'ph--frame-corners--regular',
|
|
210
|
-
key: '
|
|
211
|
-
schema:
|
|
212
|
-
component:
|
|
213
|
-
create: (props) => createNode({ type: '
|
|
214
|
-
defaultSize:
|
|
215
|
+
key: 'F',
|
|
216
|
+
schema: FrameNode,
|
|
217
|
+
component: FrameNodeView,
|
|
218
|
+
create: (props) => createNode({ type: 'frame', ...props }),
|
|
219
|
+
defaultSize: NOMINAL_SIZES.frame,
|
|
215
220
|
minSize: { width: 96, height: 60 },
|
|
216
221
|
openable: true,
|
|
217
222
|
},
|
package/src/model/store.ts
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
import * as Atom from 'effect/reactivity/Atom';
|
|
11
11
|
import type * as Registry from 'effect/reactivity/AtomRegistry';
|
|
12
12
|
|
|
13
|
-
import { type Scene, type SceneId } from './types.ts';
|
|
13
|
+
import { type Scene, type SceneId, type StyleMap } from './types.ts';
|
|
14
14
|
|
|
15
15
|
export type SceneMap = Readonly<Record<SceneId, Scene>>;
|
|
16
16
|
|
|
@@ -19,13 +19,20 @@ export type SceneStore = {
|
|
|
19
19
|
readonly scenes: Atom.Writable<SceneMap>;
|
|
20
20
|
/** Derived atom for one scene; stable per id so subscriptions do not churn. */
|
|
21
21
|
scene: (id: SceneId) => Atom.Atom<Scene | undefined>;
|
|
22
|
+
/** The drawing's style classes; a store without them offers none. */
|
|
23
|
+
readonly styles?: Atom.Writable<StyleMap>;
|
|
22
24
|
};
|
|
23
25
|
|
|
24
|
-
|
|
26
|
+
/** A store held in memory, which always keeps style classes. */
|
|
27
|
+
export type MemorySceneStore = SceneStore & { readonly styles: Atom.Writable<StyleMap> };
|
|
28
|
+
|
|
29
|
+
export const createMemoryStore = (initial: readonly Scene[] = [], initialStyles: StyleMap = {}): MemorySceneStore => {
|
|
30
|
+
const styles = Atom.keepAlive(Atom.make<StyleMap>(initialStyles));
|
|
25
31
|
const scenes = Atom.keepAlive(Atom.make<SceneMap>(Object.fromEntries(initial.map((scene) => [scene.id, scene]))));
|
|
26
32
|
const derived = new Map<SceneId, Atom.Atom<Scene | undefined>>();
|
|
27
33
|
return {
|
|
28
34
|
scenes,
|
|
35
|
+
styles,
|
|
29
36
|
scene: (id) => {
|
|
30
37
|
let atom = derived.get(id);
|
|
31
38
|
if (!atom) {
|
|
@@ -58,3 +65,6 @@ export const updateScene = (
|
|
|
58
65
|
export const putScene = (registry: Registry.AtomRegistry, store: SceneStore, scene: Scene): void => {
|
|
59
66
|
registry.set(store.scenes, { ...registry.get(store.scenes), [scene.id]: scene });
|
|
60
67
|
};
|
|
68
|
+
|
|
69
|
+
/** No classes: what a store without `styles` reads as. */
|
|
70
|
+
export const NO_STYLES: Atom.Atom<StyleMap> = Atom.keepAlive(Atom.make<StyleMap>({}));
|
package/src/model/types.ts
CHANGED
|
@@ -83,14 +83,50 @@ export type Port = Schema.Schema.Type<typeof Port>;
|
|
|
83
83
|
// Nodes
|
|
84
84
|
//
|
|
85
85
|
|
|
86
|
-
/**
|
|
86
|
+
/** The hues the style pickers offer, neutral first, then in hue order; any theme hue still renders on a node. */
|
|
87
|
+
export const STYLE_HUES = ['neutral', 'red', 'orange', 'amber', 'green', 'teal', 'sky', 'blue', 'violet'] as const;
|
|
88
|
+
export const StyleHue = Schema.Literals(STYLE_HUES);
|
|
89
|
+
export type StyleHue = Schema.Schema.Type<typeof StyleHue>;
|
|
90
|
+
|
|
91
|
+
/** How strongly a hue fills a frame: 0 is an outline (transparent), 1 to 3 are stronger to lighter fills. */
|
|
87
92
|
export const NodeTone = Schema.Literals([0, 1, 2, 3]);
|
|
88
93
|
export type NodeTone = Schema.Schema.Type<typeof NodeTone>;
|
|
89
94
|
|
|
90
|
-
/**
|
|
91
|
-
export const
|
|
92
|
-
|
|
95
|
+
/** The typefaces a shape's text may use: the theme's body face, or its fixed-width one. */
|
|
96
|
+
export const FONT_FAMILIES = ['default', 'monospace'] as const;
|
|
97
|
+
export type FontFamily = (typeof FONT_FAMILIES)[number];
|
|
98
|
+
|
|
99
|
+
/** Where text sits across a shape, and down it. */
|
|
100
|
+
export const HORIZONTAL_ALIGNS = ['left', 'center', 'right'] as const;
|
|
101
|
+
export const VERTICAL_ALIGNS = ['top', 'middle', 'bottom'] as const;
|
|
102
|
+
export type HorizontalAlign = (typeof HORIZONTAL_ALIGNS)[number];
|
|
103
|
+
export type VerticalAlign = (typeof VERTICAL_ALIGNS)[number];
|
|
104
|
+
|
|
105
|
+
/** How a line is drawn: a link's stroke, or a node's border. */
|
|
106
|
+
export const LINE_STYLES = ['solid', 'dashed', 'dotted'] as const;
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* The common base style every element shares, nodes and links alike: its colour and its line. Each kind extends it
|
|
110
|
+
* (`NodeStyle`), and a selection of several kinds edits only what they share. One field object per property, so
|
|
111
|
+
* the kinds declare the same field and the properties panel sees it as shared.
|
|
112
|
+
*/
|
|
113
|
+
export const lineStyleFields = {
|
|
114
|
+
/** One of the theme's hues: a link's stroke; a node's fill, text and border together. */
|
|
93
115
|
hue: Schema.optional(Schema.String.annotate({ title: 'Hue', [HueAnnotationId]: true })),
|
|
116
|
+
/** Solid, dashed or dotted: a link's stroke, or a node's border. */
|
|
117
|
+
lineStyle: Schema.optional(Schema.Literals(LINE_STYLES).annotate({ title: 'Line style' })),
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
/** A link's style: the common base, unset drawn neutral and solid. */
|
|
121
|
+
export const LineStyle = Schema.Struct(lineStyleFields);
|
|
122
|
+
export type LineStyle = Schema.Schema.Type<typeof LineStyle>;
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* The common base plus a frame and text: what a shape draws. A shape with more to style extends it
|
|
126
|
+
* (`Schema.Struct({ ...styleFields, … })`).
|
|
127
|
+
*/
|
|
128
|
+
export const styleFields = {
|
|
129
|
+
...lineStyleFields,
|
|
94
130
|
/** The hue's fill; unset is 2, the look a hue had before tones. */
|
|
95
131
|
tone: Schema.optional(NodeTone),
|
|
96
132
|
rounded: Schema.optional(Schema.Boolean),
|
|
@@ -98,11 +134,18 @@ export const NodeStyle = Schema.Struct({
|
|
|
98
134
|
border: Schema.optional(Schema.Boolean),
|
|
99
135
|
/** A guide: drawn dashed and unfilled, an annotation rather than content. */
|
|
100
136
|
guide: Schema.optional(Schema.Boolean),
|
|
137
|
+
/** The face of the shape's text; unset is the theme's body face. */
|
|
138
|
+
fontFamily: Schema.optional(Schema.Literals(FONT_FAMILIES).annotate({ title: 'Font' })),
|
|
101
139
|
/** Text size in the node's own scene units (the editor offers a readable range; stored values are not checked). */
|
|
102
140
|
fontSize: Schema.optional(Schema.Number.annotate({ title: 'Font size' })),
|
|
103
|
-
/**
|
|
104
|
-
|
|
105
|
-
|
|
141
|
+
/** Where the text sits across the shape; unset is the type's own (a label centres, a note starts at the left). */
|
|
142
|
+
alignHorizontal: Schema.optional(Schema.Literals(HORIZONTAL_ALIGNS).annotate({ title: 'Horizontal' })),
|
|
143
|
+
/** Where the text sits down the shape; unset is the type's own (a label is middled, a note starts at the top). */
|
|
144
|
+
alignVertical: Schema.optional(Schema.Literals(VERTICAL_ALIGNS).annotate({ title: 'Vertical' })),
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
/** Presentation choices a node carries: the shape style. */
|
|
148
|
+
export const NodeStyle = Schema.Struct(styleFields);
|
|
106
149
|
export type NodeStyle = Schema.Schema.Type<typeof NodeStyle>;
|
|
107
150
|
|
|
108
151
|
/** The fields every node type shares; a type's schema is `Schema.Struct({ ...nodeBase, type: Literal, ... })`. */
|
|
@@ -118,6 +161,10 @@ export const nodeBase = {
|
|
|
118
161
|
ports: Schema.optional(Schema.Array(Port)),
|
|
119
162
|
/** Ports spread along each side, overriding the type's layout; ignored when the node carries `ports`. */
|
|
120
163
|
portsPerSide: Schema.optional(Schema.Number.annotate({ title: 'Ports per side' })),
|
|
164
|
+
/** A style class of the drawing (`StyleClass`) the node takes its look from; its own `style` wins over it. */
|
|
165
|
+
/** The scene layer the element is on; unset, or naming no layer of the scene, it is on the bottom one. */
|
|
166
|
+
layer: Schema.optional(Schema.String.annotate({ title: 'Layer' })),
|
|
167
|
+
class: Schema.optional(Schema.String.annotate({ title: 'Class' })),
|
|
121
168
|
style: Schema.optional(NodeStyle),
|
|
122
169
|
};
|
|
123
170
|
|
|
@@ -130,6 +177,14 @@ export const boxFields = {
|
|
|
130
177
|
label: Schema.optional(Schema.String),
|
|
131
178
|
};
|
|
132
179
|
|
|
180
|
+
/**
|
|
181
|
+
* The core base every node falls back to: its frame, ports, style and `label`. A node whose type the registry does
|
|
182
|
+
* not know (its plugin is off) is drawn and edited as this, a box showing its label, so a type that keeps its title
|
|
183
|
+
* in `label` still reads as itself.
|
|
184
|
+
*/
|
|
185
|
+
export const BaseNode = Schema.Struct({ type: Schema.String, ...nodeBase, ...boxFields });
|
|
186
|
+
export type BaseNode = Schema.Schema.Type<typeof BaseNode>;
|
|
187
|
+
|
|
133
188
|
export const RectNode = Schema.Struct({
|
|
134
189
|
type: Schema.Literal('rect'),
|
|
135
190
|
...nodeBase,
|
|
@@ -145,16 +200,6 @@ export const EllipseNode = Schema.Struct({
|
|
|
145
200
|
});
|
|
146
201
|
export type EllipseNode = Schema.Schema.Type<typeof EllipseNode>;
|
|
147
202
|
|
|
148
|
-
/** UML class box: a name compartment over attribute and method compartments. */
|
|
149
|
-
export const ClassNode = Schema.Struct({
|
|
150
|
-
type: Schema.Literal('class'),
|
|
151
|
-
...nodeBase,
|
|
152
|
-
name: Schema.String,
|
|
153
|
-
attributes: Schema.Array(Schema.String),
|
|
154
|
-
methods: Schema.Array(Schema.String),
|
|
155
|
-
});
|
|
156
|
-
export type ClassNode = Schema.Schema.Type<typeof ClassNode>;
|
|
157
|
-
|
|
158
203
|
/** Free text on the canvas. Named for what it is, so a host may keep `text` for a type of its own. */
|
|
159
204
|
export const NoteNode = Schema.Struct({
|
|
160
205
|
type: Schema.Literal('note'),
|
|
@@ -163,22 +208,22 @@ export const NoteNode = Schema.Struct({
|
|
|
163
208
|
});
|
|
164
209
|
export type NoteNode = Schema.Schema.Type<typeof NoteNode>;
|
|
165
210
|
|
|
166
|
-
/**
|
|
167
|
-
export const
|
|
168
|
-
type: Schema.Literal('
|
|
211
|
+
/** A frame onto the next depth: renders its child scene scaled into this node's bounds. */
|
|
212
|
+
export const FrameNode = Schema.Struct({
|
|
213
|
+
type: Schema.Literal('frame'),
|
|
169
214
|
...nodeBase,
|
|
170
215
|
...boxFields,
|
|
171
216
|
scene: Schema.String,
|
|
172
217
|
/** Draw the child scene inside the frame rather than the label; unset, it does so while there is no label. */
|
|
173
218
|
contents: Schema.optional(Schema.Boolean.annotate({ title: 'Show contents' })),
|
|
174
219
|
});
|
|
175
|
-
export type
|
|
220
|
+
export type FrameNode = Schema.Schema.Type<typeof FrameNode>;
|
|
176
221
|
|
|
177
222
|
/** The engine's own node types. A host may add its own (decision 1); those are `NodeBase` to the engine. */
|
|
178
|
-
export const BuiltinNode = Schema.Union([RectNode, EllipseNode,
|
|
223
|
+
export const BuiltinNode = Schema.Union([RectNode, EllipseNode, NoteNode, FrameNode]);
|
|
179
224
|
export type BuiltinNode = Schema.Schema.Type<typeof BuiltinNode>;
|
|
180
225
|
export type BuiltinNodeType = BuiltinNode['type'];
|
|
181
|
-
export const NODE_TYPES: readonly BuiltinNodeType[] = ['rect', 'ellipse', '
|
|
226
|
+
export const NODE_TYPES: readonly BuiltinNodeType[] = ['rect', 'ellipse', 'note', 'frame'];
|
|
182
227
|
|
|
183
228
|
/** A node of the scene: the engine handles any `NodeBase`; built-in code narrows with the guards below. */
|
|
184
229
|
export type Node = NodeBase;
|
|
@@ -188,13 +233,12 @@ export type NodeType = string;
|
|
|
188
233
|
/** Narrows a node to one built-in type; sound because the registry maps each type name to one schema. */
|
|
189
234
|
export const isRectNode = (node: NodeBase): node is RectNode => node.type === 'rect';
|
|
190
235
|
export const isEllipseNode = (node: NodeBase): node is EllipseNode => node.type === 'ellipse';
|
|
191
|
-
export const isClassNode = (node: NodeBase): node is ClassNode => node.type === 'class';
|
|
192
236
|
export const isNoteNode = (node: NodeBase): node is NoteNode => node.type === 'note';
|
|
193
|
-
export const
|
|
237
|
+
export const isFrameNode = (node: NodeBase): node is FrameNode => node.type === 'frame';
|
|
194
238
|
/** A node built on the `box` prototype, carrying a centred, editable label. */
|
|
195
|
-
export const isBoxNode = (node: NodeBase): node is RectNode |
|
|
196
|
-
/** Whether a
|
|
197
|
-
export const showsContents = (node:
|
|
239
|
+
export const isBoxNode = (node: NodeBase): node is RectNode | FrameNode => isRectNode(node) || isFrameNode(node);
|
|
240
|
+
/** Whether a frame draws its child scene: as set, else while it has no label to show instead. */
|
|
241
|
+
export const showsContents = (node: FrameNode): boolean => node.contents ?? node.label === undefined;
|
|
198
242
|
export const isBuiltinNode = (node: NodeBase): node is BuiltinNode => NODE_TYPES.some((type) => type === node.type);
|
|
199
243
|
|
|
200
244
|
//
|
|
@@ -220,7 +264,8 @@ export const isPointEndpoint = (end: Endpoint): end is PointEndpoint => 'point'
|
|
|
220
264
|
export const endpointNode = (end: Endpoint): NodeId | undefined => ('node' in end ? end.node : undefined);
|
|
221
265
|
|
|
222
266
|
/** What is drawn at a link end. */
|
|
223
|
-
|
|
267
|
+
/** An end marker: a filled `arrow`, a `circle`, or a hollow `triangle` (inheritance). */
|
|
268
|
+
export const Marker = Schema.Literals(['arrow', 'circle', 'triangle']);
|
|
224
269
|
export type Marker = Schema.Schema.Type<typeof Marker>;
|
|
225
270
|
|
|
226
271
|
/** Markers at the source (`start`) and target (`end`) of a link. */
|
|
@@ -231,16 +276,35 @@ export const LinkEnds = Schema.Struct({
|
|
|
231
276
|
}).pipe(Annotation.FormLayoutAnnotation.set({ [Annotation.DEFAULT_LAYOUT_NAME]: pairLayout('start', 'end') }));
|
|
232
277
|
export type LinkEnds = Schema.Schema.Type<typeof LinkEnds>;
|
|
233
278
|
|
|
279
|
+
/**
|
|
280
|
+
* A named look of a drawing, kept beside its scenes: the nodes and links naming it derive their `style` from it (a
|
|
281
|
+
* link takes the common base, its colour and line style), so one class restyles both together.
|
|
282
|
+
*/
|
|
283
|
+
export const StyleClass = Schema.Struct({
|
|
284
|
+
id: Schema.String,
|
|
285
|
+
name: Schema.String,
|
|
286
|
+
style: Schema.optional(NodeStyle),
|
|
287
|
+
});
|
|
288
|
+
export type StyleClass = Schema.Schema.Type<typeof StyleClass>;
|
|
289
|
+
|
|
290
|
+
/** A drawing's style classes by id. */
|
|
291
|
+
export type StyleMap = Readonly<Record<string, StyleClass>>;
|
|
292
|
+
|
|
234
293
|
const linkBase = {
|
|
235
294
|
id: Schema.String,
|
|
236
295
|
z: Schema.String,
|
|
237
296
|
locked: Schema.optional(Schema.Boolean),
|
|
238
297
|
source: Endpoint,
|
|
239
298
|
target: Endpoint,
|
|
240
|
-
/**
|
|
241
|
-
directed: Schema.optional(Schema.Boolean),
|
|
242
|
-
/** Explicit end markers; when present they replace what `directed` implies. */
|
|
299
|
+
/** End markers; an arrow at `end` reads as the link's direction. */
|
|
243
300
|
ends: Schema.optional(LinkEnds),
|
|
301
|
+
/** A caption drawn at the middle of the link's route. */
|
|
302
|
+
text: Schema.optional(Schema.String.annotate({ title: 'Label' })),
|
|
303
|
+
/** A style class of the drawing the link takes its style from; its own `style` wins over it. */
|
|
304
|
+
/** The scene layer the element is on; unset, or naming no layer of the scene, it is on the bottom one. */
|
|
305
|
+
layer: Schema.optional(Schema.String.annotate({ title: 'Layer' })),
|
|
306
|
+
class: Schema.optional(Schema.String.annotate({ title: 'Class' })),
|
|
307
|
+
style: Schema.optional(LineStyle),
|
|
244
308
|
};
|
|
245
309
|
|
|
246
310
|
export const LineLink = Schema.Struct({ type: Schema.Literal('line'), ...linkBase });
|
|
@@ -272,8 +336,8 @@ export const LINK_TYPES: readonly LinkType[] = ['line', 'curve', 'spline', 'smar
|
|
|
272
336
|
|
|
273
337
|
export type Element = Node | Link;
|
|
274
338
|
|
|
275
|
-
/** The markers a link draws
|
|
276
|
-
export const linkMarkers = (link: Link): LinkEnds => link.ends ??
|
|
339
|
+
/** The markers a link draws. */
|
|
340
|
+
export const linkMarkers = (link: Link): LinkEnds => link.ends ?? {};
|
|
277
341
|
|
|
278
342
|
export const isNode = (element: Element): element is Node => 'center' in element;
|
|
279
343
|
export const isLink = (element: Element): element is Link => 'source' in element;
|
|
@@ -282,6 +346,22 @@ export const isLink = (element: Element): element is Link => 'source' in element
|
|
|
282
346
|
// Scene
|
|
283
347
|
//
|
|
284
348
|
|
|
349
|
+
/** A layer id, unique within its scene. */
|
|
350
|
+
export type LayerId = string;
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* A scene's layer: elements paint by their layer's order (`z`, bottom first), then by their own `z`. A hidden layer
|
|
354
|
+
* is neither drawn nor hit. A scene with no layers has one, implicitly (`DEFAULT_LAYER`).
|
|
355
|
+
*/
|
|
356
|
+
export const Layer = Schema.Struct({
|
|
357
|
+
id: Schema.String,
|
|
358
|
+
name: Schema.String,
|
|
359
|
+
/** Fractional order key (see `order.ts`): the bottom layer has the least. */
|
|
360
|
+
z: Schema.String,
|
|
361
|
+
hidden: Schema.optional(Schema.Boolean),
|
|
362
|
+
});
|
|
363
|
+
export type Layer = Schema.Schema.Type<typeof Layer>;
|
|
364
|
+
|
|
285
365
|
/**
|
|
286
366
|
* The scene schema over a set of node schemas: a host composes it from its registry's types, so the
|
|
287
367
|
* schema stays exact for every type while the engine only ever sees `NodeBase`.
|
|
@@ -292,10 +372,11 @@ export const createSceneSchema = <const Nodes extends readonly Schema.Codec<Node
|
|
|
292
372
|
name: Schema.optional(Schema.String),
|
|
293
373
|
nodes: Schema.Record(Schema.String, Schema.Union(nodes)),
|
|
294
374
|
links: Schema.Record(Schema.String, Link),
|
|
375
|
+
layers: Schema.optional(Schema.Record(Schema.String, Layer)),
|
|
295
376
|
});
|
|
296
377
|
|
|
297
378
|
/** The scene schema over the built-in node types. */
|
|
298
|
-
export const Scene = createSceneSchema([RectNode, EllipseNode,
|
|
379
|
+
export const Scene = createSceneSchema([RectNode, EllipseNode, NoteNode, FrameNode]);
|
|
299
380
|
|
|
300
381
|
/** The scene schema over any node with the shared fields: what the engine itself can validate for a host. */
|
|
301
382
|
export const OpenScene = createSceneSchema([NodeBase]);
|
|
@@ -304,6 +385,7 @@ export type Scene = {
|
|
|
304
385
|
readonly name?: string;
|
|
305
386
|
readonly nodes: Readonly<Record<NodeId, Node>>;
|
|
306
387
|
readonly links: Readonly<Record<LinkId, Link>>;
|
|
388
|
+
readonly layers?: Readonly<Record<LayerId, Layer>>;
|
|
307
389
|
};
|
|
308
390
|
|
|
309
391
|
/** A node or link of the scene by id. */
|
|
@@ -311,7 +393,9 @@ export const getElement = (scene: Scene, id: ElementId): Element | undefined =>
|
|
|
311
393
|
|
|
312
394
|
/** Property edits: the shared fields typed, a type's own fields by name. */
|
|
313
395
|
export type NodeValues = Partial<Omit<NodeBase, 'id' | 'type'>> & { readonly [key: string]: unknown };
|
|
314
|
-
|
|
396
|
+
/** Per link type, so a spline's `points` are among the values an `update` may set. */
|
|
397
|
+
type ValuesOf<T> = T extends unknown ? Partial<Omit<T, 'id' | 'type'>> : never;
|
|
398
|
+
export type LinkValues = ValuesOf<Link>;
|
|
315
399
|
|
|
316
400
|
/**
|
|
317
401
|
* What the surface asks of a projection (§3). The surface never writes coordinates itself: a
|
|
@@ -331,6 +415,10 @@ export type Intent =
|
|
|
331
415
|
* rather than a view action, so a projection that owns its own positions may rewrite or refuse it.
|
|
332
416
|
*/
|
|
333
417
|
| { kind: 'layout'; ids?: ElementId[] }
|
|
418
|
+
/** Adds a layer, or replaces the one of its id (a rename, a visibility toggle, a move in the order). */
|
|
419
|
+
| { kind: 'layer'; layer: Layer }
|
|
420
|
+
/** Removes a layer and every element on it; the last layer of a scene stays. */
|
|
421
|
+
| { kind: 'removeLayer'; id: LayerId }
|
|
334
422
|
/** Several intents applied as one model change (one undo step), e.g. a paste. */
|
|
335
423
|
| { kind: 'batch'; intents: Intent[] };
|
|
336
424
|
|
|
@@ -42,7 +42,7 @@ export class SceneManager {
|
|
|
42
42
|
return Math.max(...boxes.map(({ x, width }) => x + width));
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
/** The debug bar's zoom readout, as whole percent; the navigation bar carries the path
|
|
45
|
+
/** The debug bar's zoom readout, as whole percent; the navigation bar carries the path. */
|
|
46
46
|
async zoom(): Promise<number> {
|
|
47
47
|
const readout = await this.page.getByTestId('canvas-debug').textContent();
|
|
48
48
|
const percent = readout?.match(/(\d+)%/);
|
|
@@ -56,7 +56,7 @@ export class SceneManager {
|
|
|
56
56
|
selectedNodes(): Promise<string[]> {
|
|
57
57
|
return this.page.evaluate(() =>
|
|
58
58
|
Array.from(document.querySelectorAll<HTMLElement>('[data-node-id]'))
|
|
59
|
-
.filter((element) => element.className.includes('border-
|
|
59
|
+
.filter((element) => element.className.includes('border-focus '))
|
|
60
60
|
.map((element) => element.dataset.nodeId ?? ''),
|
|
61
61
|
);
|
|
62
62
|
}
|
|
@@ -6,11 +6,12 @@ import { type Page, expect, test } from '@playwright/test';
|
|
|
6
6
|
|
|
7
7
|
import { setupPage, storybookUrl } from '@dxos/test-utils/playwright';
|
|
8
8
|
|
|
9
|
+
import { NOMINAL_SIZES, nominalSize } from '../utils/shapes.ts';
|
|
9
10
|
import { SceneManager } from './SceneManager.ts';
|
|
10
11
|
|
|
11
12
|
const PORT = 9006;
|
|
12
13
|
// The empty canvas: every gesture here lands on blank scene, so a create is never a hit on a fixture node.
|
|
13
|
-
const DEFAULT_URL = storybookUrl('ui-react-ui-canvas-
|
|
14
|
+
const DEFAULT_URL = storybookUrl('ui-react-ui-canvas-sceneview--default', PORT);
|
|
14
15
|
|
|
15
16
|
test.describe('create sizing', () => {
|
|
16
17
|
let page: Page;
|
|
@@ -103,10 +104,11 @@ test.describe('create sizing', () => {
|
|
|
103
104
|
return (await scene.box(scene.node(id!))).width;
|
|
104
105
|
};
|
|
105
106
|
|
|
106
|
-
// A new
|
|
107
|
+
// A new rectangle is its nominal width (two major cells, 128 scene units) at any zoom, so on screen it
|
|
108
|
+
// scales with the zoom.
|
|
107
109
|
const expectDefault = async () => {
|
|
108
110
|
const width = await widthAt();
|
|
109
|
-
expect(Math.abs(width - (
|
|
111
|
+
expect(Math.abs(width - (nominalSize(NOMINAL_SIZES.rect).width * (await scene.zoom())) / 100)).toBeLessThan(4);
|
|
110
112
|
};
|
|
111
113
|
await expectDefault();
|
|
112
114
|
await scene.zoomIn(2);
|
|
@@ -14,8 +14,8 @@ import { test } from '@playwright/test';
|
|
|
14
14
|
import { storybookUrl } from '@dxos/test-utils/playwright';
|
|
15
15
|
|
|
16
16
|
const PORT = 9006;
|
|
17
|
-
const DEFAULT_URL = storybookUrl('ui-react-ui-canvas-
|
|
18
|
-
const FREEHAND_URL = storybookUrl('ui-react-ui-canvas-
|
|
17
|
+
const DEFAULT_URL = storybookUrl('ui-react-ui-canvas-sceneview--default', PORT);
|
|
18
|
+
const FREEHAND_URL = storybookUrl('ui-react-ui-canvas-sceneview--freehand', PORT);
|
|
19
19
|
|
|
20
20
|
/** Long enough to read on playback; the recording is for a person, not a machine. */
|
|
21
21
|
const BEAT = 900;
|
|
@@ -9,7 +9,7 @@ import { setupPage, storybookUrl } from '@dxos/test-utils/playwright';
|
|
|
9
9
|
import { SceneManager } from './SceneManager.ts';
|
|
10
10
|
|
|
11
11
|
const PORT = 9006;
|
|
12
|
-
const LATTICE_URL = storybookUrl('ui-react-ui-canvas-
|
|
12
|
+
const LATTICE_URL = storybookUrl('ui-react-ui-canvas-sceneview--lattice', PORT);
|
|
13
13
|
|
|
14
14
|
// The fixture, on the default 256x128 lattice with 128x64 gutters, columns and rows -1..1: A, B, C down
|
|
15
15
|
// column -1; a free cell above D and E in column 0; F spanning all three rows of column 1.
|
|
@@ -37,15 +37,12 @@ test.describe('SceneView lattice', () => {
|
|
|
37
37
|
await expect(page.getByTestId('lattice-grid')).toHaveCount(1);
|
|
38
38
|
});
|
|
39
39
|
|
|
40
|
-
test('the guides toggle hides and restores the
|
|
41
|
-
await expect(page.getByTestId('scene-frame')).toHaveCount(1);
|
|
40
|
+
test('the guides toggle hides and restores the lattice cells', async () => {
|
|
42
41
|
await page.getByTestId('toolbar-guides').click();
|
|
43
42
|
await expect(page.getByTestId('lattice-grid')).toHaveCount(0);
|
|
44
|
-
await expect(page.getByTestId('scene-frame')).toHaveCount(0);
|
|
45
43
|
await scene.focus();
|
|
46
44
|
await page.keyboard.press(';');
|
|
47
45
|
await expect(page.getByTestId('lattice-grid')).toHaveCount(1);
|
|
48
|
-
await expect(page.getByTestId('scene-frame')).toHaveCount(1);
|
|
49
46
|
});
|
|
50
47
|
|
|
51
48
|
test('a drag onto occupied cells previews in red and is refused', async () => {
|
|
@@ -107,6 +104,8 @@ test.describe('SceneView lattice', () => {
|
|
|
107
104
|
});
|
|
108
105
|
|
|
109
106
|
test('dragging a face steps it one cell and leaves the opposite face where it was', async () => {
|
|
107
|
+
// Fit frames the shapes tightly, so step out to leave room below E for the drag.
|
|
108
|
+
await scene.zoomOut();
|
|
110
109
|
await scene.clickNode('e');
|
|
111
110
|
const before = await scene.box(scene.node('e'));
|
|
112
111
|
// E's east neighbour is F, so grow downwards: the south face most of a row down.
|