@yuzhouu/canvas-kit 0.1.2

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 (263) hide show
  1. package/README.md +145 -0
  2. package/_vendor/canvas-core/bindings/BindingUtil.ts +92 -0
  3. package/_vendor/canvas-core/bindings/BindingUtilRegistry.ts +22 -0
  4. package/_vendor/canvas-core/bindings/arrow/arrowBindingHelpers.ts +52 -0
  5. package/_vendor/canvas-core/contextMenu.ts +85 -0
  6. package/_vendor/canvas-core/editor/Editor.ts +2614 -0
  7. package/_vendor/canvas-core/editor/HistoryManager.ts +83 -0
  8. package/_vendor/canvas-core/editor/StateNode.ts +162 -0
  9. package/_vendor/canvas-core/editor/camera.ts +6 -0
  10. package/_vendor/canvas-core/editor/clipboard.ts +478 -0
  11. package/_vendor/canvas-core/editor/commands.ts +833 -0
  12. package/_vendor/canvas-core/editor/edgeScroll.ts +37 -0
  13. package/_vendor/canvas-core/editor/editorComputedHelpers.ts +257 -0
  14. package/_vendor/canvas-core/editor/editorSelectionTransforms.ts +116 -0
  15. package/_vendor/canvas-core/editor/events.ts +93 -0
  16. package/_vendor/canvas-core/editor/index.ts +12 -0
  17. package/_vendor/canvas-core/editor/interactions.ts +101 -0
  18. package/_vendor/canvas-core/editor/internal.ts +22 -0
  19. package/_vendor/canvas-core/editor/managers/BindingManager.ts +262 -0
  20. package/_vendor/canvas-core/editor/managers/CameraManager.ts +147 -0
  21. package/_vendor/canvas-core/editor/managers/ClipboardManager.ts +32 -0
  22. package/_vendor/canvas-core/editor/managers/DocumentSnapshotManager.ts +256 -0
  23. package/_vendor/canvas-core/editor/managers/InputsManager.ts +319 -0
  24. package/_vendor/canvas-core/editor/managers/InteractionTransientManager.ts +218 -0
  25. package/_vendor/canvas-core/editor/managers/QueryManager.ts +837 -0
  26. package/_vendor/canvas-core/editor/managers/RenderWindowManager.ts +347 -0
  27. package/_vendor/canvas-core/editor/managers/SelectionLayoutManager.ts +196 -0
  28. package/_vendor/canvas-core/editor/managers/SelectionManager.ts +98 -0
  29. package/_vendor/canvas-core/editor/managers/SelectionOverlayManager.ts +308 -0
  30. package/_vendor/canvas-core/editor/managers/ShapeEditingManager.ts +226 -0
  31. package/_vendor/canvas-core/editor/managers/ShapeGeometryManager.ts +162 -0
  32. package/_vendor/canvas-core/editor/managers/ShapeHierarchyManager.ts +797 -0
  33. package/_vendor/canvas-core/editor/managers/SideEffectManager.ts +83 -0
  34. package/_vendor/canvas-core/editor/managers/SnapManager.ts +433 -0
  35. package/_vendor/canvas-core/editor/managers/SnapSnapshotBuilder.ts +430 -0
  36. package/_vendor/canvas-core/editor/managers/SpatialIndexManager.ts +354 -0
  37. package/_vendor/canvas-core/editor/managers/ToolManager.ts +81 -0
  38. package/_vendor/canvas-core/editor/managers/queryState.ts +583 -0
  39. package/_vendor/canvas-core/editor/managers/selectionGeometry.ts +287 -0
  40. package/_vendor/canvas-core/editor/managers/shapeOrder.ts +350 -0
  41. package/_vendor/canvas-core/editor/managers/shapeReparenting.ts +160 -0
  42. package/_vendor/canvas-core/editor/managers/snapCalculations.ts +412 -0
  43. package/_vendor/canvas-core/editor/managers/snapGeometry.ts +202 -0
  44. package/_vendor/canvas-core/editor/managers/snapIndicators.ts +403 -0
  45. package/_vendor/canvas-core/editor/managers/snapTypes.ts +122 -0
  46. package/_vendor/canvas-core/editor/queries/shapeTransforms.ts +171 -0
  47. package/_vendor/canvas-core/editor/renderWindow.ts +439 -0
  48. package/_vendor/canvas-core/editor/resizeGeometry.ts +654 -0
  49. package/_vendor/canvas-core/editor/selection.ts +489 -0
  50. package/_vendor/canvas-core/editor/selectionHandleInteractions.ts +43 -0
  51. package/_vendor/canvas-core/editor/selectionOverlay.ts +567 -0
  52. package/_vendor/canvas-core/exports/integrity.ts +174 -0
  53. package/_vendor/canvas-core/exports/svg.ts +494 -0
  54. package/_vendor/canvas-core/geometry/Box.ts +80 -0
  55. package/_vendor/canvas-core/geometry/Ellipse2d.ts +67 -0
  56. package/_vendor/canvas-core/geometry/Geometry2d.ts +52 -0
  57. package/_vendor/canvas-core/geometry/Mat.ts +92 -0
  58. package/_vendor/canvas-core/geometry/Polyline2d.ts +63 -0
  59. package/_vendor/canvas-core/geometry/Rectangle2d.ts +64 -0
  60. package/_vendor/canvas-core/geometry/Vec.ts +123 -0
  61. package/_vendor/canvas-core/geometry/index.ts +13 -0
  62. package/_vendor/canvas-core/geometry/segments.ts +65 -0
  63. package/_vendor/canvas-core/index.ts +391 -0
  64. package/_vendor/canvas-core/plugins/index.ts +5 -0
  65. package/_vendor/canvas-core/plugins/types.ts +53 -0
  66. package/_vendor/canvas-core/reactivity.ts +16 -0
  67. package/_vendor/canvas-core/schema/records.ts +221 -0
  68. package/_vendor/canvas-core/schema/snapshots.ts +527 -0
  69. package/_vendor/canvas-core/shapes/ShapeUtil.ts +516 -0
  70. package/_vendor/canvas-core/shapes/ShapeUtilRegistry.ts +26 -0
  71. package/_vendor/canvas-core/shapes/capabilities.ts +67 -0
  72. package/_vendor/canvas-core/shapes/types.ts +20 -0
  73. package/_vendor/canvas-core/toolbar.ts +148 -0
  74. package/_vendor/canvas-core/tools/hand/HandTool.ts +75 -0
  75. package/_vendor/canvas-core/tools/index.ts +2 -0
  76. package/_vendor/canvas-core/tools/select/SelectTool.ts +33 -0
  77. package/_vendor/canvas-core/tools/select/selectContainment.ts +104 -0
  78. package/_vendor/canvas-core/tools/select/selectToolHelpers.ts +801 -0
  79. package/_vendor/canvas-core/tools/select/states/BrushingState.ts +145 -0
  80. package/_vendor/canvas-core/tools/select/states/DraggingSelectionHandleInteractionState.ts +116 -0
  81. package/_vendor/canvas-core/tools/select/states/IdleState.ts +159 -0
  82. package/_vendor/canvas-core/tools/select/states/PointingCanvasState.ts +41 -0
  83. package/_vendor/canvas-core/tools/select/states/PointingResizeHandleState.ts +78 -0
  84. package/_vendor/canvas-core/tools/select/states/PointingRotateHandleState.ts +64 -0
  85. package/_vendor/canvas-core/tools/select/states/PointingSelectionHandleInteractionState.ts +34 -0
  86. package/_vendor/canvas-core/tools/select/states/PointingShapeState.ts +140 -0
  87. package/_vendor/canvas-core/tools/select/states/ResizingState.ts +171 -0
  88. package/_vendor/canvas-core/tools/select/states/RotatingState.ts +124 -0
  89. package/_vendor/canvas-core/tools/select/states/SelectStateNode.ts +128 -0
  90. package/_vendor/canvas-core/tools/select/states/TranslatingState.ts +139 -0
  91. package/_vendor/canvas-core/tools/select/states/index.ts +11 -0
  92. package/_vendor/canvas-plugin-api/feature.ts +204 -0
  93. package/_vendor/canvas-plugin-api/index.ts +142 -0
  94. package/_vendor/canvas-product-react/api/apiRegistry.ts +35 -0
  95. package/_vendor/canvas-product-react/api/canvasProductApi.ts +422 -0
  96. package/_vendor/canvas-product-react/api/index.ts +31 -0
  97. package/_vendor/canvas-product-react/capabilities/clipboard/clipboardService.ts +36 -0
  98. package/_vendor/canvas-product-react/capabilities/clipboard/systemClipboard.ts +245 -0
  99. package/_vendor/canvas-product-react/capabilities/context-menu/systemContextMenu.ts +396 -0
  100. package/_vendor/canvas-product-react/capabilities/context-menu/useContextMenuSurface.ts +59 -0
  101. package/_vendor/canvas-product-react/capabilities/export/useSvgExport.ts +156 -0
  102. package/_vendor/canvas-product-react/capabilities/history/useHistoryControls.ts +23 -0
  103. package/_vendor/canvas-product-react/capabilities/pages/usePageControls.ts +41 -0
  104. package/_vendor/canvas-product-react/capabilities/toolbar/systemToolbar.ts +490 -0
  105. package/_vendor/canvas-product-react/capabilities/toolbar/useToolbarSurfaces.ts +92 -0
  106. package/_vendor/canvas-product-react/capabilities/zoom/useZoomControls.ts +27 -0
  107. package/_vendor/canvas-product-react/index.ts +84 -0
  108. package/_vendor/canvas-product-react/persistence/index.ts +18 -0
  109. package/_vendor/canvas-product-react/persistence/localSnapshotPersistence.ts +294 -0
  110. package/_vendor/canvas-product-react/runtime/CanvasProductRoot.tsx +250 -0
  111. package/_vendor/canvas-product-react/runtime/productDefinition.ts +72 -0
  112. package/_vendor/canvas-product-react/runtime/productServices.ts +96 -0
  113. package/_vendor/canvas-react/components/CanvasView.tsx +796 -0
  114. package/_vendor/canvas-react/components/OverlayLayer.tsx +879 -0
  115. package/_vendor/canvas-react/components/PageSpace.tsx +34 -0
  116. package/_vendor/canvas-react/components/ShapeLayer.tsx +428 -0
  117. package/_vendor/canvas-react/components/index.ts +4 -0
  118. package/_vendor/canvas-react/hooks/useEditor.ts +11 -0
  119. package/_vendor/canvas-react/hooks/useValue.ts +14 -0
  120. package/_vendor/canvas-react/host/CanvasRendererRegistryContext.tsx +14 -0
  121. package/_vendor/canvas-react/host/contextMenuRegistry.ts +61 -0
  122. package/_vendor/canvas-react/host/domEventGuards.ts +61 -0
  123. package/_vendor/canvas-react/host/index.ts +38 -0
  124. package/_vendor/canvas-react/host/pluginHost.ts +635 -0
  125. package/_vendor/canvas-react/host/rendererRegistry.tsx +58 -0
  126. package/_vendor/canvas-react/host/toolbarRegistry.ts +100 -0
  127. package/_vendor/canvas-react/index.ts +3 -0
  128. package/_vendor/plugin-arrow/ArrowBindingUtil.ts +239 -0
  129. package/_vendor/plugin-arrow/ArrowShapeUtil.ts +311 -0
  130. package/_vendor/plugin-arrow/ArrowTool.ts +208 -0
  131. package/_vendor/plugin-arrow/arrowGeometry.ts +441 -0
  132. package/_vendor/plugin-arrow/arrowTerminalBindingResolver.ts +253 -0
  133. package/_vendor/plugin-arrow/arrowTerminalCommands.ts +230 -0
  134. package/_vendor/plugin-arrow/canvas.tsx +92 -0
  135. package/_vendor/plugin-arrow/core.ts +409 -0
  136. package/_vendor/plugin-arrow/feature.ts +20 -0
  137. package/_vendor/plugin-arrow/index.ts +28 -0
  138. package/_vendor/plugin-arrow/selectionHandleInteractions.ts +154 -0
  139. package/_vendor/plugin-arrow/types.ts +63 -0
  140. package/_vendor/plugin-draw/DrawShapeRenderer.tsx +60 -0
  141. package/_vendor/plugin-draw/DrawShapeUtil.ts +372 -0
  142. package/_vendor/plugin-draw/DrawTool.ts +444 -0
  143. package/_vendor/plugin-draw/canvas.tsx +24 -0
  144. package/_vendor/plugin-draw/core.ts +515 -0
  145. package/_vendor/plugin-draw/drawGeometry.ts +491 -0
  146. package/_vendor/plugin-draw/drawHitTest.ts +271 -0
  147. package/_vendor/plugin-draw/drawInterpolation.ts +250 -0
  148. package/_vendor/plugin-draw/drawPath.ts +550 -0
  149. package/_vendor/plugin-draw/drawSegments.ts +106 -0
  150. package/_vendor/plugin-draw/drawSelectionTransform.ts +67 -0
  151. package/_vendor/plugin-draw/drawShapeGeometry.ts +307 -0
  152. package/_vendor/plugin-draw/drawStrokeGeometry.ts +454 -0
  153. package/_vendor/plugin-draw/feature.ts +20 -0
  154. package/_vendor/plugin-draw/index.ts +31 -0
  155. package/_vendor/plugin-draw/types.ts +36 -0
  156. package/_vendor/plugin-eraser/EraserTool.ts +297 -0
  157. package/_vendor/plugin-eraser/canvas.tsx +616 -0
  158. package/_vendor/plugin-eraser/core.ts +49 -0
  159. package/_vendor/plugin-eraser/feature.ts +20 -0
  160. package/_vendor/plugin-eraser/index.ts +4 -0
  161. package/_vendor/plugin-frame/FrameShapeUtil.ts +173 -0
  162. package/_vendor/plugin-frame/FrameTool.ts +225 -0
  163. package/_vendor/plugin-frame/canvas.tsx +75 -0
  164. package/_vendor/plugin-frame/core.ts +534 -0
  165. package/_vendor/plugin-frame/feature.ts +20 -0
  166. package/_vendor/plugin-frame/index.ts +6 -0
  167. package/_vendor/plugin-geo/GeoShapeUtil.ts +463 -0
  168. package/_vendor/plugin-geo/GeoTools.ts +247 -0
  169. package/_vendor/plugin-geo/canvas.tsx +90 -0
  170. package/_vendor/plugin-geo/core.ts +495 -0
  171. package/_vendor/plugin-geo/feature.ts +20 -0
  172. package/_vendor/plugin-geo/index.ts +7 -0
  173. package/_vendor/plugin-geo/types.ts +27 -0
  174. package/_vendor/plugin-group/GroupShapeUtil.ts +59 -0
  175. package/_vendor/plugin-group/canvas.tsx +59 -0
  176. package/_vendor/plugin-group/core.ts +40 -0
  177. package/_vendor/plugin-group/feature.ts +20 -0
  178. package/_vendor/plugin-group/index.ts +5 -0
  179. package/_vendor/plugin-image/ImageShapeUtil.ts +95 -0
  180. package/_vendor/plugin-image/canvas.tsx +212 -0
  181. package/_vendor/plugin-image/core.ts +542 -0
  182. package/_vendor/plugin-image/crop.ts +770 -0
  183. package/_vendor/plugin-image/cropInteractions.ts +350 -0
  184. package/_vendor/plugin-image/cropSession.ts +38 -0
  185. package/_vendor/plugin-image/feature.ts +20 -0
  186. package/_vendor/plugin-image/index.ts +29 -0
  187. package/_vendor/plugin-image/types.ts +55 -0
  188. package/_vendor/plugin-image-product-react/ImageImportController.tsx +143 -0
  189. package/_vendor/plugin-image-product-react/imageAssetService.ts +52 -0
  190. package/_vendor/plugin-image-product-react/imageBlobStore.ts +315 -0
  191. package/_vendor/plugin-image-product-react/imageHostActions.ts +438 -0
  192. package/_vendor/plugin-image-product-react/imageProductApi.ts +88 -0
  193. package/_vendor/plugin-image-product-react/imageToolbarHostBridge.ts +48 -0
  194. package/_vendor/plugin-image-product-react/imageToolbarRuntime.ts +21 -0
  195. package/_vendor/plugin-image-product-react/index.ts +60 -0
  196. package/_vendor/plugin-image-product-react/snapshotGuards.ts +33 -0
  197. package/_vendor/plugin-image-product-react/svgImageAssetInliner.ts +54 -0
  198. package/_vendor/plugin-image-product-react/useImageImport.ts +64 -0
  199. package/_vendor/plugin-laser/LaserTool.ts +154 -0
  200. package/_vendor/plugin-laser/canvas.tsx +645 -0
  201. package/_vendor/plugin-laser/core.ts +49 -0
  202. package/_vendor/plugin-laser/feature.ts +20 -0
  203. package/_vendor/plugin-laser/index.ts +4 -0
  204. package/_vendor/plugin-text/ArrowLabelBindingUtil.ts +336 -0
  205. package/_vendor/plugin-text/GeoLabelBindingUtil.ts +391 -0
  206. package/_vendor/plugin-text/TextShapeSvgExporter.ts +101 -0
  207. package/_vendor/plugin-text/TextShapeUtil.ts +284 -0
  208. package/_vendor/plugin-text/TextTool.ts +228 -0
  209. package/_vendor/plugin-text/canvas.tsx +115 -0
  210. package/_vendor/plugin-text/core.ts +422 -0
  211. package/_vendor/plugin-text/feature.ts +20 -0
  212. package/_vendor/plugin-text/host/RichTextEditingBox.tsx +144 -0
  213. package/_vendor/plugin-text/host/RichTextToolbar.tsx +266 -0
  214. package/_vendor/plugin-text/host/TiptapTextEditor.tsx +191 -0
  215. package/_vendor/plugin-text/host/richTextRendering.tsx +49 -0
  216. package/_vendor/plugin-text/host/textDomSelection.ts +93 -0
  217. package/_vendor/plugin-text/host/textEditingBoxStyle.ts +36 -0
  218. package/_vendor/plugin-text/host/textEntryFocus.ts +307 -0
  219. package/_vendor/plugin-text/host/textFontLoader.ts +149 -0
  220. package/_vendor/plugin-text/host/textMeasurement.ts +258 -0
  221. package/_vendor/plugin-text/host/textMeasurementSync.ts +77 -0
  222. package/_vendor/plugin-text/host/textRangeSelection.ts +134 -0
  223. package/_vendor/plugin-text/host/textShapeRendererRuntime.tsx +89 -0
  224. package/_vendor/plugin-text/host/textShapeSync.ts +173 -0
  225. package/_vendor/plugin-text/host/tiptapTextEditorController.ts +148 -0
  226. package/_vendor/plugin-text/index.ts +18 -0
  227. package/_vendor/plugin-text/richTextUtils.ts +259 -0
  228. package/_vendor/plugin-text/textInteractions.ts +366 -0
  229. package/_vendor/plugin-text/textLabelHelpers.ts +235 -0
  230. package/_vendor/plugin-text/textShapeHelpers.ts +123 -0
  231. package/_vendor/plugin-text/textStyle.ts +96 -0
  232. package/_vendor/plugin-text/textTypes.ts +48 -0
  233. package/_vendor/signals/atom.ts +74 -0
  234. package/_vendor/signals/computed.ts +132 -0
  235. package/_vendor/signals/core.ts +130 -0
  236. package/_vendor/signals/effect.ts +117 -0
  237. package/_vendor/signals/index.ts +9 -0
  238. package/_vendor/signals/types.ts +17 -0
  239. package/_vendor/store/Store.ts +171 -0
  240. package/_vendor/store/diff.ts +229 -0
  241. package/_vendor/store/history.ts +177 -0
  242. package/_vendor/store/index.ts +32 -0
  243. package/_vendor/store/types.ts +37 -0
  244. package/api/canvasKitApi.d.ts +29 -0
  245. package/index.css +3 -0
  246. package/index.d.ts +16 -0
  247. package/index.mjs +1 -0
  248. package/internal-modules.d.ts +187 -0
  249. package/package.json +48 -0
  250. package/product/Shell.d.ts +19 -0
  251. package/product/chromeContext.d.ts +70 -0
  252. package/product/createCanvasKitEditor.d.ts +6 -0
  253. package/product/index.d.ts +7 -0
  254. package/product/persistence.d.ts +3 -0
  255. package/product/product.d.ts +22 -0
  256. package/product/useCanvasClipboardShortcuts.d.ts +6 -0
  257. package/product-iyBbEPIs.mjs +4 -0
  258. package/product.mjs +1 -0
  259. package/standard/extensions.d.ts +1 -0
  260. package/standard/index.d.ts +3 -0
  261. package/standard/standardApi.d.ts +91 -0
  262. package/standard-BmrxwqpJ.mjs +44 -0
  263. package/standard.mjs +1 -0
