@editframe/elements 0.30.2-beta.0 → 0.31.0-beta.1
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/EF_FRAMEGEN.d.ts +5 -0
- package/dist/EF_FRAMEGEN.js +20 -4
- package/dist/EF_FRAMEGEN.js.map +1 -1
- package/dist/EF_INTERACTIVE.js.map +1 -1
- package/dist/_virtual/rolldown_runtime.js +27 -0
- package/dist/canvas/EFCanvas.d.ts +311 -0
- package/dist/canvas/EFCanvas.js +1089 -0
- package/dist/canvas/EFCanvas.js.map +1 -0
- package/dist/canvas/EFCanvasItem.d.ts +55 -0
- package/dist/canvas/EFCanvasItem.js +72 -0
- package/dist/canvas/EFCanvasItem.js.map +1 -0
- package/dist/canvas/api/CanvasAPI.d.ts +115 -0
- package/dist/canvas/api/CanvasAPI.js +182 -0
- package/dist/canvas/api/CanvasAPI.js.map +1 -0
- package/dist/canvas/api/types.d.ts +42 -0
- package/dist/canvas/coordinateTransform.js +90 -0
- package/dist/canvas/coordinateTransform.js.map +1 -0
- package/dist/canvas/getElementBounds.js +40 -0
- package/dist/canvas/getElementBounds.js.map +1 -0
- package/dist/canvas/overlays/SelectionOverlay.js +265 -0
- package/dist/canvas/overlays/SelectionOverlay.js.map +1 -0
- package/dist/canvas/overlays/overlayState.js +153 -0
- package/dist/canvas/overlays/overlayState.js.map +1 -0
- package/dist/canvas/selection/SelectionController.js +105 -0
- package/dist/canvas/selection/SelectionController.js.map +1 -0
- package/dist/canvas/selection/SelectionModel.d.ts +98 -0
- package/dist/canvas/selection/SelectionModel.js +229 -0
- package/dist/canvas/selection/SelectionModel.js.map +1 -0
- package/dist/canvas/selection/selectionContext.d.ts +31 -0
- package/dist/canvas/selection/selectionContext.js +12 -0
- package/dist/canvas/selection/selectionContext.js.map +1 -0
- package/dist/elements/ContainerInfo.d.ts +29 -0
- package/dist/elements/ContainerInfo.js +30 -0
- package/dist/elements/ContainerInfo.js.map +1 -0
- package/dist/elements/EFAudio.d.ts +13 -3
- package/dist/elements/EFAudio.js +64 -10
- package/dist/elements/EFAudio.js.map +1 -1
- package/dist/elements/EFCaptions.d.ts +18 -16
- package/dist/elements/EFCaptions.js +110 -19
- package/dist/elements/EFCaptions.js.map +1 -1
- package/dist/elements/EFImage.d.ts +16 -6
- package/dist/elements/EFImage.js +79 -9
- package/dist/elements/EFImage.js.map +1 -1
- package/dist/elements/EFMedia/AssetIdMediaEngine.js +51 -4
- package/dist/elements/EFMedia/AssetIdMediaEngine.js.map +1 -1
- package/dist/elements/EFMedia/AssetMediaEngine.js +125 -52
- package/dist/elements/EFMedia/AssetMediaEngine.js.map +1 -1
- package/dist/elements/EFMedia/BaseMediaEngine.js +24 -6
- package/dist/elements/EFMedia/BaseMediaEngine.js.map +1 -1
- package/dist/elements/EFMedia/JitMediaEngine.js +12 -8
- package/dist/elements/EFMedia/JitMediaEngine.js.map +1 -1
- package/dist/elements/EFMedia/audioTasks/makeAudioBufferTask.js +46 -7
- package/dist/elements/EFMedia/audioTasks/makeAudioBufferTask.js.map +1 -1
- package/dist/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.js +98 -73
- package/dist/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.js.map +1 -1
- package/dist/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.js +28 -5
- package/dist/elements/EFMedia/audioTasks/makeAudioInitSegmentFetchTask.js.map +1 -1
- package/dist/elements/EFMedia/audioTasks/makeAudioInputTask.js +18 -6
- package/dist/elements/EFMedia/audioTasks/makeAudioInputTask.js.map +1 -1
- package/dist/elements/EFMedia/audioTasks/makeAudioSeekTask.js +8 -2
- package/dist/elements/EFMedia/audioTasks/makeAudioSeekTask.js.map +1 -1
- package/dist/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.js +31 -6
- package/dist/elements/EFMedia/audioTasks/makeAudioSegmentFetchTask.js.map +1 -1
- package/dist/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.js +28 -5
- package/dist/elements/EFMedia/audioTasks/makeAudioSegmentIdTask.js.map +1 -1
- package/dist/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.js +97 -72
- package/dist/elements/EFMedia/audioTasks/makeAudioTimeDomainAnalysisTask.js.map +1 -1
- package/dist/elements/EFMedia/shared/AudioSpanUtils.js +3 -1
- package/dist/elements/EFMedia/shared/AudioSpanUtils.js.map +1 -1
- package/dist/elements/EFMedia/shared/BufferUtils.js +1 -1
- package/dist/elements/EFMedia/shared/BufferUtils.js.map +1 -1
- package/dist/elements/EFMedia/shared/ThumbnailExtractor.js +25 -14
- package/dist/elements/EFMedia/shared/ThumbnailExtractor.js.map +1 -1
- package/dist/elements/EFMedia/tasks/makeMediaEngineTask.js +47 -16
- package/dist/elements/EFMedia/tasks/makeMediaEngineTask.js.map +1 -1
- package/dist/elements/EFMedia/videoTasks/MainVideoInputCache.js +37 -19
- package/dist/elements/EFMedia/videoTasks/MainVideoInputCache.js.map +1 -1
- package/dist/elements/EFMedia/videoTasks/ScrubInputCache.js +65 -21
- package/dist/elements/EFMedia/videoTasks/ScrubInputCache.js.map +1 -1
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoBufferTask.js +8 -3
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoBufferTask.js.map +1 -1
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoInitSegmentFetchTask.js +32 -9
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoInitSegmentFetchTask.js.map +1 -1
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoInputTask.js +33 -10
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoInputTask.js.map +1 -1
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoSeekTask.js +23 -8
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoSeekTask.js.map +1 -1
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoSegmentFetchTask.js +34 -10
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoSegmentFetchTask.js.map +1 -1
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoSegmentIdTask.js +31 -8
- package/dist/elements/EFMedia/videoTasks/makeScrubVideoSegmentIdTask.js.map +1 -1
- package/dist/elements/EFMedia/videoTasks/makeUnifiedVideoSeekTask.js +31 -114
- package/dist/elements/EFMedia/videoTasks/makeUnifiedVideoSeekTask.js.map +1 -1
- package/dist/elements/EFMedia/videoTasks/makeVideoBufferTask.js +44 -8
- package/dist/elements/EFMedia/videoTasks/makeVideoBufferTask.js.map +1 -1
- package/dist/elements/EFMedia.d.ts +18 -7
- package/dist/elements/EFMedia.js +23 -3
- package/dist/elements/EFMedia.js.map +1 -1
- package/dist/elements/EFPanZoom.d.ts +96 -0
- package/dist/elements/EFPanZoom.js +290 -0
- package/dist/elements/EFPanZoom.js.map +1 -0
- package/dist/elements/EFSourceMixin.js +7 -6
- package/dist/elements/EFSourceMixin.js.map +1 -1
- package/dist/elements/EFSurface.d.ts +6 -6
- package/dist/elements/EFSurface.js +7 -2
- package/dist/elements/EFSurface.js.map +1 -1
- package/dist/elements/EFTemporal.d.ts +2 -1
- package/dist/elements/EFTemporal.js +192 -71
- package/dist/elements/EFTemporal.js.map +1 -1
- package/dist/elements/EFText.d.ts +5 -4
- package/dist/elements/EFText.js +102 -13
- package/dist/elements/EFText.js.map +1 -1
- package/dist/elements/EFTextSegment.d.ts +32 -6
- package/dist/elements/EFTextSegment.js +53 -15
- package/dist/elements/EFTextSegment.js.map +1 -1
- package/dist/elements/EFThumbnailStrip.d.ts +129 -56
- package/dist/elements/EFThumbnailStrip.js +605 -359
- package/dist/elements/EFThumbnailStrip.js.map +1 -1
- package/dist/elements/EFTimegroup.d.ts +233 -25
- package/dist/elements/EFTimegroup.js +865 -144
- package/dist/elements/EFTimegroup.js.map +1 -1
- package/dist/elements/EFVideo.d.ts +42 -5
- package/dist/elements/EFVideo.js +165 -11
- package/dist/elements/EFVideo.js.map +1 -1
- package/dist/elements/EFWaveform.d.ts +6 -6
- package/dist/elements/EFWaveform.js +2 -1
- package/dist/elements/EFWaveform.js.map +1 -1
- package/dist/elements/ElementPositionInfo.d.ts +35 -0
- package/dist/elements/ElementPositionInfo.js +49 -0
- package/dist/elements/ElementPositionInfo.js.map +1 -0
- package/dist/elements/FetchMixin.js +16 -1
- package/dist/elements/FetchMixin.js.map +1 -1
- package/dist/elements/SessionThumbnailCache.js +154 -0
- package/dist/elements/SessionThumbnailCache.js.map +1 -0
- package/dist/elements/TargetController.js +3 -1
- package/dist/elements/TargetController.js.map +1 -1
- package/dist/elements/TimegroupController.js +9 -3
- package/dist/elements/TimegroupController.js.map +1 -1
- package/dist/elements/findRootTemporal.js +30 -0
- package/dist/elements/findRootTemporal.js.map +1 -0
- package/dist/elements/renderTemporalAudio.js +18 -5
- package/dist/elements/renderTemporalAudio.js.map +1 -1
- package/dist/elements/updateAnimations.js +171 -28
- package/dist/elements/updateAnimations.js.map +1 -1
- package/dist/getRenderInfo.d.ts +2 -2
- package/dist/gui/ContextMixin.js +4 -2
- package/dist/gui/ContextMixin.js.map +1 -1
- package/dist/gui/Controllable.js +74 -1
- package/dist/gui/Controllable.js.map +1 -1
- package/dist/gui/EFActiveRootTemporal.d.ts +50 -0
- package/dist/gui/EFActiveRootTemporal.js +94 -0
- package/dist/gui/EFActiveRootTemporal.js.map +1 -0
- package/dist/gui/EFConfiguration.d.ts +7 -1
- package/dist/gui/EFConfiguration.js.map +1 -1
- package/dist/gui/EFControls.d.ts +2 -2
- package/dist/gui/EFControls.js +109 -13
- package/dist/gui/EFControls.js.map +1 -1
- package/dist/gui/EFDial.d.ts +4 -4
- package/dist/gui/EFFilmstrip.d.ts +11 -214
- package/dist/gui/EFFilmstrip.js +53 -1152
- package/dist/gui/EFFilmstrip.js.map +1 -1
- package/dist/gui/EFFitScale.d.ts +3 -3
- package/dist/gui/EFFitScale.js +39 -12
- package/dist/gui/EFFitScale.js.map +1 -1
- package/dist/gui/EFFocusOverlay.d.ts +4 -4
- package/dist/gui/EFOverlayItem.d.ts +48 -0
- package/dist/gui/EFOverlayItem.js +97 -0
- package/dist/gui/EFOverlayItem.js.map +1 -0
- package/dist/gui/EFOverlayLayer.d.ts +70 -0
- package/dist/gui/EFOverlayLayer.js +104 -0
- package/dist/gui/EFOverlayLayer.js.map +1 -0
- package/dist/gui/EFPause.d.ts +4 -4
- package/dist/gui/EFPlay.d.ts +4 -4
- package/dist/gui/EFResizableBox.d.ts +12 -16
- package/dist/gui/EFResizableBox.js +109 -451
- package/dist/gui/EFResizableBox.js.map +1 -1
- package/dist/gui/EFScrubber.d.ts +30 -5
- package/dist/gui/EFScrubber.js +224 -31
- package/dist/gui/EFScrubber.js.map +1 -1
- package/dist/gui/EFTimeDisplay.d.ts +4 -4
- package/dist/gui/EFTimeDisplay.js +4 -1
- package/dist/gui/EFTimeDisplay.js.map +1 -1
- package/dist/gui/EFTimelineRuler.d.ts +71 -0
- package/dist/gui/EFTimelineRuler.js +320 -0
- package/dist/gui/EFTimelineRuler.js.map +1 -0
- package/dist/gui/EFToggleLoop.d.ts +4 -4
- package/dist/gui/EFTogglePlay.d.ts +4 -4
- package/dist/gui/EFTransformHandles.d.ts +91 -0
- package/dist/gui/EFTransformHandles.js +393 -0
- package/dist/gui/EFTransformHandles.js.map +1 -0
- package/dist/gui/EFWorkbench.d.ts +178 -0
- package/dist/gui/EFWorkbench.js +2067 -22
- package/dist/gui/EFWorkbench.js.map +1 -1
- package/dist/gui/FitScaleHelpers.d.ts +31 -0
- package/dist/gui/FitScaleHelpers.js +41 -0
- package/dist/gui/FitScaleHelpers.js.map +1 -0
- package/dist/gui/PlaybackController.d.ts +2 -1
- package/dist/gui/PlaybackController.js +46 -15
- package/dist/gui/PlaybackController.js.map +1 -1
- package/dist/gui/TWMixin.js +1 -1
- package/dist/gui/TWMixin.js.map +1 -1
- package/dist/gui/hierarchy/EFHierarchy.d.ts +65 -0
- package/dist/gui/hierarchy/EFHierarchy.js +338 -0
- package/dist/gui/hierarchy/EFHierarchy.js.map +1 -0
- package/dist/gui/hierarchy/EFHierarchyItem.d.ts +118 -0
- package/dist/gui/hierarchy/EFHierarchyItem.js +551 -0
- package/dist/gui/hierarchy/EFHierarchyItem.js.map +1 -0
- package/dist/gui/hierarchy/hierarchyContext.d.ts +38 -0
- package/dist/gui/hierarchy/hierarchyContext.js +8 -0
- package/dist/gui/hierarchy/hierarchyContext.js.map +1 -0
- package/dist/gui/icons.js +34 -0
- package/dist/gui/icons.js.map +1 -0
- package/dist/gui/panZoomTransformContext.js +12 -0
- package/dist/gui/panZoomTransformContext.js.map +1 -0
- package/dist/gui/previewSettingsContext.js +12 -0
- package/dist/gui/previewSettingsContext.js.map +1 -0
- package/dist/gui/timeline/EFTimeline.d.ts +270 -0
- package/dist/gui/timeline/EFTimeline.js +1369 -0
- package/dist/gui/timeline/EFTimeline.js.map +1 -0
- package/dist/gui/timeline/EFTimelineRow.js +374 -0
- package/dist/gui/timeline/EFTimelineRow.js.map +1 -0
- package/dist/gui/timeline/TrimHandles.d.ts +36 -0
- package/dist/gui/timeline/TrimHandles.js +204 -0
- package/dist/gui/timeline/TrimHandles.js.map +1 -0
- package/dist/gui/timeline/flattenHierarchy.js +31 -0
- package/dist/gui/timeline/flattenHierarchy.js.map +1 -0
- package/dist/gui/timeline/timelineStateContext.d.ts +26 -0
- package/dist/gui/timeline/timelineStateContext.js +42 -0
- package/dist/gui/timeline/timelineStateContext.js.map +1 -0
- package/dist/gui/timeline/tracks/AudioTrack.js +264 -0
- package/dist/gui/timeline/tracks/AudioTrack.js.map +1 -0
- package/dist/gui/timeline/tracks/CaptionsTrack.js +595 -0
- package/dist/gui/timeline/tracks/CaptionsTrack.js.map +1 -0
- package/dist/gui/timeline/tracks/HTMLTrack.js +19 -0
- package/dist/gui/timeline/tracks/HTMLTrack.js.map +1 -0
- package/dist/gui/timeline/tracks/ImageTrack.js +53 -0
- package/dist/gui/timeline/tracks/ImageTrack.js.map +1 -0
- package/dist/gui/timeline/tracks/TextTrack.js +250 -0
- package/dist/gui/timeline/tracks/TextTrack.js.map +1 -0
- package/dist/gui/timeline/tracks/TimegroupTrack.js +143 -0
- package/dist/gui/timeline/tracks/TimegroupTrack.js.map +1 -0
- package/dist/gui/timeline/tracks/TrackItem.js +269 -0
- package/dist/gui/timeline/tracks/TrackItem.js.map +1 -0
- package/dist/gui/timeline/tracks/VideoTrack.js +265 -0
- package/dist/gui/timeline/tracks/VideoTrack.js.map +1 -0
- package/dist/gui/timeline/tracks/WaveformTrack.js +19 -0
- package/dist/gui/timeline/tracks/WaveformTrack.js.map +1 -0
- package/dist/gui/timeline/tracks/ensureTrackItemInit.js +1 -0
- package/dist/gui/timeline/tracks/preloadTracks.js +9 -0
- package/dist/gui/timeline/tracks/renderTrackChildren.js +119 -0
- package/dist/gui/timeline/tracks/renderTrackChildren.js.map +1 -0
- package/dist/gui/timeline/tracks/waveformUtils.js +80 -0
- package/dist/gui/timeline/tracks/waveformUtils.js.map +1 -0
- package/dist/gui/transformCalculations.js +217 -0
- package/dist/gui/transformCalculations.js.map +1 -0
- package/dist/gui/transformUtils.d.ts +37 -0
- package/dist/gui/transformUtils.js +77 -0
- package/dist/gui/transformUtils.js.map +1 -0
- package/dist/gui/tree/EFTree.d.ts +59 -0
- package/dist/gui/tree/EFTree.js +174 -0
- package/dist/gui/tree/EFTree.js.map +1 -0
- package/dist/gui/tree/EFTreeItem.d.ts +38 -0
- package/dist/gui/tree/EFTreeItem.js +146 -0
- package/dist/gui/tree/EFTreeItem.js.map +1 -0
- package/dist/gui/tree/treeContext.d.ts +60 -0
- package/dist/gui/tree/treeContext.js +23 -0
- package/dist/gui/tree/treeContext.js.map +1 -0
- package/dist/index.d.ts +32 -8
- package/dist/index.js +30 -6
- package/dist/index.js.map +1 -1
- package/dist/node_modules/react/cjs/react-jsx-runtime.development.js +688 -0
- package/dist/node_modules/react/cjs/react-jsx-runtime.development.js.map +1 -0
- package/dist/node_modules/react/cjs/react.development.js +1521 -0
- package/dist/node_modules/react/cjs/react.development.js.map +1 -0
- package/dist/node_modules/react/index.js +13 -0
- package/dist/node_modules/react/index.js.map +1 -0
- package/dist/node_modules/react/jsx-runtime.js +13 -0
- package/dist/node_modules/react/jsx-runtime.js.map +1 -0
- package/dist/preview/AdaptiveResolutionTracker.js +228 -0
- package/dist/preview/AdaptiveResolutionTracker.js.map +1 -0
- package/dist/preview/RenderProfiler.js +135 -0
- package/dist/preview/RenderProfiler.js.map +1 -0
- package/dist/preview/previewSettings.js +131 -0
- package/dist/preview/previewSettings.js.map +1 -0
- package/dist/preview/previewTypes.js +64 -0
- package/dist/preview/previewTypes.js.map +1 -0
- package/dist/preview/renderTimegroupPreview.js +656 -0
- package/dist/preview/renderTimegroupPreview.js.map +1 -0
- package/dist/preview/renderTimegroupToCanvas.d.ts +37 -0
- package/dist/preview/renderTimegroupToCanvas.js +833 -0
- package/dist/preview/renderTimegroupToCanvas.js.map +1 -0
- package/dist/preview/renderTimegroupToVideo.d.ts +39 -0
- package/dist/preview/renderTimegroupToVideo.js +274 -0
- package/dist/preview/renderTimegroupToVideo.js.map +1 -0
- package/dist/preview/renderers.js +16 -0
- package/dist/preview/renderers.js.map +1 -0
- package/dist/preview/statsTrackingStrategy.js +201 -0
- package/dist/preview/statsTrackingStrategy.js.map +1 -0
- package/dist/preview/thumbnailCacheSettings.js +52 -0
- package/dist/preview/thumbnailCacheSettings.js.map +1 -0
- package/dist/preview/workers/WorkerPool.js +178 -0
- package/dist/preview/workers/WorkerPool.js.map +1 -0
- package/dist/preview/workers/encoderWorkerInline.js +103 -0
- package/dist/preview/workers/encoderWorkerInline.js.map +1 -0
- package/dist/sandbox/PlaybackControls.js +10 -0
- package/dist/sandbox/PlaybackControls.js.map +1 -0
- package/dist/sandbox/ScenarioRunner.js +1 -0
- package/dist/sandbox/index.js +2 -0
- package/dist/style.css +71 -67
- package/dist/transcoding/types/index.d.ts +2 -1
- package/dist/transcoding/utils/UrlGenerator.d.ts +6 -1
- package/dist/transcoding/utils/UrlGenerator.js +12 -3
- package/dist/transcoding/utils/UrlGenerator.js.map +1 -1
- package/dist/utils/LRUCache.js +1 -375
- package/dist/utils/LRUCache.js.map +1 -1
- package/dist/utils/frameTime.js +14 -0
- package/dist/utils/frameTime.js.map +1 -0
- package/package.json +3 -3
- package/test/profilingPlugin.ts +223 -0
- package/test/recordReplayProxyPlugin.js +22 -27
- package/test/thumbnail-performance-test.html +116 -0
- package/test/visualRegressionUtils.ts +286 -0
- package/types.json +1 -1
- package/dist/elements/TimegroupController.d.ts +0 -18
- package/dist/msToTimeCode.js +0 -17
- package/dist/msToTimeCode.js.map +0 -1
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
import { __decorate } from "../_virtual/_@oxc-project_runtime@0.94.0/helpers/decorate.js";
|
|
2
|
+
import { panZoomTransformContext } from "./panZoomTransformContext.js";
|
|
3
|
+
import { getCornerPoint, getOppositeCorner } from "./transformUtils.js";
|
|
4
|
+
import { calculateDragBounds, calculateResizeBounds, getResizeHandleCursor } from "./transformCalculations.js";
|
|
5
|
+
import { consume } from "@lit/context";
|
|
6
|
+
import { LitElement, css, html } from "lit";
|
|
7
|
+
import { customElement, property, state } from "lit/decorators.js";
|
|
8
|
+
import { styleMap } from "lit/directives/style-map.js";
|
|
9
|
+
|
|
10
|
+
//#region src/gui/EFTransformHandles.ts
|
|
11
|
+
const DEFAULT_MIN_SIZE = 10;
|
|
12
|
+
let EFTransformHandles = class EFTransformHandles$1 extends LitElement {
|
|
13
|
+
constructor(..._args) {
|
|
14
|
+
super(..._args);
|
|
15
|
+
this.bounds = {
|
|
16
|
+
x: 0,
|
|
17
|
+
y: 0,
|
|
18
|
+
width: 100,
|
|
19
|
+
height: 100
|
|
20
|
+
};
|
|
21
|
+
this.minSize = DEFAULT_MIN_SIZE;
|
|
22
|
+
this.canvasScale = 1;
|
|
23
|
+
this.enableRotation = false;
|
|
24
|
+
this.enableResize = true;
|
|
25
|
+
this.cornersOnly = false;
|
|
26
|
+
this.lockAspectRatio = false;
|
|
27
|
+
this.enableDrag = true;
|
|
28
|
+
this.interactionMode = "idle";
|
|
29
|
+
this.activeResizeHandle = null;
|
|
30
|
+
this.mouseStart = null;
|
|
31
|
+
this.initialBounds = null;
|
|
32
|
+
this.handleMouseDown = (e) => {
|
|
33
|
+
if (!this.enableDrag) return;
|
|
34
|
+
e.stopPropagation();
|
|
35
|
+
this.interactionMode = this.transitionInteractionMode("mousedown-drag");
|
|
36
|
+
this.mouseStart = {
|
|
37
|
+
x: e.clientX,
|
|
38
|
+
y: e.clientY
|
|
39
|
+
};
|
|
40
|
+
this.initialBounds = { ...this.bounds };
|
|
41
|
+
document.addEventListener("mousemove", this.handleMouseMove);
|
|
42
|
+
document.addEventListener("mouseup", this.handleMouseUp);
|
|
43
|
+
};
|
|
44
|
+
this.handleResizeMouseDown = (e, handle) => {
|
|
45
|
+
if (!this.enableResize) return;
|
|
46
|
+
e.stopPropagation();
|
|
47
|
+
e.preventDefault();
|
|
48
|
+
this.interactionMode = this.transitionInteractionMode("mousedown-resize");
|
|
49
|
+
this.activeResizeHandle = handle;
|
|
50
|
+
this.mouseStart = {
|
|
51
|
+
x: e.clientX,
|
|
52
|
+
y: e.clientY
|
|
53
|
+
};
|
|
54
|
+
this.initialBounds = { ...this.bounds };
|
|
55
|
+
document.addEventListener("mousemove", this.handleMouseMove);
|
|
56
|
+
document.addEventListener("mouseup", this.handleMouseUp);
|
|
57
|
+
};
|
|
58
|
+
this.handleRotateMouseDown = (e) => {
|
|
59
|
+
if (!this.enableRotation) return;
|
|
60
|
+
e.stopPropagation();
|
|
61
|
+
this.interactionMode = this.transitionInteractionMode("mousedown-rotate");
|
|
62
|
+
this.mouseStart = {
|
|
63
|
+
x: e.clientX,
|
|
64
|
+
y: e.clientY
|
|
65
|
+
};
|
|
66
|
+
this.initialBounds = { ...this.bounds };
|
|
67
|
+
document.addEventListener("mousemove", this.handleMouseMove);
|
|
68
|
+
document.addEventListener("mouseup", this.handleMouseUp);
|
|
69
|
+
};
|
|
70
|
+
this.handleMouseMove = (e) => {
|
|
71
|
+
if (!this.mouseStart || !this.initialBounds) return;
|
|
72
|
+
const screenDeltaX = e.clientX - this.mouseStart.x;
|
|
73
|
+
const screenDeltaY = e.clientY - this.mouseStart.y;
|
|
74
|
+
switch (this.interactionMode) {
|
|
75
|
+
case "dragging": {
|
|
76
|
+
const zoomScale = this.getZoomScale();
|
|
77
|
+
const initialCanvas = this.screenToCanvas(this.initialBounds);
|
|
78
|
+
const newPosition = calculateDragBounds({
|
|
79
|
+
x: initialCanvas.x,
|
|
80
|
+
y: initialCanvas.y
|
|
81
|
+
}, screenDeltaX, screenDeltaY, zoomScale);
|
|
82
|
+
this.dispatchBoundsChange({
|
|
83
|
+
...newPosition,
|
|
84
|
+
width: initialCanvas.width,
|
|
85
|
+
height: initialCanvas.height,
|
|
86
|
+
rotation: this.initialBounds.rotation
|
|
87
|
+
});
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
case "resizing": {
|
|
91
|
+
if (!this.activeResizeHandle) return;
|
|
92
|
+
const zoomScale = this.getZoomScale();
|
|
93
|
+
const initialCanvas = this.screenToCanvas(this.initialBounds);
|
|
94
|
+
const rotation = this.enableRotation ? this.initialBounds.rotation ?? 0 : 0;
|
|
95
|
+
const oppositeCorner = getOppositeCorner(this.activeResizeHandle);
|
|
96
|
+
const rotationRadians = rotation * Math.PI / 180;
|
|
97
|
+
const fixedCorner = getCornerPoint(initialCanvas.x, initialCanvas.y, initialCanvas.width, initialCanvas.height, rotationRadians, oppositeCorner.x, oppositeCorner.y);
|
|
98
|
+
const newCanvasBounds = calculateResizeBounds({
|
|
99
|
+
width: initialCanvas.width,
|
|
100
|
+
height: initialCanvas.height
|
|
101
|
+
}, {
|
|
102
|
+
x: initialCanvas.x,
|
|
103
|
+
y: initialCanvas.y
|
|
104
|
+
}, fixedCorner, this.activeResizeHandle, screenDeltaX, screenDeltaY, rotation, this.minSize / zoomScale, zoomScale, {
|
|
105
|
+
lockAspectRatio: this.lockAspectRatio || e.shiftKey,
|
|
106
|
+
resizeFromCenter: e.ctrlKey || e.metaKey
|
|
107
|
+
});
|
|
108
|
+
newCanvasBounds.rotation = this.initialBounds.rotation;
|
|
109
|
+
this.dispatchBoundsChange(newCanvasBounds);
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
112
|
+
case "rotating": {
|
|
113
|
+
const overlayRect = this.offsetParent?.getBoundingClientRect() ?? {
|
|
114
|
+
left: 0,
|
|
115
|
+
top: 0
|
|
116
|
+
};
|
|
117
|
+
const centerX = overlayRect.left + this.initialBounds.x + this.initialBounds.width / 2;
|
|
118
|
+
const centerY = overlayRect.top + this.initialBounds.y + this.initialBounds.height / 2;
|
|
119
|
+
const startAngle = Math.atan2(this.mouseStart.y - centerY, this.mouseStart.x - centerX) * (180 / Math.PI) + 90;
|
|
120
|
+
let deltaAngle = Math.atan2(e.clientY - centerY, e.clientX - centerX) * (180 / Math.PI) + 90 - startAngle;
|
|
121
|
+
while (deltaAngle > 180) deltaAngle -= 360;
|
|
122
|
+
while (deltaAngle < -180) deltaAngle += 360;
|
|
123
|
+
let newRotation = (this.initialBounds.rotation ?? 0) + deltaAngle;
|
|
124
|
+
if (this.rotationStep !== void 0 && this.rotationStep > 0) newRotation = Math.round(newRotation / this.rotationStep) * this.rotationStep;
|
|
125
|
+
this.dispatchEvent(new CustomEvent("rotation-change", {
|
|
126
|
+
detail: { rotation: newRotation },
|
|
127
|
+
bubbles: true,
|
|
128
|
+
composed: true
|
|
129
|
+
}));
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
case "idle": break;
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
this.handleMouseUp = () => {
|
|
136
|
+
this.cleanup();
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
static {
|
|
140
|
+
this.styles = css`
|
|
141
|
+
:host {
|
|
142
|
+
display: block;
|
|
143
|
+
position: absolute;
|
|
144
|
+
pointer-events: none;
|
|
145
|
+
}
|
|
146
|
+
.overlay {
|
|
147
|
+
position: absolute;
|
|
148
|
+
border: 2px solid var(--ef-transform-handles-border-color, #3b82f6);
|
|
149
|
+
pointer-events: none;
|
|
150
|
+
}
|
|
151
|
+
.overlay.dragging {
|
|
152
|
+
border-color: var(--ef-transform-handles-dragging-border-color, #2563eb);
|
|
153
|
+
}
|
|
154
|
+
.drag-area {
|
|
155
|
+
position: absolute;
|
|
156
|
+
inset: 0;
|
|
157
|
+
cursor: move;
|
|
158
|
+
pointer-events: none;
|
|
159
|
+
}
|
|
160
|
+
/* Only enable pointer events when actively dragging */
|
|
161
|
+
.drag-area:active {
|
|
162
|
+
pointer-events: auto;
|
|
163
|
+
}
|
|
164
|
+
.handle {
|
|
165
|
+
position: absolute;
|
|
166
|
+
width: 8px;
|
|
167
|
+
height: 8px;
|
|
168
|
+
background: var(--ef-transform-handles-handle-color, white);
|
|
169
|
+
border: 1px solid var(--ef-transform-handles-handle-border-color, #3b82f6);
|
|
170
|
+
pointer-events: auto;
|
|
171
|
+
/* Only capture pointer events, allow wheel events to pass through */
|
|
172
|
+
touch-action: none;
|
|
173
|
+
}
|
|
174
|
+
.handle.nw { top: -4px; left: -4px; }
|
|
175
|
+
.handle.n { top: -4px; left: 50%; transform: translateX(-50%); }
|
|
176
|
+
.handle.ne { top: -4px; right: -4px; }
|
|
177
|
+
.handle.e { top: 50%; right: -4px; transform: translateY(-50%); }
|
|
178
|
+
.handle.se { bottom: -4px; right: -4px; }
|
|
179
|
+
.handle.s { bottom: -4px; left: 50%; transform: translateX(-50%); }
|
|
180
|
+
.handle.sw { bottom: -4px; left: -4px; }
|
|
181
|
+
.handle.w { top: 50%; left: -4px; transform: translateY(-50%); }
|
|
182
|
+
.rotate-handle {
|
|
183
|
+
position: absolute;
|
|
184
|
+
top: -30px;
|
|
185
|
+
left: 50%;
|
|
186
|
+
transform: translateX(-50%);
|
|
187
|
+
cursor: grab;
|
|
188
|
+
pointer-events: auto;
|
|
189
|
+
/* Only capture pointer events, allow wheel events to pass through */
|
|
190
|
+
touch-action: none;
|
|
191
|
+
}
|
|
192
|
+
.rotate-handle-circle {
|
|
193
|
+
width: 24px;
|
|
194
|
+
height: 24px;
|
|
195
|
+
background: var(--ef-transform-handles-rotate-handle-color, #10b981);
|
|
196
|
+
border: 2px solid white;
|
|
197
|
+
border-radius: 50%;
|
|
198
|
+
display: flex;
|
|
199
|
+
align-items: center;
|
|
200
|
+
justify-content: center;
|
|
201
|
+
}
|
|
202
|
+
.rotate-handle-circle span {
|
|
203
|
+
font-size: 12px;
|
|
204
|
+
color: white;
|
|
205
|
+
}
|
|
206
|
+
`;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Single source of truth for zoom scale.
|
|
210
|
+
* Priority: context > prop > 1.0
|
|
211
|
+
*/
|
|
212
|
+
getZoomScale() {
|
|
213
|
+
return this.panZoomTransformFromContext?.scale ?? this.canvasScale ?? 1;
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Convert screen pixel bounds to canvas coordinates.
|
|
217
|
+
*/
|
|
218
|
+
screenToCanvas(bounds) {
|
|
219
|
+
const scale = this.getZoomScale();
|
|
220
|
+
return {
|
|
221
|
+
x: bounds.x / scale,
|
|
222
|
+
y: bounds.y / scale,
|
|
223
|
+
width: bounds.width / scale,
|
|
224
|
+
height: bounds.height / scale,
|
|
225
|
+
rotation: bounds.rotation
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
connectedCallback() {
|
|
229
|
+
super.connectedCallback();
|
|
230
|
+
this.resizeObserver = new ResizeObserver(() => {});
|
|
231
|
+
if (this.offsetParent) this.resizeObserver.observe(this.offsetParent);
|
|
232
|
+
this.addEventListener("wheel", (e) => {
|
|
233
|
+
if (this.interactionMode === "idle") {
|
|
234
|
+
const panZoom = this.closest("ef-pan-zoom");
|
|
235
|
+
if (panZoom) {
|
|
236
|
+
const wheelEvent = new WheelEvent("wheel", {
|
|
237
|
+
bubbles: true,
|
|
238
|
+
cancelable: true,
|
|
239
|
+
clientX: e.clientX,
|
|
240
|
+
clientY: e.clientY,
|
|
241
|
+
deltaX: e.deltaX,
|
|
242
|
+
deltaY: e.deltaY,
|
|
243
|
+
deltaZ: e.deltaZ,
|
|
244
|
+
deltaMode: e.deltaMode,
|
|
245
|
+
ctrlKey: e.ctrlKey,
|
|
246
|
+
metaKey: e.metaKey,
|
|
247
|
+
shiftKey: e.shiftKey,
|
|
248
|
+
altKey: e.altKey
|
|
249
|
+
});
|
|
250
|
+
panZoom.dispatchEvent(wheelEvent);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}, { passive: true });
|
|
254
|
+
}
|
|
255
|
+
disconnectedCallback() {
|
|
256
|
+
super.disconnectedCallback();
|
|
257
|
+
this.resizeObserver?.disconnect();
|
|
258
|
+
this.cleanup();
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Transition interaction mode state machine.
|
|
262
|
+
* Ensures only one mode is active at a time (invariant).
|
|
263
|
+
*/
|
|
264
|
+
transitionInteractionMode(event) {
|
|
265
|
+
if (event === "mouseup") return "idle";
|
|
266
|
+
if (this.interactionMode !== "idle") return this.interactionMode;
|
|
267
|
+
switch (event) {
|
|
268
|
+
case "mousedown-drag": return "dragging";
|
|
269
|
+
case "mousedown-resize": return "resizing";
|
|
270
|
+
case "mousedown-rotate": return "rotating";
|
|
271
|
+
default: return "idle";
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Dispatch bounds change event (one-way data flow).
|
|
276
|
+
* Parent updates element, then reads element and updates handle bounds prop.
|
|
277
|
+
* We don't modify this.bounds directly - always render from prop.
|
|
278
|
+
*/
|
|
279
|
+
dispatchBoundsChange(bounds) {
|
|
280
|
+
this.dispatchEvent(new CustomEvent("bounds-change", {
|
|
281
|
+
detail: { bounds },
|
|
282
|
+
bubbles: true,
|
|
283
|
+
composed: true
|
|
284
|
+
}));
|
|
285
|
+
}
|
|
286
|
+
cleanup() {
|
|
287
|
+
this.interactionMode = this.transitionInteractionMode("mouseup");
|
|
288
|
+
this.activeResizeHandle = null;
|
|
289
|
+
this.mouseStart = null;
|
|
290
|
+
this.initialBounds = null;
|
|
291
|
+
document.removeEventListener("mousemove", this.handleMouseMove);
|
|
292
|
+
document.removeEventListener("mouseup", this.handleMouseUp);
|
|
293
|
+
}
|
|
294
|
+
render() {
|
|
295
|
+
const currentBounds = this.bounds;
|
|
296
|
+
const rotation = this.enableRotation ? currentBounds.rotation ?? 0 : 0;
|
|
297
|
+
const overlayStyles = {
|
|
298
|
+
left: `${currentBounds.x}px`,
|
|
299
|
+
top: `${currentBounds.y}px`,
|
|
300
|
+
width: `${currentBounds.width}px`,
|
|
301
|
+
height: `${currentBounds.height}px`
|
|
302
|
+
};
|
|
303
|
+
if (this.enableRotation && rotation !== 0) {
|
|
304
|
+
overlayStyles.transform = `rotate(${rotation}deg)`;
|
|
305
|
+
overlayStyles.transformOrigin = "center";
|
|
306
|
+
}
|
|
307
|
+
const handles = this.cornersOnly ? [
|
|
308
|
+
"nw",
|
|
309
|
+
"ne",
|
|
310
|
+
"se",
|
|
311
|
+
"sw"
|
|
312
|
+
] : [
|
|
313
|
+
"nw",
|
|
314
|
+
"n",
|
|
315
|
+
"ne",
|
|
316
|
+
"e",
|
|
317
|
+
"se",
|
|
318
|
+
"s",
|
|
319
|
+
"sw",
|
|
320
|
+
"w"
|
|
321
|
+
];
|
|
322
|
+
return html`
|
|
323
|
+
<div
|
|
324
|
+
class="overlay ${this.interactionMode === "dragging" ? "dragging" : ""}"
|
|
325
|
+
style=${styleMap(overlayStyles)}
|
|
326
|
+
>
|
|
327
|
+
${this.enableDrag ? html`
|
|
328
|
+
<div
|
|
329
|
+
class="drag-area"
|
|
330
|
+
@mousedown=${this.handleMouseDown}
|
|
331
|
+
></div>
|
|
332
|
+
` : ""}
|
|
333
|
+
${this.enableResize ? handles.map((handle) => {
|
|
334
|
+
return html`
|
|
335
|
+
<div
|
|
336
|
+
class="handle ${handle}"
|
|
337
|
+
style=${styleMap({ cursor: getResizeHandleCursor(handle, this.enableRotation ? currentBounds.rotation ?? 0 : 0) })}
|
|
338
|
+
@mousedown=${(e) => this.handleResizeMouseDown(e, handle)}
|
|
339
|
+
></div>
|
|
340
|
+
`;
|
|
341
|
+
}) : ""}
|
|
342
|
+
${this.enableRotation ? html`
|
|
343
|
+
<div class="rotate-handle" @mousedown=${this.handleRotateMouseDown}>
|
|
344
|
+
<div class="rotate-handle-circle">
|
|
345
|
+
<span>↻</span>
|
|
346
|
+
</div>
|
|
347
|
+
</div>
|
|
348
|
+
` : ""}
|
|
349
|
+
</div>
|
|
350
|
+
`;
|
|
351
|
+
}
|
|
352
|
+
};
|
|
353
|
+
__decorate([property({ type: Object })], EFTransformHandles.prototype, "bounds", void 0);
|
|
354
|
+
__decorate([property({ type: Number })], EFTransformHandles.prototype, "minSize", void 0);
|
|
355
|
+
__decorate([property({ type: String })], EFTransformHandles.prototype, "target", void 0);
|
|
356
|
+
__decorate([consume({
|
|
357
|
+
context: panZoomTransformContext,
|
|
358
|
+
subscribe: true
|
|
359
|
+
})], EFTransformHandles.prototype, "panZoomTransformFromContext", void 0);
|
|
360
|
+
__decorate([property({
|
|
361
|
+
type: Number,
|
|
362
|
+
attribute: "canvas-scale"
|
|
363
|
+
})], EFTransformHandles.prototype, "canvasScale", void 0);
|
|
364
|
+
__decorate([property({
|
|
365
|
+
type: Boolean,
|
|
366
|
+
attribute: "enable-rotation"
|
|
367
|
+
})], EFTransformHandles.prototype, "enableRotation", void 0);
|
|
368
|
+
__decorate([property({
|
|
369
|
+
type: Boolean,
|
|
370
|
+
attribute: "enable-resize"
|
|
371
|
+
})], EFTransformHandles.prototype, "enableResize", void 0);
|
|
372
|
+
__decorate([property({
|
|
373
|
+
type: Boolean,
|
|
374
|
+
attribute: "corners-only"
|
|
375
|
+
})], EFTransformHandles.prototype, "cornersOnly", void 0);
|
|
376
|
+
__decorate([property({
|
|
377
|
+
type: Boolean,
|
|
378
|
+
attribute: "lock-aspect-ratio"
|
|
379
|
+
})], EFTransformHandles.prototype, "lockAspectRatio", void 0);
|
|
380
|
+
__decorate([property({
|
|
381
|
+
type: Boolean,
|
|
382
|
+
attribute: "enable-drag"
|
|
383
|
+
})], EFTransformHandles.prototype, "enableDrag", void 0);
|
|
384
|
+
__decorate([property({
|
|
385
|
+
type: Number,
|
|
386
|
+
attribute: "rotation-step"
|
|
387
|
+
})], EFTransformHandles.prototype, "rotationStep", void 0);
|
|
388
|
+
__decorate([state()], EFTransformHandles.prototype, "interactionMode", void 0);
|
|
389
|
+
EFTransformHandles = __decorate([customElement("ef-transform-handles")], EFTransformHandles);
|
|
390
|
+
|
|
391
|
+
//#endregion
|
|
392
|
+
export { EFTransformHandles };
|
|
393
|
+
//# sourceMappingURL=EFTransformHandles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EFTransformHandles.js","names":["EFTransformHandles","overlayStyles: Record<string, string>"],"sources":["../../src/gui/EFTransformHandles.ts"],"sourcesContent":["import { consume } from \"@lit/context\";\nimport { css, html, LitElement } from \"lit\";\nimport { customElement, property, state } from \"lit/decorators.js\";\nimport { styleMap } from \"lit/directives/style-map.js\";\nimport { panZoomTransformContext } from \"./panZoomTransformContext.js\";\nimport type { PanZoomTransform } from \"../elements/EFPanZoom.js\";\nimport {\n type ResizeHandle,\n calculateDragBounds,\n calculateResizeBounds,\n getResizeHandleCursor,\n} from \"./transformCalculations.js\";\nimport { getCornerPoint, getOppositeCorner } from \"./transformUtils.js\";\n\nconst DEFAULT_MIN_SIZE = 10;\n\nexport interface TransformBounds {\n x: number;\n y: number;\n width: number;\n height: number;\n rotation?: number;\n}\n\n/**\n * Interaction mode enumeration.\n * Only one mode can be active at a time (invariant).\n */\ntype InteractionMode = \"idle\" | \"dragging\" | \"resizing\" | \"rotating\";\n\n@customElement(\"ef-transform-handles\")\nexport class EFTransformHandles extends LitElement {\n @property({ type: Object })\n bounds: TransformBounds = { x: 0, y: 0, width: 100, height: 100 };\n\n @property({ type: Number })\n minSize = DEFAULT_MIN_SIZE;\n\n @property({ type: String })\n target?: string;\n\n @consume({ context: panZoomTransformContext, subscribe: true })\n panZoomTransformFromContext?: PanZoomTransform;\n\n @property({ type: Number, attribute: \"canvas-scale\" })\n canvasScale = 1;\n\n @property({ type: Boolean, attribute: \"enable-rotation\" })\n enableRotation = false;\n\n @property({ type: Boolean, attribute: \"enable-resize\" })\n enableResize = true;\n\n @property({ type: Boolean, attribute: \"corners-only\" })\n cornersOnly = false;\n\n @property({ type: Boolean, attribute: \"lock-aspect-ratio\" })\n lockAspectRatio = false;\n\n @property({ type: Boolean, attribute: \"enable-drag\" })\n enableDrag = true;\n\n @property({ type: Number, attribute: \"rotation-step\" })\n rotationStep?: number;\n\n /**\n * Current interaction mode.\n * Invariant: Only one mode active at a time.\n */\n @state()\n interactionMode: InteractionMode = \"idle\";\n\n /**\n * Active resize handle when in \"resizing\" mode.\n * Only valid when interactionMode === \"resizing\".\n */\n private activeResizeHandle: ResizeHandle | null = null;\n\n /**\n * Mouse start position for calculating deltas.\n * Only valid during active interaction.\n */\n private mouseStart: { x: number; y: number } | null = null;\n\n /**\n * Initial bounds at interaction start - NEVER mutated during interaction.\n * All calculations derive from this + mouse deltas.\n * Note: Not a @state() property to avoid re-renders during interaction.\n */\n private initialBounds: TransformBounds | null = null;\n\n static styles = css`\n :host {\n display: block;\n position: absolute;\n pointer-events: none;\n }\n .overlay {\n position: absolute;\n border: 2px solid var(--ef-transform-handles-border-color, #3b82f6);\n pointer-events: none;\n }\n .overlay.dragging {\n border-color: var(--ef-transform-handles-dragging-border-color, #2563eb);\n }\n .drag-area {\n position: absolute;\n inset: 0;\n cursor: move;\n pointer-events: none;\n }\n /* Only enable pointer events when actively dragging */\n .drag-area:active {\n pointer-events: auto;\n }\n .handle {\n position: absolute;\n width: 8px;\n height: 8px;\n background: var(--ef-transform-handles-handle-color, white);\n border: 1px solid var(--ef-transform-handles-handle-border-color, #3b82f6);\n pointer-events: auto;\n /* Only capture pointer events, allow wheel events to pass through */\n touch-action: none;\n }\n .handle.nw { top: -4px; left: -4px; }\n .handle.n { top: -4px; left: 50%; transform: translateX(-50%); }\n .handle.ne { top: -4px; right: -4px; }\n .handle.e { top: 50%; right: -4px; transform: translateY(-50%); }\n .handle.se { bottom: -4px; right: -4px; }\n .handle.s { bottom: -4px; left: 50%; transform: translateX(-50%); }\n .handle.sw { bottom: -4px; left: -4px; }\n .handle.w { top: 50%; left: -4px; transform: translateY(-50%); }\n .rotate-handle {\n position: absolute;\n top: -30px;\n left: 50%;\n transform: translateX(-50%);\n cursor: grab;\n pointer-events: auto;\n /* Only capture pointer events, allow wheel events to pass through */\n touch-action: none;\n }\n .rotate-handle-circle {\n width: 24px;\n height: 24px;\n background: var(--ef-transform-handles-rotate-handle-color, #10b981);\n border: 2px solid white;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n .rotate-handle-circle span {\n font-size: 12px;\n color: white;\n }\n `;\n\n private resizeObserver?: ResizeObserver;\n\n /**\n * Single source of truth for zoom scale.\n * Priority: context > prop > 1.0\n */\n private getZoomScale(): number {\n return this.panZoomTransformFromContext?.scale ?? this.canvasScale ?? 1;\n }\n\n /**\n * Convert screen pixel bounds to canvas coordinates.\n */\n private screenToCanvas(bounds: TransformBounds): TransformBounds {\n const scale = this.getZoomScale();\n return {\n x: bounds.x / scale,\n y: bounds.y / scale,\n width: bounds.width / scale,\n height: bounds.height / scale,\n rotation: bounds.rotation,\n };\n }\n\n connectedCallback() {\n super.connectedCallback();\n this.resizeObserver = new ResizeObserver(() => {\n // Dimensions are read directly when needed\n });\n if (this.offsetParent) {\n this.resizeObserver.observe(this.offsetParent);\n }\n // Forward wheel events to parent panzoom so zoom works even when pointer is over handles\n // Wheel events should pass through, but we'll forward them to ensure panzoom receives them\n this.addEventListener(\n \"wheel\",\n (e: WheelEvent) => {\n // Only forward if not actively interacting with handles\n if (this.interactionMode === \"idle\") {\n // Find parent panzoom and forward the event\n const panZoom = this.closest(\"ef-pan-zoom\");\n if (panZoom) {\n // Create a new wheel event and dispatch it on panzoom\n const wheelEvent = new WheelEvent(\"wheel\", {\n bubbles: true,\n cancelable: true,\n clientX: e.clientX,\n clientY: e.clientY,\n deltaX: e.deltaX,\n deltaY: e.deltaY,\n deltaZ: e.deltaZ,\n deltaMode: e.deltaMode,\n ctrlKey: e.ctrlKey,\n metaKey: e.metaKey,\n shiftKey: e.shiftKey,\n altKey: e.altKey,\n });\n panZoom.dispatchEvent(wheelEvent);\n }\n }\n },\n { passive: true },\n );\n }\n\n disconnectedCallback() {\n super.disconnectedCallback();\n this.resizeObserver?.disconnect();\n this.cleanup();\n }\n\n /**\n * Transition interaction mode state machine.\n * Ensures only one mode is active at a time (invariant).\n */\n private transitionInteractionMode(\n event:\n | \"mousedown-drag\"\n | \"mousedown-resize\"\n | \"mousedown-rotate\"\n | \"mouseup\",\n ): InteractionMode {\n if (event === \"mouseup\") {\n return \"idle\";\n }\n // Only allow transition from idle\n if (this.interactionMode !== \"idle\") {\n return this.interactionMode;\n }\n switch (event) {\n case \"mousedown-drag\":\n return \"dragging\";\n case \"mousedown-resize\":\n return \"resizing\";\n case \"mousedown-rotate\":\n return \"rotating\";\n default:\n return \"idle\";\n }\n }\n\n private handleMouseDown = (e: MouseEvent) => {\n if (!this.enableDrag) return;\n e.stopPropagation();\n this.interactionMode = this.transitionInteractionMode(\"mousedown-drag\");\n this.mouseStart = { x: e.clientX, y: e.clientY };\n this.initialBounds = { ...this.bounds };\n document.addEventListener(\"mousemove\", this.handleMouseMove);\n document.addEventListener(\"mouseup\", this.handleMouseUp);\n };\n\n private handleResizeMouseDown = (e: MouseEvent, handle: ResizeHandle) => {\n if (!this.enableResize) return;\n e.stopPropagation();\n e.preventDefault();\n this.interactionMode = this.transitionInteractionMode(\"mousedown-resize\");\n this.activeResizeHandle = handle;\n this.mouseStart = { x: e.clientX, y: e.clientY };\n // Store initial bounds as-is (in screen pixels) - we'll convert to canvas when calculating\n this.initialBounds = { ...this.bounds };\n document.addEventListener(\"mousemove\", this.handleMouseMove);\n document.addEventListener(\"mouseup\", this.handleMouseUp);\n };\n\n private handleRotateMouseDown = (e: MouseEvent) => {\n if (!this.enableRotation) return;\n e.stopPropagation();\n this.interactionMode = this.transitionInteractionMode(\"mousedown-rotate\");\n this.mouseStart = { x: e.clientX, y: e.clientY };\n this.initialBounds = { ...this.bounds };\n document.addEventListener(\"mousemove\", this.handleMouseMove);\n document.addEventListener(\"mouseup\", this.handleMouseUp);\n };\n\n /**\n * Dispatch bounds change event (one-way data flow).\n * Parent updates element, then reads element and updates handle bounds prop.\n * We don't modify this.bounds directly - always render from prop.\n */\n private dispatchBoundsChange(bounds: TransformBounds): void {\n this.dispatchEvent(\n new CustomEvent(\"bounds-change\", {\n detail: { bounds },\n bubbles: true,\n composed: true,\n }),\n );\n }\n\n private handleMouseMove = (e: MouseEvent) => {\n if (!this.mouseStart || !this.initialBounds) return;\n\n // Calculate mouse deltas in viewport coordinates\n const screenDeltaX = e.clientX - this.mouseStart.x;\n const screenDeltaY = e.clientY - this.mouseStart.y;\n\n switch (this.interactionMode) {\n case \"dragging\": {\n const zoomScale = this.getZoomScale();\n const initialCanvas = this.screenToCanvas(this.initialBounds);\n\n const newPosition = calculateDragBounds(\n { x: initialCanvas.x, y: initialCanvas.y },\n screenDeltaX,\n screenDeltaY,\n zoomScale,\n );\n\n this.dispatchBoundsChange({\n ...newPosition,\n width: initialCanvas.width,\n height: initialCanvas.height,\n rotation: this.initialBounds.rotation,\n });\n break;\n }\n\n case \"resizing\": {\n if (!this.activeResizeHandle) return;\n\n const zoomScale = this.getZoomScale();\n const initialCanvas = this.screenToCanvas(this.initialBounds);\n const rotation = this.enableRotation\n ? (this.initialBounds.rotation ?? 0)\n : 0;\n\n // Calculate the fixed corner (opposite to handle being dragged)\n const oppositeCorner = getOppositeCorner(this.activeResizeHandle);\n const rotationRadians = (rotation * Math.PI) / 180;\n const fixedCorner = getCornerPoint(\n initialCanvas.x,\n initialCanvas.y,\n initialCanvas.width,\n initialCanvas.height,\n rotationRadians,\n oppositeCorner.x,\n oppositeCorner.y,\n );\n\n const newCanvasBounds = calculateResizeBounds(\n { width: initialCanvas.width, height: initialCanvas.height },\n { x: initialCanvas.x, y: initialCanvas.y },\n fixedCorner,\n this.activeResizeHandle,\n screenDeltaX,\n screenDeltaY,\n rotation,\n this.minSize / zoomScale,\n zoomScale,\n {\n lockAspectRatio: this.lockAspectRatio || e.shiftKey,\n resizeFromCenter: e.ctrlKey || e.metaKey,\n },\n );\n\n // Preserve rotation\n newCanvasBounds.rotation = this.initialBounds.rotation;\n this.dispatchBoundsChange(newCanvasBounds);\n break;\n }\n\n case \"rotating\": {\n // Calculate center in screen coordinates (bounds are overlay-relative)\n const overlayRect = this.offsetParent?.getBoundingClientRect() ?? {\n left: 0,\n top: 0,\n };\n const centerX =\n overlayRect.left +\n this.initialBounds.x +\n this.initialBounds.width / 2;\n const centerY =\n overlayRect.top +\n this.initialBounds.y +\n this.initialBounds.height / 2;\n\n // Calculate angle from mouse start to current position\n const startAngle =\n Math.atan2(this.mouseStart.y - centerY, this.mouseStart.x - centerX) *\n (180 / Math.PI) +\n 90;\n const currentAngle =\n Math.atan2(e.clientY - centerY, e.clientX - centerX) *\n (180 / Math.PI) +\n 90;\n\n // Normalize angle difference to [-180, 180] to avoid wrapping issues\n let deltaAngle = currentAngle - startAngle;\n while (deltaAngle > 180) deltaAngle -= 360;\n while (deltaAngle < -180) deltaAngle += 360;\n\n let newRotation = (this.initialBounds.rotation ?? 0) + deltaAngle;\n if (this.rotationStep !== undefined && this.rotationStep > 0) {\n newRotation =\n Math.round(newRotation / this.rotationStep) * this.rotationStep;\n }\n\n this.dispatchEvent(\n new CustomEvent(\"rotation-change\", {\n detail: { rotation: newRotation },\n bubbles: true,\n composed: true,\n }),\n );\n break;\n }\n\n case \"idle\":\n // No action needed\n break;\n }\n };\n\n private handleMouseUp = () => {\n this.cleanup();\n };\n\n private cleanup() {\n this.interactionMode = this.transitionInteractionMode(\"mouseup\");\n this.activeResizeHandle = null;\n this.mouseStart = null;\n this.initialBounds = null;\n document.removeEventListener(\"mousemove\", this.handleMouseMove);\n document.removeEventListener(\"mouseup\", this.handleMouseUp);\n }\n\n render() {\n // Always render from bounds prop (one-way data flow)\n // During interaction: dispatch events, parent updates element, parent updates handle bounds prop\n const currentBounds = this.bounds;\n const rotation = this.enableRotation ? (currentBounds.rotation ?? 0) : 0;\n\n const overlayStyles: Record<string, string> = {\n left: `${currentBounds.x}px`,\n top: `${currentBounds.y}px`,\n width: `${currentBounds.width}px`,\n height: `${currentBounds.height}px`,\n };\n\n if (this.enableRotation && rotation !== 0) {\n overlayStyles.transform = `rotate(${rotation}deg)`;\n overlayStyles.transformOrigin = \"center\";\n }\n\n const allHandles: ResizeHandle[] = [\n \"nw\",\n \"n\",\n \"ne\",\n \"e\",\n \"se\",\n \"s\",\n \"sw\",\n \"w\",\n ];\n const cornerHandles: ResizeHandle[] = [\"nw\", \"ne\", \"se\", \"sw\"];\n const handles = this.cornersOnly ? cornerHandles : allHandles;\n\n return html`\n <div\n class=\"overlay ${this.interactionMode === \"dragging\" ? \"dragging\" : \"\"}\"\n style=${styleMap(overlayStyles)}\n >\n ${\n this.enableDrag\n ? html`\n <div\n class=\"drag-area\"\n @mousedown=${this.handleMouseDown}\n ></div>\n `\n : \"\"\n }\n ${\n this.enableResize\n ? handles.map((handle) => {\n const rotation = this.enableRotation\n ? (currentBounds.rotation ?? 0)\n : 0;\n const cursor = getResizeHandleCursor(handle, rotation);\n return html`\n <div\n class=\"handle ${handle}\"\n style=${styleMap({ cursor })}\n @mousedown=${(e: MouseEvent) => this.handleResizeMouseDown(e, handle)}\n ></div>\n `;\n })\n : \"\"\n }\n ${\n this.enableRotation\n ? html`\n <div class=\"rotate-handle\" @mousedown=${this.handleRotateMouseDown}>\n <div class=\"rotate-handle-circle\">\n <span>↻</span>\n </div>\n </div>\n `\n : \"\"\n }\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"ef-transform-handles\": EFTransformHandles;\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ContextMixinInterface } from "./ContextMixin.js";
|
|
2
|
+
import { RenderToVideoOptions } from "../preview/renderTimegroupToVideo.js";
|
|
2
3
|
import * as lit11 from "lit";
|
|
3
4
|
import { LitElement, PropertyValueMap } from "lit";
|
|
4
5
|
import * as lit_html11 from "lit-html";
|
|
@@ -9,12 +10,189 @@ declare const EFWorkbench_base: (new (...args: any[]) => ContextMixinInterface)
|
|
|
9
10
|
declare class EFWorkbench extends EFWorkbench_base {
|
|
10
11
|
static styles: lit11.CSSResult[];
|
|
11
12
|
rendering: boolean;
|
|
13
|
+
private panZoomTransform;
|
|
14
|
+
private isExporting;
|
|
15
|
+
private exportProgress;
|
|
16
|
+
private exportStatus;
|
|
17
|
+
private previewSettings;
|
|
18
|
+
private renderMode;
|
|
19
|
+
private presentationMode;
|
|
20
|
+
private previewResolutionScale;
|
|
21
|
+
private debugThumbnailTimestamps;
|
|
22
|
+
private thumbnailCacheMaxSize;
|
|
23
|
+
private thumbnailCacheStats;
|
|
24
|
+
private cacheStatsUpdateInterval;
|
|
25
|
+
private exportOptions;
|
|
26
|
+
private exportAbortController;
|
|
27
|
+
private isPlaying;
|
|
28
|
+
private isScrubbing;
|
|
29
|
+
private isAtRest;
|
|
30
|
+
/**
|
|
31
|
+
* Current adaptive resolution scale (only used when previewResolutionScale === "auto")
|
|
32
|
+
*/
|
|
33
|
+
private currentAdaptiveScale;
|
|
34
|
+
/**
|
|
35
|
+
* Playback stats for display (FPS, dropped frames, etc.)
|
|
36
|
+
* Mirrors previewSettings.showStats for direct access
|
|
37
|
+
*/
|
|
38
|
+
private showStats;
|
|
39
|
+
private statsStrategy;
|
|
40
|
+
/**
|
|
41
|
+
* Reference for tracking scrubbing state from EFScrubber.
|
|
42
|
+
* Pass this to <ef-scrubber isScrubbingRef={...}> to enable motion detection.
|
|
43
|
+
*/
|
|
44
|
+
readonly isScrubbingRef: {
|
|
45
|
+
current: boolean;
|
|
46
|
+
};
|
|
47
|
+
private restDebounceTimer;
|
|
48
|
+
private playingCheckInterval;
|
|
49
|
+
private adaptiveTracker;
|
|
50
|
+
private savePanZoomDebounceTimer;
|
|
51
|
+
private cloneOverlayRef;
|
|
52
|
+
private cloneRefresh;
|
|
53
|
+
private cloneAnimationFrame;
|
|
54
|
+
private cloneRootElement;
|
|
55
|
+
private cloneTimegroup;
|
|
56
|
+
private structureObserver;
|
|
57
|
+
private rebuildPending;
|
|
58
|
+
private canvasRefresh;
|
|
59
|
+
private canvasPreviewRef;
|
|
60
|
+
private canvasPreviewResult;
|
|
61
|
+
private canvasAnimationFrame;
|
|
62
|
+
private boundHandleTransformChanged;
|
|
12
63
|
focusOverlay: lit_html_directives_ref_js2.Ref<HTMLDivElement>;
|
|
13
64
|
handleStageWheel(event: WheelEvent): void;
|
|
14
65
|
connectedCallback(): void;
|
|
15
66
|
disconnectedCallback(): void;
|
|
67
|
+
protected firstUpdated(): void;
|
|
68
|
+
private lastCanvasZoom;
|
|
69
|
+
private zoomReinitTimeout;
|
|
70
|
+
private handleTransformChanged;
|
|
71
|
+
private getTimegroup;
|
|
72
|
+
/**
|
|
73
|
+
* Get the root timegroup ID for localStorage key generation.
|
|
74
|
+
* Returns null if no root timegroup is found or it has no ID.
|
|
75
|
+
*/
|
|
76
|
+
private getRootTimegroupId;
|
|
77
|
+
/**
|
|
78
|
+
* Get localStorage key for preview pan/zoom state.
|
|
79
|
+
*/
|
|
80
|
+
private getPreviewPanZoomStorageKey;
|
|
81
|
+
/**
|
|
82
|
+
* Save preview pan/zoom to localStorage.
|
|
83
|
+
*/
|
|
84
|
+
private savePreviewPanZoom;
|
|
85
|
+
/**
|
|
86
|
+
* Restore preview pan/zoom from localStorage.
|
|
87
|
+
*/
|
|
88
|
+
private restorePreviewPanZoom;
|
|
89
|
+
/**
|
|
90
|
+
* Debounced save of preview pan/zoom to avoid excessive localStorage writes.
|
|
91
|
+
*/
|
|
92
|
+
private debouncedSavePreviewPanZoom;
|
|
93
|
+
/**
|
|
94
|
+
* Start polling for motion state (playing/scrubbing).
|
|
95
|
+
* We use polling because:
|
|
96
|
+
* - Playing state comes from timegroup's playbackController
|
|
97
|
+
* - Scrubbing state comes from isScrubbingRef (set by EFScrubber)
|
|
98
|
+
*/
|
|
99
|
+
private startMotionStateTracking;
|
|
100
|
+
private stopMotionStateTracking;
|
|
101
|
+
/**
|
|
102
|
+
* Update motion state by checking timegroup and scrubbing ref.
|
|
103
|
+
*/
|
|
104
|
+
private updateMotionState;
|
|
105
|
+
/**
|
|
106
|
+
* Called when motion starts (playing or scrubbing began).
|
|
107
|
+
*/
|
|
108
|
+
private handleMotionStart;
|
|
109
|
+
/**
|
|
110
|
+
* Called when motion stops (not playing and not scrubbing).
|
|
111
|
+
* Starts a debounce timer before transitioning to rest state.
|
|
112
|
+
*/
|
|
113
|
+
private handleMotionStop;
|
|
114
|
+
/**
|
|
115
|
+
* Called after debounce period when we're confirmed to be at rest.
|
|
116
|
+
*/
|
|
117
|
+
private transitionToRest;
|
|
118
|
+
/**
|
|
119
|
+
* Get the effective resolution scale based on current mode and motion state.
|
|
120
|
+
* For "auto" mode, returns full resolution at rest, adaptive scale in motion.
|
|
121
|
+
*/
|
|
122
|
+
private getEffectiveResolutionScale;
|
|
123
|
+
/**
|
|
124
|
+
* Apply settings when dependencies are ready.
|
|
125
|
+
* Called from updated() hook when settings change or dependencies become available.
|
|
126
|
+
*/
|
|
127
|
+
private applySettings;
|
|
128
|
+
/**
|
|
129
|
+
* Update or create stats tracking strategy based on current mode and settings.
|
|
130
|
+
*/
|
|
131
|
+
private updateStatsStrategy;
|
|
132
|
+
private initCloneOverlay;
|
|
133
|
+
private finishCloneSetup;
|
|
134
|
+
private rebuildClone;
|
|
135
|
+
private setupStructureObserver;
|
|
136
|
+
private observeShadowRoots;
|
|
137
|
+
private updateCloneTransform;
|
|
138
|
+
private startCloneLoop;
|
|
139
|
+
private stopCloneOverlay;
|
|
140
|
+
private handlePresentationModeChange;
|
|
141
|
+
private initDomMode;
|
|
142
|
+
private stopDomMode;
|
|
143
|
+
/**
|
|
144
|
+
* Get the resolution scale for canvas rendering (for fixed scale modes).
|
|
145
|
+
*
|
|
146
|
+
* Logic:
|
|
147
|
+
* - Get actual displayed size from getBoundingClientRect()
|
|
148
|
+
* - For "Full": render at displayed size (1:1 pixel mapping)
|
|
149
|
+
* - For other settings: render at that % of displayed size
|
|
150
|
+
* - Never exceed composition size (100%)
|
|
151
|
+
*
|
|
152
|
+
* Note: For "auto" mode, use getEffectiveResolutionScale() instead.
|
|
153
|
+
*/
|
|
154
|
+
private getResolutionScale;
|
|
155
|
+
private initCanvasMode;
|
|
156
|
+
private stopCanvasMode;
|
|
157
|
+
private updateCanvasTransform;
|
|
158
|
+
initCanvasRenderer(): {
|
|
159
|
+
canvas: HTMLCanvasElement;
|
|
160
|
+
refresh: () => Promise<void>;
|
|
161
|
+
} | null;
|
|
162
|
+
/** Start video export with progress tracking */
|
|
163
|
+
startExport(options?: RenderToVideoOptions): Promise<void>;
|
|
164
|
+
/** Cancel the current export */
|
|
165
|
+
cancelExport(): void;
|
|
166
|
+
private positionPopover;
|
|
167
|
+
private handleSettingsPopoverToggle;
|
|
168
|
+
private handleExportPopoverToggle;
|
|
169
|
+
private handleStartExport;
|
|
170
|
+
private updateExportOption;
|
|
171
|
+
private formatTime;
|
|
172
|
+
private handleCancelClick;
|
|
173
|
+
private handleRenderModeChange;
|
|
174
|
+
private handleResolutionScaleChange;
|
|
175
|
+
private updateThumbnailCacheStats;
|
|
176
|
+
private startCacheStatsUpdates;
|
|
177
|
+
private stopCacheStatsUpdates;
|
|
178
|
+
private handleThumbnailCacheMaxSizeChange;
|
|
179
|
+
private handleClearThumbnailCache;
|
|
180
|
+
private formatBytes;
|
|
181
|
+
private handleDebugThumbnailTimestampsToggle;
|
|
182
|
+
private handleShowStatsToggle;
|
|
183
|
+
/**
|
|
184
|
+
* Reset and fit the preview to show all content centered.
|
|
185
|
+
* Finds the pan-zoom element and calls fitToContent() on it.
|
|
186
|
+
*/
|
|
187
|
+
private handleFitToContent;
|
|
188
|
+
private renderSettingsPopover;
|
|
189
|
+
private renderExportPopover;
|
|
190
|
+
private renderExportProgressPopover;
|
|
191
|
+
private renderToolbar;
|
|
16
192
|
update(changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>): void;
|
|
193
|
+
updated(changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>): void;
|
|
17
194
|
drawOverlays: () => void;
|
|
195
|
+
private renderPlaybackStats;
|
|
18
196
|
render(): lit_html11.TemplateResult<1>;
|
|
19
197
|
}
|
|
20
198
|
declare global {
|