@web-atoms/web-controls 2.1.214 → 2.1.217
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/basic/AtomChips.d.ts +9 -0
- package/dist/basic/AtomChips.d.ts.map +1 -1
- package/dist/basic/AtomChips.js +57 -24
- package/dist/basic/AtomChips.js.map +1 -1
- package/dist/basic/AtomRepeater.d.ts +1 -0
- package/dist/basic/AtomRepeater.d.ts.map +1 -1
- package/dist/basic/AtomRepeater.js +113 -69
- package/dist/basic/AtomRepeater.js.map +1 -1
- package/dist/basic/AtomSuggestions.d.ts +15 -0
- package/dist/basic/AtomSuggestions.d.ts.map +1 -0
- package/dist/basic/AtomSuggestions.js +123 -0
- package/dist/basic/AtomSuggestions.js.map +1 -0
- package/dist/player/AtomVideoPlayer.d.ts +18 -0
- package/dist/player/AtomVideoPlayer.d.ts.map +1 -0
- package/dist/player/AtomVideoPlayer.js +285 -0
- package/dist/player/AtomVideoPlayer.js.map +1 -0
- package/dist/player/TrackProgress.d.ts +2 -0
- package/dist/player/TrackProgress.d.ts.map +1 -0
- package/dist/player/TrackProgress.js +29 -0
- package/dist/player/TrackProgress.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/src/basic/AtomChips.tsx +83 -23
- package/src/basic/AtomRepeater.tsx +108 -60
- package/src/basic/AtomSuggestions.tsx +104 -0
- package/src/player/AtomVideoPlayer.tsx +360 -0
- package/src/player/TrackProgress.tsx +21 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import Bind from "@web-atoms/core/dist/core/Bind";
|
|
2
|
+
import { BindableProperty } from "@web-atoms/core/dist/core/BindableProperty";
|
|
3
|
+
import Colors from "@web-atoms/core/dist/core/Colors";
|
|
4
|
+
import { IDisposable } from "@web-atoms/core/dist/core/types";
|
|
5
|
+
import XNode from "@web-atoms/core/dist/core/XNode";
|
|
6
|
+
import StyleRule from "@web-atoms/core/dist/style/StyleRule";
|
|
7
|
+
import CSS from "@web-atoms/core/dist/web/styles/CSS";
|
|
8
|
+
import AtomRepeater, { askSuggestion } from "./AtomRepeater";
|
|
9
|
+
|
|
10
|
+
CSS(StyleRule()
|
|
11
|
+
.margin(5)
|
|
12
|
+
.flexLayout({})
|
|
13
|
+
.overflow("hidden")
|
|
14
|
+
.child(StyleRule(".header")
|
|
15
|
+
.color(Colors.darkOrange)
|
|
16
|
+
.whiteSpace("nowrap")
|
|
17
|
+
)
|
|
18
|
+
.child(StyleRule(".items")
|
|
19
|
+
.flexStretch()
|
|
20
|
+
.flexLayout({ justifyContent: "flex-start", gap: 0 })
|
|
21
|
+
.overflow("hidden")
|
|
22
|
+
.child(StyleRule("*")
|
|
23
|
+
.whiteSpace("nowrap")
|
|
24
|
+
.padding(3)
|
|
25
|
+
)
|
|
26
|
+
)
|
|
27
|
+
.child(StyleRule(".more")
|
|
28
|
+
.fontSize("smaller")
|
|
29
|
+
.color(Colors.blue)
|
|
30
|
+
.textTransform("lowercase")
|
|
31
|
+
.textDecoration("underline")
|
|
32
|
+
)
|
|
33
|
+
, "*[data-suggestions=suggestions]");
|
|
34
|
+
|
|
35
|
+
export default class AtomSuggestions extends AtomRepeater {
|
|
36
|
+
|
|
37
|
+
public eventItemClick: any;
|
|
38
|
+
|
|
39
|
+
@BindableProperty
|
|
40
|
+
public valuePath: any;
|
|
41
|
+
|
|
42
|
+
// Title to be displayed on the popup window for e.g. When we click on more in project tags
|
|
43
|
+
@BindableProperty
|
|
44
|
+
public title: string;
|
|
45
|
+
|
|
46
|
+
@BindableProperty
|
|
47
|
+
public match: (text) => (item) => boolean;
|
|
48
|
+
|
|
49
|
+
@BindableProperty
|
|
50
|
+
public version: number;
|
|
51
|
+
|
|
52
|
+
@BindableProperty
|
|
53
|
+
public suggestionRenderer: (item) => XNode;
|
|
54
|
+
|
|
55
|
+
private selectedItemsWatcher: IDisposable;
|
|
56
|
+
|
|
57
|
+
public onPropertyChanged(name: keyof AtomSuggestions): void {
|
|
58
|
+
super.onPropertyChanged(name);
|
|
59
|
+
switch (name) {
|
|
60
|
+
case "selectedItems":
|
|
61
|
+
this.selectedItemsWatcher?.dispose();
|
|
62
|
+
const si = this.selectedItems;
|
|
63
|
+
if (!si) {
|
|
64
|
+
this.selectedItemsWatcher = null;
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
const d = si.watch(() => this.version++);
|
|
68
|
+
this.selectedItemsWatcher = this.registerDisposable(d);
|
|
69
|
+
this.version++;
|
|
70
|
+
break;
|
|
71
|
+
case "version":
|
|
72
|
+
// this.updateVisibility(this.itemsPresenter);
|
|
73
|
+
const vp = this.valuePath ?? ((item) => item);
|
|
74
|
+
const selectedValues = (this.selectedItems ?? []).map(vp);
|
|
75
|
+
this.visibilityFilter = (item) => {
|
|
76
|
+
const v = vp(item);
|
|
77
|
+
return selectedValues.length === 0 || selectedValues.indexOf(v) === -1;
|
|
78
|
+
};
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
protected create(): void {
|
|
84
|
+
this.version = 1;
|
|
85
|
+
this.render(<div data-suggestions="suggestions" eventItemClick={(e) => this.selectedItems?.add(e.detail)}>
|
|
86
|
+
<span class="header" text={Bind.oneWay(() => this.title)}/>
|
|
87
|
+
<div class="items"></div>
|
|
88
|
+
<div class="more" eventClick={Bind.event(() => this.more())}>More</div>
|
|
89
|
+
</div>);
|
|
90
|
+
this.itemsPresenter = this.element.children[1] as HTMLElement;
|
|
91
|
+
this.updateItems();
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
protected async more() {
|
|
95
|
+
const vf = this.visibilityFilter ?? ((item) => true);
|
|
96
|
+
const selected = await askSuggestion(
|
|
97
|
+
this.items.filter(vf),
|
|
98
|
+
this.suggestionRenderer ?? this.itemRenderer,
|
|
99
|
+
(text: string) => this.match(text),
|
|
100
|
+
{ title: this.title });
|
|
101
|
+
this.selectedItems?.add(selected);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
}
|
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
import Bind from "@web-atoms/core/dist/core/Bind";
|
|
2
|
+
import { BindableProperty } from "@web-atoms/core/dist/core/BindableProperty";
|
|
3
|
+
import Colors from "@web-atoms/core/dist/core/Colors";
|
|
4
|
+
import XNode from "@web-atoms/core/dist/core/XNode";
|
|
5
|
+
import StyleRule from "@web-atoms/core/dist/style/StyleRule";
|
|
6
|
+
import { AtomControl } from "@web-atoms/core/dist/web/controls/AtomControl";
|
|
7
|
+
import CSS from "@web-atoms/core/dist/web/styles/CSS";
|
|
8
|
+
import { ChildEnumerator } from "@web-atoms/core/dist/web/core/AtomUI";
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
CSS(StyleRule()
|
|
12
|
+
.display("grid")
|
|
13
|
+
.gridTemplateRows("auto 1fr auto")
|
|
14
|
+
.gridTemplateColumns("auto 1fr auto auto")
|
|
15
|
+
.backgroundColor(Colors.black)
|
|
16
|
+
.child(StyleRule("[data-element=video]")
|
|
17
|
+
.gridRowStart("1")
|
|
18
|
+
.gridRowEnd("span 3")
|
|
19
|
+
.gridColumnStart("1")
|
|
20
|
+
.gridColumnEnd("span 3")
|
|
21
|
+
.alignSelf("stretch")
|
|
22
|
+
.justifySelf("stretch")
|
|
23
|
+
)
|
|
24
|
+
.child(StyleRule("[data-element=play-element]")
|
|
25
|
+
.gridRowStart("1")
|
|
26
|
+
.gridRowEnd("span 3")
|
|
27
|
+
.gridColumnStart("1")
|
|
28
|
+
.gridColumnEnd("span 3")
|
|
29
|
+
.alignSelf("stretch")
|
|
30
|
+
.justifySelf("stretch")
|
|
31
|
+
.flexLayout({ justifyContent: "center"})
|
|
32
|
+
.child(StyleRule("button.play")
|
|
33
|
+
.display("inline-flex")
|
|
34
|
+
.alignItems("center")
|
|
35
|
+
.justifyContent("center")
|
|
36
|
+
.color(Colors.white)
|
|
37
|
+
.backgroundColor(Colors.blue)
|
|
38
|
+
.borderRadius(9999)
|
|
39
|
+
.fontSize(25)
|
|
40
|
+
.padding(10)
|
|
41
|
+
.width(50)
|
|
42
|
+
.height(50)
|
|
43
|
+
.textAlign("center")
|
|
44
|
+
.verticalAlign("middle")
|
|
45
|
+
.child(StyleRule("i")
|
|
46
|
+
.marginLeft(4)
|
|
47
|
+
)
|
|
48
|
+
)
|
|
49
|
+
)
|
|
50
|
+
.child(StyleRule("[data-element=progress]")
|
|
51
|
+
.zIndex("11")
|
|
52
|
+
.gridRowStart("2")
|
|
53
|
+
.gridColumnStart("1")
|
|
54
|
+
.gridColumnEnd("span 3")
|
|
55
|
+
.alignSelf("flex-end")
|
|
56
|
+
.height(4)
|
|
57
|
+
.justifySelf("stretch" as any)
|
|
58
|
+
.backgroundColor(Colors.black)
|
|
59
|
+
.width("100%")
|
|
60
|
+
.cursor("pointer")
|
|
61
|
+
)
|
|
62
|
+
.child(StyleRule("[data-element=toolbar]")
|
|
63
|
+
.zIndex("10")
|
|
64
|
+
.gridRowStart("3")
|
|
65
|
+
.gridColumnStart("1")
|
|
66
|
+
.gridColumnEnd("span 3")
|
|
67
|
+
.backgroundColor(Colors.black.withAlphaPercent(0.3))
|
|
68
|
+
.color(Colors.white)
|
|
69
|
+
.flexLayout({
|
|
70
|
+
justifyContent: "flex-start"
|
|
71
|
+
})
|
|
72
|
+
.child(StyleRule("*")
|
|
73
|
+
.minWidth(20)
|
|
74
|
+
.marginLeft(5)
|
|
75
|
+
.padding(5)
|
|
76
|
+
)
|
|
77
|
+
.child(StyleRule("[data-style=button]")
|
|
78
|
+
.width(20)
|
|
79
|
+
)
|
|
80
|
+
.child(StyleRule("[data-font-size=small]")
|
|
81
|
+
.fontSize("x-small")
|
|
82
|
+
)
|
|
83
|
+
.child(StyleRule("[data-element=volume-range]")
|
|
84
|
+
.height(2)
|
|
85
|
+
.color(Colors.green)
|
|
86
|
+
)
|
|
87
|
+
.child(StyleRule("[data-element=full-screen]")
|
|
88
|
+
.marginLeft("auto")
|
|
89
|
+
.marginRight(5)
|
|
90
|
+
)
|
|
91
|
+
)
|
|
92
|
+
.and(StyleRule("[data-controls=true]")
|
|
93
|
+
.child(StyleRule("[data-element=toolbar]")
|
|
94
|
+
.display("flex")
|
|
95
|
+
)
|
|
96
|
+
.child(StyleRule("[data-element=progress]")
|
|
97
|
+
.display("flex")
|
|
98
|
+
)
|
|
99
|
+
)
|
|
100
|
+
.and(StyleRule("[data-state=pause]")
|
|
101
|
+
.child(StyleRule("[data-element=toolbar]")
|
|
102
|
+
.display("flex")
|
|
103
|
+
)
|
|
104
|
+
.child(StyleRule("[data-element=toolbar]")
|
|
105
|
+
.child(StyleRule("[data-element=pause]")
|
|
106
|
+
.display("none")
|
|
107
|
+
)
|
|
108
|
+
)
|
|
109
|
+
)
|
|
110
|
+
.and(StyleRule("[data-state=play]")
|
|
111
|
+
.child(StyleRule("[data-element=play-element]")
|
|
112
|
+
.display("none")
|
|
113
|
+
)
|
|
114
|
+
.child(StyleRule("[data-element=toolbar]")
|
|
115
|
+
.child(StyleRule("[data-element=play]")
|
|
116
|
+
.display("none")
|
|
117
|
+
)
|
|
118
|
+
)
|
|
119
|
+
.and(StyleRule("[data-controls=false]")
|
|
120
|
+
.child(StyleRule("[data-element=toolbar]")
|
|
121
|
+
.display("none")
|
|
122
|
+
)
|
|
123
|
+
.child(StyleRule("[data-element=progress]")
|
|
124
|
+
.display("none")
|
|
125
|
+
)
|
|
126
|
+
)
|
|
127
|
+
)
|
|
128
|
+
, "*[data-video-player=video-player]");
|
|
129
|
+
|
|
130
|
+
const gatherElements = (e: HTMLElement, data = {}) => {
|
|
131
|
+
const ce = ChildEnumerator.enumerate(e);
|
|
132
|
+
for (const iterator of ce) {
|
|
133
|
+
const elementName = iterator.dataset.element?.replace(/-([a-z])/g, (g) => g[1].toUpperCase());
|
|
134
|
+
if (elementName) {
|
|
135
|
+
data[elementName] = iterator;
|
|
136
|
+
}
|
|
137
|
+
gatherElements(iterator, data);
|
|
138
|
+
}
|
|
139
|
+
return data;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const numberToText = (n: number) => {
|
|
143
|
+
if (n < 10) {
|
|
144
|
+
return "0" + n;
|
|
145
|
+
}
|
|
146
|
+
return n.toString();
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
const durationText = (n: number, total: number) => {
|
|
150
|
+
if (n === null || n === undefined) {
|
|
151
|
+
return "";
|
|
152
|
+
}
|
|
153
|
+
const minutes = Math.floor(n / 60);
|
|
154
|
+
const seconds = numberToText(Math.ceil(n % 60));
|
|
155
|
+
const totalMinutes = Math.floor(total / 60);
|
|
156
|
+
const totalSeconds = numberToText(Math.ceil(total % 60));
|
|
157
|
+
return `${minutes}:${seconds} / ${totalMinutes}:${totalSeconds}`;
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
const noSoundIcon = "fa-duotone fa-volume-slash",
|
|
161
|
+
mute = "fa-duotone fa-volume-xmark",
|
|
162
|
+
low = "fa-duotone fa-volume-low",
|
|
163
|
+
mid = "fa-duotone fa-volume",
|
|
164
|
+
high = "fa-duotone fa-volume-high";
|
|
165
|
+
|
|
166
|
+
export default class AtomVideoPlayer extends AtomControl {
|
|
167
|
+
|
|
168
|
+
@BindableProperty
|
|
169
|
+
public source: any;
|
|
170
|
+
|
|
171
|
+
public duration: number;
|
|
172
|
+
|
|
173
|
+
public time: number;
|
|
174
|
+
|
|
175
|
+
private video: HTMLVideoElement;
|
|
176
|
+
|
|
177
|
+
private progress: HTMLCanvasElement;
|
|
178
|
+
|
|
179
|
+
private poster: HTMLImageElement;
|
|
180
|
+
|
|
181
|
+
private currentTimeSpan: HTMLSpanElement;
|
|
182
|
+
private soundIcon: HTMLElement;
|
|
183
|
+
private volumeRange: HTMLInputElement;
|
|
184
|
+
|
|
185
|
+
public onPropertyChanged(name: keyof AtomVideoPlayer): void {
|
|
186
|
+
switch (name) {
|
|
187
|
+
case "source":
|
|
188
|
+
this.updateSource();
|
|
189
|
+
break;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
protected create(): void {
|
|
194
|
+
this.element.dataset.videoPlayer = "video-player";
|
|
195
|
+
this.bindEvent(this.element, "togglePlay", (e: CustomEvent) => {
|
|
196
|
+
if (this.video.paused) {
|
|
197
|
+
this.video.play();
|
|
198
|
+
} else {
|
|
199
|
+
this.video.pause();
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
this.bindEvent(this.element, "volume", (e: CustomEvent) => {
|
|
203
|
+
this.video.muted = !this.video.muted;
|
|
204
|
+
this.updateVolume();
|
|
205
|
+
});
|
|
206
|
+
this.bindEvent(this.element, "fullScreen", (e: CustomEvent) => {
|
|
207
|
+
const f = e.target as HTMLElement;
|
|
208
|
+
if (this.element === document.fullscreenElement) {
|
|
209
|
+
document.exitFullscreen();
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
document.onfullscreenchange = () => {
|
|
213
|
+
if (document.fullscreenElement !== this.element) {
|
|
214
|
+
f.className = "fa-solid fa-expand";
|
|
215
|
+
document.onfullscreenchange = undefined;
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
this.element.requestFullscreen({ navigationUI: "show" });
|
|
219
|
+
f.className = "fa-solid fa-compress";
|
|
220
|
+
});
|
|
221
|
+
this.render(<div data-click-event="toggle-play" data-state="pause">
|
|
222
|
+
<video
|
|
223
|
+
event-abort={() => this.element.dataset.state = "abort"}
|
|
224
|
+
event-durationchange={(e: Event) => this.duration = this.video.duration}
|
|
225
|
+
event-ended={() => this.element.dataset.state = "ended"}
|
|
226
|
+
event-loadedmetadata={() => {
|
|
227
|
+
this.duration = this.video.duration;
|
|
228
|
+
this.updateVolume();
|
|
229
|
+
this.currentTimeSpan.textContent = durationText(0, this.duration);
|
|
230
|
+
this.updateProgress();
|
|
231
|
+
}}
|
|
232
|
+
event-pause={() => this.element.dataset.state = "pause"}
|
|
233
|
+
event-play={() => this.element.dataset.state = "play"}
|
|
234
|
+
event-progress={(e) => this.updateProgress()}
|
|
235
|
+
event-timeupdate={() => {
|
|
236
|
+
this.time = this.video.currentTime;
|
|
237
|
+
this.currentTimeSpan.textContent = durationText(this.time, this.duration);
|
|
238
|
+
this.element.dataset.state = "play";
|
|
239
|
+
this.updateProgress();
|
|
240
|
+
}}
|
|
241
|
+
event-waiting={() => this.element.dataset.state = "waiting"}
|
|
242
|
+
event-volumechange={() => this.updateVolume()}
|
|
243
|
+
autoplay={false}
|
|
244
|
+
data-element="video"/>
|
|
245
|
+
<canvas
|
|
246
|
+
data-element="progress"
|
|
247
|
+
/>
|
|
248
|
+
<img data-element="poster"/>
|
|
249
|
+
<div data-element="toolbar">
|
|
250
|
+
<i
|
|
251
|
+
data-element="play"
|
|
252
|
+
data-style="button"
|
|
253
|
+
class="fa-solid fa-play"/>
|
|
254
|
+
<i
|
|
255
|
+
data-element="pause"
|
|
256
|
+
data-style="button"
|
|
257
|
+
class="fa-solid fa-pause"/>
|
|
258
|
+
<i
|
|
259
|
+
data-click-event="volume"
|
|
260
|
+
data-style="button"
|
|
261
|
+
data-element="sound"
|
|
262
|
+
class="fa-duotone fa-volume-slash"></i>
|
|
263
|
+
<input
|
|
264
|
+
data-click-event="none"
|
|
265
|
+
data-element="volume-range"
|
|
266
|
+
type="range"
|
|
267
|
+
min={0}
|
|
268
|
+
max={1}
|
|
269
|
+
step={0.1}
|
|
270
|
+
/>
|
|
271
|
+
<span
|
|
272
|
+
data-font-size="small"
|
|
273
|
+
data-element="current" text="0:00"/>
|
|
274
|
+
<i
|
|
275
|
+
data-click-event="full-screen"
|
|
276
|
+
data-style="button"
|
|
277
|
+
data-element="full-screen"
|
|
278
|
+
class="fa-solid fa-expand"></i>
|
|
279
|
+
</div>
|
|
280
|
+
<div
|
|
281
|
+
data-element="play-element">
|
|
282
|
+
<button class="play">
|
|
283
|
+
<i class="fa-solid fa-play"/>
|
|
284
|
+
</button>
|
|
285
|
+
</div>
|
|
286
|
+
</div>);
|
|
287
|
+
|
|
288
|
+
const all = gatherElements(this.element) as any;
|
|
289
|
+
this.video = all.video;
|
|
290
|
+
this.progress = all.progress;
|
|
291
|
+
this.currentTimeSpan = all.current;
|
|
292
|
+
this.soundIcon = all.sound;
|
|
293
|
+
this.volumeRange = all.volumeRange;
|
|
294
|
+
this.bindEvent(this.volumeRange, "input", () => {
|
|
295
|
+
setTimeout(() => {
|
|
296
|
+
this.video.volume = parseFloat(this.volumeRange.value);
|
|
297
|
+
}, 1);
|
|
298
|
+
});
|
|
299
|
+
this.bindEvent(this.element, "pointerenter", () => {
|
|
300
|
+
this.element.dataset.controls = "true";
|
|
301
|
+
});
|
|
302
|
+
this.bindEvent(this.element, "pointerleave", () => {
|
|
303
|
+
this.element.dataset.controls = "false";
|
|
304
|
+
});
|
|
305
|
+
this.bindEvent(this.progress, "click", (e: MouseEvent) => {
|
|
306
|
+
e.preventDefault();
|
|
307
|
+
const scale = e.clientX / this.progress.clientWidth;
|
|
308
|
+
this.video.currentTime = this.video.duration * scale;
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
private updateProgress() {
|
|
313
|
+
const context = this.progress.getContext("2d");
|
|
314
|
+
// context.fillStyle = "rgba(0,0,0,0)";
|
|
315
|
+
context.strokeStyle = "rgba(0,0,0,0)";
|
|
316
|
+
const width = this.progress.clientWidth;
|
|
317
|
+
const height = this.progress.clientHeight;
|
|
318
|
+
this.progress.width = width;
|
|
319
|
+
this.progress.height = height;
|
|
320
|
+
context.clearRect(0,0, width, height);
|
|
321
|
+
const max = this.video.duration;
|
|
322
|
+
const seekable = this.video.buffered;
|
|
323
|
+
const scale = width / max;
|
|
324
|
+
context.fillStyle = "rgba(255,255,255,0.5)";
|
|
325
|
+
for (let index = 0; index < seekable.length; index++) {
|
|
326
|
+
const start = seekable.start(index) * scale;
|
|
327
|
+
const end = seekable.end(index) * scale;
|
|
328
|
+
context.fillRect(start, 0, end, height);
|
|
329
|
+
}
|
|
330
|
+
context.fillStyle = "#ffffff";
|
|
331
|
+
context.fillRect(0, 0, this.video.currentTime * scale, height);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
private updateVolume() {
|
|
335
|
+
if (this.video.muted) {
|
|
336
|
+
this.soundIcon.className = mute;
|
|
337
|
+
this.volumeRange.style.display = "none";
|
|
338
|
+
this.soundIcon.title = "Unmute";
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
const audio = this.video.volume;
|
|
342
|
+
this.volumeRange.style.display = "";
|
|
343
|
+
this.volumeRange.value = audio?.toString();
|
|
344
|
+
this.soundIcon.title = "Mute";
|
|
345
|
+
if (audio > 0.8) {
|
|
346
|
+
this.soundIcon.className = high;
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
if (audio < 0.2) {
|
|
350
|
+
this.soundIcon.className = low;
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
this.soundIcon.className = mid;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
protected updateSource() {
|
|
357
|
+
this.video.src = this.source;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import XNode from "@web-atoms/core/dist/core/XNode";
|
|
2
|
+
import StyleRule from "@web-atoms/core/dist/style/StyleRule";
|
|
3
|
+
import CSS from "@web-atoms/core/dist/web/styles/CSS";
|
|
4
|
+
|
|
5
|
+
CSS(StyleRule()
|
|
6
|
+
.height(5)
|
|
7
|
+
.child(StyleRule("*")
|
|
8
|
+
.position("absolute")
|
|
9
|
+
.left(0)
|
|
10
|
+
.top(0)
|
|
11
|
+
)
|
|
12
|
+
, "*[data-track-progress=track-progress]");
|
|
13
|
+
|
|
14
|
+
export default function TrackProgress(a) {
|
|
15
|
+
return <div
|
|
16
|
+
data-track-progress="track-progress">
|
|
17
|
+
<div class="available"/>
|
|
18
|
+
<div class="done"/>
|
|
19
|
+
<div class="thumb"/>
|
|
20
|
+
</div>;
|
|
21
|
+
}
|