@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.
Files changed (341) hide show
  1. package/README.md +1 -1
  2. package/dist/lib/index.mjs +9044 -1135
  3. package/dist/lib/index.mjs.map +1 -1
  4. package/dist/types/src/components/About/About.d.ts +13 -0
  5. package/dist/types/src/components/About/About.d.ts.map +1 -0
  6. package/dist/types/src/components/About/About.stories.d.ts +7 -0
  7. package/dist/types/src/components/About/About.stories.d.ts.map +1 -0
  8. package/dist/types/src/components/About/index.d.ts +2 -0
  9. package/dist/types/src/components/About/index.d.ts.map +1 -0
  10. package/dist/types/src/components/Breadcrumbs/Breadcrumbs.d.ts.map +1 -1
  11. package/dist/types/src/components/ControlFrame/ControlFrame.d.ts +8 -2
  12. package/dist/types/src/components/ControlFrame/ControlFrame.d.ts.map +1 -1
  13. package/dist/types/src/components/Layers/Layers.d.ts +30 -0
  14. package/dist/types/src/components/Layers/Layers.d.ts.map +1 -0
  15. package/dist/types/src/components/Layers/Layers.stories.d.ts +11 -0
  16. package/dist/types/src/components/Layers/Layers.stories.d.ts.map +1 -0
  17. package/dist/types/src/components/Layers/index.d.ts +2 -0
  18. package/dist/types/src/components/Layers/index.d.ts.map +1 -0
  19. package/dist/types/src/components/Palette/Palette.d.ts +20 -4
  20. package/dist/types/src/components/Palette/Palette.d.ts.map +1 -1
  21. package/dist/types/src/components/Palette/Palette.stories.d.ts +22 -0
  22. package/dist/types/src/components/Palette/Palette.stories.d.ts.map +1 -0
  23. package/dist/types/src/components/PartEditor/PartEditor.d.ts.map +1 -1
  24. package/dist/types/src/components/Properties/AlignField.d.ts +23 -0
  25. package/dist/types/src/components/Properties/AlignField.d.ts.map +1 -0
  26. package/dist/types/src/components/Properties/ClassField.d.ts +23 -0
  27. package/dist/types/src/components/Properties/ClassField.d.ts.map +1 -0
  28. package/dist/types/src/components/Properties/FontField.d.ts +7 -0
  29. package/dist/types/src/components/Properties/FontField.d.ts.map +1 -0
  30. package/dist/types/src/components/Properties/LayerField.d.ts +17 -0
  31. package/dist/types/src/components/Properties/LayerField.d.ts.map +1 -0
  32. package/dist/types/src/components/Properties/Properties.d.ts +27 -3
  33. package/dist/types/src/components/Properties/Properties.d.ts.map +1 -1
  34. package/dist/types/src/components/Properties/Properties.stories.d.ts +20 -0
  35. package/dist/types/src/components/Properties/Properties.stories.d.ts.map +1 -0
  36. package/dist/types/src/components/Properties/SceneField.d.ts +8 -0
  37. package/dist/types/src/components/Properties/SceneField.d.ts.map +1 -0
  38. package/dist/types/src/components/Properties/StyleGrid.d.ts +15 -5
  39. package/dist/types/src/components/Properties/StyleGrid.d.ts.map +1 -1
  40. package/dist/types/src/components/Properties/StyleGrid.stories.d.ts +7 -0
  41. package/dist/types/src/components/Properties/StyleGrid.stories.d.ts.map +1 -0
  42. package/dist/types/src/components/SceneLayer/SceneLayer.d.ts +31 -7
  43. package/dist/types/src/components/SceneLayer/SceneLayer.d.ts.map +1 -1
  44. package/dist/types/src/components/SceneView/Dock.d.ts +22 -0
  45. package/dist/types/src/components/SceneView/Dock.d.ts.map +1 -0
  46. package/dist/types/src/components/SceneView/Dock.stories.d.ts +15 -0
  47. package/dist/types/src/components/SceneView/Dock.stories.d.ts.map +1 -0
  48. package/dist/types/src/components/SceneView/SceneView.d.ts +33 -7
  49. package/dist/types/src/components/SceneView/SceneView.d.ts.map +1 -1
  50. package/dist/types/src/components/SceneView/SceneView.stories.d.ts +16 -6
  51. package/dist/types/src/components/SceneView/SceneView.stories.d.ts.map +1 -1
  52. package/dist/types/src/components/SceneView/SceneViewContext.d.ts +36 -4
  53. package/dist/types/src/components/SceneView/SceneViewContext.d.ts.map +1 -1
  54. package/dist/types/src/components/SceneView/Scored.stories.d.ts +4 -4
  55. package/dist/types/src/components/SceneView/Scored.stories.d.ts.map +1 -1
  56. package/dist/types/src/components/SceneView/usePointerMachine.d.ts +3 -1
  57. package/dist/types/src/components/SceneView/usePointerMachine.d.ts.map +1 -1
  58. package/dist/types/src/components/SceneView/useSceneCamera.d.ts +2 -2
  59. package/dist/types/src/components/SceneView/useSceneCamera.d.ts.map +1 -1
  60. package/dist/types/src/components/SceneView/useSceneClipboard.d.ts.map +1 -1
  61. package/dist/types/src/components/SceneView/useSceneKeys.d.ts.map +1 -1
  62. package/dist/types/src/components/SceneView/useSceneNavigation.d.ts +7 -4
  63. package/dist/types/src/components/SceneView/useSceneNavigation.d.ts.map +1 -1
  64. package/dist/types/src/components/Toolbar/Toolbar.d.ts +2 -0
  65. package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +1 -1
  66. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts.map +1 -1
  67. package/dist/types/src/components/index.d.ts +2 -0
  68. package/dist/types/src/components/index.d.ts.map +1 -1
  69. package/dist/types/src/hooks/useWheel.d.ts +5 -0
  70. package/dist/types/src/hooks/useWheel.d.ts.map +1 -1
  71. package/dist/types/src/index.d.ts +4 -1
  72. package/dist/types/src/index.d.ts.map +1 -1
  73. package/dist/types/src/model/atoms.d.ts +21 -3
  74. package/dist/types/src/model/atoms.d.ts.map +1 -1
  75. package/dist/types/src/model/projection.d.ts.map +1 -1
  76. package/dist/types/src/model/projections/constrained.d.ts +2 -1
  77. package/dist/types/src/model/projections/constrained.d.ts.map +1 -1
  78. package/dist/types/src/model/projections/dynamic.d.ts.map +1 -1
  79. package/dist/types/src/model/projections/lattice.d.ts.map +1 -1
  80. package/dist/types/src/model/registry.d.ts +21 -3
  81. package/dist/types/src/model/registry.d.ts.map +1 -1
  82. package/dist/types/src/model/store.d.ts +10 -2
  83. package/dist/types/src/model/store.d.ts.map +1 -1
  84. package/dist/types/src/model/types.d.ts +699 -338
  85. package/dist/types/src/model/types.d.ts.map +1 -1
  86. package/dist/types/src/playwright/SceneManager.d.ts +1 -1
  87. package/dist/types/src/playwright/SceneManager.d.ts.map +1 -1
  88. package/dist/types/src/utils/builder.d.ts +22 -41
  89. package/dist/types/src/utils/builder.d.ts.map +1 -1
  90. package/dist/types/src/utils/camera.d.ts +19 -10
  91. package/dist/types/src/utils/camera.d.ts.map +1 -1
  92. package/dist/types/src/utils/clipboard.d.ts +4 -2
  93. package/dist/types/src/utils/clipboard.d.ts.map +1 -1
  94. package/dist/types/src/utils/diagram.d.ts.map +1 -1
  95. package/dist/types/src/utils/group.d.ts +14 -0
  96. package/dist/types/src/utils/group.d.ts.map +1 -0
  97. package/dist/types/src/utils/group.test.d.ts +2 -0
  98. package/dist/types/src/utils/group.test.d.ts.map +1 -0
  99. package/dist/types/src/utils/gutter-route.d.ts +7 -0
  100. package/dist/types/src/utils/gutter-route.d.ts.map +1 -1
  101. package/dist/types/src/utils/hit.d.ts +3 -20
  102. package/dist/types/src/utils/hit.d.ts.map +1 -1
  103. package/dist/types/src/utils/index.d.ts +3 -0
  104. package/dist/types/src/utils/index.d.ts.map +1 -1
  105. package/dist/types/src/utils/lattice.d.ts +5 -0
  106. package/dist/types/src/utils/lattice.d.ts.map +1 -1
  107. package/dist/types/src/utils/layers.d.ts +35 -0
  108. package/dist/types/src/utils/layers.d.ts.map +1 -0
  109. package/dist/types/src/utils/layers.test.d.ts +2 -0
  110. package/dist/types/src/utils/layers.test.d.ts.map +1 -0
  111. package/dist/types/src/utils/parts.d.ts +25 -9
  112. package/dist/types/src/utils/parts.d.ts.map +1 -1
  113. package/dist/types/src/utils/ports.d.ts +2 -0
  114. package/dist/types/src/utils/ports.d.ts.map +1 -1
  115. package/dist/types/src/utils/properties.d.ts +4 -3
  116. package/dist/types/src/utils/properties.d.ts.map +1 -1
  117. package/dist/types/src/utils/route.d.ts +5 -0
  118. package/dist/types/src/utils/route.d.ts.map +1 -1
  119. package/dist/types/src/utils/scenes.d.ts +14 -0
  120. package/dist/types/src/utils/scenes.d.ts.map +1 -0
  121. package/dist/types/src/utils/scenes.test.d.ts +2 -0
  122. package/dist/types/src/utils/scenes.test.d.ts.map +1 -0
  123. package/dist/types/src/utils/shapes.d.ts +23 -9
  124. package/dist/types/src/utils/shapes.d.ts.map +1 -1
  125. package/dist/types/src/utils/style.d.ts +29 -6
  126. package/dist/types/src/utils/style.d.ts.map +1 -1
  127. package/dist/types/src/utils/style.test.d.ts +2 -0
  128. package/dist/types/src/utils/style.test.d.ts.map +1 -0
  129. package/dist/types/src/utils/testing.d.ts +2 -2
  130. package/dist/types/src/utils/testing.d.ts.map +1 -1
  131. package/dist/types/src/utils/undo.d.ts +15 -4
  132. package/dist/types/src/utils/undo.d.ts.map +1 -1
  133. package/dist/types/tsconfig.tsbuildinfo +1 -1
  134. package/package.json +20 -26
  135. package/src/components/About/About.stories.tsx +32 -0
  136. package/src/components/About/About.tsx +38 -0
  137. package/src/components/About/index.ts +5 -0
  138. package/src/components/Breadcrumbs/Breadcrumbs.tsx +17 -3
  139. package/src/components/ControlFrame/ControlFrame.tsx +138 -82
  140. package/src/components/Grid/Grid.stories.tsx +1 -1
  141. package/src/components/Layers/Layers.stories.tsx +131 -0
  142. package/src/components/Layers/Layers.tsx +216 -0
  143. package/src/components/Layers/index.ts +5 -0
  144. package/src/components/Palette/Palette.stories.tsx +135 -0
  145. package/src/components/Palette/Palette.tsx +230 -39
  146. package/src/components/PartEditor/PartEditor.tsx +4 -3
  147. package/src/components/Properties/AlignField.tsx +117 -0
  148. package/src/components/Properties/ClassField.tsx +44 -0
  149. package/src/components/Properties/FontField.tsx +73 -0
  150. package/src/components/Properties/LayerField.tsx +61 -0
  151. package/src/components/Properties/Properties.stories.tsx +113 -0
  152. package/src/components/Properties/Properties.tsx +240 -60
  153. package/src/components/Properties/SceneField.tsx +22 -0
  154. package/src/components/Properties/StyleGrid.stories.tsx +50 -0
  155. package/src/components/Properties/StyleGrid.tsx +54 -26
  156. package/src/components/SceneLayer/SceneLayer.tsx +497 -188
  157. package/src/components/SceneView/Constrained.stories.tsx +1 -1
  158. package/src/components/SceneView/Dock.stories.tsx +95 -0
  159. package/src/components/SceneView/Dock.tsx +146 -0
  160. package/src/components/SceneView/Dynamic.stories.tsx +1 -1
  161. package/src/components/SceneView/SceneView.stories.tsx +170 -18
  162. package/src/components/SceneView/SceneView.tsx +565 -151
  163. package/src/components/SceneView/SceneViewContext.ts +23 -2
  164. package/src/components/SceneView/Scored.stories.tsx +11 -11
  165. package/src/components/SceneView/usePointerMachine.ts +80 -20
  166. package/src/components/SceneView/useSceneCamera.ts +15 -8
  167. package/src/components/SceneView/useSceneClipboard.ts +4 -0
  168. package/src/components/SceneView/useSceneKeys.ts +6 -4
  169. package/src/components/SceneView/useSceneNavigation.ts +44 -33
  170. package/src/components/Toolbar/Toolbar.stories.tsx +14 -15
  171. package/src/components/Toolbar/Toolbar.tsx +15 -4
  172. package/src/components/index.ts +2 -0
  173. package/src/hooks/useWheel.ts +35 -0
  174. package/src/index.ts +4 -3
  175. package/src/model/atoms.ts +27 -3
  176. package/src/model/projection.ts +5 -0
  177. package/src/model/projections/constrained.test.ts +18 -10
  178. package/src/model/projections/constrained.ts +19 -8
  179. package/src/model/projections/dynamic.ts +5 -2
  180. package/src/model/projections/lattice.ts +15 -3
  181. package/src/model/registry.test.ts +14 -9
  182. package/src/model/registry.ts +39 -34
  183. package/src/model/store.ts +12 -2
  184. package/src/model/types.ts +124 -36
  185. package/src/playwright/SceneManager.ts +2 -2
  186. package/src/playwright/create.spec.ts +5 -3
  187. package/src/playwright/demo.spec.ts +2 -2
  188. package/src/playwright/lattice.spec.ts +4 -5
  189. package/src/playwright/scene.spec.ts +27 -25
  190. package/src/utils/builder.test.ts +46 -9
  191. package/src/utils/builder.ts +42 -18
  192. package/src/utils/camera.test.ts +22 -51
  193. package/src/utils/camera.ts +39 -32
  194. package/src/utils/clipboard.test.ts +23 -2
  195. package/src/utils/clipboard.ts +8 -4
  196. package/src/utils/diagram.test.ts +3 -1
  197. package/src/utils/diagram.ts +3 -25
  198. package/src/utils/group.test.ts +69 -0
  199. package/src/utils/group.ts +94 -0
  200. package/src/utils/gutter-route.test.ts +49 -0
  201. package/src/utils/gutter-route.ts +142 -16
  202. package/src/utils/hit.test.ts +4 -38
  203. package/src/utils/hit.ts +4 -26
  204. package/src/utils/index.ts +3 -0
  205. package/src/utils/lattice.ts +7 -1
  206. package/src/utils/layers.test.ts +139 -0
  207. package/src/utils/layers.ts +175 -0
  208. package/src/utils/parts.test.ts +66 -32
  209. package/src/utils/parts.ts +63 -41
  210. package/src/utils/ports.ts +7 -4
  211. package/src/utils/properties.test.ts +23 -6
  212. package/src/utils/properties.ts +66 -16
  213. package/src/utils/route.test.ts +51 -2
  214. package/src/utils/route.ts +133 -23
  215. package/src/utils/scenes.test.ts +38 -0
  216. package/src/utils/scenes.ts +54 -0
  217. package/src/utils/shapes.test.ts +33 -8
  218. package/src/utils/shapes.ts +51 -35
  219. package/src/utils/style.test.ts +54 -0
  220. package/src/utils/style.ts +153 -40
  221. package/src/utils/testing.test.ts +3 -3
  222. package/src/utils/testing.ts +21 -30
  223. package/src/utils/undo.test.ts +25 -2
  224. package/src/utils/undo.ts +50 -13
  225. package/dist/lib/chunk-Grid.mjs +0 -99
  226. package/dist/lib/chunk-Grid.mjs.map +0 -1
  227. package/dist/lib/scene.mjs +0 -6835
  228. package/dist/lib/scene.mjs.map +0 -1
  229. package/dist/lib/types.mjs +0 -16
  230. package/dist/lib/types.mjs.map +0 -1
  231. package/dist/types/src/archive/components/Canvas/Canvas.d.ts +0 -15
  232. package/dist/types/src/archive/components/Canvas/Canvas.d.ts.map +0 -1
  233. package/dist/types/src/archive/components/Canvas/Canvas.stories.d.ts +0 -18
  234. package/dist/types/src/archive/components/Canvas/Canvas.stories.d.ts.map +0 -1
  235. package/dist/types/src/archive/components/Canvas/index.d.ts +0 -2
  236. package/dist/types/src/archive/components/Canvas/index.d.ts.map +0 -1
  237. package/dist/types/src/archive/components/CellGrid/CellGrid.d.ts +0 -21
  238. package/dist/types/src/archive/components/CellGrid/CellGrid.d.ts.map +0 -1
  239. package/dist/types/src/archive/components/CellGrid/CellGrid.stories.d.ts +0 -21
  240. package/dist/types/src/archive/components/CellGrid/CellGrid.stories.d.ts.map +0 -1
  241. package/dist/types/src/archive/components/CellGrid/headers/Ruler.d.ts +0 -15
  242. package/dist/types/src/archive/components/CellGrid/headers/Ruler.d.ts.map +0 -1
  243. package/dist/types/src/archive/components/CellGrid/headers/TrackHeader.d.ts +0 -19
  244. package/dist/types/src/archive/components/CellGrid/headers/TrackHeader.d.ts.map +0 -1
  245. package/dist/types/src/archive/components/CellGrid/headers/index.d.ts +0 -3
  246. package/dist/types/src/archive/components/CellGrid/headers/index.d.ts.map +0 -1
  247. package/dist/types/src/archive/components/CellGrid/index.d.ts +0 -6
  248. package/dist/types/src/archive/components/CellGrid/index.d.ts.map +0 -1
  249. package/dist/types/src/archive/components/CellGrid/input/index.d.ts +0 -3
  250. package/dist/types/src/archive/components/CellGrid/input/index.d.ts.map +0 -1
  251. package/dist/types/src/archive/components/CellGrid/input/pointer.d.ts +0 -33
  252. package/dist/types/src/archive/components/CellGrid/input/pointer.d.ts.map +0 -1
  253. package/dist/types/src/archive/components/CellGrid/input/wheel.d.ts +0 -14
  254. package/dist/types/src/archive/components/CellGrid/input/wheel.d.ts.map +0 -1
  255. package/dist/types/src/archive/components/CellGrid/render/index.d.ts +0 -3
  256. package/dist/types/src/archive/components/CellGrid/render/index.d.ts.map +0 -1
  257. package/dist/types/src/archive/components/CellGrid/render/overlay-layer.d.ts +0 -21
  258. package/dist/types/src/archive/components/CellGrid/render/overlay-layer.d.ts.map +0 -1
  259. package/dist/types/src/archive/components/CellGrid/render/static-layer.d.ts +0 -36
  260. package/dist/types/src/archive/components/CellGrid/render/static-layer.d.ts.map +0 -1
  261. package/dist/types/src/archive/components/CellGrid/state/atoms.d.ts +0 -23
  262. package/dist/types/src/archive/components/CellGrid/state/atoms.d.ts.map +0 -1
  263. package/dist/types/src/archive/components/CellGrid/state/index.d.ts +0 -4
  264. package/dist/types/src/archive/components/CellGrid/state/index.d.ts.map +0 -1
  265. package/dist/types/src/archive/components/CellGrid/state/types.d.ts +0 -39
  266. package/dist/types/src/archive/components/CellGrid/state/types.d.ts.map +0 -1
  267. package/dist/types/src/archive/components/CellGrid/state/viewport.d.ts +0 -52
  268. package/dist/types/src/archive/components/CellGrid/state/viewport.d.ts.map +0 -1
  269. package/dist/types/src/archive/components/CellGrid/state/viewport.test.d.ts +0 -2
  270. package/dist/types/src/archive/components/CellGrid/state/viewport.test.d.ts.map +0 -1
  271. package/dist/types/src/archive/components/FPS.d.ts +0 -9
  272. package/dist/types/src/archive/components/FPS.d.ts.map +0 -1
  273. package/dist/types/src/archive/components/Grid/Grid.d.ts +0 -6
  274. package/dist/types/src/archive/components/Grid/Grid.d.ts.map +0 -1
  275. package/dist/types/src/archive/components/Grid/index.d.ts +0 -2
  276. package/dist/types/src/archive/components/Grid/index.d.ts.map +0 -1
  277. package/dist/types/src/archive/components/index.d.ts +0 -5
  278. package/dist/types/src/archive/components/index.d.ts.map +0 -1
  279. package/dist/types/src/archive/hooks/index.d.ts +0 -5
  280. package/dist/types/src/archive/hooks/index.d.ts.map +0 -1
  281. package/dist/types/src/archive/hooks/projection.d.ts +0 -60
  282. package/dist/types/src/archive/hooks/projection.d.ts.map +0 -1
  283. package/dist/types/src/archive/hooks/useCanvasContext.d.ts +0 -13
  284. package/dist/types/src/archive/hooks/useCanvasContext.d.ts.map +0 -1
  285. package/dist/types/src/archive/hooks/useDrag.d.ts +0 -6
  286. package/dist/types/src/archive/hooks/useDrag.d.ts.map +0 -1
  287. package/dist/types/src/archive/hooks/useWheel.d.ts +0 -8
  288. package/dist/types/src/archive/hooks/useWheel.d.ts.map +0 -1
  289. package/dist/types/src/archive/index.d.ts +0 -5
  290. package/dist/types/src/archive/index.d.ts.map +0 -1
  291. package/dist/types/src/archive/types.d.ts +0 -19
  292. package/dist/types/src/archive/types.d.ts.map +0 -1
  293. package/dist/types/src/archive/util/index.d.ts +0 -4
  294. package/dist/types/src/archive/util/index.d.ts.map +0 -1
  295. package/dist/types/src/archive/util/svg-path.d.ts +0 -4
  296. package/dist/types/src/archive/util/svg-path.d.ts.map +0 -1
  297. package/dist/types/src/archive/util/svg.d.ts +0 -32
  298. package/dist/types/src/archive/util/svg.d.ts.map +0 -1
  299. package/dist/types/src/archive/util/svg.stories.d.ts +0 -15
  300. package/dist/types/src/archive/util/svg.stories.d.ts.map +0 -1
  301. package/dist/types/src/archive/util/util.d.ts +0 -17
  302. package/dist/types/src/archive/util/util.d.ts.map +0 -1
  303. package/dist/types/src/scene.d.ts +0 -5
  304. package/dist/types/src/scene.d.ts.map +0 -1
  305. package/src/archive/components/Canvas/Canvas.stories.tsx +0 -111
  306. package/src/archive/components/Canvas/Canvas.tsx +0 -87
  307. package/src/archive/components/Canvas/index.ts +0 -5
  308. package/src/archive/components/CellGrid/CellGrid.stories.tsx +0 -239
  309. package/src/archive/components/CellGrid/CellGrid.tsx +0 -270
  310. package/src/archive/components/CellGrid/headers/Ruler.tsx +0 -71
  311. package/src/archive/components/CellGrid/headers/TrackHeader.tsx +0 -58
  312. package/src/archive/components/CellGrid/headers/index.ts +0 -6
  313. package/src/archive/components/CellGrid/index.ts +0 -9
  314. package/src/archive/components/CellGrid/input/index.ts +0 -6
  315. package/src/archive/components/CellGrid/input/pointer.ts +0 -236
  316. package/src/archive/components/CellGrid/input/wheel.ts +0 -68
  317. package/src/archive/components/CellGrid/render/index.ts +0 -6
  318. package/src/archive/components/CellGrid/render/overlay-layer.ts +0 -66
  319. package/src/archive/components/CellGrid/render/static-layer.ts +0 -112
  320. package/src/archive/components/CellGrid/state/atoms.ts +0 -43
  321. package/src/archive/components/CellGrid/state/index.ts +0 -7
  322. package/src/archive/components/CellGrid/state/types.ts +0 -40
  323. package/src/archive/components/CellGrid/state/viewport.test.ts +0 -50
  324. package/src/archive/components/CellGrid/state/viewport.ts +0 -94
  325. package/src/archive/components/FPS.tsx +0 -98
  326. package/src/archive/components/Grid/Grid.tsx +0 -17
  327. package/src/archive/components/Grid/index.ts +0 -5
  328. package/src/archive/components/index.ts +0 -8
  329. package/src/archive/hooks/index.ts +0 -8
  330. package/src/archive/hooks/projection.tsx +0 -156
  331. package/src/archive/hooks/useCanvasContext.ts +0 -32
  332. package/src/archive/hooks/useDrag.tsx +0 -96
  333. package/src/archive/hooks/useWheel.tsx +0 -80
  334. package/src/archive/index.ts +0 -8
  335. package/src/archive/types.ts +0 -14
  336. package/src/archive/util/index.ts +0 -7
  337. package/src/archive/util/svg-path.ts +0 -13
  338. package/src/archive/util/svg.stories.tsx +0 -46
  339. package/src/archive/util/svg.tsx +0 -128
  340. package/src/archive/util/util.ts +0 -50
  341. package/src/scene.ts +0 -9
