@editframe/create 0.44.0 → 0.45.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/index.js +16 -28
- package/dist/index.js.map +1 -1
- package/dist/skills/editframe-brand-video-generator/README.md +155 -0
- package/dist/skills/editframe-brand-video-generator/SKILL.md +207 -0
- package/dist/skills/editframe-brand-video-generator/references/brand-examples.md +178 -0
- package/dist/skills/editframe-brand-video-generator/references/color-psychology.md +227 -0
- package/dist/skills/editframe-brand-video-generator/references/composition-patterns.md +383 -0
- package/dist/skills/editframe-brand-video-generator/references/editing.md +66 -0
- package/dist/skills/editframe-brand-video-generator/references/emotional-arcs.md +496 -0
- package/dist/skills/editframe-brand-video-generator/references/genre-selection.md +135 -0
- package/dist/skills/editframe-brand-video-generator/references/transition-styles.md +611 -0
- package/dist/skills/editframe-brand-video-generator/references/typography-personalities.md +326 -0
- package/dist/skills/editframe-brand-video-generator/references/video-archetypes.md +86 -0
- package/dist/skills/editframe-brand-video-generator/references/video-fundamentals.md +169 -0
- package/dist/skills/editframe-brand-video-generator/references/visual-metaphors.md +50 -0
- package/dist/skills/editframe-composition/SKILL.md +169 -0
- package/dist/skills/editframe-composition/references/audio.md +483 -0
- package/dist/skills/editframe-composition/references/captions.md +844 -0
- package/dist/skills/editframe-composition/references/composition-model.md +73 -0
- package/dist/skills/editframe-composition/references/configuration.md +403 -0
- package/dist/skills/editframe-composition/references/css-parts.md +105 -0
- package/dist/skills/editframe-composition/references/css-variables.md +640 -0
- package/dist/skills/editframe-composition/references/entry-points.md +810 -0
- package/dist/skills/editframe-composition/references/events.md +499 -0
- package/dist/skills/editframe-composition/references/getting-started.md +259 -0
- package/dist/skills/editframe-composition/references/hooks.md +234 -0
- package/dist/skills/editframe-composition/references/image.md +241 -0
- package/dist/skills/editframe-composition/references/r3f.md +580 -0
- package/dist/skills/editframe-composition/references/render-api.md +484 -0
- package/dist/skills/editframe-composition/references/render-strategies.md +119 -0
- package/dist/skills/editframe-composition/references/render-to-video.md +1101 -0
- package/dist/skills/editframe-composition/references/scripting.md +606 -0
- package/dist/skills/editframe-composition/references/sequencing.md +116 -0
- package/dist/skills/editframe-composition/references/server-rendering.md +753 -0
- package/dist/skills/editframe-composition/references/surface.md +329 -0
- package/dist/skills/editframe-composition/references/text.md +627 -0
- package/dist/skills/editframe-composition/references/time-model.md +99 -0
- package/dist/skills/editframe-composition/references/timegroup-modes.md +102 -0
- package/dist/skills/editframe-composition/references/timegroup.md +457 -0
- package/dist/skills/editframe-composition/references/timeline-root.md +398 -0
- package/dist/skills/editframe-composition/references/transcription.md +47 -0
- package/dist/skills/editframe-composition/references/transitions.md +608 -0
- package/dist/skills/editframe-composition/references/use-media-info.md +357 -0
- package/dist/skills/editframe-composition/references/video.md +506 -0
- package/dist/skills/editframe-composition/references/waveform.md +327 -0
- package/dist/skills/editframe-editor-gui/SKILL.md +152 -0
- package/dist/skills/editframe-editor-gui/references/active-root-temporal.md +657 -0
- package/dist/skills/editframe-editor-gui/references/canvas.md +947 -0
- package/dist/skills/editframe-editor-gui/references/controls.md +366 -0
- package/dist/skills/editframe-editor-gui/references/dial.md +756 -0
- package/dist/skills/editframe-editor-gui/references/editor-toolkit.md +587 -0
- package/dist/skills/editframe-editor-gui/references/filmstrip.md +460 -0
- package/dist/skills/editframe-editor-gui/references/fit-scale.md +772 -0
- package/dist/skills/editframe-editor-gui/references/focus-overlay.md +561 -0
- package/dist/skills/editframe-editor-gui/references/hierarchy.md +544 -0
- package/dist/skills/editframe-editor-gui/references/overlay-item.md +634 -0
- package/dist/skills/editframe-editor-gui/references/overlay-layer.md +429 -0
- package/dist/skills/editframe-editor-gui/references/pan-zoom.md +568 -0
- package/dist/skills/editframe-editor-gui/references/pause.md +397 -0
- package/dist/skills/editframe-editor-gui/references/play.md +370 -0
- package/dist/skills/editframe-editor-gui/references/preview.md +391 -0
- package/dist/skills/editframe-editor-gui/references/resizable-box.md +749 -0
- package/dist/skills/editframe-editor-gui/references/scrubber.md +588 -0
- package/dist/skills/editframe-editor-gui/references/thumbnail-strip.md +566 -0
- package/dist/skills/editframe-editor-gui/references/time-display.md +492 -0
- package/dist/skills/editframe-editor-gui/references/timeline-ruler.md +489 -0
- package/dist/skills/editframe-editor-gui/references/timeline.md +604 -0
- package/dist/skills/editframe-editor-gui/references/toggle-loop.md +618 -0
- package/dist/skills/editframe-editor-gui/references/toggle-play.md +526 -0
- package/dist/skills/editframe-editor-gui/references/transform-handles.md +924 -0
- package/dist/skills/editframe-editor-gui/references/trim-handles.md +725 -0
- package/dist/skills/editframe-editor-gui/references/workbench.md +453 -0
- package/dist/skills/editframe-motion-design/SKILL.md +101 -0
- package/dist/skills/editframe-motion-design/references/0-editframe.md +299 -0
- package/dist/skills/editframe-motion-design/references/1-intent.md +201 -0
- package/dist/skills/editframe-motion-design/references/2-physics-model.md +405 -0
- package/dist/skills/editframe-motion-design/references/3-attention.md +350 -0
- package/dist/skills/editframe-motion-design/references/4-process.md +418 -0
- package/dist/skills/editframe-vite-plugin/SKILL.md +75 -0
- package/dist/skills/editframe-vite-plugin/references/file-api.md +111 -0
- package/dist/skills/editframe-vite-plugin/references/getting-started.md +96 -0
- package/dist/skills/editframe-vite-plugin/references/jit-transcoding.md +91 -0
- package/dist/skills/editframe-vite-plugin/references/local-assets.md +75 -0
- package/dist/skills/editframe-vite-plugin/references/visual-testing.md +136 -0
- package/dist/skills/editframe-webhooks/SKILL.md +126 -0
- package/dist/skills/editframe-webhooks/references/events.md +382 -0
- package/dist/skills/editframe-webhooks/references/getting-started.md +232 -0
- package/dist/skills/editframe-webhooks/references/security.md +418 -0
- package/dist/skills/editframe-webhooks/references/testing.md +409 -0
- package/dist/skills/editframe-webhooks/references/troubleshooting.md +457 -0
- package/dist/templates/html/AGENTS.md +13 -0
- package/dist/templates/react/AGENTS.md +13 -0
- package/dist/utils.js +15 -16
- package/dist/utils.js.map +1 -1
- package/package.json +1 -1
- package/tsdown.config.ts +4 -0
- package/dist/detectAgent.js +0 -89
- package/dist/detectAgent.js.map +0 -1
|
@@ -0,0 +1,947 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Canvas Elements
|
|
3
|
+
description: Interactive canvas area with element selection, drag-and-drop repositioning, and transform handle integration.
|
|
4
|
+
type: reference
|
|
5
|
+
nav:
|
|
6
|
+
parent: "Preview & Canvas"
|
|
7
|
+
priority: 11
|
|
8
|
+
api:
|
|
9
|
+
attributes:
|
|
10
|
+
- name: data-element-id-attribute
|
|
11
|
+
type: string
|
|
12
|
+
default: "data-element-id"
|
|
13
|
+
description: Attribute name for element identification
|
|
14
|
+
- name: enable-transform-handles
|
|
15
|
+
type: boolean
|
|
16
|
+
default: true
|
|
17
|
+
description: Show transform handles for selected elements
|
|
18
|
+
methods:
|
|
19
|
+
- name: registerElement(element, id)
|
|
20
|
+
signature: "registerElement(element: HTMLElement, id?: string): string"
|
|
21
|
+
description: Register an element for canvas management
|
|
22
|
+
returns: Element ID
|
|
23
|
+
- name: unregisterElement(element)
|
|
24
|
+
signature: "unregisterElement(element: HTMLElement | string): void"
|
|
25
|
+
description: Unregister an element
|
|
26
|
+
- name: tryRegisterElement(element)
|
|
27
|
+
signature: "tryRegisterElement(element: HTMLElement): void"
|
|
28
|
+
description: Try to register element, auto-generating ID if needed
|
|
29
|
+
- name: updateElementPosition(elementId, x, y)
|
|
30
|
+
signature: "updateElementPosition(elementId: string, x: number, y: number): void"
|
|
31
|
+
description: Update element position in canvas coordinates
|
|
32
|
+
- name: getElementData(elementId)
|
|
33
|
+
signature: "getElementData(elementId: string): CanvasElementData | null"
|
|
34
|
+
description: Get element metadata
|
|
35
|
+
returns: CanvasElementData | null
|
|
36
|
+
- name: getAllElementsData()
|
|
37
|
+
signature: "getAllElementsData(): CanvasElementData[]"
|
|
38
|
+
description: Get metadata for all elements
|
|
39
|
+
returns: CanvasElementData[]
|
|
40
|
+
- name: screenToCanvasCoords(screenX, screenY)
|
|
41
|
+
signature: "screenToCanvasCoords(screenX: number, screenY: number): { x: number, y: number }"
|
|
42
|
+
description: Convert screen coordinates to canvas coordinates
|
|
43
|
+
- name: canvasToScreenCoords(canvasX, canvasY)
|
|
44
|
+
signature: "canvasToScreenCoords(canvasX: number, canvasY: number): { x: number, y: number }"
|
|
45
|
+
description: Convert canvas coordinates to screen coordinates
|
|
46
|
+
- name: setHighlightedElement(element)
|
|
47
|
+
signature: "setHighlightedElement(element: HTMLElement | null): void"
|
|
48
|
+
description: Set the highlighted (hovered) element
|
|
49
|
+
properties:
|
|
50
|
+
- name: selectionContext
|
|
51
|
+
type: SelectionContext
|
|
52
|
+
description: Selection state and methods (provided via Lit context)
|
|
53
|
+
- name: highlightedElement
|
|
54
|
+
type: "HTMLElement | null"
|
|
55
|
+
description: Currently highlighted (hovered) element
|
|
56
|
+
- name: activeRootTemporal
|
|
57
|
+
type: "TemporalMixinInterface & HTMLElement | null"
|
|
58
|
+
description: Root temporal element containing current selection
|
|
59
|
+
events:
|
|
60
|
+
- name: activeroottemporalchange
|
|
61
|
+
detail: "{ activeRootTemporal: TemporalMixinInterface & HTMLElement | null }"
|
|
62
|
+
description: Fired when active root temporal changes
|
|
63
|
+
types:
|
|
64
|
+
- name: CanvasElementData
|
|
65
|
+
type: interface
|
|
66
|
+
definition: |
|
|
67
|
+
interface CanvasElementData {
|
|
68
|
+
id: string;
|
|
69
|
+
element: HTMLElement;
|
|
70
|
+
x: number; // Canvas x coordinate
|
|
71
|
+
y: number; // Canvas y coordinate
|
|
72
|
+
width: number; // Width in canvas units
|
|
73
|
+
height: number; // Height in canvas units
|
|
74
|
+
rotation?: number; // Rotation in degrees
|
|
75
|
+
}
|
|
76
|
+
- name: SelectionContext
|
|
77
|
+
type: interface
|
|
78
|
+
definition: |
|
|
79
|
+
interface SelectionContext extends EventTarget {
|
|
80
|
+
selectedIds: Set<string>;
|
|
81
|
+
select(id: string): void;
|
|
82
|
+
deselect(id: string): void;
|
|
83
|
+
toggle(id: string): void;
|
|
84
|
+
addToSelection(id: string): void;
|
|
85
|
+
clear(): void;
|
|
86
|
+
startBoxSelect(x: number, y: number): void;
|
|
87
|
+
updateBoxSelect(x: number, y: number): void;
|
|
88
|
+
endBoxSelect(hitTest: (bounds: DOMRect) => string[], additive: boolean): void;
|
|
89
|
+
}
|
|
90
|
+
react:
|
|
91
|
+
generate: true
|
|
92
|
+
componentName: Canvas
|
|
93
|
+
importPath: "@editframe/react"
|
|
94
|
+
propMapping:
|
|
95
|
+
data-element-id-attribute: elementIdAttribute
|
|
96
|
+
enable-transform-handles: enableTransformHandles
|
|
97
|
+
additionalProps:
|
|
98
|
+
- name: className
|
|
99
|
+
type: string
|
|
100
|
+
description: CSS classes for styling
|
|
101
|
+
- name: children
|
|
102
|
+
type: ReactNode
|
|
103
|
+
description: Canvas content elements
|
|
104
|
+
- name: ref
|
|
105
|
+
type: "Ref<HTMLElement>"
|
|
106
|
+
description: Ref to access underlying canvas element and methods
|
|
107
|
+
nav:
|
|
108
|
+
parent: "Components / Editor UI"
|
|
109
|
+
priority: 61
|
|
110
|
+
related: ["hierarchy", "overlay-layer", "transform-handles"]
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
<!-- html-only -->
|
|
114
|
+
# ef-canvas
|
|
115
|
+
<!-- /html-only -->
|
|
116
|
+
<!-- react-only -->
|
|
117
|
+
# Canvas / CanvasItem
|
|
118
|
+
<!-- /react-only -->
|
|
119
|
+
|
|
120
|
+
Interactive canvas for managing, selecting, and manipulating elements with drag-and-drop and transform handles.
|
|
121
|
+
|
|
122
|
+
<!-- react-only -->
|
|
123
|
+
> **Note**: Canvas and CanvasItem components are currently not exported from `@editframe/react`. This documentation describes the underlying `ef-canvas` and `ef-canvas-item` HTML elements. To use them in React, you'll need to use the HTML elements directly or wait for official React component export.
|
|
124
|
+
<!-- /react-only -->
|
|
125
|
+
|
|
126
|
+
## Basic Usage
|
|
127
|
+
|
|
128
|
+
<!-- html-only -->
|
|
129
|
+
Canvas automatically manages any child elements:
|
|
130
|
+
|
|
131
|
+
```html live
|
|
132
|
+
<div class="relative w-full h-[400px] border border-gray-300 rounded overflow-hidden">
|
|
133
|
+
<ef-pan-zoom class="w-full h-full">
|
|
134
|
+
<ef-canvas class="w-[1200px] h-[800px] bg-gray-100">
|
|
135
|
+
<div id="box-1" class="absolute top-8 left-8 w-32 h-32 bg-blue-500 text-white flex items-center justify-center rounded">
|
|
136
|
+
Box 1
|
|
137
|
+
</div>
|
|
138
|
+
<div id="box-2" class="absolute top-48 left-48 w-32 h-32 bg-green-500 text-white flex items-center justify-center rounded">
|
|
139
|
+
Box 2
|
|
140
|
+
</div>
|
|
141
|
+
<div id="box-3" class="absolute top-8 left-48 w-32 h-32 bg-red-500 text-white flex items-center justify-center rounded">
|
|
142
|
+
Box 3
|
|
143
|
+
</div>
|
|
144
|
+
</ef-canvas>
|
|
145
|
+
</ef-pan-zoom>
|
|
146
|
+
</div>
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Click to select, drag to move, use handles to resize and rotate.
|
|
150
|
+
<!-- /html-only -->
|
|
151
|
+
<!-- react-only -->
|
|
152
|
+
```tsx
|
|
153
|
+
import { useRef } from "react";
|
|
154
|
+
|
|
155
|
+
export const App = () => {
|
|
156
|
+
const canvasRef = useRef<HTMLElement>(null);
|
|
157
|
+
|
|
158
|
+
const handleSelect = (e: CustomEvent) => {
|
|
159
|
+
console.log("Selected:", e.detail);
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
return (
|
|
163
|
+
<div className="h-screen flex">
|
|
164
|
+
{/* Canvas with draggable items */}
|
|
165
|
+
<div className="flex-1">
|
|
166
|
+
<ef-canvas
|
|
167
|
+
ref={canvasRef as any}
|
|
168
|
+
onSelectionChange={handleSelect as any}
|
|
169
|
+
enable-transform-handles
|
|
170
|
+
>
|
|
171
|
+
<ef-timegroup mode="contain" className="w-[1920px] h-[1080px]">
|
|
172
|
+
<ef-canvas-item data-element-id="video-1">
|
|
173
|
+
<ef-video
|
|
174
|
+
src="/assets/video.mp4"
|
|
175
|
+
className="absolute"
|
|
176
|
+
style={{ left: "100px", top: "100px", width: "400px", height: "300px" }}
|
|
177
|
+
/>
|
|
178
|
+
</ef-canvas-item>
|
|
179
|
+
|
|
180
|
+
<ef-canvas-item data-element-id="text-1">
|
|
181
|
+
<ef-text
|
|
182
|
+
duration="5s"
|
|
183
|
+
className="absolute text-white text-4xl"
|
|
184
|
+
style={{ left: "200px", top: "50px" }}
|
|
185
|
+
>
|
|
186
|
+
Draggable Text
|
|
187
|
+
</ef-text>
|
|
188
|
+
</ef-canvas-item>
|
|
189
|
+
</ef-timegroup>
|
|
190
|
+
</ef-canvas>
|
|
191
|
+
</div>
|
|
192
|
+
</div>
|
|
193
|
+
);
|
|
194
|
+
};
|
|
195
|
+
```
|
|
196
|
+
<!-- /react-only -->
|
|
197
|
+
|
|
198
|
+
## Selection
|
|
199
|
+
|
|
200
|
+
<!-- html-only -->
|
|
201
|
+
Canvas provides multiple selection modes:
|
|
202
|
+
|
|
203
|
+
### Single Selection
|
|
204
|
+
|
|
205
|
+
Click an element to select it:
|
|
206
|
+
|
|
207
|
+
```javascript
|
|
208
|
+
const canvas = document.querySelector('ef-canvas');
|
|
209
|
+
|
|
210
|
+
// Listen for selection changes
|
|
211
|
+
canvas.selectionContext.addEventListener('selectionchange', () => {
|
|
212
|
+
const selected = Array.from(canvas.selectionContext.selectedIds);
|
|
213
|
+
console.log('Selected:', selected);
|
|
214
|
+
});
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### Multi-Selection
|
|
218
|
+
|
|
219
|
+
Hold **Shift** to add to selection:
|
|
220
|
+
|
|
221
|
+
- **Click**: Select single element (clear others)
|
|
222
|
+
- **Shift + Click**: Add element to selection
|
|
223
|
+
- **Ctrl/Cmd + Click**: Toggle element selection
|
|
224
|
+
|
|
225
|
+
### Box Selection
|
|
226
|
+
|
|
227
|
+
Click and drag on empty space to select multiple elements:
|
|
228
|
+
|
|
229
|
+
```html live
|
|
230
|
+
<div class="relative w-full h-[400px] border border-gray-300 rounded overflow-hidden">
|
|
231
|
+
<ef-pan-zoom class="w-full h-full">
|
|
232
|
+
<ef-canvas class="w-[1200px] h-[800px] bg-gray-100">
|
|
233
|
+
<div id="item-1" class="absolute top-12 left-12 w-24 h-24 bg-blue-500 rounded"></div>
|
|
234
|
+
<div id="item-2" class="absolute top-12 left-48 w-24 h-24 bg-green-500 rounded"></div>
|
|
235
|
+
<div id="item-3" class="absolute top-12 left-84 w-24 h-24 bg-red-500 rounded"></div>
|
|
236
|
+
<div id="item-4" class="absolute top-48 left-12 w-24 h-24 bg-purple-500 rounded"></div>
|
|
237
|
+
<div id="item-5" class="absolute top-48 left-48 w-24 h-24 bg-yellow-500 rounded"></div>
|
|
238
|
+
<div id="item-6" class="absolute top-48 left-84 w-24 h-24 bg-pink-500 rounded"></div>
|
|
239
|
+
</ef-canvas>
|
|
240
|
+
</ef-pan-zoom>
|
|
241
|
+
</div>
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
Drag on empty space to draw selection box. Hold **Shift** to add to existing selection.
|
|
245
|
+
<!-- /html-only -->
|
|
246
|
+
<!-- react-only -->
|
|
247
|
+
### Multi-Select
|
|
248
|
+
|
|
249
|
+
```tsx
|
|
250
|
+
export const MultiSelectCanvas = () => {
|
|
251
|
+
const [selectedIds, setSelectedIds] = useState<string[]>([]);
|
|
252
|
+
|
|
253
|
+
useEffect(() => {
|
|
254
|
+
const canvas = document.getElementById("canvas");
|
|
255
|
+
if (!canvas) return;
|
|
256
|
+
|
|
257
|
+
const handleSelectionChange = (e: CustomEvent) => {
|
|
258
|
+
setSelectedIds(Array.from(e.detail.selectedIds || []));
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
canvas.addEventListener("selectionchange" as any, handleSelectionChange);
|
|
262
|
+
return () => {
|
|
263
|
+
canvas.removeEventListener("selectionchange" as any, handleSelectionChange);
|
|
264
|
+
};
|
|
265
|
+
}, []);
|
|
266
|
+
|
|
267
|
+
return (
|
|
268
|
+
<div>
|
|
269
|
+
<div className="p-4 bg-gray-100">
|
|
270
|
+
<div className="font-semibold mb-2">Selected Elements:</div>
|
|
271
|
+
{selectedIds.length === 0 ? (
|
|
272
|
+
<div className="text-gray-500">None</div>
|
|
273
|
+
) : (
|
|
274
|
+
<ul className="list-disc list-inside">
|
|
275
|
+
{selectedIds.map(id => (
|
|
276
|
+
<li key={id}>{id}</li>
|
|
277
|
+
))}
|
|
278
|
+
</ul>
|
|
279
|
+
)}
|
|
280
|
+
</div>
|
|
281
|
+
|
|
282
|
+
<ef-canvas id="canvas">
|
|
283
|
+
<ef-timegroup mode="contain" className="w-[1920px] h-[1080px]">
|
|
284
|
+
<ef-canvas-item data-element-id="video-1">
|
|
285
|
+
<ef-video src="/assets/video1.mp4" />
|
|
286
|
+
</ef-canvas-item>
|
|
287
|
+
<ef-canvas-item data-element-id="video-2">
|
|
288
|
+
<ef-video src="/assets/video2.mp4" />
|
|
289
|
+
</ef-canvas-item>
|
|
290
|
+
<ef-canvas-item data-element-id="text-1">
|
|
291
|
+
<ef-text duration="5s">Title</ef-text>
|
|
292
|
+
</ef-canvas-item>
|
|
293
|
+
</ef-timegroup>
|
|
294
|
+
</ef-canvas>
|
|
295
|
+
</div>
|
|
296
|
+
);
|
|
297
|
+
};
|
|
298
|
+
```
|
|
299
|
+
<!-- /react-only -->
|
|
300
|
+
|
|
301
|
+
## Drag and Drop
|
|
302
|
+
|
|
303
|
+
<!-- html-only -->
|
|
304
|
+
Canvas supports dragging selected elements:
|
|
305
|
+
|
|
306
|
+
### Single Element Drag
|
|
307
|
+
|
|
308
|
+
Click and drag an element to move it:
|
|
309
|
+
|
|
310
|
+
```javascript
|
|
311
|
+
// Element position updates automatically
|
|
312
|
+
// No event handlers needed
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
### Multi-Element Drag
|
|
316
|
+
|
|
317
|
+
Select multiple elements and drag any selected element to move all:
|
|
318
|
+
|
|
319
|
+
```javascript
|
|
320
|
+
const canvas = document.querySelector('ef-canvas');
|
|
321
|
+
|
|
322
|
+
// Select multiple elements
|
|
323
|
+
canvas.selectionContext.select('box-1');
|
|
324
|
+
canvas.selectionContext.addToSelection('box-2');
|
|
325
|
+
canvas.selectionContext.addToSelection('box-3');
|
|
326
|
+
|
|
327
|
+
// Drag any selected element - all move together
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
### Drag Threshold
|
|
331
|
+
|
|
332
|
+
Canvas uses a 5-pixel drag threshold to distinguish clicks from drags.
|
|
333
|
+
<!-- /html-only -->
|
|
334
|
+
|
|
335
|
+
## Transform Handles
|
|
336
|
+
|
|
337
|
+
<!-- html-only -->
|
|
338
|
+
Canvas shows transform handles for selected elements:
|
|
339
|
+
|
|
340
|
+
### Single Selection
|
|
341
|
+
|
|
342
|
+
Handles for resize and rotate:
|
|
343
|
+
|
|
344
|
+
- **Corner handles**: Resize proportionally
|
|
345
|
+
- **Edge handles**: Resize in one dimension
|
|
346
|
+
- **Rotation handle**: Rotate around center
|
|
347
|
+
|
|
348
|
+
### Multi-Selection
|
|
349
|
+
|
|
350
|
+
Handles for the bounding box:
|
|
351
|
+
|
|
352
|
+
- **Corner handles**: Scale all elements proportionally
|
|
353
|
+
- **Rotation handle**: Rotate all elements around group center
|
|
354
|
+
|
|
355
|
+
### Disable Handles
|
|
356
|
+
|
|
357
|
+
```html
|
|
358
|
+
<ef-canvas enable-transform-handles="false">
|
|
359
|
+
<!-- No transform handles shown -->
|
|
360
|
+
</ef-canvas>
|
|
361
|
+
```
|
|
362
|
+
<!-- /html-only -->
|
|
363
|
+
|
|
364
|
+
## Programmatic Control
|
|
365
|
+
|
|
366
|
+
<!-- html-only -->
|
|
367
|
+
Control canvas selection via JavaScript:
|
|
368
|
+
|
|
369
|
+
```javascript
|
|
370
|
+
const canvas = document.querySelector('ef-canvas');
|
|
371
|
+
const { selectionContext } = canvas;
|
|
372
|
+
|
|
373
|
+
// Select single element
|
|
374
|
+
selectionContext.select('element-id');
|
|
375
|
+
|
|
376
|
+
// Add to selection
|
|
377
|
+
selectionContext.addToSelection('another-id');
|
|
378
|
+
|
|
379
|
+
// Toggle selection
|
|
380
|
+
selectionContext.toggle('element-id');
|
|
381
|
+
|
|
382
|
+
// Clear selection
|
|
383
|
+
selectionContext.clear();
|
|
384
|
+
|
|
385
|
+
// Get selected IDs
|
|
386
|
+
const selected = Array.from(selectionContext.selectedIds);
|
|
387
|
+
```
|
|
388
|
+
<!-- /html-only -->
|
|
389
|
+
<!-- react-only -->
|
|
390
|
+
```tsx
|
|
391
|
+
export const ProgrammaticCanvas = () => {
|
|
392
|
+
const canvasRef = useRef<any>(null);
|
|
393
|
+
|
|
394
|
+
const selectElement = (id: string) => {
|
|
395
|
+
if (canvasRef.current) {
|
|
396
|
+
canvasRef.current.selectElement(id);
|
|
397
|
+
}
|
|
398
|
+
};
|
|
399
|
+
|
|
400
|
+
const getSelection = () => {
|
|
401
|
+
if (canvasRef.current) {
|
|
402
|
+
const selected = canvasRef.current.getSelectedElements();
|
|
403
|
+
console.log("Selected elements:", selected);
|
|
404
|
+
}
|
|
405
|
+
};
|
|
406
|
+
|
|
407
|
+
return (
|
|
408
|
+
<div>
|
|
409
|
+
<div className="p-4 space-x-2">
|
|
410
|
+
<button onClick={() => selectElement("video-1")}>Select Video</button>
|
|
411
|
+
<button onClick={() => selectElement("text-1")}>Select Text</button>
|
|
412
|
+
<button onClick={getSelection}>Get Selection</button>
|
|
413
|
+
</div>
|
|
414
|
+
|
|
415
|
+
<ef-canvas ref={canvasRef} id="canvas">
|
|
416
|
+
<ef-timegroup mode="contain" className="w-[1920px] h-[1080px]">
|
|
417
|
+
<ef-canvas-item data-element-id="video-1">
|
|
418
|
+
<ef-video src="/assets/video.mp4" />
|
|
419
|
+
</ef-canvas-item>
|
|
420
|
+
<ef-canvas-item data-element-id="text-1">
|
|
421
|
+
<ef-text duration="5s">Text</ef-text>
|
|
422
|
+
</ef-canvas-item>
|
|
423
|
+
</ef-timegroup>
|
|
424
|
+
</ef-canvas>
|
|
425
|
+
</div>
|
|
426
|
+
);
|
|
427
|
+
};
|
|
428
|
+
```
|
|
429
|
+
<!-- /react-only -->
|
|
430
|
+
|
|
431
|
+
<!-- react-only -->
|
|
432
|
+
## Update Element Bounds
|
|
433
|
+
|
|
434
|
+
```tsx
|
|
435
|
+
export const BoundsControl = () => {
|
|
436
|
+
const canvasRef = useRef<any>(null);
|
|
437
|
+
|
|
438
|
+
const updatePosition = (elementId: string, x: number, y: number) => {
|
|
439
|
+
if (canvasRef.current) {
|
|
440
|
+
canvasRef.current.updateElementBounds(elementId, {
|
|
441
|
+
x,
|
|
442
|
+
y,
|
|
443
|
+
width: 400,
|
|
444
|
+
height: 300
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
};
|
|
448
|
+
|
|
449
|
+
return (
|
|
450
|
+
<div>
|
|
451
|
+
<div className="p-4">
|
|
452
|
+
<button onClick={() => updatePosition("video-1", 100, 100)}>
|
|
453
|
+
Position 1
|
|
454
|
+
</button>
|
|
455
|
+
<button onClick={() => updatePosition("video-1", 500, 200)}>
|
|
456
|
+
Position 2
|
|
457
|
+
</button>
|
|
458
|
+
</div>
|
|
459
|
+
|
|
460
|
+
<ef-canvas ref={canvasRef}>
|
|
461
|
+
<ef-timegroup mode="contain" className="w-[1920px] h-[1080px]">
|
|
462
|
+
<ef-canvas-item data-element-id="video-1">
|
|
463
|
+
<ef-video src="/assets/video.mp4" />
|
|
464
|
+
</ef-canvas-item>
|
|
465
|
+
</ef-timegroup>
|
|
466
|
+
</ef-canvas>
|
|
467
|
+
</div>
|
|
468
|
+
);
|
|
469
|
+
};
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
## Editor with Canvas
|
|
473
|
+
|
|
474
|
+
```tsx
|
|
475
|
+
export const CanvasEditor = () => {
|
|
476
|
+
return (
|
|
477
|
+
<div className="h-screen grid grid-cols-[250px_1fr]">
|
|
478
|
+
{/* Hierarchy panel */}
|
|
479
|
+
<div className="border-r bg-gray-50">
|
|
480
|
+
<ef-hierarchy target="editor-canvas" header="Layers" show-header />
|
|
481
|
+
</div>
|
|
482
|
+
|
|
483
|
+
{/* Canvas area */}
|
|
484
|
+
<div className="flex flex-col">
|
|
485
|
+
<div className="flex-1 p-4 bg-gray-900">
|
|
486
|
+
<ef-preview />
|
|
487
|
+
</div>
|
|
488
|
+
|
|
489
|
+
<ef-canvas id="editor-canvas" enable-transform-handles>
|
|
490
|
+
<ef-timegroup mode="sequence" className="w-[1920px] h-[1080px]">
|
|
491
|
+
<ef-canvas-item data-element-id="bg-video">
|
|
492
|
+
<ef-video
|
|
493
|
+
src="/assets/background.mp4"
|
|
494
|
+
className="absolute inset-0 size-full"
|
|
495
|
+
/>
|
|
496
|
+
</ef-canvas-item>
|
|
497
|
+
|
|
498
|
+
<ef-canvas-item data-element-id="title">
|
|
499
|
+
<ef-text
|
|
500
|
+
duration="5s"
|
|
501
|
+
className="absolute top-20 left-20 text-white text-5xl"
|
|
502
|
+
>
|
|
503
|
+
Video Title
|
|
504
|
+
</ef-text>
|
|
505
|
+
</ef-canvas-item>
|
|
506
|
+
</ef-timegroup>
|
|
507
|
+
</ef-canvas>
|
|
508
|
+
</div>
|
|
509
|
+
</div>
|
|
510
|
+
);
|
|
511
|
+
};
|
|
512
|
+
```
|
|
513
|
+
<!-- /react-only -->
|
|
514
|
+
|
|
515
|
+
## Element Registration
|
|
516
|
+
|
|
517
|
+
<!-- html-only -->
|
|
518
|
+
Canvas automatically registers child elements:
|
|
519
|
+
|
|
520
|
+
### Automatic Registration
|
|
521
|
+
|
|
522
|
+
All child elements are auto-registered with auto-generated IDs:
|
|
523
|
+
|
|
524
|
+
```html
|
|
525
|
+
<ef-canvas>
|
|
526
|
+
<div>Auto-registered with generated ID</div>
|
|
527
|
+
</ef-canvas>
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
### Manual Registration
|
|
531
|
+
|
|
532
|
+
Provide explicit IDs for stable references:
|
|
533
|
+
|
|
534
|
+
```html
|
|
535
|
+
<ef-canvas>
|
|
536
|
+
<div id="my-element">Registered as 'my-element'</div>
|
|
537
|
+
</ef-canvas>
|
|
538
|
+
```
|
|
539
|
+
|
|
540
|
+
### Programmatic Registration
|
|
541
|
+
|
|
542
|
+
Register elements via JavaScript:
|
|
543
|
+
|
|
544
|
+
```javascript
|
|
545
|
+
const canvas = document.querySelector('ef-canvas');
|
|
546
|
+
const element = document.createElement('div');
|
|
547
|
+
|
|
548
|
+
// Register with auto-generated ID
|
|
549
|
+
canvas.tryRegisterElement(element);
|
|
550
|
+
|
|
551
|
+
// Register with explicit ID
|
|
552
|
+
const id = canvas.registerElement(element, 'my-custom-id');
|
|
553
|
+
|
|
554
|
+
// Unregister
|
|
555
|
+
canvas.unregisterElement('my-custom-id');
|
|
556
|
+
```
|
|
557
|
+
<!-- /html-only -->
|
|
558
|
+
<!-- react-only -->
|
|
559
|
+
Canvas identifies elements using data attributes:
|
|
560
|
+
|
|
561
|
+
- `data-element-id` - Primary element identifier (default)
|
|
562
|
+
- `data-timegroup-id` - For timegroup elements
|
|
563
|
+
- Custom attribute via `element-id-attribute` prop
|
|
564
|
+
<!-- /react-only -->
|
|
565
|
+
|
|
566
|
+
<!-- html-only -->
|
|
567
|
+
## Position Management
|
|
568
|
+
|
|
569
|
+
Update element positions in canvas coordinates:
|
|
570
|
+
|
|
571
|
+
```javascript
|
|
572
|
+
const canvas = document.querySelector('ef-canvas');
|
|
573
|
+
|
|
574
|
+
// Update position (canvas coordinates)
|
|
575
|
+
canvas.updateElementPosition('element-id', 100, 200);
|
|
576
|
+
|
|
577
|
+
// Get element metadata
|
|
578
|
+
const data = canvas.getElementData('element-id');
|
|
579
|
+
console.log('Position:', data.x, data.y);
|
|
580
|
+
console.log('Size:', data.width, data.height);
|
|
581
|
+
console.log('Rotation:', data.rotation);
|
|
582
|
+
|
|
583
|
+
// Get all elements
|
|
584
|
+
const allElements = canvas.getAllElementsData();
|
|
585
|
+
```
|
|
586
|
+
|
|
587
|
+
## Coordinate Conversion
|
|
588
|
+
|
|
589
|
+
Convert between screen and canvas coordinates:
|
|
590
|
+
|
|
591
|
+
```javascript
|
|
592
|
+
const canvas = document.querySelector('ef-canvas');
|
|
593
|
+
|
|
594
|
+
// Screen to canvas
|
|
595
|
+
const canvasPos = canvas.screenToCanvasCoords(clientX, clientY);
|
|
596
|
+
|
|
597
|
+
// Canvas to screen
|
|
598
|
+
const screenPos = canvas.canvasToScreenCoords(canvasX, canvasY);
|
|
599
|
+
```
|
|
600
|
+
|
|
601
|
+
Coordinates account for pan/zoom transforms.
|
|
602
|
+
|
|
603
|
+
## Hover Highlighting
|
|
604
|
+
|
|
605
|
+
Canvas tracks hovered elements:
|
|
606
|
+
|
|
607
|
+
```javascript
|
|
608
|
+
const canvas = document.querySelector('ef-canvas');
|
|
609
|
+
|
|
610
|
+
// Get highlighted element
|
|
611
|
+
console.log(canvas.highlightedElement);
|
|
612
|
+
|
|
613
|
+
// Programmatically highlight
|
|
614
|
+
const element = document.getElementById('my-element');
|
|
615
|
+
canvas.setHighlightedElement(element);
|
|
616
|
+
|
|
617
|
+
// Clear highlight
|
|
618
|
+
canvas.setHighlightedElement(null);
|
|
619
|
+
```
|
|
620
|
+
|
|
621
|
+
Highlighted elements get `data-highlighted` attribute for styling:
|
|
622
|
+
|
|
623
|
+
```css
|
|
624
|
+
[data-highlighted] {
|
|
625
|
+
outline: 2px solid blue;
|
|
626
|
+
}
|
|
627
|
+
```
|
|
628
|
+
|
|
629
|
+
## Selection Styling
|
|
630
|
+
|
|
631
|
+
Selected elements get `data-selected` attribute:
|
|
632
|
+
|
|
633
|
+
```css
|
|
634
|
+
[data-selected] {
|
|
635
|
+
outline: 2px solid #2196f3;
|
|
636
|
+
}
|
|
637
|
+
```
|
|
638
|
+
|
|
639
|
+
## Pan/Zoom Integration
|
|
640
|
+
|
|
641
|
+
Canvas works seamlessly with ef-pan-zoom:
|
|
642
|
+
|
|
643
|
+
```html
|
|
644
|
+
<ef-pan-zoom>
|
|
645
|
+
<ef-canvas>
|
|
646
|
+
<!-- Elements stay correctly positioned during pan/zoom -->
|
|
647
|
+
</ef-canvas>
|
|
648
|
+
</ef-pan-zoom>
|
|
649
|
+
```
|
|
650
|
+
|
|
651
|
+
Canvas automatically:
|
|
652
|
+
|
|
653
|
+
- Converts coordinates accounting for zoom
|
|
654
|
+
- Updates transform handles
|
|
655
|
+
- Maintains correct hit testing
|
|
656
|
+
|
|
657
|
+
## Active Root Temporal
|
|
658
|
+
|
|
659
|
+
Canvas tracks the root temporal element containing selection:
|
|
660
|
+
|
|
661
|
+
```javascript
|
|
662
|
+
const canvas = document.querySelector('ef-canvas');
|
|
663
|
+
|
|
664
|
+
// Get active root temporal
|
|
665
|
+
console.log(canvas.activeRootTemporal);
|
|
666
|
+
|
|
667
|
+
// Listen for changes
|
|
668
|
+
canvas.addEventListener('activeroottemporalchange', (e) => {
|
|
669
|
+
console.log('Active root:', e.detail.activeRootTemporal);
|
|
670
|
+
});
|
|
671
|
+
```
|
|
672
|
+
|
|
673
|
+
Useful for timeline scrubbing and playback controls.
|
|
674
|
+
|
|
675
|
+
## Nested Elements
|
|
676
|
+
|
|
677
|
+
Canvas supports nested element hierarchies:
|
|
678
|
+
|
|
679
|
+
```html
|
|
680
|
+
<ef-canvas>
|
|
681
|
+
<div id="parent" class="absolute top-0 left-0 w-64 h-48">
|
|
682
|
+
<div id="child" class="absolute top-4 left-4 w-32 h-24">
|
|
683
|
+
<!-- Nested element -->
|
|
684
|
+
</div>
|
|
685
|
+
</div>
|
|
686
|
+
</ef-canvas>
|
|
687
|
+
```
|
|
688
|
+
|
|
689
|
+
Both parent and child are selectable and draggable.
|
|
690
|
+
|
|
691
|
+
## Element Metadata
|
|
692
|
+
|
|
693
|
+
Canvas tracks element bounds and transforms:
|
|
694
|
+
|
|
695
|
+
```typescript
|
|
696
|
+
interface CanvasElementData {
|
|
697
|
+
id: string;
|
|
698
|
+
element: HTMLElement;
|
|
699
|
+
x: number; // Top-left x in canvas coordinates
|
|
700
|
+
y: number; // Top-left y in canvas coordinates
|
|
701
|
+
width: number; // Width in canvas units
|
|
702
|
+
height: number; // Height in canvas units
|
|
703
|
+
rotation?: number; // Rotation in degrees
|
|
704
|
+
}
|
|
705
|
+
```
|
|
706
|
+
|
|
707
|
+
Metadata updates automatically when elements change.
|
|
708
|
+
|
|
709
|
+
## Coordinate System
|
|
710
|
+
|
|
711
|
+
Canvas uses a unified coordinate calculation:
|
|
712
|
+
|
|
713
|
+
- **Dimensions**: From `offsetWidth`/`offsetHeight` (unaffected by transforms)
|
|
714
|
+
- **Center**: From `getBoundingClientRect()` center (rotation-invariant)
|
|
715
|
+
- **Top-left**: Calculated from center minus half dimensions
|
|
716
|
+
|
|
717
|
+
This approach works correctly for rotated, scaled, and nested elements.
|
|
718
|
+
|
|
719
|
+
## Performance
|
|
720
|
+
|
|
721
|
+
Canvas optimizes for interactive editing:
|
|
722
|
+
|
|
723
|
+
- Single RAF loop for overlays and handles
|
|
724
|
+
- Pointer capture during drag operations
|
|
725
|
+
- Efficient hit testing with z-order respect
|
|
726
|
+
- No change detection overhead
|
|
727
|
+
|
|
728
|
+
## Deprecated: ef-canvas-item
|
|
729
|
+
|
|
730
|
+
The `ef-canvas-item` wrapper is deprecated. Use plain HTML elements:
|
|
731
|
+
|
|
732
|
+
```html
|
|
733
|
+
<!-- Old (deprecated) -->
|
|
734
|
+
<ef-canvas>
|
|
735
|
+
<ef-canvas-item id="item-1">
|
|
736
|
+
<div>Content</div>
|
|
737
|
+
</ef-canvas-item>
|
|
738
|
+
</ef-canvas>
|
|
739
|
+
|
|
740
|
+
<!-- New (recommended) -->
|
|
741
|
+
<ef-canvas>
|
|
742
|
+
<div id="item-1">Content</div>
|
|
743
|
+
</ef-canvas>
|
|
744
|
+
```
|
|
745
|
+
|
|
746
|
+
All DOM nodes in canvas are now automatically selectable.
|
|
747
|
+
<!-- /html-only -->
|
|
748
|
+
|
|
749
|
+
## Events
|
|
750
|
+
|
|
751
|
+
<!-- html-only -->
|
|
752
|
+
### activeroottemporalchange
|
|
753
|
+
|
|
754
|
+
Fired when active root temporal changes. See the Active Root Temporal section above.
|
|
755
|
+
<!-- /html-only -->
|
|
756
|
+
<!-- react-only -->
|
|
757
|
+
### selectionchange
|
|
758
|
+
|
|
759
|
+
Fired when selection changes:
|
|
760
|
+
|
|
761
|
+
```typescript
|
|
762
|
+
interface SelectionChangeDetail {
|
|
763
|
+
selectedIds: Set<string>;
|
|
764
|
+
previousIds: Set<string>;
|
|
765
|
+
}
|
|
766
|
+
```
|
|
767
|
+
|
|
768
|
+
### elementmoved
|
|
769
|
+
|
|
770
|
+
Fired when element is dragged:
|
|
771
|
+
|
|
772
|
+
```typescript
|
|
773
|
+
interface ElementMovedDetail {
|
|
774
|
+
elementId: string;
|
|
775
|
+
x: number;
|
|
776
|
+
y: number;
|
|
777
|
+
}
|
|
778
|
+
```
|
|
779
|
+
|
|
780
|
+
### elementresized
|
|
781
|
+
|
|
782
|
+
Fired when element is resized:
|
|
783
|
+
|
|
784
|
+
```typescript
|
|
785
|
+
interface ElementResizedDetail {
|
|
786
|
+
elementId: string;
|
|
787
|
+
width: number;
|
|
788
|
+
height: number;
|
|
789
|
+
}
|
|
790
|
+
```
|
|
791
|
+
|
|
792
|
+
### elementrotated
|
|
793
|
+
|
|
794
|
+
Fired when element is rotated:
|
|
795
|
+
|
|
796
|
+
```typescript
|
|
797
|
+
interface ElementRotatedDetail {
|
|
798
|
+
elementId: string;
|
|
799
|
+
rotation: number;
|
|
800
|
+
}
|
|
801
|
+
```
|
|
802
|
+
|
|
803
|
+
## Methods
|
|
804
|
+
|
|
805
|
+
### selectElement()
|
|
806
|
+
|
|
807
|
+
Select an element by ID:
|
|
808
|
+
|
|
809
|
+
```tsx
|
|
810
|
+
canvas.selectElement("element-id");
|
|
811
|
+
```
|
|
812
|
+
|
|
813
|
+
### getSelectedElements()
|
|
814
|
+
|
|
815
|
+
Get selected elements:
|
|
816
|
+
|
|
817
|
+
```tsx
|
|
818
|
+
const elements = canvas.getSelectedElements();
|
|
819
|
+
// Returns: HTMLElement[]
|
|
820
|
+
```
|
|
821
|
+
|
|
822
|
+
### updateElementBounds()
|
|
823
|
+
|
|
824
|
+
Update element transform:
|
|
825
|
+
|
|
826
|
+
```tsx
|
|
827
|
+
canvas.updateElementBounds("element-id", {
|
|
828
|
+
x: 100,
|
|
829
|
+
y: 100,
|
|
830
|
+
width: 400,
|
|
831
|
+
height: 300,
|
|
832
|
+
rotation: 45
|
|
833
|
+
});
|
|
834
|
+
```
|
|
835
|
+
|
|
836
|
+
### clearSelection()
|
|
837
|
+
|
|
838
|
+
Clear all selections:
|
|
839
|
+
|
|
840
|
+
```tsx
|
|
841
|
+
canvas.clearSelection();
|
|
842
|
+
```
|
|
843
|
+
|
|
844
|
+
### deleteSelected()
|
|
845
|
+
|
|
846
|
+
Delete selected elements:
|
|
847
|
+
|
|
848
|
+
```tsx
|
|
849
|
+
canvas.deleteSelected();
|
|
850
|
+
```
|
|
851
|
+
<!-- /react-only -->
|
|
852
|
+
|
|
853
|
+
<!-- react-only -->
|
|
854
|
+
## CSS Customization
|
|
855
|
+
|
|
856
|
+
```css
|
|
857
|
+
ef-canvas {
|
|
858
|
+
--canvas-bg: #1f2937;
|
|
859
|
+
--canvas-grid-color: rgba(255, 255, 255, 0.1);
|
|
860
|
+
--canvas-selection-color: #3b82f6;
|
|
861
|
+
--canvas-highlight-color: #10b981;
|
|
862
|
+
}
|
|
863
|
+
```
|
|
864
|
+
|
|
865
|
+
## Canvas Features
|
|
866
|
+
|
|
867
|
+
- **Click to select**: Click elements to select them
|
|
868
|
+
- **Drag to move**: Drag selected elements to reposition
|
|
869
|
+
- **Transform handles**: Resize and rotate with handles
|
|
870
|
+
- **Multi-select**: Hold Cmd/Ctrl to select multiple elements
|
|
871
|
+
- **Keyboard shortcuts**: Arrow keys to nudge, Delete to remove
|
|
872
|
+
- **Selection box**: Click and drag background for box selection
|
|
873
|
+
- **Snap to grid**: Optional grid snapping for precise placement
|
|
874
|
+
|
|
875
|
+
## CanvasItem Wrapper
|
|
876
|
+
|
|
877
|
+
`ef-canvas-item` is a wrapper that makes elements interactive:
|
|
878
|
+
|
|
879
|
+
- Registers element with canvas for selection
|
|
880
|
+
- Provides drag-and-drop behavior
|
|
881
|
+
- Manages transform handles display
|
|
882
|
+
- Tracks element bounds for manipulation
|
|
883
|
+
- Forwards pointer events to canvas
|
|
884
|
+
|
|
885
|
+
## Keyboard Shortcuts
|
|
886
|
+
|
|
887
|
+
- **Arrow keys**: Nudge selected element (1px or 10px with Shift)
|
|
888
|
+
- **Delete/Backspace**: Delete selected elements
|
|
889
|
+
- **Cmd/Ctrl + A**: Select all
|
|
890
|
+
- **Cmd/Ctrl + D**: Duplicate selected
|
|
891
|
+
- **Escape**: Clear selection
|
|
892
|
+
- **Cmd/Ctrl + Z**: Undo (if implemented)
|
|
893
|
+
|
|
894
|
+
## Integration Patterns
|
|
895
|
+
|
|
896
|
+
### With Hierarchy
|
|
897
|
+
|
|
898
|
+
```tsx
|
|
899
|
+
<div className="flex">
|
|
900
|
+
<ef-hierarchy target="canvas" />
|
|
901
|
+
<ef-canvas id="canvas">
|
|
902
|
+
{/* content */}
|
|
903
|
+
</ef-canvas>
|
|
904
|
+
</div>
|
|
905
|
+
```
|
|
906
|
+
|
|
907
|
+
### With OverlayLayer
|
|
908
|
+
|
|
909
|
+
```tsx
|
|
910
|
+
<ef-canvas>
|
|
911
|
+
<ef-overlay-layer>
|
|
912
|
+
<ef-overlay-item element-id="selected">
|
|
913
|
+
<ef-transform-handles />
|
|
914
|
+
</ef-overlay-item>
|
|
915
|
+
</ef-overlay-layer>
|
|
916
|
+
|
|
917
|
+
{/* content */}
|
|
918
|
+
</ef-canvas>
|
|
919
|
+
```
|
|
920
|
+
|
|
921
|
+
## Important Notes
|
|
922
|
+
|
|
923
|
+
- Canvas and CanvasItem are not yet exported from React package
|
|
924
|
+
- Use HTML element syntax in React until official export
|
|
925
|
+
- Canvas provides selection context via Lit context system
|
|
926
|
+
- Transform handles require `enable-transform-handles` prop
|
|
927
|
+
- Elements must be wrapped in `ef-canvas-item` to be interactive
|
|
928
|
+
- Canvas automatically manages overlay positioning
|
|
929
|
+
- Supports nested timegroups and complex hierarchies
|
|
930
|
+
- Selection state syncs with Hierarchy component
|
|
931
|
+
|
|
932
|
+
## Future React Components
|
|
933
|
+
|
|
934
|
+
When officially exported, usage will be:
|
|
935
|
+
|
|
936
|
+
```tsx
|
|
937
|
+
import { Canvas, CanvasItem } from "@editframe/react";
|
|
938
|
+
|
|
939
|
+
<Canvas enableTransformHandles onSelectionChange={handleChange}>
|
|
940
|
+
<Timegroup mode="contain" className="w-[1920px] h-[1080px]">
|
|
941
|
+
<CanvasItem elementId="video-1">
|
|
942
|
+
<Video src="/assets/video.mp4" />
|
|
943
|
+
</CanvasItem>
|
|
944
|
+
</Timegroup>
|
|
945
|
+
</Canvas>
|
|
946
|
+
```
|
|
947
|
+
<!-- /react-only -->
|