@hyperframes/studio 0.7.54 → 0.7.56
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/dist/assets/{index-uBY329wb.js → index-BWnOxAiH.js} +1 -1
- package/dist/assets/{index-CMHYjEZ5.js → index-C4csZims.js} +1 -1
- package/dist/assets/index-CmVCjZjp.js +423 -0
- package/dist/assets/index-D-GyYi2d.css +1 -0
- package/dist/{chunk-SOTCF4DF.js → chunk-5QSIMBEJ.js} +2 -1
- package/dist/chunk-5QSIMBEJ.js.map +1 -0
- package/dist/{domEditingLayers-2ECJK24D.js → domEditingLayers-6LQGKPOI.js} +2 -2
- package/dist/index.d.ts +146 -144
- package/dist/index.html +2 -2
- package/dist/index.js +40582 -37803
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
- package/src/App.tsx +106 -96
- package/src/components/EditorShell.tsx +253 -0
- package/src/components/StudioGlobalDragOverlay.tsx +1 -3
- package/src/components/StudioLeftSidebar.tsx +13 -3
- package/src/components/StudioRightPanel.tsx +8 -3
- package/src/components/TimelineToolbar.test.tsx +2 -2
- package/src/components/TimelineToolbar.tsx +179 -121
- package/src/components/editor/CanvasContextMenu.test.tsx +115 -0
- package/src/components/editor/CanvasContextMenu.tsx +198 -0
- package/src/components/editor/DomEditCropHandles.test.tsx +74 -3
- package/src/components/editor/DomEditCropHandles.tsx +64 -29
- package/src/components/editor/DomEditOverlay.test.ts +245 -279
- package/src/components/editor/DomEditOverlay.tsx +158 -126
- package/src/components/editor/DomEditOverlayHover.test.tsx +90 -0
- package/src/components/editor/DomEditRotateHandle.tsx +38 -18
- package/src/components/editor/DomEditSelectionChrome.test.tsx +80 -0
- package/src/components/editor/DomEditSelectionChrome.tsx +257 -0
- package/src/components/editor/LayersPanel.test.ts +66 -2
- package/src/components/editor/LayersPanel.tsx +43 -1
- package/src/components/editor/OffCanvasIndicators.test.tsx +34 -0
- package/src/components/editor/OffCanvasIndicators.tsx +49 -10
- package/src/components/editor/SnapGuideOverlay.tsx +28 -23
- package/src/components/editor/SnapToolbar.test.tsx +0 -1
- package/src/components/editor/anchoredResizeCommitFeedsOffset.test.ts +214 -0
- package/src/components/editor/anchoredResizeReleaseShift.test.ts +208 -0
- package/src/components/editor/canvasContextMenuZOrder.test.ts +435 -0
- package/src/components/editor/canvasContextMenuZOrder.ts +352 -0
- package/src/components/editor/domEditNudge.test.ts +80 -0
- package/src/components/editor/domEditNudge.ts +44 -0
- package/src/components/editor/domEditOverlayGeometry.test.ts +167 -1
- package/src/components/editor/domEditOverlayGeometry.ts +280 -20
- package/src/components/editor/domEditOverlayGestures.ts +44 -41
- package/src/components/editor/domEditOverlayStartGesture.ts +62 -2
- package/src/components/editor/domEditResizeLocal.test.ts +131 -0
- package/src/components/editor/domEditResizeLocal.ts +125 -0
- package/src/components/editor/domEditingDom.ts +1 -1
- package/src/components/editor/manualEditsDomPatches.test.ts +47 -0
- package/src/components/editor/manualOffsetDrag.ts +55 -12
- package/src/components/editor/offCanvasIndicatorGeometry.ts +31 -10
- package/src/components/editor/offCanvasIndicatorRefresh.test.tsx +25 -1
- package/src/components/editor/resizeDraft.ts +108 -0
- package/src/components/editor/snapEngine.test.ts +12 -78
- package/src/components/editor/snapEngine.ts +13 -53
- package/src/components/editor/useDomEditNudge.test.tsx +228 -0
- package/src/components/editor/useDomEditNudge.ts +254 -0
- package/src/components/editor/useDomEditOverlayGestures.ts +115 -116
- package/src/components/editor/useDomEditOverlayRects.ts +9 -2
- package/src/components/nle/AssetPreviewOverlay.tsx +147 -0
- package/src/components/nle/NLEContext.test.ts +144 -0
- package/src/components/nle/NLEContext.tsx +328 -0
- package/src/components/nle/NLEPreview.tsx +1 -1
- package/src/components/nle/PreviewOverlays.tsx +240 -0
- package/src/components/nle/PreviewPane.tsx +163 -0
- package/src/components/nle/TimelinePane.test.ts +60 -0
- package/src/components/nle/TimelinePane.tsx +293 -0
- package/src/components/nle/TimelineResizeDivider.tsx +8 -2
- package/src/components/nle/useCompositionStack.test.tsx +44 -0
- package/src/components/nle/useCompositionStack.ts +17 -9
- package/src/components/nle/useTimelineEditCallbacks.ts +217 -0
- package/src/components/sidebar/AssetCard.tsx +326 -0
- package/src/components/sidebar/AssetContextMenu.tsx +15 -30
- package/src/components/sidebar/AssetsTab.test.ts +96 -0
- package/src/components/sidebar/AssetsTab.tsx +71 -206
- package/src/components/sidebar/AudioRow.tsx +39 -2
- package/src/components/sidebar/BlocksTab.tsx +8 -1
- package/src/components/sidebar/LeftSidebar.tsx +4 -1
- package/src/components/sidebar/assetHelpers.ts +29 -0
- package/src/contexts/DomEditContext.tsx +9 -0
- package/src/contexts/StudioContext.tsx +9 -8
- package/src/contexts/TimelineEditContext.tsx +3 -7
- package/src/hooks/deleteSelectedKeyframes.ts +35 -0
- package/src/hooks/domEditCommitTypes.ts +12 -1
- package/src/hooks/domSelectionTestHarness.ts +14 -0
- package/src/hooks/gestureTransaction.test.ts +311 -0
- package/src/hooks/gestureTransaction.ts +199 -0
- package/src/hooks/gsapDragCommit.test.ts +141 -41
- package/src/hooks/gsapDragCommit.ts +64 -74
- package/src/hooks/gsapDragStaticSetHelpers.ts +9 -21
- package/src/hooks/gsapResizeIntercept.test.ts +69 -0
- package/src/hooks/gsapResizeIntercept.ts +38 -10
- package/src/hooks/gsapRuntimeBridge.test.ts +49 -13
- package/src/hooks/gsapRuntimeBridge.ts +4 -4
- package/src/hooks/gsapScriptCommitTypes.ts +16 -5
- package/src/hooks/gsapShared.test.ts +19 -1
- package/src/hooks/gsapShared.ts +14 -0
- package/src/hooks/patchDocumentRootDuration.test.ts +71 -0
- package/src/hooks/timelineEditingGsap.ts +14 -0
- package/src/hooks/timelineEditingHelpers.test.ts +34 -1
- package/src/hooks/timelineEditingHelpers.ts +22 -48
- package/src/hooks/timelineMoveAdapter.test.ts +77 -0
- package/src/hooks/timelineMoveAdapter.ts +37 -0
- package/src/hooks/useAnimatedPropertyCommit.test.tsx +36 -8
- package/src/hooks/useAnimatedPropertyCommit.ts +31 -26
- package/src/hooks/useAppHotkeys.ts +16 -25
- package/src/hooks/useBlockHandlers.ts +55 -27
- package/src/hooks/useContextMenuDismiss.ts +31 -7
- package/src/hooks/useDomEditCommits.test.tsx +154 -1
- package/src/hooks/useDomEditCommits.ts +100 -3
- package/src/hooks/useDomEditPositionPatchCommit.ts +2 -0
- package/src/hooks/useDomEditSession.ts +2 -2
- package/src/hooks/useDomEditTextCommits.test.tsx +135 -0
- package/src/hooks/useDomEditTextCommits.ts +8 -3
- package/src/hooks/useDomEditWiring.ts +7 -9
- package/src/hooks/useDomGeometryCommits.ts +17 -2
- package/src/hooks/useDomSelection.test.ts +2 -36
- package/src/hooks/useDomSelection.ts +81 -51
- package/src/hooks/useDomSelectionSelectionGuards.test.ts +276 -0
- package/src/hooks/useElementLifecycleOps.test.tsx +258 -0
- package/src/hooks/useElementLifecycleOps.ts +40 -71
- package/src/hooks/useEnableKeyframes.test.ts +142 -1
- package/src/hooks/useEnableKeyframes.ts +73 -29
- package/src/hooks/useGestureCommit.test.tsx +130 -0
- package/src/hooks/useGestureCommit.ts +32 -8
- package/src/hooks/useGroupCommits.ts +7 -28
- package/src/hooks/useGsapAwareEditing.test.tsx +239 -0
- package/src/hooks/useGsapAwareEditing.ts +115 -20
- package/src/hooks/useGsapKeyframeOps.test.tsx +59 -0
- package/src/hooks/useGsapKeyframeOps.ts +38 -10
- package/src/hooks/useGsapScriptCommits.test.tsx +40 -1
- package/src/hooks/useGsapScriptCommits.ts +134 -50
- package/src/hooks/useGsapSelectionHandlers.ts +49 -16
- package/src/hooks/useMusicBeatAnalysis.ts +72 -36
- package/src/hooks/usePersistentEditHistory.test.ts +33 -0
- package/src/hooks/usePersistentEditHistory.ts +92 -58
- package/src/hooks/usePreviewPersistence.ts +25 -7
- package/src/hooks/useRazorSplit.history.test.tsx +303 -0
- package/src/hooks/useRazorSplit.test.ts +269 -0
- package/src/hooks/useRazorSplit.testHelpers.ts +64 -0
- package/src/hooks/useRazorSplit.ts +107 -36
- package/src/hooks/useRenderClipContent.ts +24 -6
- package/src/hooks/useStudioContextValue.ts +15 -5
- package/src/hooks/useStudioUrlState.ts +2 -4
- package/src/hooks/useTimelineEditing.test.tsx +20 -29
- package/src/hooks/useTimelineEditingTypes.ts +6 -0
- package/src/hooks/useTimelineGroupEditing.ts +18 -4
- package/src/index.ts +1 -1
- package/src/player/components/ImageThumbnail.test.tsx +173 -0
- package/src/player/components/ImageThumbnail.tsx +160 -0
- package/src/player/components/PlayheadIndicator.tsx +50 -5
- package/src/player/components/Timeline.test.ts +102 -19
- package/src/player/components/Timeline.tsx +194 -194
- package/src/player/components/TimelineCanvas.tsx +207 -521
- package/src/player/components/TimelineClip.tsx +4 -1
- package/src/player/components/TimelineClipDiamonds.tsx +9 -40
- package/src/player/components/TimelineEmptyState.tsx +3 -1
- package/src/player/components/TimelineLanes.tsx +487 -0
- package/src/player/components/TimelineOverlays.tsx +122 -0
- package/src/player/components/VideoThumbnail.test.tsx +152 -0
- package/src/player/components/VideoThumbnail.tsx +28 -7
- package/src/player/components/thumbnailUtils.test.ts +127 -0
- package/src/player/components/thumbnailUtils.ts +54 -0
- package/src/player/components/timelineCallbacks.ts +18 -18
- package/src/player/components/timelineClipChildren.tsx +39 -0
- package/src/player/components/timelineClipDragCommit.test.ts +943 -0
- package/src/player/components/timelineClipDragCommit.ts +401 -0
- package/src/player/components/timelineClipDragPreview.test.ts +144 -0
- package/src/player/components/timelineClipDragPreview.ts +323 -38
- package/src/player/components/timelineClipDragTypes.ts +65 -0
- package/src/player/components/timelineCollision.test.ts +477 -0
- package/src/player/components/timelineCollision.ts +263 -0
- package/src/player/components/timelineDragDrop.ts +69 -60
- package/src/player/components/timelineEditCapabilities.ts +52 -0
- package/src/player/components/timelineEditing.test.ts +1 -1
- package/src/player/components/timelineEditing.ts +76 -59
- package/src/player/components/timelineGroupEditing.test.ts +126 -0
- package/src/player/components/timelineGroupEditing.ts +133 -0
- package/src/player/components/timelineGroupResizeCommit.ts +54 -0
- package/src/player/components/timelineLayout.test.ts +95 -23
- package/src/player/components/timelineLayout.ts +120 -61
- package/src/player/components/timelineMarquee.test.ts +197 -0
- package/src/player/components/timelineMarquee.ts +112 -0
- package/src/player/components/timelineMultiDragPreview.test.ts +127 -0
- package/src/player/components/timelineMultiDragPreview.ts +106 -0
- package/src/player/components/timelineOptimisticRevision.ts +42 -0
- package/src/player/components/timelineSnapping.test.ts +134 -0
- package/src/player/components/timelineSnapping.ts +110 -0
- package/src/player/components/timelineStackingSync.test.ts +244 -0
- package/src/player/components/timelineStackingSync.ts +331 -0
- package/src/player/components/timelineTheme.ts +6 -1
- package/src/player/components/timelineZones.test.ts +425 -0
- package/src/player/components/timelineZones.ts +198 -0
- package/src/player/components/timelineZoom.test.ts +67 -0
- package/src/player/components/timelineZoom.ts +51 -0
- package/src/player/components/useResolvedTimelineEditCallbacks.ts +25 -3
- package/src/player/components/useTimelineClipDrag.resize.test.tsx +241 -0
- package/src/player/components/useTimelineClipDrag.ts +380 -386
- package/src/player/components/useTimelineEditPinning.ts +121 -0
- package/src/player/components/useTimelineGeometry.ts +129 -0
- package/src/player/components/useTimelinePlayhead.ts +4 -16
- package/src/player/components/useTimelineRangeSelection.ts +341 -50
- package/src/player/components/useTimelineStackingSync.test.tsx +67 -0
- package/src/player/components/useTimelineStackingSync.ts +83 -0
- package/src/player/hooks/useExpandedTimelineElements.ts +0 -3
- package/src/player/hooks/usePlaybackKeyboard.ts +5 -0
- package/src/player/hooks/useTimelinePlayer.ts +48 -53
- package/src/player/hooks/useTimelinePlayerLoop.ts +85 -0
- package/src/player/hooks/useTimelineSyncCallbacks.test.ts +219 -0
- package/src/player/hooks/useTimelineSyncCallbacks.ts +104 -4
- package/src/player/index.ts +3 -2
- package/src/player/lib/time.test.ts +19 -19
- package/src/player/lib/time.ts +6 -2
- package/src/player/lib/timelineDOM.test.ts +25 -98
- package/src/player/lib/timelineDOM.ts +12 -81
- package/src/player/lib/timelineElementHelpers.ts +54 -5
- package/src/player/lib/timelineIframeHelpers.ts +2 -0
- package/src/player/store/playerStore.ts +62 -3
- package/src/styles/studio.css +27 -0
- package/src/utils/assetClickBehavior.test.ts +104 -0
- package/src/utils/assetClickBehavior.ts +75 -0
- package/src/utils/assetPreviewStore.ts +33 -0
- package/src/utils/blockInstaller.ts +10 -2
- package/src/utils/canvasNudgeGate.test.ts +31 -0
- package/src/utils/canvasNudgeGate.ts +32 -0
- package/src/utils/editHistory.test.ts +35 -0
- package/src/utils/gsapSoftReload.test.ts +119 -1
- package/src/utils/gsapSoftReload.ts +153 -0
- package/src/utils/mediaTypes.ts +3 -2
- package/src/utils/resizeDebug.ts +55 -0
- package/src/utils/rootDuration.test.ts +90 -1
- package/src/utils/rootDuration.ts +76 -13
- package/src/utils/sdkCutover.ts +4 -0
- package/src/utils/studioFileHistory.ts +37 -7
- package/src/utils/studioHelpers.ts +62 -0
- package/src/utils/studioPreviewHelpers.test.ts +65 -8
- package/src/utils/studioPreviewHelpers.ts +10 -0
- package/src/utils/studioTelemetry.ts +4 -1
- package/src/utils/studioUiPreferences.test.ts +38 -0
- package/src/utils/studioUiPreferences.ts +27 -0
- package/src/utils/studioUrlState.test.ts +0 -1
- package/src/utils/timelineAssetDrop.ts +62 -0
- package/src/utils/timelineDiscovery.ts +0 -17
- package/src/utils/timelineInspector.test.ts +121 -0
- package/src/utils/timelineInspector.ts +32 -1
- package/dist/assets/index-Dq7FEg0K.css +0 -1
- package/dist/assets/index-pRhCpGPz.js +0 -423
- package/dist/chunk-SOTCF4DF.js.map +0 -1
- package/src/components/StudioPreviewArea.tsx +0 -500
- package/src/components/nle/NLELayout.test.ts +0 -12
- package/src/components/nle/NLELayout.tsx +0 -591
- package/src/player/components/TimelineLayerGutter.tsx +0 -61
- package/src/player/components/TimelineSelectionOverlays.tsx +0 -55
- package/src/player/components/timelineMarqueeSelection.test.ts +0 -87
- package/src/player/components/timelineSnapTargets.test.ts +0 -144
- package/src/player/components/timelineSnapTargets.ts +0 -164
- package/src/player/components/useTimelineClipDrag.test.tsx +0 -506
- package/src/player/components/useTimelineClipGroupDrag.ts +0 -417
- package/src/player/components/useTimelineMarqueeSelection.test.tsx +0 -231
- package/src/player/components/useTimelineMarqueeSelection.ts +0 -276
- package/src/utils/timelineDiscovery.test.ts +0 -90
- /package/dist/{domEditingLayers-2ECJK24D.js.map → domEditingLayers-6LQGKPOI.js.map} +0 -0
|
@@ -11,10 +11,10 @@ import {
|
|
|
11
11
|
hasDomEditRotationChanged,
|
|
12
12
|
resolveDomEditCoordinateScale,
|
|
13
13
|
resolveDomEditGroupOverlayRect,
|
|
14
|
-
resolveDomEditResizeGesture,
|
|
15
14
|
resolveDomEditRotationGesture,
|
|
16
15
|
} from "./DomEditOverlay";
|
|
17
16
|
import type { DomEditSelection } from "./domEditing";
|
|
17
|
+
import { resolveResizeCenterAnchorOffset } from "./domEditOverlayGestures";
|
|
18
18
|
|
|
19
19
|
// React 19 warns unless the test environment opts into act().
|
|
20
20
|
globalThis.IS_REACT_ACT_ENVIRONMENT = true;
|
|
@@ -84,21 +84,37 @@ vi.mock("./useDomEditOverlayRects", async () => {
|
|
|
84
84
|
};
|
|
85
85
|
});
|
|
86
86
|
|
|
87
|
+
const previewHelperSpies = vi.hoisted(() => ({
|
|
88
|
+
getPreviewTargetFromPointer: vi.fn<() => HTMLElement | null>(() => null),
|
|
89
|
+
}));
|
|
90
|
+
|
|
91
|
+
vi.mock("../../utils/studioPreviewHelpers", async () => {
|
|
92
|
+
const actual = await vi.importActual<typeof import("../../utils/studioPreviewHelpers")>(
|
|
93
|
+
"../../utils/studioPreviewHelpers",
|
|
94
|
+
);
|
|
95
|
+
return {
|
|
96
|
+
...actual,
|
|
97
|
+
getPreviewTargetFromPointer: previewHelperSpies.getPreviewTargetFromPointer,
|
|
98
|
+
};
|
|
99
|
+
});
|
|
100
|
+
|
|
87
101
|
vi.mock("./domEditOverlayGeometry", async () => {
|
|
88
102
|
const actual = await vi.importActual<typeof import("./domEditOverlayGeometry")>(
|
|
89
103
|
"./domEditOverlayGeometry",
|
|
90
104
|
);
|
|
91
105
|
|
|
106
|
+
const stubRect = {
|
|
107
|
+
left: 24,
|
|
108
|
+
top: 36,
|
|
109
|
+
width: 180,
|
|
110
|
+
height: 72,
|
|
111
|
+
editScaleX: 1,
|
|
112
|
+
editScaleY: 1,
|
|
113
|
+
};
|
|
92
114
|
return {
|
|
93
115
|
...actual,
|
|
94
|
-
toOverlayRect: () =>
|
|
95
|
-
|
|
96
|
-
top: 36,
|
|
97
|
-
width: 180,
|
|
98
|
-
height: 72,
|
|
99
|
-
editScaleX: 1,
|
|
100
|
-
editScaleY: 1,
|
|
101
|
-
}),
|
|
116
|
+
toOverlayRect: () => stubRect,
|
|
117
|
+
orientedOverlayRect: () => stubRect,
|
|
102
118
|
};
|
|
103
119
|
});
|
|
104
120
|
|
|
@@ -126,6 +142,103 @@ function createOverlayProps(args: {
|
|
|
126
142
|
};
|
|
127
143
|
}
|
|
128
144
|
|
|
145
|
+
/**
|
|
146
|
+
* Stub element-level getBoundingClientRect to a fixed 800×450 rect (happy-dom
|
|
147
|
+
* returns all-zeros for unlaid-out elements, which gates the RAF compRect
|
|
148
|
+
* update). Returns a restore function to call in teardown.
|
|
149
|
+
*/
|
|
150
|
+
function stubViewportRect(): () => void {
|
|
151
|
+
const original = Element.prototype.getBoundingClientRect;
|
|
152
|
+
Element.prototype.getBoundingClientRect = function (): DOMRect {
|
|
153
|
+
return {
|
|
154
|
+
left: 0,
|
|
155
|
+
top: 0,
|
|
156
|
+
right: 800,
|
|
157
|
+
bottom: 450,
|
|
158
|
+
width: 800,
|
|
159
|
+
height: 450,
|
|
160
|
+
x: 0,
|
|
161
|
+
y: 0,
|
|
162
|
+
toJSON: () => ({}),
|
|
163
|
+
};
|
|
164
|
+
};
|
|
165
|
+
return () => {
|
|
166
|
+
Element.prototype.getBoundingClientRect = original;
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Flush the mount's RAF ticks so the compRect update lands. Two animation-frame
|
|
172
|
+
* ticks: the first scheduled by useMountEffect's update(), the second by
|
|
173
|
+
* update()'s tail recursion.
|
|
174
|
+
*/
|
|
175
|
+
async function flushOverlayRaf(): Promise<void> {
|
|
176
|
+
await act(async () => {
|
|
177
|
+
await new Promise<void>((resolve) => {
|
|
178
|
+
requestAnimationFrame(() => requestAnimationFrame(() => resolve()));
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/** A fully-populated DomEditSelection with per-test overrides (capabilities are
|
|
184
|
+
* merged so a test can flip a single flag without restating the whole set). */
|
|
185
|
+
function makeDomEditSelection(
|
|
186
|
+
overrides: Partial<DomEditSelection> = {},
|
|
187
|
+
capabilityOverrides: Partial<DomEditSelection["capabilities"]> = {},
|
|
188
|
+
): DomEditSelection {
|
|
189
|
+
const base: DomEditSelection = {
|
|
190
|
+
element: document.createElement("div"),
|
|
191
|
+
id: "hero-title",
|
|
192
|
+
selector: ".hero-title",
|
|
193
|
+
selectorIndex: 0,
|
|
194
|
+
sourceFile: "index.html",
|
|
195
|
+
tagName: "div",
|
|
196
|
+
label: "Hero Title",
|
|
197
|
+
textContent: "Hello",
|
|
198
|
+
textFields: [],
|
|
199
|
+
capabilities: {
|
|
200
|
+
canEditText: true,
|
|
201
|
+
canEditLayout: true,
|
|
202
|
+
canMove: true,
|
|
203
|
+
canApplyManualOffset: true,
|
|
204
|
+
canApplyManualSize: false,
|
|
205
|
+
canApplyManualRotation: false,
|
|
206
|
+
canAdjustOpacity: true,
|
|
207
|
+
canAdjustFill: true,
|
|
208
|
+
canAdjustBorderRadius: true,
|
|
209
|
+
canAdjustStroke: true,
|
|
210
|
+
canAdjustShadow: true,
|
|
211
|
+
canAdjustZIndex: true,
|
|
212
|
+
},
|
|
213
|
+
computedStyle: {
|
|
214
|
+
display: "block",
|
|
215
|
+
position: "absolute",
|
|
216
|
+
},
|
|
217
|
+
};
|
|
218
|
+
return {
|
|
219
|
+
...base,
|
|
220
|
+
...overrides,
|
|
221
|
+
capabilities: { ...base.capabilities, ...capabilityOverrides },
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/** Query the composition-canvas overlay and assert it mounted. */
|
|
226
|
+
function getOverlay(host: HTMLElement): HTMLDivElement {
|
|
227
|
+
const overlay = host.querySelector<HTMLDivElement>('[aria-label="Composition canvas"]');
|
|
228
|
+
expect(overlay).toBeTruthy();
|
|
229
|
+
if (!overlay) throw new Error("Expected composition canvas overlay");
|
|
230
|
+
return overlay;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/** Dispatch a left-button pointerdown at (clientX, clientY) inside act(). */
|
|
234
|
+
function dispatchOverlayPointerDown(target: Element, clientX = 120, clientY = 80): void {
|
|
235
|
+
act(() => {
|
|
236
|
+
target.dispatchEvent(
|
|
237
|
+
new PointerEvent("pointerdown", { bubbles: true, button: 0, clientX, clientY }),
|
|
238
|
+
);
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
|
|
129
242
|
describe("focusDomEditOverlayElement", () => {
|
|
130
243
|
it("focuses the canvas overlay without scrolling", () => {
|
|
131
244
|
const calls: Array<FocusOptions | undefined> = [];
|
|
@@ -144,41 +257,84 @@ describe("DomEditOverlay", () => {
|
|
|
144
257
|
gestureSpies.onPointerMove.mockClear();
|
|
145
258
|
gestureSpies.onPointerUp.mockClear();
|
|
146
259
|
gestureSpies.clearPointerState.mockClear();
|
|
260
|
+
previewHelperSpies.getPreviewTargetFromPointer.mockReset();
|
|
261
|
+
previewHelperSpies.getPreviewTargetFromPointer.mockReturnValue(null);
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
it("selects on the first click over an element even before a hover is resolved", async () => {
|
|
265
|
+
// Regression: this used to start a marquee whenever hoverSelectionRef was null.
|
|
266
|
+
// The RAF hover loop populates that ref ASYNCHRONOUSLY, so a genuine first
|
|
267
|
+
// click over an element read null and was misread as empty canvas — the
|
|
268
|
+
// marquee swallowed the selecting onMouseDown, so nothing selected until the
|
|
269
|
+
// SECOND click. With a synchronous pointer hit-test finding an element, the
|
|
270
|
+
// marquee must NOT start and onCanvasMouseDown must fire on the first click.
|
|
271
|
+
const restoreRect = stubViewportRect();
|
|
272
|
+
const originalPointerCapture = HTMLDivElement.prototype.setPointerCapture;
|
|
273
|
+
HTMLDivElement.prototype.setPointerCapture = () => {};
|
|
274
|
+
|
|
275
|
+
// An element IS under the pointer, but no hover has been resolved yet.
|
|
276
|
+
previewHelperSpies.getPreviewTargetFromPointer.mockReturnValue(document.createElement("div"));
|
|
277
|
+
|
|
278
|
+
const host = document.createElement("div");
|
|
279
|
+
document.body.append(host);
|
|
280
|
+
const root = createRoot(host);
|
|
281
|
+
const iframeRef = { current: document.createElement("iframe") as HTMLIFrameElement | null };
|
|
282
|
+
const onCanvasMouseDown = vi.fn();
|
|
283
|
+
const onMarqueeSelect = vi.fn();
|
|
284
|
+
|
|
285
|
+
function Harness() {
|
|
286
|
+
return React.createElement(DomEditOverlay, {
|
|
287
|
+
...createOverlayProps({
|
|
288
|
+
iframeRef,
|
|
289
|
+
selection: null,
|
|
290
|
+
hoverSelection: null,
|
|
291
|
+
onSelectionChange: () => {},
|
|
292
|
+
}),
|
|
293
|
+
onCanvasMouseDown,
|
|
294
|
+
onMarqueeSelect,
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
act(() => {
|
|
299
|
+
root.render(React.createElement(Harness));
|
|
300
|
+
});
|
|
301
|
+
await flushOverlayRaf();
|
|
302
|
+
|
|
303
|
+
const overlay = getOverlay(host);
|
|
304
|
+
|
|
305
|
+
act(() => {
|
|
306
|
+
overlay.dispatchEvent(
|
|
307
|
+
new PointerEvent("pointerdown", { bubbles: true, button: 0, clientX: 120, clientY: 80 }),
|
|
308
|
+
);
|
|
309
|
+
overlay.dispatchEvent(
|
|
310
|
+
new MouseEvent("mousedown", { bubbles: true, button: 0, clientX: 120, clientY: 80 }),
|
|
311
|
+
);
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
// No marquee started; the click reached the selecting mouse-down handler.
|
|
315
|
+
expect(onMarqueeSelect).not.toHaveBeenCalled();
|
|
316
|
+
expect(onCanvasMouseDown).toHaveBeenCalledTimes(1);
|
|
317
|
+
|
|
318
|
+
act(() => {
|
|
319
|
+
root.unmount();
|
|
320
|
+
});
|
|
321
|
+
HTMLDivElement.prototype.setPointerCapture = originalPointerCapture;
|
|
322
|
+
restoreRect();
|
|
323
|
+
host.remove();
|
|
147
324
|
});
|
|
148
325
|
|
|
149
326
|
it("does not start a drag from a stale hover target on canvas pointer-down", () => {
|
|
150
327
|
const host = document.createElement("div");
|
|
151
328
|
document.body.append(host);
|
|
152
329
|
const root = createRoot(host);
|
|
153
|
-
const selection
|
|
154
|
-
element: document.createElement("div"),
|
|
330
|
+
const selection = makeDomEditSelection({
|
|
155
331
|
id: "cta-label",
|
|
156
332
|
selector: ".cta-label",
|
|
157
|
-
selectorIndex: 0,
|
|
158
|
-
sourceFile: "index.html",
|
|
159
333
|
tagName: "span",
|
|
160
334
|
label: "CTA Label",
|
|
161
335
|
textContent: "Add to basket",
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
canEditText: true,
|
|
165
|
-
canEditLayout: true,
|
|
166
|
-
canMove: true,
|
|
167
|
-
canApplyManualOffset: true,
|
|
168
|
-
canApplyManualSize: false,
|
|
169
|
-
canApplyManualRotation: false,
|
|
170
|
-
canAdjustOpacity: true,
|
|
171
|
-
canAdjustFill: true,
|
|
172
|
-
canAdjustBorderRadius: true,
|
|
173
|
-
canAdjustStroke: true,
|
|
174
|
-
canAdjustShadow: true,
|
|
175
|
-
canAdjustZIndex: true,
|
|
176
|
-
},
|
|
177
|
-
computedStyle: {
|
|
178
|
-
display: "inline",
|
|
179
|
-
position: "static",
|
|
180
|
-
},
|
|
181
|
-
};
|
|
336
|
+
computedStyle: { display: "inline", position: "static" },
|
|
337
|
+
});
|
|
182
338
|
|
|
183
339
|
let currentSelection: DomEditSelection | null = null;
|
|
184
340
|
const iframeRef = { current: document.createElement("iframe") as HTMLIFrameElement | null };
|
|
@@ -202,19 +358,9 @@ describe("DomEditOverlay", () => {
|
|
|
202
358
|
root.render(React.createElement(Harness));
|
|
203
359
|
});
|
|
204
360
|
|
|
205
|
-
const overlay = host
|
|
206
|
-
expect(overlay).toBeTruthy();
|
|
361
|
+
const overlay = getOverlay(host);
|
|
207
362
|
|
|
208
|
-
|
|
209
|
-
overlay.dispatchEvent(
|
|
210
|
-
new PointerEvent("pointerdown", {
|
|
211
|
-
bubbles: true,
|
|
212
|
-
button: 0,
|
|
213
|
-
clientX: 120,
|
|
214
|
-
clientY: 80,
|
|
215
|
-
}),
|
|
216
|
-
);
|
|
217
|
-
});
|
|
363
|
+
dispatchOverlayPointerDown(overlay);
|
|
218
364
|
|
|
219
365
|
expect(gestureSpies.startGesture).not.toHaveBeenCalled();
|
|
220
366
|
expect(currentSelection).toBe(null);
|
|
@@ -233,53 +379,12 @@ describe("DomEditOverlay", () => {
|
|
|
233
379
|
// box (and other bounded UI) behind `compRect.width > 0` (added in the
|
|
234
380
|
// keyframes PR a468550f). Stub element-level getBoundingClientRect for
|
|
235
381
|
// the test so the RAF compRect update produces a real width.
|
|
236
|
-
const
|
|
237
|
-
Element.prototype.getBoundingClientRect = function (): DOMRect {
|
|
238
|
-
return {
|
|
239
|
-
left: 0,
|
|
240
|
-
top: 0,
|
|
241
|
-
right: 800,
|
|
242
|
-
bottom: 450,
|
|
243
|
-
width: 800,
|
|
244
|
-
height: 450,
|
|
245
|
-
x: 0,
|
|
246
|
-
y: 0,
|
|
247
|
-
toJSON: () => ({}),
|
|
248
|
-
};
|
|
249
|
-
};
|
|
382
|
+
const restoreRect = stubViewportRect();
|
|
250
383
|
|
|
251
384
|
const host = document.createElement("div");
|
|
252
385
|
document.body.append(host);
|
|
253
386
|
const root = createRoot(host);
|
|
254
|
-
const selection
|
|
255
|
-
element: document.createElement("div"),
|
|
256
|
-
id: "hero-title",
|
|
257
|
-
selector: ".hero-title",
|
|
258
|
-
selectorIndex: 0,
|
|
259
|
-
sourceFile: "index.html",
|
|
260
|
-
tagName: "div",
|
|
261
|
-
label: "Hero Title",
|
|
262
|
-
textContent: "Hello",
|
|
263
|
-
textFields: [],
|
|
264
|
-
capabilities: {
|
|
265
|
-
canEditText: true,
|
|
266
|
-
canEditLayout: true,
|
|
267
|
-
canMove: true,
|
|
268
|
-
canApplyManualOffset: true,
|
|
269
|
-
canApplyManualSize: false,
|
|
270
|
-
canApplyManualRotation: false,
|
|
271
|
-
canAdjustOpacity: true,
|
|
272
|
-
canAdjustFill: true,
|
|
273
|
-
canAdjustBorderRadius: true,
|
|
274
|
-
canAdjustStroke: true,
|
|
275
|
-
canAdjustShadow: true,
|
|
276
|
-
canAdjustZIndex: true,
|
|
277
|
-
},
|
|
278
|
-
computedStyle: {
|
|
279
|
-
display: "block",
|
|
280
|
-
position: "absolute",
|
|
281
|
-
},
|
|
282
|
-
};
|
|
387
|
+
const selection = makeDomEditSelection();
|
|
283
388
|
|
|
284
389
|
let currentSelection: DomEditSelection | null = selection;
|
|
285
390
|
const iframeRef = { current: document.createElement("iframe") as HTMLIFrameElement | null };
|
|
@@ -307,30 +412,16 @@ describe("DomEditOverlay", () => {
|
|
|
307
412
|
// Flush the mount's RAF tick so the compRect update lands before the
|
|
308
413
|
// pointer-down. Two animation-frame ticks: the first scheduled by
|
|
309
414
|
// useMountEffect's update(), the second by update()'s tail recursion.
|
|
310
|
-
await
|
|
311
|
-
await new Promise<void>((resolve) => {
|
|
312
|
-
requestAnimationFrame(() => requestAnimationFrame(() => resolve()));
|
|
313
|
-
});
|
|
314
|
-
});
|
|
415
|
+
await flushOverlayRaf();
|
|
315
416
|
|
|
316
|
-
|
|
317
|
-
expect(overlay).toBeTruthy();
|
|
417
|
+
getOverlay(host);
|
|
318
418
|
|
|
319
419
|
const selectionBox = host.querySelector(
|
|
320
420
|
'[data-dom-edit-selection-box="true"]',
|
|
321
421
|
) as HTMLDivElement;
|
|
322
422
|
expect(selectionBox).toBeTruthy();
|
|
323
423
|
|
|
324
|
-
|
|
325
|
-
selectionBox.dispatchEvent(
|
|
326
|
-
new PointerEvent("pointerdown", {
|
|
327
|
-
bubbles: true,
|
|
328
|
-
button: 0,
|
|
329
|
-
clientX: 120,
|
|
330
|
-
clientY: 80,
|
|
331
|
-
}),
|
|
332
|
-
);
|
|
333
|
-
});
|
|
424
|
+
dispatchOverlayPointerDown(selectionBox);
|
|
334
425
|
|
|
335
426
|
expect(currentSelection).toBe(selection);
|
|
336
427
|
expect(gestureSpies.startGesture).toHaveBeenCalledWith(
|
|
@@ -342,58 +433,17 @@ describe("DomEditOverlay", () => {
|
|
|
342
433
|
root.unmount();
|
|
343
434
|
});
|
|
344
435
|
HTMLDivElement.prototype.setPointerCapture = originalPointerCapture;
|
|
345
|
-
|
|
436
|
+
restoreRect();
|
|
346
437
|
host.remove();
|
|
347
438
|
});
|
|
348
439
|
|
|
349
440
|
it("passes the tracked hover selection when clicking the existing selection box", async () => {
|
|
350
|
-
const
|
|
351
|
-
Element.prototype.getBoundingClientRect = function (): DOMRect {
|
|
352
|
-
return {
|
|
353
|
-
left: 0,
|
|
354
|
-
top: 0,
|
|
355
|
-
right: 800,
|
|
356
|
-
bottom: 450,
|
|
357
|
-
width: 800,
|
|
358
|
-
height: 450,
|
|
359
|
-
x: 0,
|
|
360
|
-
y: 0,
|
|
361
|
-
toJSON: () => ({}),
|
|
362
|
-
};
|
|
363
|
-
};
|
|
441
|
+
const restoreRect = stubViewportRect();
|
|
364
442
|
|
|
365
443
|
const host = document.createElement("div");
|
|
366
444
|
document.body.append(host);
|
|
367
445
|
const root = createRoot(host);
|
|
368
|
-
const selection:
|
|
369
|
-
element: document.createElement("div"),
|
|
370
|
-
id: "hero-title",
|
|
371
|
-
selector: ".hero-title",
|
|
372
|
-
selectorIndex: 0,
|
|
373
|
-
sourceFile: "index.html",
|
|
374
|
-
tagName: "div",
|
|
375
|
-
label: "Hero Title",
|
|
376
|
-
textContent: "Hello",
|
|
377
|
-
textFields: [],
|
|
378
|
-
capabilities: {
|
|
379
|
-
canEditText: true,
|
|
380
|
-
canEditLayout: true,
|
|
381
|
-
canMove: false,
|
|
382
|
-
canApplyManualOffset: false,
|
|
383
|
-
canApplyManualSize: false,
|
|
384
|
-
canApplyManualRotation: false,
|
|
385
|
-
canAdjustOpacity: true,
|
|
386
|
-
canAdjustFill: true,
|
|
387
|
-
canAdjustBorderRadius: true,
|
|
388
|
-
canAdjustStroke: true,
|
|
389
|
-
canAdjustShadow: true,
|
|
390
|
-
canAdjustZIndex: true,
|
|
391
|
-
},
|
|
392
|
-
computedStyle: {
|
|
393
|
-
display: "block",
|
|
394
|
-
position: "absolute",
|
|
395
|
-
},
|
|
396
|
-
};
|
|
446
|
+
const selection = makeDomEditSelection({}, { canMove: false, canApplyManualOffset: false });
|
|
397
447
|
const hoverSelection: DomEditSelection = { ...selection, id: "hovered-sibling" };
|
|
398
448
|
const onCanvasMouseDown = vi.fn();
|
|
399
449
|
const iframeRef = { current: document.createElement("iframe") as HTMLIFrameElement | null };
|
|
@@ -414,11 +464,7 @@ describe("DomEditOverlay", () => {
|
|
|
414
464
|
root.render(React.createElement(Harness));
|
|
415
465
|
});
|
|
416
466
|
|
|
417
|
-
await
|
|
418
|
-
await new Promise<void>((resolve) => {
|
|
419
|
-
requestAnimationFrame(() => requestAnimationFrame(() => resolve()));
|
|
420
|
-
});
|
|
421
|
-
});
|
|
467
|
+
await flushOverlayRaf();
|
|
422
468
|
|
|
423
469
|
const selectionBox = host.querySelector(
|
|
424
470
|
'[data-dom-edit-selection-box="true"]',
|
|
@@ -437,7 +483,7 @@ describe("DomEditOverlay", () => {
|
|
|
437
483
|
act(() => {
|
|
438
484
|
root.unmount();
|
|
439
485
|
});
|
|
440
|
-
|
|
486
|
+
restoreRect();
|
|
441
487
|
host.remove();
|
|
442
488
|
});
|
|
443
489
|
});
|
|
@@ -513,130 +559,10 @@ describe("filterNestedDomEditGroupItems", () => {
|
|
|
513
559
|
});
|
|
514
560
|
});
|
|
515
561
|
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
originWidth: 240,
|
|
521
|
-
originHeight: 120,
|
|
522
|
-
actualWidth: 240,
|
|
523
|
-
actualHeight: 120,
|
|
524
|
-
scaleX: 1,
|
|
525
|
-
scaleY: 1,
|
|
526
|
-
dx: 30,
|
|
527
|
-
dy: 12,
|
|
528
|
-
uniform: false,
|
|
529
|
-
}),
|
|
530
|
-
).toEqual({
|
|
531
|
-
overlayWidth: 270,
|
|
532
|
-
overlayHeight: 132,
|
|
533
|
-
width: 270,
|
|
534
|
-
height: 132,
|
|
535
|
-
});
|
|
536
|
-
});
|
|
537
|
-
|
|
538
|
-
it("divides the cursor delta by the element's content scale (rescaled element)", () => {
|
|
539
|
-
// Element renders at 2x via a GSAP scale: a 30px cursor delta must grow the
|
|
540
|
-
// CSS box by only 15px so the RENDERED box tracks the pointer 1:1.
|
|
541
|
-
const next = resolveDomEditResizeGesture({
|
|
542
|
-
originWidth: 480, // 240 css x 2 content scale (overlay px at editScale 1)
|
|
543
|
-
originHeight: 240,
|
|
544
|
-
actualWidth: 240,
|
|
545
|
-
actualHeight: 120,
|
|
546
|
-
scaleX: 1,
|
|
547
|
-
scaleY: 1,
|
|
548
|
-
contentScaleX: 2,
|
|
549
|
-
contentScaleY: 2,
|
|
550
|
-
dx: 30,
|
|
551
|
-
dy: 12,
|
|
552
|
-
uniform: false,
|
|
553
|
-
});
|
|
554
|
-
expect(next.width).toBe(255);
|
|
555
|
-
expect(next.height).toBe(126);
|
|
556
|
-
// The overlay box keeps tracking the raw cursor.
|
|
557
|
-
expect(next.overlayWidth).toBe(510);
|
|
558
|
-
expect(next.overlayHeight).toBe(252);
|
|
559
|
-
});
|
|
560
|
-
|
|
561
|
-
it("treats a missing/invalid content scale as 1 (unscaled element)", () => {
|
|
562
|
-
const next = resolveDomEditResizeGesture({
|
|
563
|
-
originWidth: 240,
|
|
564
|
-
originHeight: 120,
|
|
565
|
-
actualWidth: 240,
|
|
566
|
-
actualHeight: 120,
|
|
567
|
-
scaleX: 1,
|
|
568
|
-
scaleY: 1,
|
|
569
|
-
contentScaleX: 0,
|
|
570
|
-
contentScaleY: Number.NaN,
|
|
571
|
-
dx: 30,
|
|
572
|
-
dy: 12,
|
|
573
|
-
uniform: false,
|
|
574
|
-
});
|
|
575
|
-
expect(next.width).toBe(270);
|
|
576
|
-
expect(next.height).toBe(132);
|
|
577
|
-
});
|
|
578
|
-
|
|
579
|
-
it("snaps width and height to the same value when Shift is held", () => {
|
|
580
|
-
expect(
|
|
581
|
-
resolveDomEditResizeGesture({
|
|
582
|
-
originWidth: 240,
|
|
583
|
-
originHeight: 120,
|
|
584
|
-
actualWidth: 240,
|
|
585
|
-
actualHeight: 120,
|
|
586
|
-
scaleX: 1,
|
|
587
|
-
scaleY: 1,
|
|
588
|
-
dx: 30,
|
|
589
|
-
dy: 12,
|
|
590
|
-
uniform: true,
|
|
591
|
-
}),
|
|
592
|
-
).toEqual({
|
|
593
|
-
overlayWidth: 270,
|
|
594
|
-
overlayHeight: 270,
|
|
595
|
-
width: 270,
|
|
596
|
-
height: 270,
|
|
597
|
-
});
|
|
598
|
-
});
|
|
599
|
-
|
|
600
|
-
it("uses the dominant pointer delta for uniform shrink", () => {
|
|
601
|
-
expect(
|
|
602
|
-
resolveDomEditResizeGesture({
|
|
603
|
-
originWidth: 300,
|
|
604
|
-
originHeight: 180,
|
|
605
|
-
actualWidth: 300,
|
|
606
|
-
actualHeight: 180,
|
|
607
|
-
scaleX: 1,
|
|
608
|
-
scaleY: 1,
|
|
609
|
-
dx: 8,
|
|
610
|
-
dy: -40,
|
|
611
|
-
uniform: true,
|
|
612
|
-
}),
|
|
613
|
-
).toMatchObject({
|
|
614
|
-
width: 260,
|
|
615
|
-
height: 260,
|
|
616
|
-
});
|
|
617
|
-
});
|
|
618
|
-
|
|
619
|
-
it("writes source-local dimensions when the edited source is scaled down in master view", () => {
|
|
620
|
-
expect(
|
|
621
|
-
resolveDomEditResizeGesture({
|
|
622
|
-
originWidth: 100,
|
|
623
|
-
originHeight: 50,
|
|
624
|
-
actualWidth: 400,
|
|
625
|
-
actualHeight: 200,
|
|
626
|
-
scaleX: 0.25,
|
|
627
|
-
scaleY: 0.25,
|
|
628
|
-
dx: 25,
|
|
629
|
-
dy: 10,
|
|
630
|
-
uniform: false,
|
|
631
|
-
}),
|
|
632
|
-
).toEqual({
|
|
633
|
-
overlayWidth: 125,
|
|
634
|
-
overlayHeight: 60,
|
|
635
|
-
width: 500,
|
|
636
|
-
height: 240,
|
|
637
|
-
});
|
|
638
|
-
});
|
|
639
|
-
});
|
|
562
|
+
// Note: the resize SIZE math moved from the AABB screen-space
|
|
563
|
+
// resolveDomEditResizeGesture (removed) to the local-space (OBB) model in
|
|
564
|
+
// domEditResizeLocal.ts — see domEditResizeLocal.test.ts, which re-covers the
|
|
565
|
+
// independent-axis, aspect-lock, and scaled-master-view cases plus rotated axes.
|
|
640
566
|
|
|
641
567
|
describe("resolveDomEditRotationGesture", () => {
|
|
642
568
|
it("rotates by the pointer angle around the element center", () => {
|
|
@@ -701,3 +627,43 @@ describe("resolveDomEditRotationGesture", () => {
|
|
|
701
627
|
expect(hasDomEditRotationChanged(0, 0)).toBe(false);
|
|
702
628
|
});
|
|
703
629
|
});
|
|
630
|
+
|
|
631
|
+
// resolveResizeCenterAnchorOffset is the UNROTATED (AABB) fallback used only when
|
|
632
|
+
// the element's real transformed corners can't be measured. Center-anchored: a
|
|
633
|
+
// width/height change grows the box from its top-left, drifting the center by half
|
|
634
|
+
// the size change per axis, so the pin translates back by that half-delta. It is
|
|
635
|
+
// handle-independent — all four corners scale about the same center.
|
|
636
|
+
describe("resolveResizeCenterAnchorOffset", () => {
|
|
637
|
+
it("grow: translates back by half the size change on both axes", () => {
|
|
638
|
+
expect(
|
|
639
|
+
resolveResizeCenterAnchorOffset({
|
|
640
|
+
originWidth: 200,
|
|
641
|
+
originHeight: 100,
|
|
642
|
+
overlayWidth: 230,
|
|
643
|
+
overlayHeight: 112,
|
|
644
|
+
}),
|
|
645
|
+
).toEqual({ dx: -15, dy: -6 });
|
|
646
|
+
});
|
|
647
|
+
|
|
648
|
+
it("shrink: translates forward by half the (positive) size change", () => {
|
|
649
|
+
expect(
|
|
650
|
+
resolveResizeCenterAnchorOffset({
|
|
651
|
+
originWidth: 200,
|
|
652
|
+
originHeight: 100,
|
|
653
|
+
overlayWidth: 160,
|
|
654
|
+
overlayHeight: 80,
|
|
655
|
+
}),
|
|
656
|
+
).toEqual({ dx: 20, dy: 10 });
|
|
657
|
+
});
|
|
658
|
+
|
|
659
|
+
it("no size change: zero offset", () => {
|
|
660
|
+
expect(
|
|
661
|
+
resolveResizeCenterAnchorOffset({
|
|
662
|
+
originWidth: 200,
|
|
663
|
+
originHeight: 100,
|
|
664
|
+
overlayWidth: 200,
|
|
665
|
+
overlayHeight: 100,
|
|
666
|
+
}),
|
|
667
|
+
).toEqual({ dx: 0, dy: 0 });
|
|
668
|
+
});
|
|
669
|
+
});
|