@streamscloud/kit 0.0.1-1770903095775 → 0.0.1-1770982902053
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/core/graphql.d.ts +1 -0
- package/dist/core/graphql.js +2 -1
- package/dist/core/utils/image-preloader.d.ts +1 -0
- package/dist/core/utils/image-preloader.js +10 -0
- package/dist/core/utils/index.d.ts +1 -0
- package/dist/core/utils/index.js +1 -0
- package/dist/ui/media-playback/index.d.ts +2 -0
- package/dist/ui/media-playback/index.js +2 -0
- package/dist/ui/media-playback/playback-manager.svelte.d.ts +25 -0
- package/dist/ui/media-playback/playback-manager.svelte.js +62 -0
- package/dist/ui/media-playback/volume-manager.svelte.d.ts +10 -0
- package/dist/ui/media-playback/volume-manager.svelte.js +25 -0
- package/dist/ui/video/cmp.video.svelte +371 -0
- package/dist/ui/video/cmp.video.svelte.d.ts +33 -0
- package/dist/ui/video/index.d.ts +2 -0
- package/dist/ui/video/index.js +1 -0
- package/dist/ui/video/types.d.ts +1 -0
- package/dist/ui/video/types.js +1 -0
- package/package.json +9 -1
package/dist/core/graphql.d.ts
CHANGED
package/dist/core/graphql.js
CHANGED
|
@@ -3,7 +3,8 @@ export const createGQLClient = (data) => createClient({
|
|
|
3
3
|
url: data.url,
|
|
4
4
|
requestPolicy: 'network-only',
|
|
5
5
|
fetchOptions: {
|
|
6
|
-
credentials: 'include'
|
|
6
|
+
credentials: 'include',
|
|
7
|
+
headers: data.headers
|
|
7
8
|
},
|
|
8
9
|
fetch: data.customFetch ?? fetch,
|
|
9
10
|
exchanges: [fetchExchange]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const preloadImage: (src: string, throwOnError?: boolean) => Promise<void>;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export const preloadImage = (src, throwOnError = false) => {
|
|
2
|
+
return new Promise((resolve, reject) => {
|
|
3
|
+
const img = new Image();
|
|
4
|
+
img.decoding = 'async';
|
|
5
|
+
img.loading = 'eager';
|
|
6
|
+
img.onload = () => resolve();
|
|
7
|
+
img.onerror = (e) => (throwOnError ? reject(e) : resolve());
|
|
8
|
+
img.src = src;
|
|
9
|
+
});
|
|
10
|
+
};
|
|
@@ -6,6 +6,7 @@ export * from './date-helper';
|
|
|
6
6
|
export * from './dom-helper';
|
|
7
7
|
export * from './href-validator';
|
|
8
8
|
export * from './html-helper';
|
|
9
|
+
export * from './image-preloader';
|
|
9
10
|
export * from './lazy-init';
|
|
10
11
|
export * from './number-helper';
|
|
11
12
|
export * from './string-generator';
|
package/dist/core/utils/index.js
CHANGED
|
@@ -6,6 +6,7 @@ export * from './date-helper';
|
|
|
6
6
|
export * from './dom-helper';
|
|
7
7
|
export * from './href-validator';
|
|
8
8
|
export * from './html-helper';
|
|
9
|
+
export * from './image-preloader';
|
|
9
10
|
export * from './lazy-init';
|
|
10
11
|
export * from './number-helper';
|
|
11
12
|
export * from './string-generator';
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
type MediaCallbacks = {
|
|
2
|
+
onPlay: () => void;
|
|
3
|
+
onPause: () => void;
|
|
4
|
+
onStop: (inRespectToNewlyActivatedVideo: boolean) => void;
|
|
5
|
+
onToggle?: () => void;
|
|
6
|
+
onVolumeChange?: (volume: number) => void;
|
|
7
|
+
onMute?: () => void;
|
|
8
|
+
onUnmute?: () => void;
|
|
9
|
+
};
|
|
10
|
+
declare class PlaybackManagerInstance {
|
|
11
|
+
private _mountedPlayableComponents;
|
|
12
|
+
private _playingComponentId;
|
|
13
|
+
registerMountedPlayer: (componentId: string, callbacks: MediaCallbacks) => void;
|
|
14
|
+
unregisterPlayer: (componentId: string) => void;
|
|
15
|
+
setPlayingComponent: (componentId: string | null) => void;
|
|
16
|
+
play: (componentId: string | null) => void;
|
|
17
|
+
pause: (componentId?: string) => void;
|
|
18
|
+
stop: (componentId: string) => void;
|
|
19
|
+
toggle: (componentId: string) => void;
|
|
20
|
+
setVolume: (componentId: string, volume: number) => void;
|
|
21
|
+
mute: (componentId: string) => void;
|
|
22
|
+
unmute: (componentId: string) => void;
|
|
23
|
+
}
|
|
24
|
+
export declare const PlaybackManager: PlaybackManagerInstance;
|
|
25
|
+
export {};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
class PlaybackManagerInstance {
|
|
2
|
+
_mountedPlayableComponents = new Map();
|
|
3
|
+
_playingComponentId = null;
|
|
4
|
+
registerMountedPlayer = (componentId, callbacks) => {
|
|
5
|
+
this._mountedPlayableComponents.set(componentId, callbacks);
|
|
6
|
+
};
|
|
7
|
+
unregisterPlayer = (componentId) => {
|
|
8
|
+
this._mountedPlayableComponents.delete(componentId);
|
|
9
|
+
if (this._playingComponentId === componentId) {
|
|
10
|
+
this._playingComponentId = null;
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
setPlayingComponent = (componentId) => {
|
|
14
|
+
if (this._playingComponentId === componentId) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
if (this._playingComponentId) {
|
|
18
|
+
const callbacks = this._mountedPlayableComponents.get(this._playingComponentId);
|
|
19
|
+
callbacks?.onStop(true);
|
|
20
|
+
}
|
|
21
|
+
this._playingComponentId = componentId;
|
|
22
|
+
};
|
|
23
|
+
play = (componentId) => {
|
|
24
|
+
this.setPlayingComponent(componentId);
|
|
25
|
+
if (componentId) {
|
|
26
|
+
const callbacks = this._mountedPlayableComponents.get(componentId);
|
|
27
|
+
callbacks?.onPlay();
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
pause = (componentId) => {
|
|
31
|
+
const id = componentId || this._playingComponentId;
|
|
32
|
+
if (!id) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const callbacks = this._mountedPlayableComponents.get(id);
|
|
36
|
+
callbacks?.onPause();
|
|
37
|
+
};
|
|
38
|
+
stop = (componentId) => {
|
|
39
|
+
const callbacks = this._mountedPlayableComponents.get(componentId);
|
|
40
|
+
callbacks?.onStop(false);
|
|
41
|
+
if (this._playingComponentId === componentId) {
|
|
42
|
+
this._playingComponentId = null;
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
toggle = (componentId) => {
|
|
46
|
+
const callbacks = this._mountedPlayableComponents.get(componentId);
|
|
47
|
+
callbacks?.onToggle?.();
|
|
48
|
+
};
|
|
49
|
+
setVolume = (componentId, volume) => {
|
|
50
|
+
const callbacks = this._mountedPlayableComponents.get(componentId);
|
|
51
|
+
callbacks?.onVolumeChange?.(volume);
|
|
52
|
+
};
|
|
53
|
+
mute = (componentId) => {
|
|
54
|
+
const callbacks = this._mountedPlayableComponents.get(componentId);
|
|
55
|
+
callbacks?.onMute?.();
|
|
56
|
+
};
|
|
57
|
+
unmute = (componentId) => {
|
|
58
|
+
const callbacks = this._mountedPlayableComponents.get(componentId);
|
|
59
|
+
callbacks?.onUnmute?.();
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
export const PlaybackManager = new PlaybackManagerInstance();
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
declare class VolumeManagerInstance {
|
|
2
|
+
private _volumeLevel;
|
|
3
|
+
private _isMuted;
|
|
4
|
+
get volumeLevel(): number;
|
|
5
|
+
get isMuted(): boolean;
|
|
6
|
+
set volumeLevel(level: number);
|
|
7
|
+
set isMuted(state: boolean);
|
|
8
|
+
}
|
|
9
|
+
export declare const MediaVolumeManager: VolumeManagerInstance;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
class VolumeManagerInstance {
|
|
2
|
+
_volumeLevel = $state(1);
|
|
3
|
+
_isMuted = $state(false);
|
|
4
|
+
get volumeLevel() {
|
|
5
|
+
return this._volumeLevel;
|
|
6
|
+
}
|
|
7
|
+
get isMuted() {
|
|
8
|
+
return this._isMuted;
|
|
9
|
+
}
|
|
10
|
+
set volumeLevel(level) {
|
|
11
|
+
if (level < 0) {
|
|
12
|
+
this._volumeLevel = 0;
|
|
13
|
+
}
|
|
14
|
+
else if (level > 1) {
|
|
15
|
+
this._volumeLevel = 1;
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
this._volumeLevel = level;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
set isMuted(state) {
|
|
22
|
+
this._isMuted = state;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export const MediaVolumeManager = new VolumeManagerInstance();
|
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
<script lang="ts">import { randomNanoid } from '../../core/utils';
|
|
2
|
+
import { Icon } from '../icon';
|
|
3
|
+
import { MediaVolumeManager, PlaybackManager } from '../media-playback';
|
|
4
|
+
import { SeekBar } from '../seek-bar';
|
|
5
|
+
import IconPause from '@fluentui/svg-icons/icons/pause_20_regular.svg?raw';
|
|
6
|
+
import IconPlay from '@fluentui/svg-icons/icons/play_20_regular.svg?raw';
|
|
7
|
+
import IconSpeaker from '@fluentui/svg-icons/icons/speaker_2_20_regular.svg?raw';
|
|
8
|
+
import IconSpeakerMute from '@fluentui/svg-icons/icons/speaker_mute_20_regular.svg?raw';
|
|
9
|
+
import { untrack } from 'svelte';
|
|
10
|
+
import { fade } from 'svelte/transition';
|
|
11
|
+
let { src, poster, id = randomNanoid(), controls = true, autoplay = false, loop = false, inert = false, allowPreloading = false, hideSpeaker = false, hidePlayButton = false, intersectionContainer, scrubberPosition = 'bottom', on } = $props();
|
|
12
|
+
let video = $state(null);
|
|
13
|
+
let videoContainerRef = $state(null);
|
|
14
|
+
let showControlsOnHover = $state(false);
|
|
15
|
+
let isVideoPaused = $state(true);
|
|
16
|
+
let percentageCompleted = $state(0);
|
|
17
|
+
let everActivated = $state(false);
|
|
18
|
+
let autoplayState = $state(untrack(() => autoplay));
|
|
19
|
+
$effect(() => {
|
|
20
|
+
void src;
|
|
21
|
+
untrack(() => {
|
|
22
|
+
if (video?.src) {
|
|
23
|
+
video.src = src;
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
// Register player instance in global playback manager
|
|
28
|
+
$effect(() => untrack(() => {
|
|
29
|
+
PlaybackManager.registerMountedPlayer(id, {
|
|
30
|
+
onPlay: () => {
|
|
31
|
+
void play();
|
|
32
|
+
},
|
|
33
|
+
onPause: () => {
|
|
34
|
+
pause();
|
|
35
|
+
},
|
|
36
|
+
onStop: (inRespectToNewlyActivatedVideo) => {
|
|
37
|
+
if (autoplayState === 'on-appearance' && inRespectToNewlyActivatedVideo) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
stop();
|
|
41
|
+
},
|
|
42
|
+
onToggle: () => {
|
|
43
|
+
togglePlay();
|
|
44
|
+
},
|
|
45
|
+
onVolumeChange: (volume) => {
|
|
46
|
+
setVolume(volume);
|
|
47
|
+
},
|
|
48
|
+
onMute: () => {
|
|
49
|
+
setMuted(true);
|
|
50
|
+
},
|
|
51
|
+
onUnmute: () => {
|
|
52
|
+
setMuted(false);
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
on?.loaded?.({ id, src });
|
|
56
|
+
return () => {
|
|
57
|
+
PlaybackManager.unregisterPlayer(id);
|
|
58
|
+
};
|
|
59
|
+
}));
|
|
60
|
+
// Intersection observer for lazy loading and autoplay-on-appearance
|
|
61
|
+
$effect(() => {
|
|
62
|
+
if (!videoContainerRef) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const container = videoContainerRef;
|
|
66
|
+
return untrack(() => {
|
|
67
|
+
const observer = new IntersectionObserver((entries) => {
|
|
68
|
+
if (!video) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const [entry] = entries;
|
|
72
|
+
if ((entry.isIntersecting || allowPreloading) && !video.src) {
|
|
73
|
+
video.src = src;
|
|
74
|
+
video.load();
|
|
75
|
+
if (autoplayState === true) {
|
|
76
|
+
const handleCanPlay = () => {
|
|
77
|
+
if (entry.isIntersecting) {
|
|
78
|
+
void play();
|
|
79
|
+
autoplayState = false;
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
video.addEventListener('canplay', handleCanPlay, { once: true });
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (autoplayState !== 'on-appearance') {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
if (!entry.isIntersecting) {
|
|
90
|
+
stop();
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
if (entry.intersectionRatio < 0.4) {
|
|
94
|
+
pause();
|
|
95
|
+
}
|
|
96
|
+
if (entry.intersectionRatio >= 0.6) {
|
|
97
|
+
const handleCanPlay = () => {
|
|
98
|
+
if (entry.intersectionRatio >= 0.6) {
|
|
99
|
+
void play();
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
if (video.readyState >= 3) {
|
|
103
|
+
void play();
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
video.addEventListener('canplay', handleCanPlay, { once: true });
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}, {
|
|
110
|
+
root: intersectionContainer,
|
|
111
|
+
rootMargin: '0px',
|
|
112
|
+
threshold: [0.1, 0.4, 0.6]
|
|
113
|
+
});
|
|
114
|
+
observer.observe(container);
|
|
115
|
+
return () => {
|
|
116
|
+
observer.disconnect();
|
|
117
|
+
};
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
const play = async () => {
|
|
121
|
+
if (!video) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
try {
|
|
125
|
+
await video.play();
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
// can't play video without interaction with document, just ignoring the error
|
|
129
|
+
}
|
|
130
|
+
finally {
|
|
131
|
+
if (video.paused) {
|
|
132
|
+
video.muted = true;
|
|
133
|
+
void video.play();
|
|
134
|
+
}
|
|
135
|
+
everActivated = true;
|
|
136
|
+
}
|
|
137
|
+
on?.started?.({ id, src });
|
|
138
|
+
};
|
|
139
|
+
const pause = () => {
|
|
140
|
+
video?.pause();
|
|
141
|
+
};
|
|
142
|
+
const stop = () => {
|
|
143
|
+
if (!video) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
video.pause();
|
|
147
|
+
video.currentTime = 0;
|
|
148
|
+
video.load();
|
|
149
|
+
onPause();
|
|
150
|
+
};
|
|
151
|
+
const togglePlay = (e) => {
|
|
152
|
+
e?.stopPropagation();
|
|
153
|
+
if (!video) {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
if (video.paused) {
|
|
157
|
+
void play();
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
pause();
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
const toggleMute = (e) => {
|
|
164
|
+
e?.stopPropagation();
|
|
165
|
+
setMuted(!video?.muted);
|
|
166
|
+
};
|
|
167
|
+
const onVolumeChange = () => {
|
|
168
|
+
if (!video) {
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
MediaVolumeManager.volumeLevel = video.volume;
|
|
172
|
+
MediaVolumeManager.isMuted = video.muted;
|
|
173
|
+
};
|
|
174
|
+
const onTimeUpdate = () => {
|
|
175
|
+
if (!video) {
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
percentageCompleted = video.currentTime / video.duration || 0;
|
|
179
|
+
notifyProgress();
|
|
180
|
+
};
|
|
181
|
+
const onLoaded = () => {
|
|
182
|
+
setVolume(MediaVolumeManager.volumeLevel);
|
|
183
|
+
setMuted(MediaVolumeManager.isMuted);
|
|
184
|
+
};
|
|
185
|
+
const onEnded = () => {
|
|
186
|
+
percentageCompleted = 1;
|
|
187
|
+
notifyProgress();
|
|
188
|
+
on?.ended?.({ id, src });
|
|
189
|
+
if (!loop) {
|
|
190
|
+
everActivated = false;
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
const onPlay = () => {
|
|
194
|
+
isVideoPaused = false;
|
|
195
|
+
PlaybackManager.setPlayingComponent(id);
|
|
196
|
+
};
|
|
197
|
+
const onPause = () => {
|
|
198
|
+
isVideoPaused = true;
|
|
199
|
+
};
|
|
200
|
+
const setVolume = (level) => {
|
|
201
|
+
MediaVolumeManager.volumeLevel = level;
|
|
202
|
+
if (video) {
|
|
203
|
+
video.volume = level;
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
const setMuted = (state) => {
|
|
207
|
+
MediaVolumeManager.isMuted = state;
|
|
208
|
+
};
|
|
209
|
+
$effect(() => {
|
|
210
|
+
if (video) {
|
|
211
|
+
video.muted = MediaVolumeManager.isMuted;
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
const notifyProgress = () => {
|
|
215
|
+
on?.progress?.(percentageCompleted);
|
|
216
|
+
};
|
|
217
|
+
const handleSeek = (percent) => {
|
|
218
|
+
if (video) {
|
|
219
|
+
video.currentTime = video.duration * percent;
|
|
220
|
+
percentageCompleted = percent;
|
|
221
|
+
notifyProgress();
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
</script>
|
|
225
|
+
|
|
226
|
+
<div class="video" role="none" inert={inert} bind:this={videoContainerRef}>
|
|
227
|
+
<video
|
|
228
|
+
class="video__video"
|
|
229
|
+
class:video__video--not-activated={!everActivated}
|
|
230
|
+
width="100%"
|
|
231
|
+
controls={controls && everActivated}
|
|
232
|
+
poster={poster}
|
|
233
|
+
loop={loop}
|
|
234
|
+
preload="metadata"
|
|
235
|
+
onvolumechange={onVolumeChange}
|
|
236
|
+
ontimeupdate={onTimeUpdate}
|
|
237
|
+
onloadeddata={onLoaded}
|
|
238
|
+
onended={onEnded}
|
|
239
|
+
onplay={onPlay}
|
|
240
|
+
onpause={onPause}
|
|
241
|
+
playsinline
|
|
242
|
+
bind:this={video}>
|
|
243
|
+
<track src="" kind="captions" />
|
|
244
|
+
</video>
|
|
245
|
+
{#if !everActivated && poster}
|
|
246
|
+
<img class="video__poster" src={poster} alt="" />
|
|
247
|
+
{/if}
|
|
248
|
+
{#if !controls || !everActivated}
|
|
249
|
+
<div class="video__overlay" onclick={togglePlay} onkeydown={() => ({})} role="none">
|
|
250
|
+
{#if isVideoPaused && !hidePlayButton}
|
|
251
|
+
<button type="button" aria-label="play" class="video__playback-button" onclick={togglePlay} onkeydown={() => ({})}>
|
|
252
|
+
<Icon src={IconPlay} color="white" />
|
|
253
|
+
</button>
|
|
254
|
+
{:else if showControlsOnHover && !hidePlayButton}
|
|
255
|
+
<button type="button" aria-label="pause" class="video__playback-button video__playback-button--pause" onclick={togglePlay} onkeydown={() => ({})}>
|
|
256
|
+
<Icon src={IconPause} color="white" />
|
|
257
|
+
</button>
|
|
258
|
+
{/if}
|
|
259
|
+
{#if (showControlsOnHover || MediaVolumeManager.isMuted) && !hideSpeaker}
|
|
260
|
+
<button type="button" aria-label={MediaVolumeManager.isMuted ? 'mute' : 'unmute'} class="video__mute-button" onclick={toggleMute}>
|
|
261
|
+
{#if MediaVolumeManager.isMuted}
|
|
262
|
+
<Icon src={IconSpeakerMute} color="white" />
|
|
263
|
+
{:else}
|
|
264
|
+
<Icon src={IconSpeaker} color="white" />
|
|
265
|
+
{/if}
|
|
266
|
+
</button>
|
|
267
|
+
{/if}
|
|
268
|
+
|
|
269
|
+
{#if everActivated}
|
|
270
|
+
<div
|
|
271
|
+
class="video__progress-container"
|
|
272
|
+
class:video__progress-container--top={scrubberPosition === 'top'}
|
|
273
|
+
class:video__progress-container--bottom={scrubberPosition === 'bottom'}
|
|
274
|
+
onmouseenter={() => (showControlsOnHover = true)}
|
|
275
|
+
onmouseleave={() => (showControlsOnHover = false)}
|
|
276
|
+
role="none">
|
|
277
|
+
{#if showControlsOnHover || (!showControlsOnHover && isVideoPaused)}
|
|
278
|
+
<div
|
|
279
|
+
class="video__seek-bar"
|
|
280
|
+
transition:fade={{ duration: isVideoPaused ? 0 : 300 }}
|
|
281
|
+
onclick={(e) => e.stopPropagation()}
|
|
282
|
+
onkeydown={() => ({})}
|
|
283
|
+
role="none">
|
|
284
|
+
<SeekBar value={percentageCompleted} on={{ seek: handleSeek }} />
|
|
285
|
+
</div>
|
|
286
|
+
{/if}
|
|
287
|
+
</div>
|
|
288
|
+
{/if}
|
|
289
|
+
</div>
|
|
290
|
+
{/if}
|
|
291
|
+
</div>
|
|
292
|
+
|
|
293
|
+
<style>.video {
|
|
294
|
+
--_video--background-color: var(--video--background-color, #000000);
|
|
295
|
+
--_video--border-radius: var(--video--border-radius, 0);
|
|
296
|
+
--_video--media-fit: var(--video--media-fit, contain);
|
|
297
|
+
--_video--poster--media-fit: var(--video--poster--media-fit, cover);
|
|
298
|
+
height: 100%;
|
|
299
|
+
min-height: 100%;
|
|
300
|
+
max-height: 100%;
|
|
301
|
+
width: 100%;
|
|
302
|
+
min-width: 100%;
|
|
303
|
+
max-width: 100%;
|
|
304
|
+
cursor: pointer;
|
|
305
|
+
position: relative;
|
|
306
|
+
overflow: hidden;
|
|
307
|
+
border-radius: var(--_video--border-radius);
|
|
308
|
+
background: var(--_video--background-color);
|
|
309
|
+
}
|
|
310
|
+
.video__playback-button {
|
|
311
|
+
--icon--filter: drop-shadow(1px 1px #000000);
|
|
312
|
+
position: absolute;
|
|
313
|
+
top: 50%;
|
|
314
|
+
left: 50%;
|
|
315
|
+
transform: translate(-50%, -50%);
|
|
316
|
+
font-size: 2em;
|
|
317
|
+
}
|
|
318
|
+
.video__playback-button--pause {
|
|
319
|
+
/* Set 'container-type: inline-size;' to reference container*/
|
|
320
|
+
}
|
|
321
|
+
@container (width < 576px) {
|
|
322
|
+
.video__playback-button--pause {
|
|
323
|
+
display: none;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
.video__mute-button {
|
|
327
|
+
position: absolute;
|
|
328
|
+
top: 0.625em;
|
|
329
|
+
right: 0.625em;
|
|
330
|
+
font-size: 1em;
|
|
331
|
+
z-index: 1;
|
|
332
|
+
}
|
|
333
|
+
.video__poster {
|
|
334
|
+
object-fit: var(--_video--poster--media-fit);
|
|
335
|
+
min-width: 100%;
|
|
336
|
+
min-height: 100%;
|
|
337
|
+
max-width: 100%;
|
|
338
|
+
max-height: 100%;
|
|
339
|
+
}
|
|
340
|
+
.video__video {
|
|
341
|
+
object-fit: var(--_video--media-fit);
|
|
342
|
+
min-width: 100%;
|
|
343
|
+
min-height: 100%;
|
|
344
|
+
max-width: 100%;
|
|
345
|
+
max-height: 100%;
|
|
346
|
+
}
|
|
347
|
+
.video__video--not-activated {
|
|
348
|
+
visibility: hidden;
|
|
349
|
+
height: 0;
|
|
350
|
+
min-height: 0;
|
|
351
|
+
}
|
|
352
|
+
.video__overlay {
|
|
353
|
+
position: absolute;
|
|
354
|
+
inset: 0;
|
|
355
|
+
background-color: transparent;
|
|
356
|
+
}
|
|
357
|
+
.video__progress-container {
|
|
358
|
+
position: absolute;
|
|
359
|
+
left: 0;
|
|
360
|
+
right: 0;
|
|
361
|
+
z-index: 1;
|
|
362
|
+
padding-inline: 0.25rem;
|
|
363
|
+
}
|
|
364
|
+
.video__progress-container--top {
|
|
365
|
+
top: 0;
|
|
366
|
+
padding-bottom: 0.9375rem;
|
|
367
|
+
}
|
|
368
|
+
.video__progress-container--bottom {
|
|
369
|
+
bottom: 0;
|
|
370
|
+
padding-top: 0.9375rem;
|
|
371
|
+
}</style>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { ScrubberPosition } from './types';
|
|
2
|
+
type Props = {
|
|
3
|
+
src: string;
|
|
4
|
+
poster: string | null | undefined;
|
|
5
|
+
id?: string;
|
|
6
|
+
controls?: boolean;
|
|
7
|
+
autoplay?: true | false | 'on-appearance';
|
|
8
|
+
loop?: boolean;
|
|
9
|
+
intersectionContainer?: HTMLElement;
|
|
10
|
+
inert?: boolean;
|
|
11
|
+
allowPreloading?: boolean;
|
|
12
|
+
hideSpeaker?: boolean;
|
|
13
|
+
hidePlayButton?: boolean;
|
|
14
|
+
scrubberPosition?: ScrubberPosition;
|
|
15
|
+
on?: {
|
|
16
|
+
started?: (data: {
|
|
17
|
+
id: string;
|
|
18
|
+
src: string;
|
|
19
|
+
}) => void;
|
|
20
|
+
loaded?: (data: {
|
|
21
|
+
id: string;
|
|
22
|
+
src: string;
|
|
23
|
+
}) => void;
|
|
24
|
+
progress?: (value: number) => void;
|
|
25
|
+
ended?: (data: {
|
|
26
|
+
id: string;
|
|
27
|
+
src: string;
|
|
28
|
+
}) => void;
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
declare const Cmp: import("svelte").Component<Props, {}, "">;
|
|
32
|
+
type Cmp = ReturnType<typeof Cmp>;
|
|
33
|
+
export default Cmp;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Video } from './cmp.video.svelte';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type ScrubberPosition = 'top' | 'bottom';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@streamscloud/kit",
|
|
3
|
-
"version": "0.0.1-
|
|
3
|
+
"version": "0.0.1-1770982902053",
|
|
4
4
|
"author": "StreamsCloud",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -124,6 +124,10 @@
|
|
|
124
124
|
"types": "./dist/ui/loading/index.d.ts",
|
|
125
125
|
"svelte": "./dist/ui/loading/index.js"
|
|
126
126
|
},
|
|
127
|
+
"./ui/media-playback": {
|
|
128
|
+
"types": "./dist/ui/media-playback/index.d.ts",
|
|
129
|
+
"svelte": "./dist/ui/media-playback/index.js"
|
|
130
|
+
},
|
|
127
131
|
"./ui/progress": {
|
|
128
132
|
"types": "./dist/ui/progress/index.d.ts",
|
|
129
133
|
"svelte": "./dist/ui/progress/index.js"
|
|
@@ -144,6 +148,10 @@
|
|
|
144
148
|
"types": "./dist/ui/time-ago/index.d.ts",
|
|
145
149
|
"svelte": "./dist/ui/time-ago/index.js"
|
|
146
150
|
},
|
|
151
|
+
"./ui/video": {
|
|
152
|
+
"types": "./dist/ui/video/index.d.ts",
|
|
153
|
+
"svelte": "./dist/ui/video/index.js"
|
|
154
|
+
},
|
|
147
155
|
"./styles/base": {
|
|
148
156
|
"sass": "./dist/styles/_index.scss"
|
|
149
157
|
},
|