@editframe/elements 0.21.0-beta.0 → 0.23.7-beta.0
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.js +2 -3
- package/dist/attachContextRoot.d.ts +1 -0
- package/dist/attachContextRoot.js +9 -0
- package/dist/elements/ContextProxiesController.d.ts +1 -2
- package/dist/elements/EFAudio.js +2 -2
- package/dist/elements/EFCaptions.d.ts +1 -3
- package/dist/elements/EFCaptions.js +59 -51
- package/dist/elements/EFImage.js +2 -2
- package/dist/elements/EFMedia/AssetIdMediaEngine.js +1 -2
- package/dist/elements/EFMedia/AssetMediaEngine.js +1 -3
- package/dist/elements/EFMedia/BufferedSeekingInput.d.ts +1 -1
- package/dist/elements/EFMedia/BufferedSeekingInput.js +2 -4
- package/dist/elements/EFMedia/audioTasks/makeAudioBufferTask.js +4 -7
- package/dist/elements/EFMedia/audioTasks/makeAudioFrequencyAnalysisTask.js +1 -2
- package/dist/elements/EFMedia/shared/AudioSpanUtils.js +5 -9
- package/dist/elements/EFMedia/shared/BufferUtils.js +1 -3
- package/dist/elements/EFMedia/videoTasks/makeVideoBufferTask.js +4 -7
- package/dist/elements/EFMedia.d.ts +19 -0
- package/dist/elements/EFMedia.js +19 -2
- package/dist/elements/EFSourceMixin.js +1 -1
- package/dist/elements/EFSurface.js +1 -1
- package/dist/elements/EFTemporal.browsertest.d.ts +11 -0
- package/dist/elements/EFTemporal.d.ts +10 -0
- package/dist/elements/EFTemporal.js +82 -5
- package/dist/elements/EFThumbnailStrip.js +9 -16
- package/dist/elements/EFTimegroup.browsertest.d.ts +3 -3
- package/dist/elements/EFTimegroup.d.ts +35 -14
- package/dist/elements/EFTimegroup.js +73 -120
- package/dist/elements/EFVideo.d.ts +10 -0
- package/dist/elements/EFVideo.js +15 -2
- package/dist/elements/EFWaveform.js +10 -18
- package/dist/elements/SampleBuffer.js +1 -2
- package/dist/elements/TargetController.js +2 -2
- package/dist/elements/renderTemporalAudio.d.ts +10 -0
- package/dist/elements/renderTemporalAudio.js +35 -0
- package/dist/elements/updateAnimations.js +7 -10
- package/dist/gui/ContextMixin.d.ts +5 -5
- package/dist/gui/ContextMixin.js +151 -117
- package/dist/gui/Controllable.browsertest.d.ts +0 -0
- package/dist/gui/Controllable.d.ts +15 -0
- package/dist/gui/Controllable.js +9 -0
- package/dist/gui/EFConfiguration.js +1 -1
- package/dist/gui/EFControls.browsertest.d.ts +11 -0
- package/dist/gui/EFControls.d.ts +18 -4
- package/dist/gui/EFControls.js +67 -25
- package/dist/gui/EFDial.browsertest.d.ts +0 -0
- package/dist/gui/EFDial.d.ts +18 -0
- package/dist/gui/EFDial.js +141 -0
- package/dist/gui/EFFilmstrip.browsertest.d.ts +11 -0
- package/dist/gui/EFFilmstrip.d.ts +12 -2
- package/dist/gui/EFFilmstrip.js +140 -34
- package/dist/gui/EFFitScale.js +2 -4
- package/dist/gui/EFFocusOverlay.js +1 -1
- package/dist/gui/EFPause.browsertest.d.ts +0 -0
- package/dist/gui/EFPause.d.ts +23 -0
- package/dist/gui/EFPause.js +59 -0
- package/dist/gui/EFPlay.browsertest.d.ts +0 -0
- package/dist/gui/EFPlay.d.ts +23 -0
- package/dist/gui/EFPlay.js +59 -0
- package/dist/gui/EFPreview.d.ts +4 -0
- package/dist/gui/EFPreview.js +15 -6
- package/dist/gui/EFResizableBox.browsertest.d.ts +0 -0
- package/dist/gui/EFResizableBox.d.ts +34 -0
- package/dist/gui/EFResizableBox.js +547 -0
- package/dist/gui/EFScrubber.d.ts +9 -3
- package/dist/gui/EFScrubber.js +7 -7
- package/dist/gui/EFTimeDisplay.d.ts +7 -1
- package/dist/gui/EFTimeDisplay.js +5 -5
- package/dist/gui/EFToggleLoop.d.ts +9 -3
- package/dist/gui/EFToggleLoop.js +6 -4
- package/dist/gui/EFTogglePlay.d.ts +12 -4
- package/dist/gui/EFTogglePlay.js +24 -19
- package/dist/gui/EFWorkbench.js +1 -1
- package/dist/gui/PlaybackController.d.ts +67 -0
- package/dist/gui/PlaybackController.js +310 -0
- package/dist/gui/TWMixin.js +1 -1
- package/dist/gui/TargetOrContextMixin.d.ts +10 -0
- package/dist/gui/TargetOrContextMixin.js +98 -0
- package/dist/gui/efContext.d.ts +2 -2
- package/dist/index.d.ts +4 -0
- package/dist/index.js +5 -1
- package/dist/otel/setupBrowserTracing.d.ts +1 -1
- package/dist/otel/setupBrowserTracing.js +6 -4
- package/dist/otel/tracingHelpers.js +1 -2
- package/dist/style.css +1 -1
- package/package.json +5 -5
- package/src/elements/ContextProxiesController.ts +10 -10
- package/src/elements/EFAudio.ts +1 -0
- package/src/elements/EFCaptions.browsertest.ts +128 -58
- package/src/elements/EFCaptions.ts +60 -34
- package/src/elements/EFImage.browsertest.ts +1 -2
- package/src/elements/EFMedia/JitMediaEngine.browsertest.ts +3 -0
- package/src/elements/EFMedia/audioTasks/makeAudioSeekTask.chunkboundary.regression.browsertest.ts +1 -1
- package/src/elements/EFMedia.browsertest.ts +8 -15
- package/src/elements/EFMedia.ts +38 -7
- package/src/elements/EFSurface.browsertest.ts +2 -6
- package/src/elements/EFSurface.ts +1 -0
- package/src/elements/EFTemporal.browsertest.ts +58 -1
- package/src/elements/EFTemporal.ts +140 -4
- package/src/elements/EFThumbnailStrip.browsertest.ts +2 -8
- package/src/elements/EFThumbnailStrip.ts +1 -0
- package/src/elements/EFTimegroup.browsertest.ts +6 -7
- package/src/elements/EFTimegroup.ts +163 -244
- package/src/elements/EFVideo.browsertest.ts +143 -47
- package/src/elements/EFVideo.ts +26 -0
- package/src/elements/FetchContext.browsertest.ts +7 -2
- package/src/elements/TargetController.browsertest.ts +1 -0
- package/src/elements/TargetController.ts +1 -0
- package/src/elements/renderTemporalAudio.ts +108 -0
- package/src/elements/updateAnimations.browsertest.ts +181 -6
- package/src/elements/updateAnimations.ts +6 -6
- package/src/gui/ContextMixin.browsertest.ts +274 -27
- package/src/gui/ContextMixin.ts +230 -175
- package/src/gui/Controllable.browsertest.ts +258 -0
- package/src/gui/Controllable.ts +41 -0
- package/src/gui/EFControls.browsertest.ts +294 -80
- package/src/gui/EFControls.ts +139 -28
- package/src/gui/EFDial.browsertest.ts +84 -0
- package/src/gui/EFDial.ts +172 -0
- package/src/gui/EFFilmstrip.browsertest.ts +712 -0
- package/src/gui/EFFilmstrip.ts +213 -23
- package/src/gui/EFPause.browsertest.ts +202 -0
- package/src/gui/EFPause.ts +73 -0
- package/src/gui/EFPlay.browsertest.ts +202 -0
- package/src/gui/EFPlay.ts +73 -0
- package/src/gui/EFPreview.ts +20 -5
- package/src/gui/EFResizableBox.browsertest.ts +79 -0
- package/src/gui/EFResizableBox.ts +898 -0
- package/src/gui/EFScrubber.ts +7 -5
- package/src/gui/EFTimeDisplay.browsertest.ts +19 -19
- package/src/gui/EFTimeDisplay.ts +3 -1
- package/src/gui/EFToggleLoop.ts +6 -5
- package/src/gui/EFTogglePlay.ts +30 -23
- package/src/gui/PlaybackController.ts +522 -0
- package/src/gui/TWMixin.css +3 -0
- package/src/gui/TargetOrContextMixin.ts +185 -0
- package/src/gui/efContext.ts +2 -2
- package/src/otel/setupBrowserTracing.ts +17 -12
- package/test/cache-integration-verification.browsertest.ts +1 -1
- package/types.json +1 -1
- package/dist/elements/ContextProxiesController.js +0 -49
- /package/dist/_virtual/{_@oxc-project_runtime@0.93.0 → _@oxc-project_runtime@0.94.0}/helpers/decorate.js +0 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { consume } from "@lit/context";
|
|
2
|
+
import { css, html, LitElement } from "lit";
|
|
3
|
+
import { customElement, state } from "lit/decorators.js";
|
|
4
|
+
import { attachContextRoot } from "../attachContextRoot.js";
|
|
5
|
+
import type { ControllableInterface } from "./Controllable.js";
|
|
6
|
+
import { efContext } from "./efContext.js";
|
|
7
|
+
import { playingContext } from "./playingContext.js";
|
|
8
|
+
import { TargetOrContextMixin } from "./TargetOrContextMixin.js";
|
|
9
|
+
|
|
10
|
+
attachContextRoot();
|
|
11
|
+
|
|
12
|
+
@customElement("ef-pause")
|
|
13
|
+
export class EFPause extends TargetOrContextMixin(LitElement, efContext) {
|
|
14
|
+
static styles = [
|
|
15
|
+
css`
|
|
16
|
+
:host {
|
|
17
|
+
display: block;
|
|
18
|
+
}
|
|
19
|
+
:host([hidden]) {
|
|
20
|
+
display: none;
|
|
21
|
+
}
|
|
22
|
+
div {
|
|
23
|
+
all: inherit;
|
|
24
|
+
}
|
|
25
|
+
`,
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
@consume({ context: playingContext, subscribe: true })
|
|
29
|
+
@state()
|
|
30
|
+
playing = false;
|
|
31
|
+
|
|
32
|
+
get efContext(): ControllableInterface | null {
|
|
33
|
+
return this.effectiveContext;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
connectedCallback() {
|
|
37
|
+
super.connectedCallback();
|
|
38
|
+
this.addEventListener("click", this.handleClick);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
disconnectedCallback() {
|
|
42
|
+
super.disconnectedCallback();
|
|
43
|
+
this.removeEventListener("click", this.handleClick);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
updated(changedProperties: Map<string | number | symbol, unknown>): void {
|
|
47
|
+
super.updated(changedProperties);
|
|
48
|
+
|
|
49
|
+
if (changedProperties.has("playing")) {
|
|
50
|
+
this.style.display = this.playing ? "" : "none";
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
render() {
|
|
55
|
+
return html`
|
|
56
|
+
<div>
|
|
57
|
+
<slot></slot>
|
|
58
|
+
</div>
|
|
59
|
+
`;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
handleClick = () => {
|
|
63
|
+
if (this.efContext) {
|
|
64
|
+
this.efContext.pause();
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
declare global {
|
|
70
|
+
interface HTMLElementTagNameMap {
|
|
71
|
+
"ef-pause": EFPause;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { html, render } from "lit";
|
|
2
|
+
import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
|
|
3
|
+
|
|
4
|
+
import "../elements/EFTimegroup.js";
|
|
5
|
+
import "../elements/EFVideo.js";
|
|
6
|
+
import "./EFConfiguration.js";
|
|
7
|
+
import "./EFPlay.js";
|
|
8
|
+
import "./EFPreview.js";
|
|
9
|
+
|
|
10
|
+
describe("EFPlay", () => {
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
while (document.body.children.length) {
|
|
13
|
+
document.body.children[0]?.remove();
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
afterEach(() => {
|
|
18
|
+
const elements = document.querySelectorAll("ef-play");
|
|
19
|
+
for (const element of elements) {
|
|
20
|
+
element.remove();
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test("should be defined", () => {
|
|
25
|
+
const element = document.createElement("ef-play");
|
|
26
|
+
expect(element).toBeDefined();
|
|
27
|
+
expect(element.tagName).toBe("EF-PLAY");
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test("should be visible when not playing", async () => {
|
|
31
|
+
const container = document.createElement("div");
|
|
32
|
+
render(
|
|
33
|
+
html`
|
|
34
|
+
<ef-configuration api-host="http://localhost:63315" signing-url="">
|
|
35
|
+
<ef-preview id="test-preview">
|
|
36
|
+
<ef-video src="bars-n-tone.mp4"></ef-video>
|
|
37
|
+
<ef-play>Play Button</ef-play>
|
|
38
|
+
</ef-preview>
|
|
39
|
+
</ef-configuration>
|
|
40
|
+
`,
|
|
41
|
+
container,
|
|
42
|
+
);
|
|
43
|
+
document.body.appendChild(container);
|
|
44
|
+
|
|
45
|
+
const play = container.querySelector("ef-play") as any;
|
|
46
|
+
const preview = container.querySelector("ef-preview") as any;
|
|
47
|
+
const video = container.querySelector("ef-video") as any;
|
|
48
|
+
|
|
49
|
+
await play.updateComplete;
|
|
50
|
+
await preview.updateComplete;
|
|
51
|
+
await video.updateComplete;
|
|
52
|
+
|
|
53
|
+
await video.mediaEngineTask.run();
|
|
54
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
55
|
+
await play.updateComplete;
|
|
56
|
+
|
|
57
|
+
expect(play.playing).toBe(false);
|
|
58
|
+
expect(getComputedStyle(play).display).not.toBe("none");
|
|
59
|
+
|
|
60
|
+
container.remove();
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test("should be hidden when playing", async () => {
|
|
64
|
+
const container = document.createElement("div");
|
|
65
|
+
render(
|
|
66
|
+
html`
|
|
67
|
+
<ef-configuration api-host="http://localhost:63315" signing-url="">
|
|
68
|
+
<ef-preview id="test-preview">
|
|
69
|
+
<ef-video src="bars-n-tone.mp4"></ef-video>
|
|
70
|
+
<ef-play id="play-btn">Play Button</ef-play>
|
|
71
|
+
</ef-preview>
|
|
72
|
+
</ef-configuration>
|
|
73
|
+
`,
|
|
74
|
+
container,
|
|
75
|
+
);
|
|
76
|
+
document.body.appendChild(container);
|
|
77
|
+
|
|
78
|
+
const play = container.querySelector("ef-play") as any;
|
|
79
|
+
const preview = container.querySelector("ef-preview") as any;
|
|
80
|
+
const video = container.querySelector("ef-video") as any;
|
|
81
|
+
|
|
82
|
+
await play.updateComplete;
|
|
83
|
+
await preview.updateComplete;
|
|
84
|
+
await video.updateComplete;
|
|
85
|
+
|
|
86
|
+
await video.mediaEngineTask.run();
|
|
87
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
88
|
+
await play.updateComplete;
|
|
89
|
+
|
|
90
|
+
expect(getComputedStyle(play).display).not.toBe("none");
|
|
91
|
+
|
|
92
|
+
// Manually trigger playing state change to test visibility mechanism
|
|
93
|
+
play.playing = true;
|
|
94
|
+
await play.updateComplete;
|
|
95
|
+
|
|
96
|
+
expect(getComputedStyle(play).display).toBe("none");
|
|
97
|
+
|
|
98
|
+
container.remove();
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
test("should call play() when clicked", async () => {
|
|
102
|
+
const container = document.createElement("div");
|
|
103
|
+
render(
|
|
104
|
+
html`
|
|
105
|
+
<ef-configuration api-host="http://localhost:63315" signing-url="">
|
|
106
|
+
<ef-preview id="test-preview">
|
|
107
|
+
<ef-video src="bars-n-tone.mp4"></ef-video>
|
|
108
|
+
<ef-play>Play Button</ef-play>
|
|
109
|
+
</ef-preview>
|
|
110
|
+
</ef-configuration>
|
|
111
|
+
`,
|
|
112
|
+
container,
|
|
113
|
+
);
|
|
114
|
+
document.body.appendChild(container);
|
|
115
|
+
|
|
116
|
+
const play = container.querySelector("ef-play") as any;
|
|
117
|
+
const preview = container.querySelector("ef-preview") as any;
|
|
118
|
+
const video = container.querySelector("ef-video") as any;
|
|
119
|
+
|
|
120
|
+
await play.updateComplete;
|
|
121
|
+
await preview.updateComplete;
|
|
122
|
+
await video.updateComplete;
|
|
123
|
+
|
|
124
|
+
await video.mediaEngineTask.run();
|
|
125
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
126
|
+
await play.updateComplete;
|
|
127
|
+
|
|
128
|
+
const playSpy = vi.spyOn(video.playbackController, "play");
|
|
129
|
+
|
|
130
|
+
play.click();
|
|
131
|
+
|
|
132
|
+
expect(playSpy).toHaveBeenCalledTimes(1);
|
|
133
|
+
|
|
134
|
+
playSpy.mockRestore();
|
|
135
|
+
container.remove();
|
|
136
|
+
}, 1000);
|
|
137
|
+
|
|
138
|
+
test("should pass through children with default slot", async () => {
|
|
139
|
+
const container = document.createElement("div");
|
|
140
|
+
render(
|
|
141
|
+
html`
|
|
142
|
+
<ef-configuration api-host="http://localhost:63315" signing-url="">
|
|
143
|
+
<ef-preview id="test-preview">
|
|
144
|
+
<ef-video src="bars-n-tone.mp4"></ef-video>
|
|
145
|
+
<ef-play><span id="play-content">▶ Play</span></ef-play>
|
|
146
|
+
</ef-preview>
|
|
147
|
+
</ef-configuration>
|
|
148
|
+
`,
|
|
149
|
+
container,
|
|
150
|
+
);
|
|
151
|
+
document.body.appendChild(container);
|
|
152
|
+
|
|
153
|
+
const play = container.querySelector("ef-play") as any;
|
|
154
|
+
const content = container.querySelector("#play-content");
|
|
155
|
+
|
|
156
|
+
await play.updateComplete;
|
|
157
|
+
|
|
158
|
+
expect(content).toBeDefined();
|
|
159
|
+
expect(content?.textContent).toBe("▶ Play");
|
|
160
|
+
|
|
161
|
+
container.remove();
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
test("should work with target attribute", async () => {
|
|
165
|
+
const container = document.createElement("div");
|
|
166
|
+
render(
|
|
167
|
+
html`
|
|
168
|
+
<ef-configuration api-host="http://localhost:63315" signing-url="">
|
|
169
|
+
<ef-preview id="test-preview">
|
|
170
|
+
<ef-video src="bars-n-tone.mp4"></ef-video>
|
|
171
|
+
</ef-preview>
|
|
172
|
+
<ef-play target="test-preview">Play Button</ef-play>
|
|
173
|
+
</ef-configuration>
|
|
174
|
+
`,
|
|
175
|
+
container,
|
|
176
|
+
);
|
|
177
|
+
document.body.appendChild(container);
|
|
178
|
+
|
|
179
|
+
const play = container.querySelector("ef-play") as any;
|
|
180
|
+
const preview = container.querySelector("ef-preview") as any;
|
|
181
|
+
const video = container.querySelector("ef-video") as any;
|
|
182
|
+
|
|
183
|
+
await play.updateComplete;
|
|
184
|
+
await preview.updateComplete;
|
|
185
|
+
await video.updateComplete;
|
|
186
|
+
|
|
187
|
+
await video.mediaEngineTask.run();
|
|
188
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
189
|
+
await play.updateComplete;
|
|
190
|
+
|
|
191
|
+
expect(play.efContext).toBe(preview);
|
|
192
|
+
|
|
193
|
+
const playSpy = vi.spyOn(video.playbackController, "play");
|
|
194
|
+
|
|
195
|
+
play.click();
|
|
196
|
+
|
|
197
|
+
expect(playSpy).toHaveBeenCalledTimes(1);
|
|
198
|
+
|
|
199
|
+
playSpy.mockRestore();
|
|
200
|
+
container.remove();
|
|
201
|
+
}, 1000);
|
|
202
|
+
});
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { consume } from "@lit/context";
|
|
2
|
+
import { css, html, LitElement } from "lit";
|
|
3
|
+
import { customElement, state } from "lit/decorators.js";
|
|
4
|
+
import { attachContextRoot } from "../attachContextRoot.js";
|
|
5
|
+
import type { ControllableInterface } from "./Controllable.js";
|
|
6
|
+
import { efContext } from "./efContext.js";
|
|
7
|
+
import { playingContext } from "./playingContext.js";
|
|
8
|
+
import { TargetOrContextMixin } from "./TargetOrContextMixin.js";
|
|
9
|
+
|
|
10
|
+
attachContextRoot();
|
|
11
|
+
|
|
12
|
+
@customElement("ef-play")
|
|
13
|
+
export class EFPlay extends TargetOrContextMixin(LitElement, efContext) {
|
|
14
|
+
static styles = [
|
|
15
|
+
css`
|
|
16
|
+
:host {
|
|
17
|
+
display: block;
|
|
18
|
+
}
|
|
19
|
+
:host([hidden]) {
|
|
20
|
+
display: none;
|
|
21
|
+
}
|
|
22
|
+
div {
|
|
23
|
+
all: inherit;
|
|
24
|
+
}
|
|
25
|
+
`,
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
@consume({ context: playingContext, subscribe: true })
|
|
29
|
+
@state()
|
|
30
|
+
playing = false;
|
|
31
|
+
|
|
32
|
+
get efContext(): ControllableInterface | null {
|
|
33
|
+
return this.effectiveContext;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
connectedCallback() {
|
|
37
|
+
super.connectedCallback();
|
|
38
|
+
this.addEventListener("click", this.handleClick);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
disconnectedCallback() {
|
|
42
|
+
super.disconnectedCallback();
|
|
43
|
+
this.removeEventListener("click", this.handleClick);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
updated(changedProperties: Map<string | number | symbol, unknown>): void {
|
|
47
|
+
super.updated(changedProperties);
|
|
48
|
+
|
|
49
|
+
if (changedProperties.has("playing")) {
|
|
50
|
+
this.style.display = this.playing ? "none" : "";
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
render() {
|
|
55
|
+
return html`
|
|
56
|
+
<div>
|
|
57
|
+
<slot></slot>
|
|
58
|
+
</div>
|
|
59
|
+
`;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
handleClick = () => {
|
|
63
|
+
if (this.efContext) {
|
|
64
|
+
this.efContext.play();
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
declare global {
|
|
70
|
+
interface HTMLElementTagNameMap {
|
|
71
|
+
"ef-play": EFPlay;
|
|
72
|
+
}
|
|
73
|
+
}
|
package/src/gui/EFPreview.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { provide } from "@lit/context";
|
|
2
2
|
import { css, html, LitElement } from "lit";
|
|
3
3
|
import { customElement } from "lit/decorators.js";
|
|
4
|
+
import { isEFTemporal } from "../elements/EFTemporal.js";
|
|
4
5
|
import { EFTargetable } from "../elements/TargetController.js";
|
|
5
6
|
import { ContextMixin } from "./ContextMixin.js";
|
|
6
7
|
import { focusedElementContext } from "./focusedElementContext.js";
|
|
@@ -21,26 +22,40 @@ export class EFPreview extends EFTargetable(ContextMixin(TWMixin(LitElement))) {
|
|
|
21
22
|
@provide({ context: focusedElementContext })
|
|
22
23
|
focusedElement?: HTMLElement;
|
|
23
24
|
|
|
25
|
+
/**
|
|
26
|
+
* Find the closest temporal element (timegroup, video, audio, etc.)
|
|
27
|
+
*/
|
|
28
|
+
private findClosestTemporal(element: HTMLElement | null): HTMLElement | null {
|
|
29
|
+
let current = element;
|
|
30
|
+
while (current && current !== this) {
|
|
31
|
+
if (isEFTemporal(current)) {
|
|
32
|
+
return current;
|
|
33
|
+
}
|
|
34
|
+
current = current.parentElement;
|
|
35
|
+
}
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
|
|
24
39
|
constructor() {
|
|
25
40
|
super();
|
|
26
41
|
this.addEventListener("mouseover", (e) => {
|
|
27
42
|
const target = e.target as HTMLElement;
|
|
28
|
-
const
|
|
29
|
-
if (target !== this &&
|
|
43
|
+
const temporal = this.findClosestTemporal(target);
|
|
44
|
+
if (target !== this && temporal) {
|
|
30
45
|
this.focusedElement = target;
|
|
31
46
|
}
|
|
32
47
|
});
|
|
33
48
|
this.addEventListener("mouseout", (e) => {
|
|
34
49
|
const relatedTarget = e.relatedTarget as HTMLElement;
|
|
35
|
-
const
|
|
50
|
+
const targetingTemporal = this.findClosestTemporal(relatedTarget);
|
|
36
51
|
// Clear focus if:
|
|
37
52
|
// 1. Moving outside the preview entirely, or
|
|
38
53
|
// 2. Moving to the preview itself, or
|
|
39
|
-
// 3. Moving to an element that's not within a
|
|
54
|
+
// 3. Moving to an element that's not within a temporal
|
|
40
55
|
if (
|
|
41
56
|
!this.contains(relatedTarget) ||
|
|
42
57
|
relatedTarget === this ||
|
|
43
|
-
!
|
|
58
|
+
!targetingTemporal
|
|
44
59
|
) {
|
|
45
60
|
this.focusedElement = undefined;
|
|
46
61
|
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { html, render } from "lit";
|
|
2
|
+
import { afterEach, describe, expect, test } from "vitest";
|
|
3
|
+
import type { EFResizableBox } from "./EFResizableBox";
|
|
4
|
+
import "./EFResizableBox";
|
|
5
|
+
|
|
6
|
+
describe("EFResizableBox", () => {
|
|
7
|
+
afterEach(() => {
|
|
8
|
+
document.body.innerHTML = "";
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
test("should render with default bounds", async () => {
|
|
12
|
+
const container = document.createElement("div");
|
|
13
|
+
document.body.appendChild(container);
|
|
14
|
+
render(html`<ef-resizable-box></ef-resizable-box>`, container);
|
|
15
|
+
const el = container.querySelector<EFResizableBox>("ef-resizable-box")!;
|
|
16
|
+
await el.updateComplete;
|
|
17
|
+
|
|
18
|
+
const box = el.shadowRoot!.querySelector(".box") as HTMLDivElement;
|
|
19
|
+
expect(box).to.exist;
|
|
20
|
+
expect(box.style.left).to.equal("0px");
|
|
21
|
+
expect(box.style.top).to.equal("0px");
|
|
22
|
+
expect(box.style.width).to.equal("100px");
|
|
23
|
+
expect(box.style.height).to.equal("100px");
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test("should render with provided bounds", async () => {
|
|
27
|
+
const container = document.createElement("div");
|
|
28
|
+
document.body.appendChild(container);
|
|
29
|
+
const bounds = { x: 50, y: 50, width: 200, height: 150 };
|
|
30
|
+
render(
|
|
31
|
+
html`<ef-resizable-box .bounds=${bounds}></ef-resizable-box>`,
|
|
32
|
+
container,
|
|
33
|
+
);
|
|
34
|
+
const el = container.querySelector<EFResizableBox>("ef-resizable-box")!;
|
|
35
|
+
await el.updateComplete;
|
|
36
|
+
|
|
37
|
+
const box = el.shadowRoot!.querySelector(".box") as HTMLDivElement;
|
|
38
|
+
expect(box.style.left).to.equal("50px");
|
|
39
|
+
expect(box.style.top).to.equal("50px");
|
|
40
|
+
expect(box.style.width).to.equal("200px");
|
|
41
|
+
expect(box.style.height).to.equal("150px");
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test("should be constrained by its parent", async () => {
|
|
45
|
+
const container = document.createElement("div");
|
|
46
|
+
container.style.width = "300px";
|
|
47
|
+
container.style.height = "300px";
|
|
48
|
+
container.style.position = "relative";
|
|
49
|
+
document.body.appendChild(container);
|
|
50
|
+
const bounds = { x: 250, y: 250, width: 100, height: 100 };
|
|
51
|
+
render(
|
|
52
|
+
html`<ef-resizable-box .bounds=${bounds}></ef-resizable-box>`,
|
|
53
|
+
container,
|
|
54
|
+
);
|
|
55
|
+
const el = container.querySelector<EFResizableBox>("ef-resizable-box")!;
|
|
56
|
+
await el.updateComplete;
|
|
57
|
+
|
|
58
|
+
const box = el.shadowRoot!.querySelector(".box") as HTMLDivElement;
|
|
59
|
+
const downEvent = new PointerEvent("pointerdown", {
|
|
60
|
+
clientX: 260,
|
|
61
|
+
clientY: 260,
|
|
62
|
+
bubbles: true,
|
|
63
|
+
composed: true,
|
|
64
|
+
});
|
|
65
|
+
box.dispatchEvent(downEvent);
|
|
66
|
+
|
|
67
|
+
const moveEvent = new PointerEvent("pointermove", {
|
|
68
|
+
clientX: 350,
|
|
69
|
+
clientY: 350,
|
|
70
|
+
bubbles: true,
|
|
71
|
+
composed: true,
|
|
72
|
+
});
|
|
73
|
+
document.dispatchEvent(moveEvent);
|
|
74
|
+
await el.updateComplete;
|
|
75
|
+
|
|
76
|
+
expect(el.bounds.x).to.be.lessThanOrEqual(200);
|
|
77
|
+
expect(el.bounds.y).to.be.lessThanOrEqual(200);
|
|
78
|
+
});
|
|
79
|
+
});
|