@@ -0,0 +1,287 @@
1
+ import { computed, type Signal } from "@land/signals";
2
+ import { Box } from "../../geometry/Box";
3
+ import { Vec } from "../../geometry/Vec";
4
+ import type { ShapeId, ShapeRecord } from "../../schema/records";
5
+ import type { Editor } from "../Editor";
6
+ import type { ShapeGeometry } from "../queries/shapeTransforms";
7
+ import { areBoxesEqual, areVecsEqual } from "../editorComputedHelpers";
8
+
9
+ export interface SelectionGeometrySnapshot {
10
+ outlineVertices: Vec[];
11
+ pageBounds: Box | null;
12
+ frameBounds: Box | null;
13
+ rotation: number;
14
+ rotationCenter: Vec | null;
15
+ toolbarAnchorPagePoint: Vec | null;
16
+ hasRotation: boolean;
17
+ hasCompatibleRotation: boolean;
18
+ }
19
+
20
+ export interface SelectionGeometrySignals {
21
+ geometry: Signal<SelectionGeometrySnapshot>;
22
+ pageOutlineVertices: Signal<Vec[]>;
23
+ pageBounds: Signal<Box | null>;
24
+ frameBounds: Signal<Box | null>;
25
+ hasRotation: Signal<boolean>;
26
+ rotation: Signal<number>;
27
+ rotationCenter: Signal<Vec | null>;
28
+ hasCompatibleRotation: Signal<boolean>;
29
+ toolbarAnchorPagePoint: Signal<Vec | null>;
30
+ }
31
+
32
+ const SELECTION_ROTATION_EPSILON = 1e-3;
33
+
34
+ function areVecArraysEqual(a: Vec[], b: Vec[]): boolean {
35
+ if (a === b) return true;
36
+ if (a.length !== b.length) return false;
37
+ for (let i = 0; i < a.length; i += 1) {
38
+ const left = a[i];
39
+ const right = b[i];
40
+ if (!left || !right || !areVecsEqual(left, right)) {
41
+ return false;
42
+ }
43
+ }
44
+ return true;
45
+ }
46
+
47
+ function areSelectionGeometrySnapshotsEqual(
48
+ a: SelectionGeometrySnapshot,
49
+ b: SelectionGeometrySnapshot,
50
+ ): boolean {
51
+ return (
52
+ areVecArraysEqual(a.outlineVertices, b.outlineVertices) &&
53
+ areBoxesEqual(a.pageBounds, b.pageBounds) &&
54
+ areBoxesEqual(a.frameBounds, b.frameBounds) &&
55
+ a.rotation === b.rotation &&
56
+ areVecsEqual(a.rotationCenter, b.rotationCenter) &&
57
+ areVecsEqual(a.toolbarAnchorPagePoint, b.toolbarAnchorPagePoint) &&
58
+ a.hasRotation === b.hasRotation &&
59
+ a.hasCompatibleRotation === b.hasCompatibleRotation
60
+ );
61
+ }
62
+
63
+ function normalizeSelectionAngle(angle: number): number {
64
+ let next = angle;
65
+ while (next <= -Math.PI) next += Math.PI * 2;
66
+ while (next > Math.PI) next -= Math.PI * 2;
67
+ return next;
68
+ }
69
+
70
+ function normalizeSelectionFrameAngle(angle: number): number {
71
+ let next = normalizeSelectionAngle(angle);
72
+ while (next <= -Math.PI / 2) next += Math.PI;
73
+ while (next > Math.PI / 2) next -= Math.PI;
74
+ return Math.abs(next) < SELECTION_ROTATION_EPSILON ? 0 : next;
75
+ }
76
+
77
+ function areAnglesCompatible(a: number, b: number): boolean {
78
+ return (
79
+ Math.abs(
80
+ normalizeSelectionFrameAngle(a) - normalizeSelectionFrameAngle(b),
81
+ ) < SELECTION_ROTATION_EPSILON
82
+ );
83
+ }
84
+
85
+ function hasCompatibleSelectionRotation(shapes: ShapeRecord[]): boolean {
86
+ if (shapes.length === 0) {
87
+ return true;
88
+ }
89
+
90
+ const base = normalizeSelectionFrameAngle(shapes[0]?.rotation ?? 0);
91
+ return shapes.every((shape) => areAnglesCompatible(shape.rotation, base));
92
+ }
93
+
94
+ function getSelectionRotation(
95
+ shapes: ShapeRecord[],
96
+ hasCompatibleRotation: boolean,
97
+ ): number {
98
+ if (shapes.length === 0 || !hasCompatibleRotation) {
99
+ return 0;
100
+ }
101
+
102
+ return normalizeSelectionFrameAngle(shapes[0]?.rotation ?? 0);
103
+ }
104
+
105
+ function hasSelectionRotation(shapes: ShapeRecord[]): boolean {
106
+ return shapes.some(
107
+ (shape) =>
108
+ Math.abs(normalizeSelectionFrameAngle(shape.rotation)) >
109
+ SELECTION_ROTATION_EPSILON,
110
+ );
111
+ }
112
+
113
+ function getEmptySelectionGeometrySnapshot(): SelectionGeometrySnapshot {
114
+ return {
115
+ outlineVertices: [],
116
+ pageBounds: null,
117
+ frameBounds: null,
118
+ rotation: 0,
119
+ rotationCenter: null,
120
+ toolbarAnchorPagePoint: null,
121
+ hasRotation: false,
122
+ hasCompatibleRotation: true,
123
+ };
124
+ }
125
+
126
+ function getSelectionGeometrySnapshot(
127
+ editor: Editor,
128
+ getShapeGeometrySnapshot: (shapeId: ShapeId) => ShapeGeometry | undefined,
129
+ ): SelectionGeometrySnapshot {
130
+ const selectedShapeIds = editor.selectedShapeIdsSignal.get();
131
+ if (selectedShapeIds.length === 0) {
132
+ return getEmptySelectionGeometrySnapshot();
133
+ }
134
+
135
+ const outlineVertices: Vec[] = [];
136
+ const selectionPagePoints: Vec[] = [];
137
+ const selectionFrameShapes: ShapeRecord[] = [];
138
+ let minX = Infinity;
139
+ let minY = Infinity;
140
+ let maxX = -Infinity;
141
+ let maxY = -Infinity;
142
+ for (const shapeId of selectedShapeIds) {
143
+ const shape = editor.getShapeSignal(shapeId).get();
144
+ if (!shape) {
145
+ continue;
146
+ }
147
+
148
+ const shapeUtil = editor.getShapeUtil(shape);
149
+ const pointSelection = shapeUtil.getSelectionPagePoints(shape);
150
+ if (pointSelection) {
151
+ selectionPagePoints.push(...pointSelection);
152
+ }
153
+
154
+ if (
155
+ !shapeUtil.contributesToSelectionFrame(shape, {
156
+ editor,
157
+ })
158
+ ) {
159
+ continue;
160
+ }
161
+ selectionFrameShapes.push(shape);
162
+
163
+ const geometry = getShapeGeometrySnapshot(shapeId);
164
+ if (!geometry) {
165
+ continue;
166
+ }
167
+
168
+ outlineVertices.push(...geometry.outlineVertices);
169
+ minX = Math.min(minX, geometry.bounds.minX);
170
+ minY = Math.min(minY, geometry.bounds.minY);
171
+ maxX = Math.max(maxX, geometry.bounds.maxX);
172
+ maxY = Math.max(maxY, geometry.bounds.maxY);
173
+ }
174
+
175
+ const pageBounds =
176
+ Number.isFinite(minX) &&
177
+ Number.isFinite(minY) &&
178
+ Number.isFinite(maxX) &&
179
+ Number.isFinite(maxY)
180
+ ? new Box(minX, minY, maxX - minX, maxY - minY)
181
+ : null;
182
+ const hasCompatibleRotation =
183
+ selectionFrameShapes.length > 0
184
+ ? hasCompatibleSelectionRotation(selectionFrameShapes)
185
+ : true;
186
+ const rotation = getSelectionRotation(
187
+ selectionFrameShapes,
188
+ hasCompatibleRotation,
189
+ );
190
+ const hasRotation = hasSelectionRotation(selectionFrameShapes);
191
+ const rotationCenter = pageBounds?.center ?? null;
192
+ const frameBounds =
193
+ outlineVertices.length === 0
194
+ ? null
195
+ : Math.abs(rotation) < SELECTION_ROTATION_EPSILON || !rotationCenter
196
+ ? Box.FromPoints(outlineVertices)
197
+ : Box.FromPoints(
198
+ outlineVertices.map((point) =>
199
+ Vec.RotWith(point, rotationCenter, -rotation),
200
+ ),
201
+ );
202
+ const pointToolbarBounds =
203
+ selectionPagePoints.length > 0 ? Box.FromPoints(selectionPagePoints) : null;
204
+ const toolbarAnchorPagePoint =
205
+ frameBounds && rotationCenter
206
+ ? Math.abs(rotation) < SELECTION_ROTATION_EPSILON
207
+ ? new Vec(frameBounds.center.x, frameBounds.minY)
208
+ : Vec.RotWith(
209
+ new Vec(frameBounds.center.x, frameBounds.minY),
210
+ rotationCenter,
211
+ rotation,
212
+ )
213
+ : pointToolbarBounds
214
+ ? new Vec(pointToolbarBounds.center.x, pointToolbarBounds.minY)
215
+ : null;
216
+
217
+ return {
218
+ outlineVertices,
219
+ pageBounds,
220
+ frameBounds,
221
+ rotation,
222
+ rotationCenter,
223
+ toolbarAnchorPagePoint,
224
+ hasRotation,
225
+ hasCompatibleRotation,
226
+ };
227
+ }
228
+
229
+ export function createSelectionGeometrySignals(
230
+ editor: Editor,
231
+ getShapeGeometrySnapshot: (shapeId: ShapeId) => ShapeGeometry | undefined,
232
+ ): SelectionGeometrySignals {
233
+ const geometry = computed<SelectionGeometrySnapshot>(
234
+ "editor.query.selectionGeometry",
235
+ () => getSelectionGeometrySnapshot(editor, getShapeGeometrySnapshot),
236
+ { isEqual: areSelectionGeometrySnapshotsEqual },
237
+ );
238
+ const pageOutlineVertices = computed<Vec[]>(
239
+ "editor.query.selectionPageOutlineVertices",
240
+ () => geometry.get().outlineVertices,
241
+ { isEqual: areVecArraysEqual },
242
+ );
243
+ const pageBounds = computed<Box | null>(
244
+ "editor.query.selectionPageBounds",
245
+ () => geometry.get().pageBounds,
246
+ { isEqual: areBoxesEqual },
247
+ );
248
+ const frameBounds = computed<Box | null>(
249
+ "editor.query.selectionFrameBounds",
250
+ () => geometry.get().frameBounds,
251
+ { isEqual: areBoxesEqual },
252
+ );
253
+ const rotation = computed<number>(
254
+ "editor.query.selectionRotation",
255
+ () => geometry.get().rotation,
256
+ );
257
+ const hasRotation = computed<boolean>(
258
+ "editor.query.selectionHasRotation",
259
+ () => geometry.get().hasRotation,
260
+ );
261
+ const rotationCenter = computed<Vec | null>(
262
+ "editor.query.selectionRotationCenter",
263
+ () => geometry.get().rotationCenter,
264
+ { isEqual: areVecsEqual },
265
+ );
266
+ const hasCompatibleRotation = computed<boolean>(
267
+ "editor.query.selectionHasCompatibleRotation",
268
+ () => geometry.get().hasCompatibleRotation,
269
+ );
270
+ const toolbarAnchorPagePoint = computed<Vec | null>(
271
+ "editor.query.selectionToolbarAnchorPagePoint",
272
+ () => geometry.get().toolbarAnchorPagePoint,
273
+ { isEqual: areVecsEqual },
274
+ );
275
+
276
+ return {
277
+ geometry,
278
+ pageOutlineVertices,
279
+ pageBounds,
280
+ frameBounds,
281
+ hasRotation,
282
+ rotation,
283
+ rotationCenter,
284
+ hasCompatibleRotation,
285
+ toolbarAnchorPagePoint,
286
+ };
287
+ }
@@ -0,0 +1,350 @@
1
+ import type { ChangeSource } from "@land/store";
2
+ import type { ShapeId, ShapeRecord } from "../../schema/records";
3
+ import type {
4
+ Editor,
5
+ LayerOrderDirection,
6
+ LayerOrderEdge,
7
+ ShapeSelectionSiblingOrderState,
8
+ ShapeSiblingOrderDirection,
9
+ ShapeSiblingOrderEdge,
10
+ ShapeSiblingOrderState,
11
+ } from "../Editor";
12
+
13
+ export function getSelectionParentOrderState(
14
+ editor: Editor,
15
+ ): ShapeSelectionSiblingOrderState | null {
16
+ const selectedShapes = editor.getSelectedShapes();
17
+ if (selectedShapes.length === 0) {
18
+ return null;
19
+ }
20
+ if (selectedShapes.some((shape) => shape.isLocked)) {
21
+ return null;
22
+ }
23
+
24
+ const parentId = selectedShapes[0]?.parentId;
25
+ if (!parentId) {
26
+ return null;
27
+ }
28
+ if (selectedShapes.some((shape) => shape.parentId !== parentId)) {
29
+ return null;
30
+ }
31
+
32
+ const siblings = editor.getChildShapes(parentId);
33
+ const selectedIds = new Set(selectedShapes.map((shape) => shape.id));
34
+ const selectedIndexes = siblings
35
+ .map((shape, index) => (selectedIds.has(shape.id) ? index : -1))
36
+ .filter((index) => index >= 0);
37
+ if (selectedIndexes.length !== selectedShapes.length) {
38
+ return null;
39
+ }
40
+
41
+ return {
42
+ parentId,
43
+ selectedCount: selectedShapes.length,
44
+ count: siblings.length,
45
+ canMoveBackward: selectedIndexes.some(
46
+ (index) => index > 0 && !selectedIds.has(siblings[index - 1]!.id),
47
+ ),
48
+ canMoveForward: selectedIndexes.some(
49
+ (index) =>
50
+ index < siblings.length - 1 &&
51
+ !selectedIds.has(siblings[index + 1]!.id),
52
+ ),
53
+ };
54
+ }
55
+
56
+ export function getSelectionSiblingOrderState(
57
+ editor: Editor,
58
+ ): ShapeSiblingOrderState | null {
59
+ const selectedShapes = editor.getSelectedShapes();
60
+ if (selectedShapes.length !== 1) {
61
+ return null;
62
+ }
63
+
64
+ const [shape] = selectedShapes;
65
+ if (!shape || shape.isLocked) {
66
+ return null;
67
+ }
68
+
69
+ const siblings = editor.getChildShapes(shape.parentId);
70
+ const index = siblings.findIndex((sibling) => sibling.id === shape.id);
71
+ if (index < 0) {
72
+ return null;
73
+ }
74
+
75
+ return {
76
+ parentId: shape.parentId,
77
+ index,
78
+ count: siblings.length,
79
+ canMoveBackward: index > 0,
80
+ canMoveForward: index < siblings.length - 1,
81
+ };
82
+ }
83
+
84
+ export function canMoveSelectionWithinParent(
85
+ editor: Editor,
86
+ direction: ShapeSiblingOrderDirection,
87
+ ): boolean {
88
+ const state = getSelectionSiblingOrderState(editor);
89
+ return direction === "backward"
90
+ ? (state?.canMoveBackward ?? false)
91
+ : (state?.canMoveForward ?? false);
92
+ }
93
+
94
+ export function canMoveSelectionInParentOrder(
95
+ editor: Editor,
96
+ direction: ShapeSiblingOrderDirection,
97
+ ): boolean {
98
+ const state = getSelectionParentOrderState(editor);
99
+ return direction === "backward"
100
+ ? (state?.canMoveBackward ?? false)
101
+ : (state?.canMoveForward ?? false);
102
+ }
103
+
104
+ export function getShapeParentOrderState(
105
+ editor: Editor,
106
+ ids: readonly ShapeId[],
107
+ ): ShapeSelectionSiblingOrderState | null {
108
+ const selectedShapes = ids
109
+ .map((id) => editor.getShape(id))
110
+ .filter((shape): shape is ShapeRecord => !!shape);
111
+ if (selectedShapes.length === 0) {
112
+ return null;
113
+ }
114
+ if (
115
+ selectedShapes.some(
116
+ (shape) => shape.isLocked || editor.isShapeHidden(shape.id),
117
+ )
118
+ ) {
119
+ return null;
120
+ }
121
+
122
+ const parentId = selectedShapes[0]?.parentId;
123
+ if (!parentId) {
124
+ return null;
125
+ }
126
+ if (selectedShapes.some((shape) => shape.parentId !== parentId)) {
127
+ return null;
128
+ }
129
+
130
+ const siblings = editor.getChildShapes(parentId);
131
+ const selectedIds = new Set(selectedShapes.map((shape) => shape.id));
132
+ const selectedIndexes = siblings
133
+ .map((shape, index) => (selectedIds.has(shape.id) ? index : -1))
134
+ .filter((index) => index >= 0);
135
+ if (selectedIndexes.length !== selectedShapes.length) {
136
+ return null;
137
+ }
138
+
139
+ return {
140
+ parentId,
141
+ selectedCount: selectedShapes.length,
142
+ count: siblings.length,
143
+ canMoveBackward: selectedIndexes.some(
144
+ (index) => index > 0 && !selectedIds.has(siblings[index - 1]!.id),
145
+ ),
146
+ canMoveForward: selectedIndexes.some(
147
+ (index) =>
148
+ index < siblings.length - 1 &&
149
+ !selectedIds.has(siblings[index + 1]!.id),
150
+ ),
151
+ };
152
+ }
153
+
154
+ export function canMoveShapesInParentOrder(
155
+ editor: Editor,
156
+ ids: readonly ShapeId[],
157
+ direction: LayerOrderDirection,
158
+ ): boolean {
159
+ const state = getShapeParentOrderState(editor, ids);
160
+ return direction === "backward"
161
+ ? (state?.canMoveBackward ?? false)
162
+ : (state?.canMoveForward ?? false);
163
+ }
164
+
165
+ export function moveShapesInParentOrder(
166
+ editor: Editor,
167
+ ids: readonly ShapeId[],
168
+ direction: LayerOrderDirection,
169
+ source: ChangeSource = "user",
170
+ ): boolean {
171
+ const state = getShapeParentOrderState(editor, ids);
172
+ if (!state) {
173
+ return false;
174
+ }
175
+
176
+ const siblings = editor.getChildShapes(state.parentId);
177
+ const selectedIds = new Set(ids);
178
+ const nextSiblings = [...siblings];
179
+ let didMove = false;
180
+
181
+ if (direction === "backward") {
182
+ for (let index = 1; index < nextSiblings.length; index += 1) {
183
+ const current = nextSiblings[index];
184
+ const before = nextSiblings[index - 1];
185
+ if (
186
+ current &&
187
+ before &&
188
+ selectedIds.has(current.id) &&
189
+ !selectedIds.has(before.id)
190
+ ) {
191
+ nextSiblings[index - 1] = current;
192
+ nextSiblings[index] = before;
193
+ didMove = true;
194
+ }
195
+ }
196
+ } else {
197
+ for (let index = nextSiblings.length - 2; index >= 0; index -= 1) {
198
+ const current = nextSiblings[index];
199
+ const after = nextSiblings[index + 1];
200
+ if (
201
+ current &&
202
+ after &&
203
+ selectedIds.has(current.id) &&
204
+ !selectedIds.has(after.id)
205
+ ) {
206
+ nextSiblings[index] = after;
207
+ nextSiblings[index + 1] = current;
208
+ didMove = true;
209
+ }
210
+ }
211
+ }
212
+
213
+ if (!didMove) {
214
+ return false;
215
+ }
216
+
217
+ editor.cancelInteractionForCommand();
218
+ editor.commands.updateShapes(
219
+ nextSiblings.map((sibling, index) => ({
220
+ id: sibling.id,
221
+ index: editor.createShapeIndex(index + 1),
222
+ })),
223
+ { source },
224
+ );
225
+ return true;
226
+ }
227
+
228
+ export function moveShapesToParentOrderEdge(
229
+ editor: Editor,
230
+ ids: readonly ShapeId[],
231
+ edge: LayerOrderEdge,
232
+ source: ChangeSource = "user",
233
+ ): boolean {
234
+ const state = getShapeParentOrderState(editor, ids);
235
+ if (!state) {
236
+ return false;
237
+ }
238
+
239
+ const siblings = editor.getChildShapes(state.parentId);
240
+ const selectedIds = new Set(ids);
241
+ const selectedSiblings: ShapeRecord[] = [];
242
+ const unselectedSiblings: ShapeRecord[] = [];
243
+
244
+ for (const sibling of siblings) {
245
+ if (selectedIds.has(sibling.id)) {
246
+ selectedSiblings.push(sibling);
247
+ } else {
248
+ unselectedSiblings.push(sibling);
249
+ }
250
+ }
251
+
252
+ if (selectedSiblings.length !== selectedIds.size) {
253
+ return false;
254
+ }
255
+
256
+ const canMove =
257
+ edge === "back" ? state.canMoveBackward : state.canMoveForward;
258
+ if (!canMove) {
259
+ return false;
260
+ }
261
+
262
+ const nextSiblings =
263
+ edge === "back"
264
+ ? [...selectedSiblings, ...unselectedSiblings]
265
+ : [...unselectedSiblings, ...selectedSiblings];
266
+
267
+ editor.cancelInteractionForCommand();
268
+ editor.commands.updateShapes(
269
+ nextSiblings.map((sibling, index) => ({
270
+ id: sibling.id,
271
+ index: editor.createShapeIndex(index + 1),
272
+ })),
273
+ { source },
274
+ );
275
+ return true;
276
+ }
277
+
278
+ export function moveSelectionWithinParent(
279
+ editor: Editor,
280
+ direction: ShapeSiblingOrderDirection,
281
+ source: ChangeSource = "user",
282
+ ): boolean {
283
+ const selectedShapes = editor.getSelectedShapes();
284
+ if (selectedShapes.length !== 1) {
285
+ return false;
286
+ }
287
+
288
+ const [shape] = selectedShapes;
289
+ if (!shape || shape.isLocked) {
290
+ return false;
291
+ }
292
+
293
+ const state = getSelectionSiblingOrderState(editor);
294
+ if (!state) {
295
+ return false;
296
+ }
297
+
298
+ const siblings = editor.getChildShapes(state.parentId);
299
+ const fromIndex = state.index;
300
+ if (siblings[fromIndex]?.id !== shape.id) {
301
+ return false;
302
+ }
303
+ const toIndex = direction === "backward" ? fromIndex - 1 : fromIndex + 1;
304
+ if (toIndex < 0 || toIndex >= siblings.length) {
305
+ return false;
306
+ }
307
+
308
+ const nextSiblings = [...siblings];
309
+ const [movedShape] = nextSiblings.splice(fromIndex, 1);
310
+ if (!movedShape) {
311
+ return false;
312
+ }
313
+ nextSiblings.splice(toIndex, 0, movedShape);
314
+
315
+ editor.cancelInteractionForCommand();
316
+ editor.commands.updateShapes(
317
+ nextSiblings.map((sibling, index) => ({
318
+ id: sibling.id,
319
+ index: editor.createShapeIndex(index + 1),
320
+ })),
321
+ { source },
322
+ );
323
+ return true;
324
+ }
325
+
326
+ export function moveSelectionInParentOrder(
327
+ editor: Editor,
328
+ direction: ShapeSiblingOrderDirection,
329
+ source: ChangeSource = "user",
330
+ ): boolean {
331
+ return moveShapesInParentOrder(
332
+ editor,
333
+ editor.getSelectedShapeIds(),
334
+ direction,
335
+ source,
336
+ );
337
+ }
338
+
339
+ export function moveSelectionToParentOrderEdge(
340
+ editor: Editor,
341
+ edge: ShapeSiblingOrderEdge,
342
+ source: ChangeSource = "user",
343
+ ): boolean {
344
+ return moveShapesToParentOrderEdge(
345
+ editor,
346
+ editor.getSelectedShapeIds(),
347
+ edge,
348
+ source,
349
+ );
350
+ }