@streamscloud/kit 0.2.26 → 0.2.27-1775564173889

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.
@@ -46,7 +46,7 @@ const onColorPickerInput = ({ hex }) => {
46
46
  </script>
47
47
 
48
48
  <div class="color-picker">
49
- <Dropdown position="bottom-start" keepDropdownOpen>
49
+ <Dropdown position="bottom-start" keepDropdownOpen panel={false}>
50
50
  {#snippet trigger()}
51
51
  <div class="color-picker__trigger">
52
52
  {#if children}
@@ -50,7 +50,7 @@ const selectedRatioLabel = $derived.by(() => {
50
50
  {#if cropper.ratioOptions}
51
51
  <div class="img-cropper-toolbar__ratio">
52
52
  <span class="img-cropper-toolbar__label">{loc.ratio}</span>
53
- <Dropdown position="top">
53
+ <Dropdown position="top" panel={false}>
54
54
  {#snippet trigger()}
55
55
  <div class="img-cropper-toolbar__ratio-trigger">{selectedRatioLabel}</div>
56
56
  {/snippet}
@@ -5,7 +5,7 @@ import IconEmoji from '@fluentui/svg-icons/icons/emoji_20_regular.svg?raw';
5
5
  let { dropdownPosition = 'bottom-end', on, children } = $props();
6
6
  </script>
7
7
 
8
- <Dropdown position={dropdownPosition} keepDropdownOpen={true}>
8
+ <Dropdown position={dropdownPosition} keepDropdownOpen={true} panel={false}>
9
9
  {#snippet trigger()}
10
10
  <div class="emoji-picker__trigger">
11
11
  {#if children}
@@ -11,7 +11,7 @@ let { on } = $props();
11
11
  </button>
12
12
 
13
13
  <div class="input-emoji-picker">
14
- <Dropdown position="bottom-end" matchTriggerWidth on={{ mounted: (e) => (toggleDropdown = e.toggleOpen) }} keepDropdownOpen={true}>
14
+ <Dropdown position="bottom-end" matchTriggerWidth panel={false} on={{ mounted: (e) => (toggleDropdown = e.toggleOpen) }} keepDropdownOpen={true}>
15
15
  {#snippet trigger()}
16
16
  <div class="input-emoji-picker__trigger-stub"></div>
17
17
  {/snippet}
@@ -8,7 +8,7 @@ import IconSpeaker from '@fluentui/svg-icons/icons/speaker_2_20_regular.svg?raw'
8
8
  import IconSpeakerMute from '@fluentui/svg-icons/icons/speaker_mute_20_regular.svg?raw';
9
9
  import { untrack } from 'svelte';
10
10
  import { fade } from 'svelte/transition';
11
- let { src, poster, id = randomNanoid(), controls = false, autoplay = false, loop = false, inert = false, allowPreloading = false, hideSpeaker = false, hidePlayButton = false, intersectionContainer, scrubberPosition = 'bottom', on } = $props();
11
+ let { src, poster, id = randomNanoid(), autoplay = false, loop = false, inert = false, allowPreloading = false, hideSpeaker = false, hidePlayButton = false, intersectionContainer, scrubberPosition = 'bottom', on } = $props();
12
12
  let video = $state(null);
13
13
  let videoContainerRef = $state(null);
14
14
  let showControlsOnHover = $state(false);
@@ -25,6 +25,15 @@ $effect(() => {
25
25
  }
26
26
  });
27
27
  });
28
+ // Eagerly load video on mount when no poster — show first frame immediately
29
+ $effect(() => {
30
+ if (!poster && video && !video.src) {
31
+ untrack(() => {
32
+ video.src = src;
33
+ video.load();
34
+ });
35
+ }
36
+ });
28
37
  // Register player instance in global playback manager
29
38
  $effect(() => untrack(() => {
30
39
  PlaybackManager.registerMountedPlayer(id, {
@@ -111,7 +120,7 @@ $effect(() => {
111
120
  }
112
121
  }, {
113
122
  root: intersectionContainer,
114
- rootMargin: '0px',
123
+ rootMargin: poster ? '0px' : '200px',
115
124
  threshold: [0.1, 0.4, 0.6]
116
125
  });
117
126
  observer.observe(container);
@@ -229,12 +238,11 @@ const handleSeek = (percent) => {
229
238
  <div class="video" role="none" inert={inert} bind:this={videoContainerRef}>
230
239
  <video
231
240
  class="video__video"
232
- class:video__video--not-activated={!everActivated}
241
+ class:video__video--not-activated={!everActivated && !!poster}
233
242
  width="100%"
234
- controls={controls && everActivated}
235
243
  poster={poster}
236
244
  loop={loop}
237
- preload="metadata"
245
+ preload={poster ? 'metadata' : 'auto'}
238
246
  onvolumechange={onVolumeChange}
239
247
  ontimeupdate={onTimeUpdate}
240
248
  onloadeddata={onLoaded}
@@ -248,56 +256,54 @@ const handleSeek = (percent) => {
248
256
  {#if !everActivated && poster}
249
257
  <img class="video__poster" src={poster} alt="" />
250
258
  {/if}
251
- {#if !controls || !everActivated}
252
- <div
253
- class="video__overlay"
254
- onclick={togglePlay}
255
- onkeydown={() => ({})}
256
- onmouseenter={() => (showControlsOnHover = true)}
257
- onmouseleave={() => (showControlsOnHover = false)}
258
- role="none">
259
- {#if isVideoPaused && !hidePlayButton}
260
- <button type="button" aria-label="play" class="video__playback-button" onclick={togglePlay} onkeydown={() => ({})}>
261
- <Icon src={IconPlay} color="white" />
262
- </button>
263
- {:else if showControlsOnHover && !hidePlayButton}
264
- <button type="button" aria-label="pause" class="video__playback-button video__playback-button--pause" onclick={togglePlay} onkeydown={() => ({})}>
265
- <Icon src={IconPause} color="white" />
266
- </button>
267
- {/if}
259
+ <div
260
+ class="video__overlay"
261
+ onclick={togglePlay}
262
+ onkeydown={() => ({})}
263
+ onmouseenter={() => (showControlsOnHover = true)}
264
+ onmouseleave={() => (showControlsOnHover = false)}
265
+ role="none">
266
+ {#if isVideoPaused && !hidePlayButton}
267
+ <button type="button" aria-label="play" class="video__playback-button" onclick={togglePlay} onkeydown={() => ({})}>
268
+ <Icon src={IconPlay} color="white" />
269
+ </button>
270
+ {:else if showControlsOnHover && !hidePlayButton}
271
+ <button type="button" aria-label="pause" class="video__playback-button video__playback-button--pause" onclick={togglePlay} onkeydown={() => ({})}>
272
+ <Icon src={IconPause} color="white" />
273
+ </button>
274
+ {/if}
268
275
 
269
- {#if (showControlsOnHover || MediaVolumeManager.isMuted) && !hideSpeaker}
270
- <button type="button" aria-label={MediaVolumeManager.isMuted ? 'mute' : 'unmute'} class="video__mute-button" onclick={toggleMute}>
271
- {#if MediaVolumeManager.isMuted}
272
- <Icon src={IconSpeakerMute} color="white" />
273
- {:else}
274
- <Icon src={IconSpeaker} color="white" />
275
- {/if}
276
- </button>
277
- {/if}
276
+ {#if (showControlsOnHover || MediaVolumeManager.isMuted) && !hideSpeaker}
277
+ <button type="button" aria-label={MediaVolumeManager.isMuted ? 'mute' : 'unmute'} class="video__mute-button" onclick={toggleMute}>
278
+ {#if MediaVolumeManager.isMuted}
279
+ <Icon src={IconSpeakerMute} color="white" />
280
+ {:else}
281
+ <Icon src={IconSpeaker} color="white" />
282
+ {/if}
283
+ </button>
284
+ {/if}
278
285
 
279
- {#if everActivated}
280
- <div
281
- class="video__progress-container"
282
- class:video__progress-container--top={scrubberPosition === 'top'}
283
- class:video__progress-container--bottom={scrubberPosition === 'bottom'}
284
- onmouseenter={() => (showProgressOnHover = true)}
285
- onmouseleave={() => (showProgressOnHover = false)}
286
- role="none">
287
- {#if showProgressOnHover || (!showProgressOnHover && isVideoPaused)}
288
- <div
289
- class="video__seek-bar"
290
- transition:fade={{ duration: isVideoPaused ? 0 : 300 }}
291
- onclick={(e) => e.stopPropagation()}
292
- onkeydown={() => ({})}
293
- role="none">
294
- <SeekBar value={percentageCompleted} on={{ seek: handleSeek }} />
295
- </div>
296
- {/if}
297
- </div>
298
- {/if}
299
- </div>
300
- {/if}
286
+ {#if everActivated}
287
+ <div
288
+ class="video__progress-container"
289
+ class:video__progress-container--top={scrubberPosition === 'top'}
290
+ class:video__progress-container--bottom={scrubberPosition === 'bottom'}
291
+ onmouseenter={() => (showProgressOnHover = true)}
292
+ onmouseleave={() => (showProgressOnHover = false)}
293
+ role="none">
294
+ {#if showProgressOnHover || (!showProgressOnHover && isVideoPaused)}
295
+ <div
296
+ class="video__seek-bar"
297
+ transition:fade={{ duration: isVideoPaused ? 0 : 300 }}
298
+ onclick={(e) => e.stopPropagation()}
299
+ onkeydown={() => ({})}
300
+ role="none">
301
+ <SeekBar value={percentageCompleted} on={{ seek: handleSeek }} />
302
+ </div>
303
+ {/if}
304
+ </div>
305
+ {/if}
306
+ </div>
301
307
  </div>
302
308
 
303
309
  <!--
@@ -6,8 +6,6 @@ type Props = {
6
6
  poster: string | null | undefined;
7
7
  /** Unique player ID for the global PlaybackManager @default nanoid() */
8
8
  id?: string;
9
- /** Show native browser controls once playback starts */
10
- controls?: boolean;
11
9
  /** Autoplay strategy: `true` plays immediately, `'on-appearance'` plays when scrolled into view */
12
10
  autoplay?: true | false | 'on-appearance';
13
11
  loop?: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@streamscloud/kit",
3
- "version": "0.2.26",
3
+ "version": "0.2.27-1775564173889",
4
4
  "author": "StreamsCloud",
5
5
  "repository": {
6
6
  "type": "git",