@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.
- package/README.md +145 -0
- package/_vendor/canvas-core/bindings/BindingUtil.ts +92 -0
- package/_vendor/canvas-core/bindings/BindingUtilRegistry.ts +22 -0
- package/_vendor/canvas-core/bindings/arrow/arrowBindingHelpers.ts +52 -0
- package/_vendor/canvas-core/contextMenu.ts +85 -0
- package/_vendor/canvas-core/editor/Editor.ts +2614 -0
- package/_vendor/canvas-core/editor/HistoryManager.ts +83 -0
- package/_vendor/canvas-core/editor/StateNode.ts +162 -0
- package/_vendor/canvas-core/editor/camera.ts +6 -0
- package/_vendor/canvas-core/editor/clipboard.ts +478 -0
- package/_vendor/canvas-core/editor/commands.ts +833 -0
- package/_vendor/canvas-core/editor/edgeScroll.ts +37 -0
- package/_vendor/canvas-core/editor/editorComputedHelpers.ts +257 -0
- package/_vendor/canvas-core/editor/editorSelectionTransforms.ts +116 -0
- package/_vendor/canvas-core/editor/events.ts +93 -0
- package/_vendor/canvas-core/editor/index.ts +12 -0
- package/_vendor/canvas-core/editor/interactions.ts +101 -0
- package/_vendor/canvas-core/editor/internal.ts +22 -0
- package/_vendor/canvas-core/editor/managers/BindingManager.ts +262 -0
- package/_vendor/canvas-core/editor/managers/CameraManager.ts +147 -0
- package/_vendor/canvas-core/editor/managers/ClipboardManager.ts +32 -0
- package/_vendor/canvas-core/editor/managers/DocumentSnapshotManager.ts +256 -0
- package/_vendor/canvas-core/editor/managers/InputsManager.ts +319 -0
- package/_vendor/canvas-core/editor/managers/InteractionTransientManager.ts +218 -0
- package/_vendor/canvas-core/editor/managers/QueryManager.ts +837 -0
- package/_vendor/canvas-core/editor/managers/RenderWindowManager.ts +347 -0
- package/_vendor/canvas-core/editor/managers/SelectionLayoutManager.ts +196 -0
- package/_vendor/canvas-core/editor/managers/SelectionManager.ts +98 -0
- package/_vendor/canvas-core/editor/managers/SelectionOverlayManager.ts +308 -0
- package/_vendor/canvas-core/editor/managers/ShapeEditingManager.ts +226 -0
- package/_vendor/canvas-core/editor/managers/ShapeGeometryManager.ts +162 -0
- package/_vendor/canvas-core/editor/managers/ShapeHierarchyManager.ts +797 -0
- package/_vendor/canvas-core/editor/managers/SideEffectManager.ts +83 -0
- package/_vendor/canvas-core/editor/managers/SnapManager.ts +433 -0
- package/_vendor/canvas-core/editor/managers/SnapSnapshotBuilder.ts +430 -0
- package/_vendor/canvas-core/editor/managers/SpatialIndexManager.ts +354 -0
- package/_vendor/canvas-core/editor/managers/ToolManager.ts +81 -0
- package/_vendor/canvas-core/editor/managers/queryState.ts +583 -0
- package/_vendor/canvas-core/editor/managers/selectionGeometry.ts +287 -0
- package/_vendor/canvas-core/editor/managers/shapeOrder.ts +350 -0
- package/_vendor/canvas-core/editor/managers/shapeReparenting.ts +160 -0
- package/_vendor/canvas-core/editor/managers/snapCalculations.ts +412 -0
- package/_vendor/canvas-core/editor/managers/snapGeometry.ts +202 -0
- package/_vendor/canvas-core/editor/managers/snapIndicators.ts +403 -0
- package/_vendor/canvas-core/editor/managers/snapTypes.ts +122 -0
- package/_vendor/canvas-core/editor/queries/shapeTransforms.ts +171 -0
- package/_vendor/canvas-core/editor/renderWindow.ts +439 -0
- package/_vendor/canvas-core/editor/resizeGeometry.ts +654 -0
- package/_vendor/canvas-core/editor/selection.ts +489 -0
- package/_vendor/canvas-core/editor/selectionHandleInteractions.ts +43 -0
- package/_vendor/canvas-core/editor/selectionOverlay.ts +567 -0
- package/_vendor/canvas-core/exports/integrity.ts +174 -0
- package/_vendor/canvas-core/exports/svg.ts +494 -0
- package/_vendor/canvas-core/geometry/Box.ts +80 -0
- package/_vendor/canvas-core/geometry/Ellipse2d.ts +67 -0
- package/_vendor/canvas-core/geometry/Geometry2d.ts +52 -0
- package/_vendor/canvas-core/geometry/Mat.ts +92 -0
- package/_vendor/canvas-core/geometry/Polyline2d.ts +63 -0
- package/_vendor/canvas-core/geometry/Rectangle2d.ts +64 -0
- package/_vendor/canvas-core/geometry/Vec.ts +123 -0
- package/_vendor/canvas-core/geometry/index.ts +13 -0
- package/_vendor/canvas-core/geometry/segments.ts +65 -0
- package/_vendor/canvas-core/index.ts +391 -0
- package/_vendor/canvas-core/plugins/index.ts +5 -0
- package/_vendor/canvas-core/plugins/types.ts +53 -0
- package/_vendor/canvas-core/reactivity.ts +16 -0
- package/_vendor/canvas-core/schema/records.ts +221 -0
- package/_vendor/canvas-core/schema/snapshots.ts +527 -0
- package/_vendor/canvas-core/shapes/ShapeUtil.ts +516 -0
- package/_vendor/canvas-core/shapes/ShapeUtilRegistry.ts +26 -0
- package/_vendor/canvas-core/shapes/capabilities.ts +67 -0
- package/_vendor/canvas-core/shapes/types.ts +20 -0
- package/_vendor/canvas-core/toolbar.ts +148 -0
- package/_vendor/canvas-core/tools/hand/HandTool.ts +75 -0
- package/_vendor/canvas-core/tools/index.ts +2 -0
- package/_vendor/canvas-core/tools/select/SelectTool.ts +33 -0
- package/_vendor/canvas-core/tools/select/selectContainment.ts +104 -0
- package/_vendor/canvas-core/tools/select/selectToolHelpers.ts +801 -0
- package/_vendor/canvas-core/tools/select/states/BrushingState.ts +145 -0
- package/_vendor/canvas-core/tools/select/states/DraggingSelectionHandleInteractionState.ts +116 -0
- package/_vendor/canvas-core/tools/select/states/IdleState.ts +159 -0
- package/_vendor/canvas-core/tools/select/states/PointingCanvasState.ts +41 -0
- package/_vendor/canvas-core/tools/select/states/PointingResizeHandleState.ts +78 -0
- package/_vendor/canvas-core/tools/select/states/PointingRotateHandleState.ts +64 -0
- package/_vendor/canvas-core/tools/select/states/PointingSelectionHandleInteractionState.ts +34 -0
- package/_vendor/canvas-core/tools/select/states/PointingShapeState.ts +140 -0
- package/_vendor/canvas-core/tools/select/states/ResizingState.ts +171 -0
- package/_vendor/canvas-core/tools/select/states/RotatingState.ts +124 -0
- package/_vendor/canvas-core/tools/select/states/SelectStateNode.ts +128 -0
- package/_vendor/canvas-core/tools/select/states/TranslatingState.ts +139 -0
- package/_vendor/canvas-core/tools/select/states/index.ts +11 -0
- package/_vendor/canvas-plugin-api/feature.ts +204 -0
- package/_vendor/canvas-plugin-api/index.ts +142 -0
- package/_vendor/canvas-product-react/api/apiRegistry.ts +35 -0
- package/_vendor/canvas-product-react/api/canvasProductApi.ts +422 -0
- package/_vendor/canvas-product-react/api/index.ts +31 -0
- package/_vendor/canvas-product-react/capabilities/clipboard/clipboardService.ts +36 -0
- package/_vendor/canvas-product-react/capabilities/clipboard/systemClipboard.ts +245 -0
- package/_vendor/canvas-product-react/capabilities/context-menu/systemContextMenu.ts +396 -0
- package/_vendor/canvas-product-react/capabilities/context-menu/useContextMenuSurface.ts +59 -0
- package/_vendor/canvas-product-react/capabilities/export/useSvgExport.ts +156 -0
- package/_vendor/canvas-product-react/capabilities/history/useHistoryControls.ts +23 -0
- package/_vendor/canvas-product-react/capabilities/pages/usePageControls.ts +41 -0
- package/_vendor/canvas-product-react/capabilities/toolbar/systemToolbar.ts +490 -0
- package/_vendor/canvas-product-react/capabilities/toolbar/useToolbarSurfaces.ts +92 -0
- package/_vendor/canvas-product-react/capabilities/zoom/useZoomControls.ts +27 -0
- package/_vendor/canvas-product-react/index.ts +84 -0
- package/_vendor/canvas-product-react/persistence/index.ts +18 -0
- package/_vendor/canvas-product-react/persistence/localSnapshotPersistence.ts +294 -0
- package/_vendor/canvas-product-react/runtime/CanvasProductRoot.tsx +250 -0
- package/_vendor/canvas-product-react/runtime/productDefinition.ts +72 -0
- package/_vendor/canvas-product-react/runtime/productServices.ts +96 -0
- package/_vendor/canvas-react/components/CanvasView.tsx +796 -0
- package/_vendor/canvas-react/components/OverlayLayer.tsx +879 -0
- package/_vendor/canvas-react/components/PageSpace.tsx +34 -0
- package/_vendor/canvas-react/components/ShapeLayer.tsx +428 -0
- package/_vendor/canvas-react/components/index.ts +4 -0
- package/_vendor/canvas-react/hooks/useEditor.ts +11 -0
- package/_vendor/canvas-react/hooks/useValue.ts +14 -0
- package/_vendor/canvas-react/host/CanvasRendererRegistryContext.tsx +14 -0
- package/_vendor/canvas-react/host/contextMenuRegistry.ts +61 -0
- package/_vendor/canvas-react/host/domEventGuards.ts +61 -0
- package/_vendor/canvas-react/host/index.ts +38 -0
- package/_vendor/canvas-react/host/pluginHost.ts +635 -0
- package/_vendor/canvas-react/host/rendererRegistry.tsx +58 -0
- package/_vendor/canvas-react/host/toolbarRegistry.ts +100 -0
- package/_vendor/canvas-react/index.ts +3 -0
- package/_vendor/plugin-arrow/ArrowBindingUtil.ts +239 -0
- package/_vendor/plugin-arrow/ArrowShapeUtil.ts +311 -0
- package/_vendor/plugin-arrow/ArrowTool.ts +208 -0
- package/_vendor/plugin-arrow/arrowGeometry.ts +441 -0
- package/_vendor/plugin-arrow/arrowTerminalBindingResolver.ts +253 -0
- package/_vendor/plugin-arrow/arrowTerminalCommands.ts +230 -0
- package/_vendor/plugin-arrow/canvas.tsx +92 -0
- package/_vendor/plugin-arrow/core.ts +409 -0
- package/_vendor/plugin-arrow/feature.ts +20 -0
- package/_vendor/plugin-arrow/index.ts +28 -0
- package/_vendor/plugin-arrow/selectionHandleInteractions.ts +154 -0
- package/_vendor/plugin-arrow/types.ts +63 -0
- package/_vendor/plugin-draw/DrawShapeRenderer.tsx +60 -0
- package/_vendor/plugin-draw/DrawShapeUtil.ts +372 -0
- package/_vendor/plugin-draw/DrawTool.ts +444 -0
- package/_vendor/plugin-draw/canvas.tsx +24 -0
- package/_vendor/plugin-draw/core.ts +515 -0
- package/_vendor/plugin-draw/drawGeometry.ts +491 -0
- package/_vendor/plugin-draw/drawHitTest.ts +271 -0
- package/_vendor/plugin-draw/drawInterpolation.ts +250 -0
- package/_vendor/plugin-draw/drawPath.ts +550 -0
- package/_vendor/plugin-draw/drawSegments.ts +106 -0
- package/_vendor/plugin-draw/drawSelectionTransform.ts +67 -0
- package/_vendor/plugin-draw/drawShapeGeometry.ts +307 -0
- package/_vendor/plugin-draw/drawStrokeGeometry.ts +454 -0
- package/_vendor/plugin-draw/feature.ts +20 -0
- package/_vendor/plugin-draw/index.ts +31 -0
- package/_vendor/plugin-draw/types.ts +36 -0
- package/_vendor/plugin-eraser/EraserTool.ts +297 -0
- package/_vendor/plugin-eraser/canvas.tsx +616 -0
- package/_vendor/plugin-eraser/core.ts +49 -0
- package/_vendor/plugin-eraser/feature.ts +20 -0
- package/_vendor/plugin-eraser/index.ts +4 -0
- package/_vendor/plugin-frame/FrameShapeUtil.ts +173 -0
- package/_vendor/plugin-frame/FrameTool.ts +225 -0
- package/_vendor/plugin-frame/canvas.tsx +75 -0
- package/_vendor/plugin-frame/core.ts +534 -0
- package/_vendor/plugin-frame/feature.ts +20 -0
- package/_vendor/plugin-frame/index.ts +6 -0
- package/_vendor/plugin-geo/GeoShapeUtil.ts +463 -0
- package/_vendor/plugin-geo/GeoTools.ts +247 -0
- package/_vendor/plugin-geo/canvas.tsx +90 -0
- package/_vendor/plugin-geo/core.ts +495 -0
- package/_vendor/plugin-geo/feature.ts +20 -0
- package/_vendor/plugin-geo/index.ts +7 -0
- package/_vendor/plugin-geo/types.ts +27 -0
- package/_vendor/plugin-group/GroupShapeUtil.ts +59 -0
- package/_vendor/plugin-group/canvas.tsx +59 -0
- package/_vendor/plugin-group/core.ts +40 -0
- package/_vendor/plugin-group/feature.ts +20 -0
- package/_vendor/plugin-group/index.ts +5 -0
- package/_vendor/plugin-image/ImageShapeUtil.ts +95 -0
- package/_vendor/plugin-image/canvas.tsx +212 -0
- package/_vendor/plugin-image/core.ts +542 -0
- package/_vendor/plugin-image/crop.ts +770 -0
- package/_vendor/plugin-image/cropInteractions.ts +350 -0
- package/_vendor/plugin-image/cropSession.ts +38 -0
- package/_vendor/plugin-image/feature.ts +20 -0
- package/_vendor/plugin-image/index.ts +29 -0
- package/_vendor/plugin-image/types.ts +55 -0
- package/_vendor/plugin-image-product-react/ImageImportController.tsx +143 -0
- package/_vendor/plugin-image-product-react/imageAssetService.ts +52 -0
- package/_vendor/plugin-image-product-react/imageBlobStore.ts +315 -0
- package/_vendor/plugin-image-product-react/imageHostActions.ts +438 -0
- package/_vendor/plugin-image-product-react/imageProductApi.ts +88 -0
- package/_vendor/plugin-image-product-react/imageToolbarHostBridge.ts +48 -0
- package/_vendor/plugin-image-product-react/imageToolbarRuntime.ts +21 -0
- package/_vendor/plugin-image-product-react/index.ts +60 -0
- package/_vendor/plugin-image-product-react/snapshotGuards.ts +33 -0
- package/_vendor/plugin-image-product-react/svgImageAssetInliner.ts +54 -0
- package/_vendor/plugin-image-product-react/useImageImport.ts +64 -0
- package/_vendor/plugin-laser/LaserTool.ts +154 -0
- package/_vendor/plugin-laser/canvas.tsx +645 -0
- package/_vendor/plugin-laser/core.ts +49 -0
- package/_vendor/plugin-laser/feature.ts +20 -0
- package/_vendor/plugin-laser/index.ts +4 -0
- package/_vendor/plugin-text/ArrowLabelBindingUtil.ts +336 -0
- package/_vendor/plugin-text/GeoLabelBindingUtil.ts +391 -0
- package/_vendor/plugin-text/TextShapeSvgExporter.ts +101 -0
- package/_vendor/plugin-text/TextShapeUtil.ts +284 -0
- package/_vendor/plugin-text/TextTool.ts +228 -0
- package/_vendor/plugin-text/canvas.tsx +115 -0
- package/_vendor/plugin-text/core.ts +422 -0
- package/_vendor/plugin-text/feature.ts +20 -0
- package/_vendor/plugin-text/host/RichTextEditingBox.tsx +144 -0
- package/_vendor/plugin-text/host/RichTextToolbar.tsx +266 -0
- package/_vendor/plugin-text/host/TiptapTextEditor.tsx +191 -0
- package/_vendor/plugin-text/host/richTextRendering.tsx +49 -0
- package/_vendor/plugin-text/host/textDomSelection.ts +93 -0
- package/_vendor/plugin-text/host/textEditingBoxStyle.ts +36 -0
- package/_vendor/plugin-text/host/textEntryFocus.ts +307 -0
- package/_vendor/plugin-text/host/textFontLoader.ts +149 -0
- package/_vendor/plugin-text/host/textMeasurement.ts +258 -0
- package/_vendor/plugin-text/host/textMeasurementSync.ts +77 -0
- package/_vendor/plugin-text/host/textRangeSelection.ts +134 -0
- package/_vendor/plugin-text/host/textShapeRendererRuntime.tsx +89 -0
- package/_vendor/plugin-text/host/textShapeSync.ts +173 -0
- package/_vendor/plugin-text/host/tiptapTextEditorController.ts +148 -0
- package/_vendor/plugin-text/index.ts +18 -0
- package/_vendor/plugin-text/richTextUtils.ts +259 -0
- package/_vendor/plugin-text/textInteractions.ts +366 -0
- package/_vendor/plugin-text/textLabelHelpers.ts +235 -0
- package/_vendor/plugin-text/textShapeHelpers.ts +123 -0
- package/_vendor/plugin-text/textStyle.ts +96 -0
- package/_vendor/plugin-text/textTypes.ts +48 -0
- package/_vendor/signals/atom.ts +74 -0
- package/_vendor/signals/computed.ts +132 -0
- package/_vendor/signals/core.ts +130 -0
- package/_vendor/signals/effect.ts +117 -0
- package/_vendor/signals/index.ts +9 -0
- package/_vendor/signals/types.ts +17 -0
- package/_vendor/store/Store.ts +171 -0
- package/_vendor/store/diff.ts +229 -0
- package/_vendor/store/history.ts +177 -0
- package/_vendor/store/index.ts +32 -0
- package/_vendor/store/types.ts +37 -0
- package/api/canvasKitApi.d.ts +29 -0
- package/index.css +3 -0
- package/index.d.ts +16 -0
- package/index.mjs +1 -0
- package/internal-modules.d.ts +187 -0
- package/package.json +48 -0
- package/product/Shell.d.ts +19 -0
- package/product/chromeContext.d.ts +70 -0
- package/product/createCanvasKitEditor.d.ts +6 -0
- package/product/index.d.ts +7 -0
- package/product/persistence.d.ts +3 -0
- package/product/product.d.ts +22 -0
- package/product/useCanvasClipboardShortcuts.d.ts +6 -0
- package/product-iyBbEPIs.mjs +4 -0
- package/product.mjs +1 -0
- package/standard/extensions.d.ts +1 -0
- package/standard/index.d.ts +3 -0
- package/standard/standardApi.d.ts +91 -0
- package/standard-BmrxwqpJ.mjs +44 -0
- package/standard.mjs +1 -0
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
import { UNINITIALIZED, computed, type Signal } from "@land/signals";
|
|
2
|
+
import type { Box } from "../../geometry/Box";
|
|
3
|
+
import type { ShapeId, ShapeRecord } from "../../schema/records";
|
|
4
|
+
import type {
|
|
5
|
+
Editor,
|
|
6
|
+
EditorRenderHostDebugState,
|
|
7
|
+
RenderItem,
|
|
8
|
+
} from "../Editor";
|
|
9
|
+
import {
|
|
10
|
+
areRenderHostDebugStatesEqual,
|
|
11
|
+
areRenderItemsEqual,
|
|
12
|
+
areShapeIdArraysEqual,
|
|
13
|
+
areShapeIdSetsEqual,
|
|
14
|
+
} from "../editorComputedHelpers";
|
|
15
|
+
import {
|
|
16
|
+
collectRetainedShapeRanges,
|
|
17
|
+
doRangesContainRanges,
|
|
18
|
+
formatShapeIndexRanges,
|
|
19
|
+
getExpandedRetainedBaseShapeRanges,
|
|
20
|
+
getExpandedRetainedShapeRanges,
|
|
21
|
+
getRetainedRenderWindowExtraShapeBudget,
|
|
22
|
+
getShapeIdArrayChangeCounts,
|
|
23
|
+
getShapeIndexRangeSpan,
|
|
24
|
+
getShapeIndexRangesForShapeIds,
|
|
25
|
+
getShapeWindowSnapshot,
|
|
26
|
+
mergeShapeIndexRanges,
|
|
27
|
+
type ShapeWindowSnapshot,
|
|
28
|
+
} from "../renderWindow";
|
|
29
|
+
|
|
30
|
+
interface RenderHostDebugSnapshot {
|
|
31
|
+
metrics: EditorRenderHostDebugState;
|
|
32
|
+
retainedShapeIds: ShapeId[];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Render culling and retained-window signals for the editor render host. The
|
|
37
|
+
* Editor exposes thin signal aliases that forward here.
|
|
38
|
+
*/
|
|
39
|
+
export class RenderWindowManager {
|
|
40
|
+
readonly shapeRenderOrderByIdSignal: Signal<Map<ShapeId, number>>;
|
|
41
|
+
readonly culledShapeIdsSignal: Signal<ShapeId[]>;
|
|
42
|
+
readonly culledShapeIdSetSignal: Signal<Set<ShapeId>>;
|
|
43
|
+
readonly selectedShapeIdsOutsideCulledSignal: Signal<ShapeId[]>;
|
|
44
|
+
readonly selectedShapeIdsOutsideCulledSetSignal: Signal<Set<ShapeId>>;
|
|
45
|
+
readonly renderVisibleShapeIdsSignal: Signal<ShapeId[]>;
|
|
46
|
+
readonly retainedShapeIdsSignal: Signal<ShapeId[]>;
|
|
47
|
+
readonly renderVisibleShapeIdSetSignal: Signal<Set<ShapeId>>;
|
|
48
|
+
readonly renderItemsSignal: Signal<RenderItem[]>;
|
|
49
|
+
readonly renderHostDebugStateSignal: Signal<EditorRenderHostDebugState>;
|
|
50
|
+
|
|
51
|
+
private readonly retainedBaseShapeWindowSnapshotSignal: Signal<ShapeWindowSnapshot>;
|
|
52
|
+
private readonly retainedShapeWindowSnapshotSignal: Signal<ShapeWindowSnapshot>;
|
|
53
|
+
private readonly renderHostDebugStateSnapshotSignal: Signal<RenderHostDebugSnapshot>;
|
|
54
|
+
|
|
55
|
+
constructor(private readonly editor: Editor) {
|
|
56
|
+
this.shapeRenderOrderByIdSignal = computed<Map<ShapeId, number>>(
|
|
57
|
+
"editor.shapeRenderOrderById",
|
|
58
|
+
() => {
|
|
59
|
+
const orderById = new Map<ShapeId, number>();
|
|
60
|
+
for (const [index, shapeId] of editor.currentPageShapeIdsSignal
|
|
61
|
+
.get()
|
|
62
|
+
.entries()) {
|
|
63
|
+
orderById.set(shapeId, index);
|
|
64
|
+
}
|
|
65
|
+
return orderById;
|
|
66
|
+
},
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
this.culledShapeIdsSignal = computed<ShapeId[]>(
|
|
70
|
+
"editor.culledShapeIds",
|
|
71
|
+
() => {
|
|
72
|
+
const currentPageId = editor.getCurrentPageId();
|
|
73
|
+
const viewportBounds = editor.renderViewportPageBoundsSignal.get();
|
|
74
|
+
return viewportBounds
|
|
75
|
+
? editor.spatialIndex.getShapeIdsInViewport(viewportBounds, {
|
|
76
|
+
pageId: currentPageId,
|
|
77
|
+
})
|
|
78
|
+
: editor.currentPageShapeIdsSignal.get();
|
|
79
|
+
},
|
|
80
|
+
{ isEqual: areShapeIdArraysEqual },
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
this.retainedBaseShapeWindowSnapshotSignal =
|
|
84
|
+
computed<ShapeWindowSnapshot>(
|
|
85
|
+
"editor.retainedBaseShapeWindowSnapshot",
|
|
86
|
+
(previous) => {
|
|
87
|
+
const pageShapeIds = editor.currentPageShapeIdsSignal.get();
|
|
88
|
+
const viewportReady = editor.viewportPageBoundsSignal.get() !== null;
|
|
89
|
+
const nextShapeIds = this.culledShapeIdsSignal.get();
|
|
90
|
+
const pageIndexById = this.getPageIndexById(pageShapeIds);
|
|
91
|
+
const nextRanges = getShapeIndexRangesForShapeIds(
|
|
92
|
+
nextShapeIds,
|
|
93
|
+
pageIndexById,
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
if (
|
|
97
|
+
previous !== UNINITIALIZED &&
|
|
98
|
+
previous &&
|
|
99
|
+
previous.viewportReady &&
|
|
100
|
+
nextRanges.length > 0
|
|
101
|
+
) {
|
|
102
|
+
const previousRanges = mergeShapeIndexRanges(
|
|
103
|
+
previous.ranges.map((range) => ({ ...range })),
|
|
104
|
+
);
|
|
105
|
+
const expandedRanges = getExpandedRetainedBaseShapeRanges(
|
|
106
|
+
previousRanges,
|
|
107
|
+
nextRanges,
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
if (expandedRanges) {
|
|
111
|
+
return getShapeWindowSnapshot(pageShapeIds, expandedRanges, {
|
|
112
|
+
viewportReady,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return getShapeWindowSnapshot(pageShapeIds, nextRanges, {
|
|
118
|
+
viewportReady,
|
|
119
|
+
});
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
isEqual: (left, right) =>
|
|
123
|
+
areShapeIdArraysEqual(left.shapeIds, right.shapeIds),
|
|
124
|
+
},
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
this.culledShapeIdSetSignal = computed<Set<ShapeId>>(
|
|
128
|
+
"editor.culledShapeIdSet",
|
|
129
|
+
() => new Set(this.culledShapeIdsSignal.get()),
|
|
130
|
+
{ isEqual: areShapeIdSetsEqual },
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
this.selectedShapeIdsOutsideCulledSignal = computed<ShapeId[]>(
|
|
134
|
+
"editor.selectedShapeIdsOutsideCulled",
|
|
135
|
+
() => {
|
|
136
|
+
const selectedIds = editor.selectedShapeIdsSignal.get();
|
|
137
|
+
if (selectedIds.length === 0) {
|
|
138
|
+
return [];
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const culledIds = this.culledShapeIdSetSignal.get();
|
|
142
|
+
return selectedIds.filter((shapeId) => !culledIds.has(shapeId));
|
|
143
|
+
},
|
|
144
|
+
{ isEqual: areShapeIdArraysEqual },
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
this.selectedShapeIdsOutsideCulledSetSignal = computed<Set<ShapeId>>(
|
|
148
|
+
"editor.selectedShapeIdsOutsideCulledSet",
|
|
149
|
+
() => new Set(this.selectedShapeIdsOutsideCulledSignal.get()),
|
|
150
|
+
{ isEqual: areShapeIdSetsEqual },
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
this.renderVisibleShapeIdsSignal = computed<ShapeId[]>(
|
|
154
|
+
"editor.renderVisibleShapeIds",
|
|
155
|
+
() => {
|
|
156
|
+
const selectedIdsOutsideCulled =
|
|
157
|
+
this.selectedShapeIdsOutsideCulledSignal.get();
|
|
158
|
+
if (selectedIdsOutsideCulled.length === 0) {
|
|
159
|
+
return this.culledShapeIdsSignal.get();
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const culledIds = this.culledShapeIdSetSignal.get();
|
|
163
|
+
const selectedIds = this.selectedShapeIdsOutsideCulledSetSignal.get();
|
|
164
|
+
return editor.currentPageShapeIdsSignal
|
|
165
|
+
.get()
|
|
166
|
+
.filter(
|
|
167
|
+
(shapeId) => culledIds.has(shapeId) || selectedIds.has(shapeId),
|
|
168
|
+
);
|
|
169
|
+
},
|
|
170
|
+
{ isEqual: areShapeIdArraysEqual },
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
this.retainedShapeWindowSnapshotSignal = computed<ShapeWindowSnapshot>(
|
|
174
|
+
"editor.retainedShapeWindowSnapshot",
|
|
175
|
+
(previous) => {
|
|
176
|
+
const pageShapeIds = editor.currentPageShapeIdsSignal.get();
|
|
177
|
+
const viewportReady = editor.viewportPageBoundsSignal.get() !== null;
|
|
178
|
+
const baseShapeIds =
|
|
179
|
+
this.retainedBaseShapeWindowSnapshotSignal.get().shapeIds;
|
|
180
|
+
const extraShapeBudget =
|
|
181
|
+
getRetainedRenderWindowExtraShapeBudget(baseShapeIds.length);
|
|
182
|
+
const nextRanges = collectRetainedShapeRanges(
|
|
183
|
+
pageShapeIds,
|
|
184
|
+
baseShapeIds,
|
|
185
|
+
this.selectedShapeIdsOutsideCulledSetSignal.get(),
|
|
186
|
+
extraShapeBudget,
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
if (
|
|
190
|
+
viewportReady &&
|
|
191
|
+
previous !== UNINITIALIZED &&
|
|
192
|
+
previous &&
|
|
193
|
+
previous.viewportReady &&
|
|
194
|
+
previous.ranges.length > 0 &&
|
|
195
|
+
nextRanges.length > 0
|
|
196
|
+
) {
|
|
197
|
+
const previousRanges = mergeShapeIndexRanges(
|
|
198
|
+
previous.ranges.map((range) => ({ ...range })),
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
if (doRangesContainRanges(previousRanges, nextRanges)) {
|
|
202
|
+
return getShapeWindowSnapshot(pageShapeIds, previousRanges, {
|
|
203
|
+
viewportReady,
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const expandedRanges = getExpandedRetainedShapeRanges(
|
|
208
|
+
previousRanges,
|
|
209
|
+
nextRanges,
|
|
210
|
+
extraShapeBudget,
|
|
211
|
+
);
|
|
212
|
+
if (expandedRanges) {
|
|
213
|
+
return getShapeWindowSnapshot(pageShapeIds, expandedRanges, {
|
|
214
|
+
viewportReady,
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return getShapeWindowSnapshot(pageShapeIds, nextRanges, {
|
|
220
|
+
viewportReady,
|
|
221
|
+
});
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
isEqual: (left, right) =>
|
|
225
|
+
areShapeIdArraysEqual(left.shapeIds, right.shapeIds),
|
|
226
|
+
},
|
|
227
|
+
);
|
|
228
|
+
|
|
229
|
+
this.retainedShapeIdsSignal = computed<ShapeId[]>(
|
|
230
|
+
"editor.retainedShapeIds",
|
|
231
|
+
() => this.retainedShapeWindowSnapshotSignal.get().shapeIds,
|
|
232
|
+
{ isEqual: areShapeIdArraysEqual },
|
|
233
|
+
);
|
|
234
|
+
|
|
235
|
+
this.renderVisibleShapeIdSetSignal = computed<Set<ShapeId>>(
|
|
236
|
+
"editor.renderVisibleShapeIdSet",
|
|
237
|
+
() => new Set(this.renderVisibleShapeIdsSignal.get()),
|
|
238
|
+
{ isEqual: areShapeIdSetsEqual },
|
|
239
|
+
);
|
|
240
|
+
|
|
241
|
+
this.renderItemsSignal = computed<RenderItem[]>(
|
|
242
|
+
"editor.renderItems",
|
|
243
|
+
() => {
|
|
244
|
+
const selectedIds = editor.selectedShapeIdSetSignal.get();
|
|
245
|
+
const renderItems: RenderItem[] = [];
|
|
246
|
+
for (const shapeId of this.renderVisibleShapeIdsSignal.get()) {
|
|
247
|
+
const shape = editor.getShape(shapeId);
|
|
248
|
+
const bounds = editor.queries.getShapePageBoundsSignal(shapeId).get();
|
|
249
|
+
if (!shape || shape.isHidden || !bounds) {
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
renderItems.push({
|
|
254
|
+
id: shape.id,
|
|
255
|
+
shape,
|
|
256
|
+
bounds,
|
|
257
|
+
isSelected: selectedIds.has(shape.id),
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
return renderItems;
|
|
262
|
+
},
|
|
263
|
+
{ isEqual: areRenderItemsEqual },
|
|
264
|
+
);
|
|
265
|
+
|
|
266
|
+
this.renderHostDebugStateSnapshotSignal =
|
|
267
|
+
computed<RenderHostDebugSnapshot>(
|
|
268
|
+
"editor.renderHostDebugState.snapshot",
|
|
269
|
+
(previous) => {
|
|
270
|
+
const culledShapeIds = this.culledShapeIdsSignal.get();
|
|
271
|
+
const visibleShapeIds = this.renderVisibleShapeIdsSignal.get();
|
|
272
|
+
const retainedBaseShapeIds =
|
|
273
|
+
this.retainedBaseShapeWindowSnapshotSignal.get().shapeIds;
|
|
274
|
+
const retainedShapeIds = this.retainedShapeIdsSignal.get();
|
|
275
|
+
const selectedOutsideCulled =
|
|
276
|
+
this.selectedShapeIdsOutsideCulledSignal.get();
|
|
277
|
+
|
|
278
|
+
const pageIndexById = this.getPageIndexById(
|
|
279
|
+
editor.currentPageShapeIdsSignal.get(),
|
|
280
|
+
);
|
|
281
|
+
|
|
282
|
+
const culledRanges = getShapeIndexRangesForShapeIds(
|
|
283
|
+
culledShapeIds,
|
|
284
|
+
pageIndexById,
|
|
285
|
+
);
|
|
286
|
+
const retainedRanges = getShapeIndexRangesForShapeIds(
|
|
287
|
+
retainedShapeIds,
|
|
288
|
+
pageIndexById,
|
|
289
|
+
);
|
|
290
|
+
const retainedBaseRanges = getShapeIndexRangesForShapeIds(
|
|
291
|
+
retainedBaseShapeIds,
|
|
292
|
+
pageIndexById,
|
|
293
|
+
);
|
|
294
|
+
const previousRetainedIds =
|
|
295
|
+
previous === UNINITIALIZED ? [] : previous.retainedShapeIds;
|
|
296
|
+
const retainedChange = getShapeIdArrayChangeCounts(
|
|
297
|
+
previousRetainedIds,
|
|
298
|
+
retainedShapeIds,
|
|
299
|
+
);
|
|
300
|
+
|
|
301
|
+
const metrics: EditorRenderHostDebugState = {
|
|
302
|
+
culledCount: culledShapeIds.length,
|
|
303
|
+
visibleCount: visibleShapeIds.length,
|
|
304
|
+
retainedCount: retainedShapeIds.length,
|
|
305
|
+
retainedBaseCount: retainedBaseShapeIds.length,
|
|
306
|
+
culledRangeCount: culledRanges.length,
|
|
307
|
+
culledRangeSpan: getShapeIndexRangeSpan(culledRanges),
|
|
308
|
+
culledRangesSummary: formatShapeIndexRanges(culledRanges),
|
|
309
|
+
retainedBaseRangeCount: retainedBaseRanges.length,
|
|
310
|
+
retainedBaseRangeSpan: getShapeIndexRangeSpan(retainedBaseRanges),
|
|
311
|
+
retainedBaseRangesSummary:
|
|
312
|
+
formatShapeIndexRanges(retainedBaseRanges),
|
|
313
|
+
retainedRangeCount: retainedRanges.length,
|
|
314
|
+
retainedRangeSpan: getShapeIndexRangeSpan(retainedRanges),
|
|
315
|
+
retainedRangesSummary: formatShapeIndexRanges(retainedRanges),
|
|
316
|
+
retainedChangeCount: retainedChange.changed,
|
|
317
|
+
retainedAddedCount: retainedChange.added,
|
|
318
|
+
retainedRemovedCount: retainedChange.removed,
|
|
319
|
+
selectedOutsideCulledCount: selectedOutsideCulled.length,
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
return {
|
|
323
|
+
metrics,
|
|
324
|
+
retainedShapeIds: retainedShapeIds.slice(),
|
|
325
|
+
};
|
|
326
|
+
},
|
|
327
|
+
{
|
|
328
|
+
isEqual: (left, right) =>
|
|
329
|
+
areRenderHostDebugStatesEqual(left.metrics, right.metrics),
|
|
330
|
+
},
|
|
331
|
+
);
|
|
332
|
+
|
|
333
|
+
this.renderHostDebugStateSignal = computed<EditorRenderHostDebugState>(
|
|
334
|
+
"editor.renderHostDebugState.public",
|
|
335
|
+
() => this.renderHostDebugStateSnapshotSignal.get().metrics,
|
|
336
|
+
{ isEqual: areRenderHostDebugStatesEqual },
|
|
337
|
+
);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
private getPageIndexById(shapeIds: ShapeId[]): Map<ShapeId, number> {
|
|
341
|
+
const pageIndexById = new Map<ShapeId, number>();
|
|
342
|
+
for (const [index, shapeId] of shapeIds.entries()) {
|
|
343
|
+
pageIndexById.set(shapeId, index);
|
|
344
|
+
}
|
|
345
|
+
return pageIndexById;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import type { ChangeSource } from "@land/store";
|
|
2
|
+
import { Box } from "../../geometry/Box";
|
|
3
|
+
import { Vec } from "../../geometry/Vec";
|
|
4
|
+
import type { PageId, ShapeRecord } from "../../schema/records";
|
|
5
|
+
import type { Editor } from "../Editor";
|
|
6
|
+
import { clampCameraZoom } from "../camera";
|
|
7
|
+
import { isValidBox } from "../editorComputedHelpers";
|
|
8
|
+
|
|
9
|
+
export type AlignMode =
|
|
10
|
+
| "left"
|
|
11
|
+
| "horizontal-center"
|
|
12
|
+
| "right"
|
|
13
|
+
| "top"
|
|
14
|
+
| "vertical-center"
|
|
15
|
+
| "bottom";
|
|
16
|
+
|
|
17
|
+
export type DistributeAxis = "horizontal" | "vertical";
|
|
18
|
+
|
|
19
|
+
export interface ZoomToBoundsOptions {
|
|
20
|
+
padding?: number;
|
|
21
|
+
maxZoom?: number;
|
|
22
|
+
source?: ChangeSource;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface AlignableEntry {
|
|
26
|
+
shape: ShapeRecord;
|
|
27
|
+
bounds: Box;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Selection alignment / distribution and viewport-fit camera moves. These are
|
|
32
|
+
* cohesive, page-space selection operations carved out of the Editor facade so
|
|
33
|
+
* the editor can stay a thinner delegating surface. The Editor exposes thin
|
|
34
|
+
* wrappers that forward here.
|
|
35
|
+
*/
|
|
36
|
+
export class SelectionLayoutManager {
|
|
37
|
+
constructor(private readonly editor: Editor) {}
|
|
38
|
+
|
|
39
|
+
align(mode: AlignMode, source: ChangeSource = "user"): boolean {
|
|
40
|
+
const entries = this.getAlignableEntries();
|
|
41
|
+
if (entries.length < 2) {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const union = Box.FromPoints(
|
|
46
|
+
entries.flatMap((entry) => [
|
|
47
|
+
new Vec(entry.bounds.minX, entry.bounds.minY),
|
|
48
|
+
new Vec(entry.bounds.maxX, entry.bounds.maxY),
|
|
49
|
+
]),
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
const updates = entries.map(({ shape, bounds }) => {
|
|
53
|
+
let dx = 0;
|
|
54
|
+
let dy = 0;
|
|
55
|
+
switch (mode) {
|
|
56
|
+
case "left":
|
|
57
|
+
dx = union.minX - bounds.minX;
|
|
58
|
+
break;
|
|
59
|
+
case "right":
|
|
60
|
+
dx = union.maxX - bounds.maxX;
|
|
61
|
+
break;
|
|
62
|
+
case "horizontal-center":
|
|
63
|
+
dx = union.center.x - bounds.center.x;
|
|
64
|
+
break;
|
|
65
|
+
case "top":
|
|
66
|
+
dy = union.minY - bounds.minY;
|
|
67
|
+
break;
|
|
68
|
+
case "bottom":
|
|
69
|
+
dy = union.maxY - bounds.maxY;
|
|
70
|
+
break;
|
|
71
|
+
case "vertical-center":
|
|
72
|
+
dy = union.center.y - bounds.center.y;
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
return { id: shape.id, x: shape.x + dx, y: shape.y + dy };
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
this.editor.commands.updateShapes(updates, { source });
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
distribute(axis: DistributeAxis, source: ChangeSource = "user"): boolean {
|
|
83
|
+
const entries = this.getAlignableEntries();
|
|
84
|
+
if (entries.length < 3) {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const isHorizontal = axis === "horizontal";
|
|
89
|
+
const size = (b: Box) => (isHorizontal ? b.w : b.h);
|
|
90
|
+
const min = (b: Box) => (isHorizontal ? b.minX : b.minY);
|
|
91
|
+
const axisKey = isHorizontal ? "x" : "y";
|
|
92
|
+
|
|
93
|
+
const sorted = [...entries].sort(
|
|
94
|
+
(a, b) => a.bounds.center[axisKey] - b.bounds.center[axisKey],
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
const first = sorted[0]!.bounds;
|
|
98
|
+
const last = sorted[sorted.length - 1]!.bounds;
|
|
99
|
+
const span = (isHorizontal ? last.maxX : last.maxY) - min(first);
|
|
100
|
+
const totalSize = sorted.reduce((sum, entry) => sum + size(entry.bounds), 0);
|
|
101
|
+
const gap = (span - totalSize) / (sorted.length - 1);
|
|
102
|
+
|
|
103
|
+
let cursor = min(first);
|
|
104
|
+
const updates = sorted.map(({ shape, bounds }, index) => {
|
|
105
|
+
const target =
|
|
106
|
+
index === 0 || index === sorted.length - 1 ? min(bounds) : cursor;
|
|
107
|
+
const delta = target - min(bounds);
|
|
108
|
+
cursor = target + size(bounds) + gap;
|
|
109
|
+
return isHorizontal
|
|
110
|
+
? { id: shape.id, x: shape.x + delta }
|
|
111
|
+
: { id: shape.id, y: shape.y + delta };
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
this.editor.commands.updateShapes(updates, { source });
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
getCurrentPageBounds(
|
|
119
|
+
pageId: PageId = this.editor.getCurrentPageId(),
|
|
120
|
+
): Box | null {
|
|
121
|
+
return this.boundsOfShapeIds(this.editor.getCurrentPageShapeIds());
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
zoomToBounds(bounds: Box, options?: ZoomToBoundsOptions): boolean {
|
|
125
|
+
const viewport = this.editor.viewportScreenSizeSignal.get();
|
|
126
|
+
if (viewport.w <= 0 || viewport.h <= 0 || !isValidBox(bounds)) {
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
if (bounds.w <= 0 && bounds.h <= 0) {
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const padding = options?.padding ?? 64;
|
|
134
|
+
const usableW = Math.max(1, viewport.w - padding * 2);
|
|
135
|
+
const usableH = Math.max(1, viewport.h - padding * 2);
|
|
136
|
+
const zoomX = bounds.w > 0 ? usableW / bounds.w : Infinity;
|
|
137
|
+
const zoomY = bounds.h > 0 ? usableH / bounds.h : Infinity;
|
|
138
|
+
const maxZoom = options?.maxZoom ?? 1;
|
|
139
|
+
const zoom = clampCameraZoom(Math.min(zoomX, zoomY, maxZoom));
|
|
140
|
+
|
|
141
|
+
const center = bounds.center;
|
|
142
|
+
this.editor.setCamera(
|
|
143
|
+
{
|
|
144
|
+
x: center.x - viewport.w / 2 / zoom,
|
|
145
|
+
y: center.y - viewport.h / 2 / zoom,
|
|
146
|
+
z: zoom,
|
|
147
|
+
},
|
|
148
|
+
{ source: options?.source ?? "user" },
|
|
149
|
+
);
|
|
150
|
+
return true;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
zoomToFit(options?: ZoomToBoundsOptions): boolean {
|
|
154
|
+
const bounds = this.getCurrentPageBounds();
|
|
155
|
+
return bounds ? this.zoomToBounds(bounds, options) : false;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
zoomToSelection(options?: ZoomToBoundsOptions): boolean {
|
|
159
|
+
const bounds = this.boundsOfShapeIds(this.editor.getSelectedShapeIds());
|
|
160
|
+
return bounds ? this.zoomToBounds(bounds, options) : false;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
private boundsOfShapeIds(ids: Iterable<ShapeRecord["id"]>): Box | null {
|
|
164
|
+
const points: Vec[] = [];
|
|
165
|
+
for (const id of ids) {
|
|
166
|
+
const bounds = this.editor.getShapePageBounds(id);
|
|
167
|
+
if (bounds && isValidBox(bounds)) {
|
|
168
|
+
points.push(
|
|
169
|
+
new Vec(bounds.minX, bounds.minY),
|
|
170
|
+
new Vec(bounds.maxX, bounds.maxY),
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
if (points.length === 0) {
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
const box = Box.FromPoints(points);
|
|
178
|
+
return isValidBox(box) ? box : null;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
private getAlignableEntries(): AlignableEntry[] {
|
|
182
|
+
const pageId = this.editor.getCurrentPageId();
|
|
183
|
+
const entries: AlignableEntry[] = [];
|
|
184
|
+
for (const id of this.editor.getSelectedShapeIds()) {
|
|
185
|
+
const shape = this.editor.getShape(id);
|
|
186
|
+
if (!shape || shape.isLocked || shape.parentId !== pageId) {
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
const bounds = this.editor.getShapePageBounds(id);
|
|
190
|
+
if (bounds && isValidBox(bounds)) {
|
|
191
|
+
entries.push({ shape, bounds });
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return entries;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import type { ChangeSource } from "@land/store";
|
|
2
|
+
import type { PageId, SessionRecord, ShapeId } from "../../schema/records";
|
|
3
|
+
import type { Editor } from "../Editor";
|
|
4
|
+
import { areShapeIdArraysEqual } from "../editorComputedHelpers";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Selection-set writes for the editor: which shapes are selected per page. These
|
|
8
|
+
* are cohesive session-state mutations carved out of the Editor facade so the
|
|
9
|
+
* editor can stay a thinner delegating surface. Selection geometry and the
|
|
10
|
+
* rotation/flip transforms live elsewhere (selection.ts /
|
|
11
|
+
* editorSelectionTransforms.ts); this manager only owns the selected-id sets.
|
|
12
|
+
* The Editor exposes thin wrappers that forward here.
|
|
13
|
+
*/
|
|
14
|
+
export class SelectionManager {
|
|
15
|
+
constructor(private readonly editor: Editor) {}
|
|
16
|
+
|
|
17
|
+
setSelectedShapeIds(
|
|
18
|
+
ids: ShapeId[],
|
|
19
|
+
options?: { pageId?: PageId; source?: ChangeSource },
|
|
20
|
+
): void {
|
|
21
|
+
const editor = this.editor;
|
|
22
|
+
const pageId = options?.pageId ?? editor.getCurrentPageId();
|
|
23
|
+
const source = options?.source ?? "user";
|
|
24
|
+
const uniqueIds = Array.from(
|
|
25
|
+
new Set(
|
|
26
|
+
ids.filter((id) => {
|
|
27
|
+
return (
|
|
28
|
+
editor.getShapePageId(id) === pageId && !editor.isShapeHidden(id)
|
|
29
|
+
);
|
|
30
|
+
}),
|
|
31
|
+
),
|
|
32
|
+
);
|
|
33
|
+
const session = editor.sessionSignal.get();
|
|
34
|
+
const currentIds = session.pageSelectedShapeIdsById[pageId] ?? [];
|
|
35
|
+
|
|
36
|
+
if (editor.getShapeEditingSession() && !editor.completeEditing(source)) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (areShapeIdArraysEqual(currentIds, uniqueIds)) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
editor.setBindingPreview(null);
|
|
45
|
+
editor.setSnapIndicators(null);
|
|
46
|
+
editor.updateSession(
|
|
47
|
+
{
|
|
48
|
+
pageSelectedShapeIdsById: {
|
|
49
|
+
...session.pageSelectedShapeIdsById,
|
|
50
|
+
[pageId]: uniqueIds,
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
source,
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
removeShapeIdsFromSelection(
|
|
58
|
+
ids: ShapeId[],
|
|
59
|
+
source: ChangeSource = "system",
|
|
60
|
+
): void {
|
|
61
|
+
if (ids.length === 0) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const editor = this.editor;
|
|
66
|
+
const removedIds = new Set(ids);
|
|
67
|
+
const session = editor.sessionSignal.get();
|
|
68
|
+
const nextSelectionByPage: SessionRecord["pageSelectedShapeIdsById"] = {};
|
|
69
|
+
|
|
70
|
+
for (const page of editor.getPages()) {
|
|
71
|
+
nextSelectionByPage[page.id] = (
|
|
72
|
+
session.pageSelectedShapeIdsById[page.id] ?? []
|
|
73
|
+
).filter((shapeId) => !removedIds.has(shapeId));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
editor.updateSession(
|
|
77
|
+
{ pageSelectedShapeIdsById: nextSelectionByPage },
|
|
78
|
+
source,
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
selectAllCurrentPage(source: ChangeSource = "user"): void {
|
|
83
|
+
const editor = this.editor;
|
|
84
|
+
const currentPageId = editor.getCurrentPageId();
|
|
85
|
+
const shapeIds = editor
|
|
86
|
+
.getCurrentPageShapeIds()
|
|
87
|
+
.map((shapeId) => editor.getShape(shapeId))
|
|
88
|
+
.filter(
|
|
89
|
+
(shape) =>
|
|
90
|
+
!!shape &&
|
|
91
|
+
shape.parentId === currentPageId &&
|
|
92
|
+
!editor.isShapeHidden(shape.id),
|
|
93
|
+
)
|
|
94
|
+
.map((shape) => shape!.id);
|
|
95
|
+
|
|
96
|
+
editor.commands.setSelectedShapeIds(shapeIds, { source });
|
|
97
|
+
}
|
|
98
|
+
}
|