@@ -19,7 +19,7 @@ import type * as Util from '@dxos/react-ui/Util';
19
19
  import { createBasicExtensions, createThemeExtensions } from '@dxos/ui-editor';
20
20
  import { mx } from '@dxos/ui-theme';
21
21
 
22
- import { type PartEditing, type PartKey, isMultiline } from '../../utils/parts.ts';
22
+ import { type PartEditing, type PartKey } from '../../utils/parts.ts';
23
23
 
24
24
  export type TextPartProps = Util.ThemedClassName<
25
25
  PropsWithChildren<{
@@ -36,7 +36,8 @@ export const TextPart = ({ classNames, part, text, editing, children }: TextPart
36
36
  <PartEditor classNames={classNames} part={part} text={text} editing={editing} />
37
37
  ) : (
38
38
  <div className={mx(classNames)} data-part={part}>
39
- {children}
39
+ {/* The text's own box, which a part's layout may pad (its line boxes, not the part's frame). */}
40
+ <span className='inline-block'>{children}</span>
40
41
  </div>
41
42
  );
42
43
 
@@ -46,7 +47,7 @@ const stop = (event: React.SyntheticEvent) => event.stopPropagation();
46
47
 
47
48
  const PartEditor = ({ classNames, part, text, editing }: PartEditorProps) => {
48
49
  const themeMode = Hooks.useThemeMode();
49
- const multiline = isMultiline(part);
50
+ const multiline = editing.multiline;
50
51
  // Commit or cancel once: the editor unmounts on either, and its focus loss must not commit again.
51
52
  const done = useRef(false);
52
53
  const finish = (action: () => void) => {
@@ -0,0 +1,117 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import type * as SchemaAST from 'effect/SchemaAST';
6
+ import React from 'react';
7
+
8
+ import { Form, type FormFieldRenderer, useFormFieldState } from '@dxos/react-ui-form';
9
+ import * as Layout from '@dxos/react-ui/Layout';
10
+ import * as ToggleGroup from '@dxos/react-ui/ToggleGroup';
11
+
12
+ import { type HorizontalAlign, NodeStyle, type VerticalAlign } from '../../model/types.ts';
13
+
14
+ export type IconToggleOption<T extends string> = { value: T; icon: string; label: string };
15
+
16
+ export type IconToggleProps<T extends string> = {
17
+ label: string;
18
+ options: readonly IconToggleOption<T>[];
19
+ value?: T;
20
+ readonly?: boolean;
21
+ onValueChange: (value: T) => void;
22
+ };
23
+
24
+ /** One choice of a few, each an icon: a single-select toggle group whose pressed item is the value. */
25
+ export const IconToggle = <T extends string>({
26
+ label,
27
+ options,
28
+ value,
29
+ readonly,
30
+ onValueChange,
31
+ }: IconToggleProps<T>) => (
32
+ <ToggleGroup.Root
33
+ type='single'
34
+ value={value ?? ''}
35
+ aria-label={label}
36
+ onValueChange={(next) => {
37
+ // Pressing the pressed item releases it in the group; the value stays what it was.
38
+ const option = options.find((candidate) => candidate.value === next);
39
+ if (option) {
40
+ onValueChange(option.value);
41
+ }
42
+ }}
43
+ >
44
+ {options.map((option) => (
45
+ <ToggleGroup.Item
46
+ key={option.value}
47
+ value={option.value}
48
+ variant='ghost'
49
+ icon={option.icon}
50
+ iconOnly
51
+ label={option.label}
52
+ disabled={readonly}
53
+ data-testid={`align-${option.value}`}
54
+ />
55
+ ))}
56
+ </ToggleGroup.Root>
57
+ );
58
+
59
+ const HORIZONTAL: readonly IconToggleOption<HorizontalAlign>[] = [
60
+ { value: 'left', icon: 'ph--align-left-simple--regular', label: 'Align left' },
61
+ { value: 'center', icon: 'ph--align-center-vertical-simple--regular', label: 'Align centre' },
62
+ { value: 'right', icon: 'ph--align-right-simple--regular', label: 'Align right' },
63
+ ];
64
+
65
+ const VERTICAL: readonly IconToggleOption<VerticalAlign>[] = [
66
+ { value: 'top', icon: 'ph--align-top-simple--regular', label: 'Align top' },
67
+ { value: 'middle', icon: 'ph--align-center-horizontal-simple--regular', label: 'Align middle' },
68
+ { value: 'bottom', icon: 'ph--align-bottom-simple--regular', label: 'Align bottom' },
69
+ ];
70
+
71
+ /**
72
+ * The text alignment, rendered at `style.alignHorizontal`: the horizontal and vertical toggles side by side in one
73
+ * row (`style.alignVertical` is hidden). Each writes only its own path, so an edit across a selection leaves each
74
+ * element's other style values.
75
+ */
76
+ export const AlignField: FormFieldRenderer = ({ jsonPath, readonly }) => {
77
+ const stylePath = (jsonPath ?? 'style.alignHorizontal').split('.').slice(0, -1);
78
+ const horizontalField = useFormFieldState('AlignField', [...stylePath, 'alignHorizontal']);
79
+ const verticalField = useFormFieldState('AlignField', [...stylePath, 'alignVertical']);
80
+ const horizontal = HORIZONTAL.find((option) => option.value === horizontalField.getValue())?.value;
81
+ const vertical = VERTICAL.find((option) => option.value === verticalField.getValue())?.value;
82
+ // A pick is a commit: a toggle never blurs, so it commits itself.
83
+ const pick = (field: typeof horizontalField, ast: SchemaAST.AST, value: string) => {
84
+ field.onValueChange(ast, value);
85
+ field.onBlur();
86
+ };
87
+ return (
88
+ <Layout.Container
89
+ layout='row'
90
+ gutter='inherit'
91
+ align='start'
92
+ gap='md'
93
+ columns='repeat(2, minmax(0, 1fr))'
94
+ // The properties panel is narrow, and the two toggles fit side by side there, so the row never stacks.
95
+ fixed
96
+ >
97
+ <Form.Field path={jsonPath} label='Horizontal' readonly={readonly}>
98
+ <IconToggle
99
+ label='Horizontal alignment'
100
+ options={HORIZONTAL}
101
+ value={horizontal}
102
+ readonly={!!readonly}
103
+ onValueChange={(next) => pick(horizontalField, NodeStyle.fields.alignHorizontal.ast, next)}
104
+ />
105
+ </Form.Field>
106
+ <Form.Field path={[...stylePath, 'alignVertical'].join('.')} label='Vertical' readonly={readonly}>
107
+ <IconToggle
108
+ label='Vertical alignment'
109
+ options={VERTICAL}
110
+ value={vertical}
111
+ readonly={!!readonly}
112
+ onValueChange={(next) => pick(verticalField, NodeStyle.fields.alignVertical.ast, next)}
113
+ />
114
+ </Form.Field>
115
+ </Layout.Container>
116
+ );
117
+ };
@@ -0,0 +1,44 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import React, { createContext, useContext, useMemo } from 'react';
6
+
7
+ import { Form, type FormFieldRenderer, SelectControl } from '@dxos/react-ui-form';
8
+
9
+ import { type StyleMap } from '../../model/types.ts';
10
+
11
+ /** The drawing's style classes, for the class field; unset when the host offers none, so the field is hidden. */
12
+ export const StyleClassesContext = createContext<StyleMap | undefined>(undefined);
13
+
14
+ /** The select's value for no class: a select item needs a non-empty value. */
15
+ const NO_CLASS = '-';
16
+
17
+ /** An element's style class: one of the drawing's, or none, leaving only its own style. */
18
+ export const ClassField: FormFieldRenderer = ({ type, label, jsonPath, readonly, getValue, onValueChange, onBlur }) => {
19
+ const styles = useContext(StyleClassesContext);
20
+ const items = useMemo(
21
+ () => [
22
+ { value: NO_CLASS, label: 'None' },
23
+ ...Object.values(styles ?? {})
24
+ .map((styleClass) => ({ value: styleClass.id, label: styleClass.name }))
25
+ .sort((left, right) => left.label.localeCompare(right.label)),
26
+ ],
27
+ [styles],
28
+ );
29
+ const value: unknown = getValue();
30
+ return (
31
+ <Form.Field path={jsonPath} label={label} readonly={readonly}>
32
+ <SelectControl
33
+ items={items}
34
+ value={typeof value === 'string' ? value : NO_CLASS}
35
+ readonly={readonly}
36
+ onValueChange={(next) => {
37
+ // A choice is a commit: the select never blurs, so it commits itself.
38
+ onValueChange(type, next === NO_CLASS ? undefined : next);
39
+ onBlur();
40
+ }}
41
+ />
42
+ </Form.Field>
43
+ );
44
+ };
@@ -0,0 +1,73 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import React from 'react';
6
+
7
+ import { Form, type FormFieldRenderer, SelectControl, useFormFieldState } from '@dxos/react-ui-form';
8
+ import * as Input from '@dxos/react-ui/Input';
9
+ import * as Layout from '@dxos/react-ui/Layout';
10
+
11
+ import { FONT_FAMILIES, type FontFamily, NodeStyle } from '../../model/types.ts';
12
+
13
+ const FAMILY_LABELS: Record<FontFamily, string> = { default: 'Default', monospace: 'Monospace' };
14
+
15
+ const FAMILIES = FONT_FAMILIES.map((value) => ({ value, label: FAMILY_LABELS[value] }));
16
+
17
+ /** The editor's readable range; stored sizes outside it are kept, not checked. */
18
+ const FONT_SIZE = { min: 8, max: 80, step: 1 };
19
+
20
+ /**
21
+ * The text face and size, rendered at `style.fontFamily`: the two side by side in one row (`style.fontSize` is
22
+ * hidden). Each writes only its own path, so an edit across a selection leaves each element's other style values.
23
+ */
24
+ export const FontField: FormFieldRenderer = ({ jsonPath, readonly }) => {
25
+ const stylePath = (jsonPath ?? 'style.fontFamily').split('.').slice(0, -1);
26
+ const familyField = useFormFieldState('FontField', [...stylePath, 'fontFamily']);
27
+ const sizeField = useFormFieldState('FontField', [...stylePath, 'fontSize']);
28
+ const family: unknown = familyField.getValue();
29
+ const size: unknown = sizeField.getValue();
30
+ return (
31
+ <Layout.Container
32
+ layout='row'
33
+ gutter='inherit'
34
+ align='start'
35
+ gap='md'
36
+ columns='repeat(2, minmax(0, 1fr))'
37
+ // The properties panel is narrow, and the two controls fit side by side there, so the row never stacks.
38
+ fixed
39
+ >
40
+ <Form.Field path={jsonPath} label='Font' readonly={readonly}>
41
+ <SelectControl
42
+ items={FAMILIES}
43
+ value={typeof family === 'string' ? family : 'default'}
44
+ readonly={readonly}
45
+ onValueChange={(next) => {
46
+ const fontFamily = FONT_FAMILIES.find((family) => family === next);
47
+ // The body face is the default, so it is stored as unset; a pick never blurs, so it commits itself.
48
+ familyField.onValueChange(
49
+ NodeStyle.fields.fontFamily.ast,
50
+ fontFamily === 'default' ? undefined : fontFamily,
51
+ );
52
+ familyField.onBlur();
53
+ }}
54
+ />
55
+ </Form.Field>
56
+ <Form.Field path={[...stylePath, 'fontSize'].join('.')} label='Font size' readonly={readonly}>
57
+ <Input.Number
58
+ disabled={!!readonly}
59
+ {...FONT_SIZE}
60
+ formatOptions={{ maximumFractionDigits: 0 }}
61
+ value={typeof size === 'number' ? String(size) : ''}
62
+ onValueChange={(_, fontSize) => {
63
+ if (!Number.isNaN(fontSize)) {
64
+ // A stepper press never blurs, so each value commits itself.
65
+ sizeField.onValueChange(NodeStyle.fields.fontSize.ast, fontSize);
66
+ sizeField.onBlur();
67
+ }
68
+ }}
69
+ />
70
+ </Form.Field>
71
+ </Layout.Container>
72
+ );
73
+ };
@@ -0,0 +1,61 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import * as Schema from 'effect/Schema';
6
+ import React, { createContext, useContext, useMemo } from 'react';
7
+
8
+ import { Form, type FormFieldRenderer, SelectControl, useFormFieldState } from '@dxos/react-ui-form';
9
+ import * as Layout from '@dxos/react-ui/Layout';
10
+
11
+ import { type Layer } from '../../model/types.ts';
12
+ import { ClassField, StyleClassesContext } from './ClassField.tsx';
13
+
14
+ /** The scene's layers, bottom first, for the layer field. */
15
+ export const LayersContext = createContext<readonly Layer[]>([]);
16
+
17
+ /** The layer an element is on: one of the scene's, listed top first as they stack. */
18
+ export const LayerField: FormFieldRenderer = ({ type, label, jsonPath, readonly, getValue, onValueChange, onBlur }) => {
19
+ const layers = useContext(LayersContext);
20
+ const items = useMemo(() => [...layers].reverse().map((layer) => ({ value: layer.id, label: layer.name })), [layers]);
21
+ const value: unknown = getValue();
22
+ return (
23
+ <Form.Field path={jsonPath} label={label} readonly={readonly}>
24
+ <SelectControl
25
+ items={items}
26
+ value={typeof value === 'string' ? value : undefined}
27
+ readonly={readonly}
28
+ onValueChange={(next) => {
29
+ // A choice is a commit: the select never blurs, so it commits itself.
30
+ onValueChange(type, next);
31
+ onBlur();
32
+ }}
33
+ />
34
+ </Form.Field>
35
+ );
36
+ };
37
+
38
+ /**
39
+ * The layer and the style class side by side in one row, rendered at `layer` (`class` is hidden); the class select
40
+ * only while the host offers classes.
41
+ */
42
+ export const LayerClassField: FormFieldRenderer = (props) => {
43
+ const styles = useContext(StyleClassesContext);
44
+ const classField = useFormFieldState('LayerClassField', ['class']);
45
+ return (
46
+ <Layout.Container
47
+ layout='row'
48
+ gutter='inherit'
49
+ align='start'
50
+ gap='md'
51
+ columns='repeat(2, minmax(0, 1fr))'
52
+ // The properties panel is narrow, and the two selects fit side by side there, so the row never stacks.
53
+ fixed
54
+ >
55
+ <LayerField {...props} />
56
+ {styles && (
57
+ <ClassField {...classField} type={Schema.String.ast} label='Class' jsonPath='class' readonly={props.readonly} />
58
+ )}
59
+ </Layout.Container>
60
+ );
61
+ };
@@ -0,0 +1,113 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import { useAtomValue } from '@effect/atom-react/Hooks';
6
+ import { type Meta, type StoryObj } from '@storybook/react-vite';
7
+ import React, { useLayoutEffect, useMemo } from 'react';
8
+ import { expect, within } from 'storybook/test';
9
+
10
+ import { Panel } from '@dxos/react-ui';
11
+ import { translations as formTranslations } from '@dxos/react-ui-form/translations';
12
+ import { JsonHighlighter } from '@dxos/react-ui-syntax-highlighter';
13
+ import { withLayout, withRegistry, withTheme } from '@dxos/react-ui/testing';
14
+ import { translations as uiTranslations } from '@dxos/react-ui/translations';
15
+
16
+ import { useRegistry, useSceneProjection } from '../../hooks/index.ts';
17
+ import { createSceneViewAtoms } from '../../model/atoms.ts';
18
+ import { createMemoryStore } from '../../model/store.ts';
19
+ import { type ElementId, type Layer, type Scene, getElement } from '../../model/types.ts';
20
+ import { SceneBuilder } from '../../utils/builder.ts';
21
+ import { between } from '../../utils/order.ts';
22
+ import { Properties } from './Properties.tsx';
23
+
24
+ const box = (x: number, y = 0) => ({ x, y, width: 256, height: 128 });
25
+
26
+ /** Three layers, bottom first; `a` and the link are on the diagram layer, `n` on notes. */
27
+ const LAYERS: Layer[] = (() => {
28
+ const background: Layer = { id: 'background', name: 'Background', z: between() };
29
+ const notes: Layer = { id: 'notes', name: 'Notes', z: between(background.z) };
30
+ const diagram: Layer = { id: 'diagram', name: 'Diagram', z: between(notes.z) };
31
+ return [background, notes, diagram];
32
+ })();
33
+
34
+ const SCENE: Scene = (() => {
35
+ const {
36
+ scenes: [scene],
37
+ } = SceneBuilder.scene('root', [
38
+ SceneBuilder.rect('a', box(0)).properties({ label: 'Rect', layer: 'diagram', style: { hue: 'teal' } }),
39
+ SceneBuilder.rect('b', box(400)).properties({ label: 'Other', layer: 'background' }),
40
+ SceneBuilder.note('n', box(0, 200)).properties({ layer: 'notes' }),
41
+ SceneBuilder.link('curve', 'a', 'b')
42
+ .id('ab')
43
+ .properties({ layer: 'diagram', style: { lineStyle: 'dashed' } }),
44
+ ]).build();
45
+ return { ...scene, layers: Object.fromEntries(LAYERS.map((layer) => [layer.id, layer])) };
46
+ })();
47
+
48
+ type StoryArgs = {
49
+ /** The elements the panel edits. */
50
+ select: ElementId[];
51
+ };
52
+
53
+ /** The panel alone, over an in-memory scene and a fixed selection: no canvas, so every field can be read in full. */
54
+ const DefaultStory = ({ select }: StoryArgs) => {
55
+ const registry = useRegistry();
56
+ const store = useMemo(() => createMemoryStore([SCENE]), []);
57
+ const atoms = useMemo(() => createSceneViewAtoms(SCENE.id), []);
58
+ const projection = useSceneProjection({ store, atoms });
59
+ useLayoutEffect(() => {
60
+ registry.set(atoms.selection, new Set(select));
61
+ }, [registry, atoms, select]);
62
+ // The selection as the model holds it, beside the panel editing it, so every edit shows what it wrote.
63
+ const scene = useAtomValue(projection.scene);
64
+ const styles = useAtomValue(store.styles);
65
+ const selected = select.flatMap((id) => getElement(scene, id) ?? []);
66
+ return (
67
+ <Panel.Root>
68
+ <Panel.Body classNames='grid grid-cols-[20rem_1fr] gap-3'>
69
+ <Properties projection={projection} atoms={atoms} styles={store.styles} />
70
+ <JsonHighlighter data={{ selected, styles }} classNames='text-sm' />
71
+ </Panel.Body>
72
+ </Panel.Root>
73
+ );
74
+ };
75
+
76
+ const meta: Meta<StoryArgs> = {
77
+ title: 'ui/react-ui-canvas/Properties',
78
+ render: DefaultStory,
79
+ decorators: [withRegistry, withTheme(), withLayout({ layout: 'column', classNames: 'w-[50rem]' })],
80
+ args: { select: ['a'] },
81
+ parameters: { translations: [...uiTranslations, ...formTranslations] },
82
+ };
83
+
84
+ export default meta;
85
+
86
+ type Story = StoryObj<StoryArgs>;
87
+
88
+ /** A shape: its layer, class and the whole style (the full colour grid, line style and text alignment). */
89
+ export const Default: Story = {};
90
+
91
+ /** A link: the common style base alone (the outline row and line style), its layer and class. */
92
+ export const Link: Story = { args: { select: ['ab'] } };
93
+
94
+ /** A shape and a link: only what both declare, the style narrowed to its common base. */
95
+ export const Mixed: Story = { args: { select: ['a', 'ab'] } };
96
+
97
+ /** A note on its own layer. */
98
+ export const Note: Story = { args: { select: ['n'] } };
99
+
100
+ /** The layer selector lists the scene's layers top first and shows the element's own. */
101
+ export const Test: Story = {
102
+ args: { select: ['n'] },
103
+ play: async ({ canvasElement }) => {
104
+ const canvas = within(canvasElement);
105
+ // 1. The panel renders the selection's fields.
106
+ await expect(await canvas.findByTestId('properties')).toBeInTheDocument();
107
+ // 2. The layer field shows the note's layer.
108
+ const layer = await canvas.findByRole('combobox', { name: 'Layer' });
109
+ await expect(layer).toHaveTextContent('Notes');
110
+ // 3. The font row shows the face, unset reading as the body face.
111
+ await expect(await canvas.findByRole('combobox', { name: 'Font' })).toHaveTextContent('Default');
112
+ },
113
+ };