@munsonlabs/video-player 0.1.3 → 0.1.5

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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @munsonlabs/video-player
2
2
 
3
- A Vue 3 video player supporting YouTube, Vimeo, Dailymotion, Brightcove, JW Player, and plain HTML5 (MP4, HLS, DASH). Includes a sticky stage player, lazy loading, custom actions, IMA ad support, header bidding, captions, adaptive quality selection, and web component exports.
3
+ A Vue 3 video player supporting YouTube, Vimeo, Dailymotion, Brightcove, JW Player, and plain HTML5 (MP4, HLS, DASH). Includes a sticky stage player, lazy loading, custom actions, IMA ad support, header bidding, captions, adaptive quality selection, headless controls, and web component exports.
4
4
 
5
5
  ## Installation
6
6
 
@@ -39,7 +39,6 @@ import '@munsonlabs/video-player/style'
39
39
 
40
40
  <template>
41
41
  <VideoStage @state-change="onStateChange" />
42
-
43
42
  <VideoItem v-for="video in videos" :key="video.videoUrl" v-bind="video" />
44
43
  </template>
45
44
  ```
@@ -73,25 +72,27 @@ const stage = ref(null)
73
72
 
74
73
  ## VideoItem / VideoPlayer Props
75
74
 
76
- | Prop | Type | Default | Description |
77
- | ------------------- | -------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
78
- | `videoUrl` | `string` | - | **Required.** The video URL or platform-specific URI (the platform is auto-detected from its shape, see [Platform URLs](#platform-urls)) |
79
- | `title` | `string` | `''` | Video title |
80
- | `poster` | `string` | `''` | Poster image URL |
81
- | `aspectRatio` | `string` | `'16:9'` | e.g. `'16:9'`, `'9:16'`, `'4:3'` |
82
- | `autoplay` | `boolean` | `false` | Autoplay on mount (mutes automatically) |
83
- | `muted` | `boolean` | `false` | Start muted |
84
- | `lazy` | `boolean` | `true`\* | Show placeholder until clicked |
85
- | `nativeUi` | `boolean` | `false` | Use the platform's native controls (YouTube, Vimeo, Dailymotion only) |
86
- | `autoStage` | `boolean` | `false` | Immediately send this video to the stage on mount |
87
- | `playbackRate` | `number` | `1` | Initial playback rate |
88
- | `adTagUrl` | `string` | `''` | VAST or VMAP ad tag URL |
89
- | `adTagParams` | `object` | - | Fills `{macro}` tokens on whichever ad tag URL ends up in use, see [Ads](#ads-ima--vast--vmap) |
90
- | `headerBidding` | `object` | - | Runs a Prebid.js auction before the ad plays, see [Header bidding](#header-bidding-prebidjs) |
91
- | `tracks` | `array` | - | WebVTT caption/subtitle tracks, see [Captions](#captions-webvtt) |
92
- | `payload` | `object` | `{}` | Arbitrary data attached to every `state-change` event |
93
- | `action` | `PlayerAction` | `null` | Button shown in the player HUD, see [Actions](#actions) |
94
- | `disableTapCapture` | `boolean` | `false` | Disables the full-video tap-to-reveal-controls overlay (tap/click still reaches embed platform UI underneath, e.g. YouTube's own) |
75
+ | Prop | Type | Default | Description |
76
+ | ------------------- | -------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
77
+ | `videoUrl` | `string` | - | **Required.** The video URL or platform-specific URI (the platform is auto-detected from its shape, see [Platform URLs](#platform-urls)) |
78
+ | `title` | `string` | `''` | Video title |
79
+ | `poster` | `string` | `''` | Poster image URL |
80
+ | `aspectRatio` | `string` | `'16:9'` | e.g. `'16:9'`, `'9:16'`, `'4:3'` |
81
+ | `autoplay` | `boolean` | `false` | Autoplay on mount (mutes automatically) |
82
+ | `muted` | `boolean` | `false` | Start muted |
83
+ | `lazy` | `boolean` | `true`\* | Show placeholder until clicked |
84
+ | `nativeUi` | `boolean` | `false` | Use the platform's native controls (YouTube, Vimeo, Dailymotion only) |
85
+ | `autoStage` | `boolean` | `false` | Immediately send this video to the stage on mount |
86
+ | `playbackRate` | `number` | `1` | Initial playback rate |
87
+ | `adTagUrl` | `string` | `''` | VAST or VMAP ad tag URL |
88
+ | `adTagParams` | `object` | - | Fills `{macro}` tokens on whichever ad tag URL ends up in use, see [Ads](#ads-ima--vast--vmap) |
89
+ | `headerBidding` | `object` | - | Runs a Prebid.js auction before the ad plays, see [Header bidding](#header-bidding-prebidjs) |
90
+ | `tracks` | `array` | - | WebVTT caption/subtitle tracks, see [Captions](#captions-webvtt) |
91
+ | `payload` | `object` | `{}` | Arbitrary data attached to every `state-change` event |
92
+ | `action` | `PlayerAction` | `null` | Button shown in the player HUD, see [Actions](#actions) |
93
+ | `disableTapCapture` | `boolean` | `false` | Disables the full-video tap-to-reveal-controls overlay (tap/click still reaches embed platform UI underneath, e.g. YouTube's own) |
94
+ | `controls` | `boolean` | `true` | Set `false` to render a bare `<video>` with no built-in HUD - drive playback with your own UI instead, see [Headless Controls](#headless-controls) |
95
+ | `playWhenInView` | `boolean` | `false` | Auto-play once at least half the player is visible, auto-pause once it isn't - e.g. for a scroll-snap feed. Implies `muted` unless `muted` is set explicitly |
95
96
 
96
97
  \* `lazy` only has an effect on `VideoItem` (default `true`), which is what actually implements the placeholder-until-clicked behaviour. `VideoPlayer` accepts the prop for type compatibility but defaults to `false` and never reads it: using `<VideoPlayer>` directly always mounts the real player immediately, regardless of `lazy`.
97
98
 
@@ -112,6 +113,78 @@ The platform is detected automatically from the shape of `videoUrl`; there's no
112
113
 
113
114
  ---
114
115
 
116
+ ## Headless Controls
117
+
118
+ Unstyled control primitives for building your own HUD instead of the built-in one (set `:controls="false"` on the player) - each is published both as a Vue component and as a `muns-controls-*` custom element from the exact same source:
119
+
120
+ Building a custom HUD out of these and never rendering `VideoPlayer`'s own built-in controls? Import `@munsonlabs/video-player/style/controls` instead of the full `/style` bundle to skip the built-in HUD/popup CSS - see [Web Component Usage](#web-component-usage) for the full breakdown of the three style bundles.
121
+
122
+ ```ts
123
+ import {
124
+ PlayButton,
125
+ MuteButton,
126
+ FullscreenButton,
127
+ LoopButton,
128
+ PipButton,
129
+ CaptionsButton,
130
+ QualityButton,
131
+ PlaybackRateButton,
132
+ Buffering,
133
+ Scrubber,
134
+ VolumeSlider,
135
+ TimeDisplay,
136
+ } from '@munsonlabs/video-player'
137
+ ```
138
+
139
+ Every control takes a `player` prop pointing at the same object `VideoPlayer`/`VideoItem`/`VideoStage` expose via a template ref:
140
+
141
+ ```vue
142
+ <script setup>
143
+ import { ref } from 'vue'
144
+ import { VideoPlayer, PlayButton, MuteButton, Scrubber } from '@munsonlabs/video-player'
145
+ const player = ref(null)
146
+ </script>
147
+
148
+ <template>
149
+ <VideoPlayer ref="player" video-url="..." :controls="false" />
150
+ <PlayButton :player="player" />
151
+ <MuteButton :player="player" />
152
+ <Scrubber :player="player" />
153
+ </template>
154
+ ```
155
+
156
+ Or, `<label for>`-style, skip the ref and point at an element id instead:
157
+
158
+ ```vue
159
+ <VideoPlayer id="my-player" video-url="..." :controls="false" />
160
+ <PlayButton for="my-player" />
161
+ ```
162
+
163
+ `player` and `for` are both optional - if both are given, `player` wins. Each button's own CSS uses `:where()` (zero specificity), so a single class you add always wins, and most expose their state via a scoped slot for full custom markup (e.g. `PlayButton`'s `#default="{ isPlaying }"`). See [Web Component Usage](#web-component-usage) for using these as raw custom elements (`<muns-controls-play-button>` etc.) outside Vue.
164
+
165
+ ### Building your own wrapper component
166
+
167
+ `useForwardedPlayer` is what `VideoItem`/`VideoStage` themselves use to forward a template-ref'd `VideoPlayer`'s controls/state onto their own `defineExpose` - the same mechanism is available for building a custom wrapper component of your own:
168
+
169
+ ```vue
170
+ <script setup>
171
+ import { useForwardedPlayer } from '@munsonlabs/video-player'
172
+ const { playerRef, forwarded } = useForwardedPlayer()
173
+
174
+ defineExpose(forwarded)
175
+ </script>
176
+
177
+ <template>
178
+ <VideoPlayer ref="playerRef" v-bind="$attrs" />
179
+ </template>
180
+ ```
181
+
182
+ `useForwardedPlayer` owns the ref itself - bind `playerRef` on the wrapped `VideoPlayer`, and spread (or pass) `forwarded` into your own `defineExpose`, so callers holding a ref to your wrapper get the same imperative API `VideoPlayer` exposes. Pass a `guard` callback to intercept a method call before it reaches the underlying player - return `false` to swallow it, or run a side effect (e.g. mounting a lazy placeholder first) before returning `true`.
183
+
184
+ Because it's wired up via `defineExpose`, this works the same way whether your wrapper is used as a Vue component (a template ref) or as a custom element ([Web Component Usage](#web-component-usage)) - Vue copies `defineExpose`d properties onto the element itself, so `document.querySelector('muns-video-item').togglePlay()` calls the same forwarded method a Vue template ref would.
185
+
186
+ ---
187
+
115
188
  ## Events
116
189
 
117
190
  ### `state-change`
@@ -141,6 +214,8 @@ interface StateChangeEvent {
141
214
  | 'controlsclose'
142
215
  | 'bufferstart'
143
216
  | 'bufferend'
217
+ | 'timeupdate'
218
+ | 'tap'
144
219
  currentTime: number
145
220
  duration: number
146
221
  src: string
@@ -162,6 +237,8 @@ interface StateChangeEvent {
162
237
 
163
238
  `captionchange` / `qualitychange` fire on every `setCaptionTrack()`/`setQuality()` call (whichever menu triggered it) and whenever the adapter detects the active track/level changed on its own (e.g. hls.js's/dash.js's own ABR switching), but never before playback has started (so the initial caption/quality detection at mount doesn't get reported as a "change"). See [Captions](#captions-webvtt) and [Video Quality](#video-quality-hls--dash).
164
239
 
240
+ `tap` fires whenever the full-video tap-to-reveal-controls overlay is tapped/clicked (see `disableTapCapture`), regardless of `controls`. With the built-in HUD (`controls: true`) this is the same gesture that reveals it; with `controls: false` it's the only signal a headless consumer gets for "the user tapped the video", useful for showing/hiding your own custom HUD in response.
241
+
165
242
  `pipchange` fires whenever Picture-in-Picture is entered or exited, whether that came from the player's own PiP button or the user closing the browser's floating PiP window directly.
166
243
 
167
244
  `loopchange` fires whenever `toggleLoop()` is called (e.g. from the "Loop" row in the controls menu), never before playback has started.
@@ -296,7 +373,7 @@ While an ad is playing, a small overlay shows an "Ad" badge with a countdown to
296
373
 
297
374
  ### Ad visibility behaviour
298
375
 
299
- An ad pauses itself whenever the tab is hidden or the window loses focus (checked on `visibilitychange`/`blur`, and once up front when the ad starts, so a mid-roll beginning on an already-backgrounded tab is caught too) — no manual setup needed, and it doesn't auto-resume, since the point is to stop burning an impression on nobody watching. If Picture-in-Picture is active when an ad starts, it's exited too (skipped on iOS, where the ad creative renders directly into the same `<video>` element PiP is already mirroring, so there's nothing to lose by staying in PiP there).
376
+ An ad pauses itself whenever the tab is hidden or the window loses focus (checked on `visibilitychange`/`blur`, and once up front when the ad starts, so a mid-roll beginning on an already-backgrounded tab is caught too): no manual setup needed, and it doesn't auto-resume, since the point is to stop burning an impression on nobody watching. If Picture-in-Picture is active when an ad starts, it's exited too (skipped on iOS, where the ad creative renders directly into the same `<video>` element PiP is already mirroring, so there's nothing to lose by staying in PiP there).
300
377
 
301
378
  ---
302
379
 
@@ -351,7 +428,7 @@ Programmatically, a template ref on `VideoPlayer` exposes `setQuality(index)` (`
351
428
 
352
429
  ## Picture-in-Picture
353
430
 
354
- A Picture-in-Picture button appears in the controls automatically wherever the browser supports it (`document.pictureInPictureEnabled`) — nothing to configure. It stays in sync whether PiP is toggled from the player's own button or the browser's floating window is closed directly.
431
+ A Picture-in-Picture button appears in the controls automatically wherever the browser supports it (`document.pictureInPictureEnabled`), nothing to configure. It stays in sync whether PiP is toggled from the player's own button or the browser's floating window is closed directly.
355
432
 
356
433
  Only supported on the native `<video>` path (plain MP4/HLS): YouTube, Vimeo, and Dailymotion are unsupported here since their iframe content isn't a real `<video>` element this package controls directly. Brightcove and JW Player get it for free, same reasoning as captions/quality above.
357
434
 
@@ -376,7 +453,7 @@ Icons use `fill="currentColor"`, so `--mvp-btn-color` recolours the icon along w
376
453
 
377
454
  ### Controls popup position
378
455
 
379
- The expanded controls popup is centered over the video by default. Set `--mvp-popup-align: flex-end` to anchor it to the bottom edge instead:
456
+ The expanded controls popup is centred over the video by default. Set `--mvp-popup-align: flex-end` to anchor it to the bottom edge instead:
380
457
 
381
458
  ```css
382
459
  .my-video-wrapper {
@@ -425,7 +502,15 @@ window.addEventListener('video-state', (e) => {
425
502
 
426
503
  ## Web Component Usage
427
504
 
428
- Import the element bundle to register all components as native custom elements. These elements render into the light DOM, not a shadow root (`shadowRoot: false` — YouTube/Vimeo/Dailymotion's SDKs mount by resolving a plain element ID, which can't see into a shadow root), so styling has nowhere encapsulated to live. Rather than requiring a separate stylesheet import, the element bundle links its own `dist/style.css` onto the page automatically the moment it's imported — nothing else to remember there. Vue is an external dependency: use an import map to provide it:
505
+ Import an element bundle to register components as native custom elements. These elements render into the light DOM, not a shadow root (`shadowRoot: false`: YouTube/Vimeo/Dailymotion's SDKs mount by resolving a plain element ID, which can't see into a shadow root), so styling has nowhere encapsulated to live. Vue is an external dependency: use an import map to provide it:
506
+
507
+ Three bundles are available: pick exactly one, since importing more than one double-registers any tag they share and throws:
508
+
509
+ - `@munsonlabs/video-player/element`: everything (the built-in player plus all headless controls). Links its own styles onto the page automatically the moment it's imported, nothing else to remember there, whether you load it via a bundler, unpkg, jsdelivr, or esm.sh.
510
+ - `@munsonlabs/video-player/element/core`: just `<muns-video-player>`/`<muns-video-stage>`/`<muns-video-item>`/`<muns-video-placeholder>`, for pages that only ever use the built-in controls. Doesn't inject any CSS itself, so also import `@munsonlabs/video-player/style/core` (or your own equivalent styles).
511
+ - `@munsonlabs/video-player/element/controls`: just the `<muns-controls-*>` primitives, for building a custom HUD without the built-in player shell. Also doesn't inject any CSS, so import `@munsonlabs/video-player/style/controls` alongside it (or your own equivalent styles).
512
+
513
+ The `core`/`controls` bundles leave styling entirely up to you: defer the import, swap in your own stylesheet, or skip it if you're providing your own styles. Only the combined `element` bundle is zero-config.
429
514
 
430
515
  ```html
431
516
  <script type="importmap">
@@ -437,11 +522,11 @@ Import the element bundle to register all components as native custom elements.
437
522
  </script>
438
523
  <script type="module" src="@munsonlabs/video-player/element"></script>
439
524
 
440
- <video-item
525
+ <muns-video-item
441
526
  video-url="https://cdn.jwplayer.com/videos/O5chtspP-4VHSaSK0.mp4"
442
527
  title="Big Buck Bunny"
443
528
  poster="https://storage.googleapis.com/gtv-videos-bucket/sample/images/BigBuckBunny.jpg"
444
- ></video-item>
529
+ ></muns-video-item>
445
530
  ```
446
531
 
447
532
  If you'll be playing HLS (`.m3u8`) or DASH (`.mpd`) sources, also add an import map entry for `hls.js`/`dashjs`. Each is loaded on demand via a dynamic `import(...)`, which (unlike the Vue usage, where your own bundler resolves it) has no resolution path in a plain browser without one:
@@ -460,37 +545,66 @@ If you'll be playing HLS (`.m3u8`) or DASH (`.mpd`) sources, also add an import
460
545
 
461
546
  ### Available elements
462
547
 
463
- | Element | Vue equivalent |
464
- | --------------------- | -------------------- |
465
- | `<video-item>` | `<VideoItem>` |
466
- | `<video-stage>` | `<VideoStage>` |
467
- | `<video-player>` | `<VideoPlayer>` |
468
- | `<video-placeholder>` | `<VideoPlaceholder>` |
548
+ Core (in `/element` and `/element/core`):
549
+
550
+ | Element | Vue equivalent |
551
+ | -------------------------- | -------------------- |
552
+ | `<muns-video-item>` | `<VideoItem>` |
553
+ | `<muns-video-stage>` | `<VideoStage>` |
554
+ | `<muns-video-player>` | `<VideoPlayer>` |
555
+ | `<muns-video-placeholder>` | `<VideoPlaceholder>` |
556
+
557
+ Controls (in `/element` and `/element/controls`), see [Headless Controls](#headless-controls) for what each does:
558
+
559
+ | Element | Vue equivalent |
560
+ | -------------------------------------- | ---------------------- |
561
+ | `<muns-controls-play-button>` | `<PlayButton>` |
562
+ | `<muns-controls-mute-button>` | `<MuteButton>` |
563
+ | `<muns-controls-fullscreen-button>` | `<FullscreenButton>` |
564
+ | `<muns-controls-loop-button>` | `<LoopButton>` |
565
+ | `<muns-controls-pip-button>` | `<PipButton>` |
566
+ | `<muns-controls-captions-button>` | `<CaptionsButton>` |
567
+ | `<muns-controls-quality-button>` | `<QualityButton>` |
568
+ | `<muns-controls-playback-rate-button>` | `<PlaybackRateButton>` |
569
+ | `<muns-controls-buffering>` | `<Buffering>` |
570
+ | `<muns-controls-scrubber>` | `<Scrubber>` |
571
+ | `<muns-controls-volume-slider>` | `<VolumeSlider>` |
572
+ | `<muns-controls-time-display>` | `<TimeDisplay>` |
469
573
 
470
574
  ### Passing objects as web component attributes
471
575
 
472
576
  Boolean and string props map directly to HTML attributes. Object props (`payload`) can be passed as inline JSON strings: Vue's custom element runtime parses them automatically:
473
577
 
474
578
  ```html
475
- <video-item video-url="https://..." ad-tag-url="https://..." payload='{"articleId":"123"}'></video-item>
579
+ <muns-video-item video-url="https://..." ad-tag-url="https://..." payload='{"articleId":"123"}'></muns-video-item>
476
580
  ```
477
581
 
478
582
  For `action` (which contains a function), set it via JavaScript:
479
583
 
480
584
  ```js
481
- const player = document.querySelector('video-item')
585
+ const player = document.querySelector('muns-video-item')
482
586
  player.action = { icon: '<svg.../>', label: 'Save', onClick: () => {} }
483
587
  ```
484
588
 
485
589
  ### Listening to events
486
590
 
487
591
  ```js
488
- const player = document.querySelector('video-item')
592
+ const player = document.querySelector('muns-video-item')
489
593
  player.addEventListener('state-change', (e) => {
490
594
  console.log(e.detail)
491
595
  })
492
596
  ```
493
597
 
598
+ ### Calling player methods
599
+
600
+ `<muns-video-item>`/`<muns-video-stage>` expose the same forwarded API a Vue template ref would (see [Building your own wrapper component](#building-your-own-wrapper-component)) - methods and state land directly on the element itself:
601
+
602
+ ```js
603
+ const player = document.querySelector('muns-video-item')
604
+ player.togglePlay()
605
+ console.log(player.isPlaying)
606
+ ```
607
+
494
608
  ---
495
609
 
496
610
  ## Peer Dependencies
@@ -0,0 +1 @@
1
+ .mvp-scrubber-wrap[data-v-043f1bc8]{width:100%;position:relative}:where(.mvp-scrubber[data-v-043f1bc8]){cursor:pointer;accent-color:currentColor;width:100%}.mvp-scrubber__preview[data-v-043f1bc8]{font-variant-numeric:tabular-nums;pointer-events:none;white-space:nowrap;margin-bottom:.4rem;font-size:.75rem;position:absolute;bottom:100%;transform:translate(-50%)}:where(.mvp-play-button[data-v-218255bd]){all:unset;cursor:pointer;justify-content:center;align-items:center;display:inline-flex}:where(.mvp-play-button svg[data-v-218255bd]){width:1em;height:1em}:where(.mvp-mute-button[data-v-1940b598]){all:unset;cursor:pointer;justify-content:center;align-items:center;display:inline-flex}:where(.mvp-mute-button svg[data-v-1940b598]){width:1em;height:1em}:where(.mvp-fullscreen-button[data-v-b93a5017]){all:unset;cursor:pointer;justify-content:center;align-items:center;display:inline-flex}:where(.mvp-fullscreen-button svg[data-v-b93a5017]){width:1em;height:1em}@keyframes spin-259be2b2{to{transform:rotate(360deg)}}.spinner[data-v-259be2b2]{border-style:solid;border-color:#fffc #ffffff26 #ffffff26;border-radius:50%;animation:.75s linear infinite spin-259be2b2}:where(.mvp-buffering[data-v-454fec7f]){justify-content:center;align-items:center;display:inline-flex}:where(.mvp-loop-button[data-v-e32eda25]){all:unset;cursor:pointer;opacity:.6;justify-content:center;align-items:center;display:inline-flex}:where(.mvp-loop-button--active[data-v-e32eda25]){opacity:1}:where(.mvp-loop-button svg[data-v-e32eda25]){width:1em;height:1em}:where(.mvp-pip-button[data-v-f654d93e]){all:unset;cursor:pointer;opacity:.6;justify-content:center;align-items:center;display:inline-flex}:where(.mvp-pip-button--active[data-v-f654d93e]){opacity:1}:where(.mvp-pip-button svg[data-v-f654d93e]){width:1em;height:1em}:where(.mvp-captions-button[data-v-2572c4f2]){all:unset;cursor:pointer;opacity:.6;justify-content:center;align-items:center;display:inline-flex}:where(.mvp-captions-button--active[data-v-2572c4f2]){opacity:1}:where(.mvp-captions-button svg[data-v-2572c4f2]){width:1em;height:1em}:where(.mvp-quality-button[data-v-d1352878]){all:unset;cursor:pointer;opacity:.6;justify-content:center;align-items:center;display:inline-flex}:where(.mvp-quality-button--active[data-v-d1352878]){opacity:1}:where(.mvp-quality-button svg[data-v-d1352878]){width:1em;height:1em}:where(.mvp-playback-rate-button[data-v-646c9bc1]){all:unset;cursor:pointer;font-variant-numeric:tabular-nums;opacity:.6;justify-content:center;align-items:center;display:inline-flex}:where(.mvp-playback-rate-button--active[data-v-646c9bc1]){opacity:1}.mvp-volume-wrap[data-v-fbb2c86c]{align-items:center;gap:.75rem;width:100%;display:flex}:where(.mvp-volume-slider[data-v-fbb2c86c]){cursor:pointer;accent-color:currentColor;flex:1}:where(.mvp-volume-pct[data-v-fbb2c86c]){font-variant-numeric:tabular-nums;text-align:right;min-width:2rem}:where(.mvp-time-display[data-v-0285c811]){font-variant-numeric:tabular-nums;align-items:center;gap:.35rem;display:inline-flex}:where(.mvp-time-display__dot[data-v-0285c811]){background:currentColor;border-radius:50%;width:6px;height:6px}
@@ -0,0 +1,103 @@
1
+ //#region src/types/playback.d.ts
2
+ interface CaptionTrackInfo {
3
+ index: number;
4
+ label: string;
5
+ language: string;
6
+ }
7
+ interface QualityLevelInfo {
8
+ index: number;
9
+ height: number;
10
+ bitrate: number;
11
+ label: string;
12
+ }
13
+ //#endregion
14
+ //#region src/types.d.ts
15
+ interface PlayerHandle {
16
+ isPlaying: boolean;
17
+ togglePlay(): void;
18
+ hasEnded: boolean;
19
+ isError: boolean;
20
+ retry(): void;
21
+ isMuted: boolean;
22
+ toggleMute(): void;
23
+ isAudible: boolean;
24
+ isFullscreen: boolean;
25
+ toggleFullscreen(): void;
26
+ current: number;
27
+ total: number;
28
+ seek(percent: number): void;
29
+ bufferedDisplay: number;
30
+ isBuffering: boolean;
31
+ isLive: boolean;
32
+ vol: number;
33
+ setVolume(vol: number): void;
34
+ isLooping: boolean;
35
+ toggleLoop(): void;
36
+ supportsPip: boolean;
37
+ isPipActive: boolean;
38
+ togglePip(): void;
39
+ supportsCaptions: boolean;
40
+ captionTracks: CaptionTrackInfo[];
41
+ activeCaptionIndex: number | null;
42
+ setCaptionTrack(index: number | null): void;
43
+ supportsQuality: boolean;
44
+ qualityLevels: QualityLevelInfo[];
45
+ currentQualityIndex: number | null;
46
+ isAutoQuality: boolean;
47
+ setQuality(index: number | null): void;
48
+ supportsPlaybackRate: boolean;
49
+ playbackRate: number;
50
+ setPlaybackRate(rate: number): void;
51
+ }
52
+ //#endregion
53
+ //#region src/elements/controls.d.ts
54
+ declare const ScrubberElement: import("vue").VueElementConstructor<{
55
+ player?: PlayerHandle | null;
56
+ for?: string;
57
+ }>;
58
+ declare const PlayButtonElement: import("vue").VueElementConstructor<{
59
+ player?: PlayerHandle | null;
60
+ for?: string;
61
+ }>;
62
+ declare const MuteButtonElement: import("vue").VueElementConstructor<{
63
+ player?: PlayerHandle | null;
64
+ for?: string;
65
+ }>;
66
+ declare const FullscreenButtonElement: import("vue").VueElementConstructor<{
67
+ player?: PlayerHandle | null;
68
+ for?: string;
69
+ }>;
70
+ declare const BufferingElement: import("vue").VueElementConstructor<{
71
+ player?: PlayerHandle | null;
72
+ for?: string;
73
+ }>;
74
+ declare const LoopButtonElement: import("vue").VueElementConstructor<{
75
+ player?: PlayerHandle | null;
76
+ for?: string;
77
+ }>;
78
+ declare const PipButtonElement: import("vue").VueElementConstructor<{
79
+ player?: PlayerHandle | null;
80
+ for?: string;
81
+ }>;
82
+ declare const CaptionsButtonElement: import("vue").VueElementConstructor<{
83
+ player?: PlayerHandle | null;
84
+ for?: string;
85
+ }>;
86
+ declare const QualityButtonElement: import("vue").VueElementConstructor<{
87
+ player?: PlayerHandle | null;
88
+ for?: string;
89
+ }>;
90
+ declare const PlaybackRateButtonElement: import("vue").VueElementConstructor<{
91
+ player?: PlayerHandle | null;
92
+ for?: string;
93
+ }>;
94
+ declare const VolumeSliderElement: import("vue").VueElementConstructor<{
95
+ player?: PlayerHandle | null;
96
+ for?: string;
97
+ }>;
98
+ declare const TimeDisplayElement: import("vue").VueElementConstructor<{
99
+ player?: PlayerHandle | null;
100
+ for?: string;
101
+ }>;
102
+ //#endregion
103
+ export { BufferingElement, CaptionsButtonElement, FullscreenButtonElement, LoopButtonElement, MuteButtonElement, PipButtonElement, PlayButtonElement, PlaybackRateButtonElement, QualityButtonElement, ScrubberElement, TimeDisplayElement, VolumeSliderElement };
@@ -0,0 +1 @@
1
+ import{Fragment as e,computed as t,createBlock as n,createCommentVNode as r,createElementBlock as i,createElementVNode as a,createTextVNode as o,createVNode as s,defineComponent as c,defineCustomElement as l,mergeProps as u,normalizeClass as d,normalizeStyle as f,onBeforeUnmount as p,openBlock as m,ref as h,renderSlot as g,toDisplayString as _,toRef as v,unref as y,watch as ee}from"vue";const b=[.5,.75,1,1.25,1.5,2];function te(e){let n=h(!1),r=h(null),i=!1,a=null,o=t(()=>{let t=e.value;return t?.total?t.current/t.total*100:0}),s=t(()=>r.value??o.value),c=t(()=>r.value!==null),l=t(()=>{let t=e.value?.total??0;return s.value/100*t});function u(){a&&clearTimeout(a),a=null,r.value=null}function d(t){let o=e.value;o?.total&&(n.value||=(i=o.isPlaying,o.isPlaying&&o.togglePlay(),!0),a&&clearTimeout(a),a=null,r.value=Number(t.target.value))}function f(t){if(!n.value)return;n.value=!1;let o=e.value;if(!o)return;let s=Number(t.target.value);r.value=s,o.seek(s),i&&o.togglePlay(),a&&clearTimeout(a),a=setTimeout(u,3e3)}return ee(()=>e.value?.current,()=>{let t=e.value;if(r.value===null||!t?.total)return;let n=r.value/100*t.total;Math.abs(t.current-n)<1&&u()}),p(u),{scrubbing:c,displayPercent:s,previewSeconds:l,onInput:d,onChange:f}}function x(e,n){return t(()=>e.value?e.value:n.value?document.getElementById(n.value)??null:null)}function S(e,t=!1){if(!e||isNaN(e))return`0:00`;let n=t?Math.round(e):Math.floor(e);return`${Math.floor(n/60)}:${String(n%60).padStart(2,`0`)}`}const ne={class:`mvp-scrubber-wrap`},re=[`value`];var ie=c({inheritAttrs:!1,__name:`Scrubber`,props:{player:{type:[Object,null],required:!1},for:{type:String,required:!1}},setup(e){let t=e,n=x(v(t,`player`),v(t,`for`)),{scrubbing:o,displayPercent:s,previewSeconds:c,onInput:l,onChange:d}=te(n),p=e=>S(e,!0);return(e,t)=>(m(),i(`div`,ne,[a(`input`,u({type:`range`,min:`0`,max:`100`,step:`0.1`,value:y(s),class:`mvp-scrubber`},e.$attrs,{style:{"--p":`${y(s)}%`,"--b":`${y(n)?.bufferedDisplay??0}%`},onInput:t[0]||=(...e)=>y(l)&&y(l)(...e),onChange:t[1]||=(...e)=>y(d)&&y(d)(...e)}),null,16,re),g(e.$slots,`preview`,{scrubbing:y(o),percent:y(s),previewSeconds:y(c),formatTime:p},()=>[y(o)?(m(),i(`div`,{key:0,class:`mvp-scrubber__preview`,style:f({left:`${y(s)}%`})},_(p(y(c))),5)):r(`v-if`,!0)],!0)]))}}),C=(e,t)=>{let n=e.__vccOpts||e;for(let[e,r]of t)n[e]=r;return n},ae=C(ie,[[`__scopeId`,`data-v-043f1bc8`]]);const oe={},se={viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,"stroke-width":`2`,"stroke-linecap":`round`,"stroke-linejoin":`round`};function ce(e,t){return m(),i(`svg`,se,[...t[0]||=[a(`rect`,{x:`2`,y:`5`,width:`20`,height:`14`,rx:`2`},null,-1),a(`path`,{d:`M9.5 10.5a2 2 0 0 0-3.5 1.3v.4a2 2 0 0 0 3.5 1.3`},null,-1),a(`path`,{d:`M17.5 10.5a2 2 0 0 0-3.5 1.3v.4a2 2 0 0 0 3.5 1.3`},null,-1)]])}var le=C(oe,[[`render`,ce]]);const ue={},de={viewBox:`0 0 24 24`,fill:`currentColor`};function fe(e,t){return m(),i(`svg`,de,[...t[0]||=[a(`path`,{d:`M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z`},null,-1)]])}var pe=C(ue,[[`render`,fe]]);const w={},T={viewBox:`0 0 24 24`,fill:`currentColor`};function E(e,t){return m(),i(`svg`,T,[...t[0]||=[a(`path`,{d:`M5 16h3v3h2v-5H5v2zm3-8H5v2h5V5H8v3zm6 11h2v-3h3v-2h-5v5zm2-11V5h-2v5h5V8h-3z`},null,-1)]])}var D=C(w,[[`render`,E]]);const O={},k={viewBox:`0 0 24 24`,fill:`currentColor`};function A(e,t){return m(),i(`svg`,k,[...t[0]||=[a(`path`,{d:`M7 7h10v3l4-4-4-4v3H5v6h2V7zm10 10H7v-3l-4 4 4 4v-3h12v-6h-2v4z`},null,-1)]])}var j=C(O,[[`render`,A]]);const M={},N={viewBox:`0 0 24 24`,fill:`currentColor`};function P(e,t){return m(),i(`svg`,N,[...t[0]||=[a(`path`,{d:`M6 19h4V5H6v14zm8-14v14h4V5h-4z`},null,-1)]])}var F=C(M,[[`render`,P]]);const I={},L={viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,"stroke-width":`2`,"stroke-linecap":`round`,"stroke-linejoin":`round`};function R(e,t){return m(),i(`svg`,L,[...t[0]||=[a(`rect`,{x:`2`,y:`4`,width:`20`,height:`16`,rx:`2`},null,-1),a(`rect`,{x:`12`,y:`11`,width:`8`,height:`6`,rx:`1`,fill:`currentColor`,stroke:`none`},null,-1)]])}var z=C(I,[[`render`,R]]);const B={},me={viewBox:`0 0 24 24`,fill:`currentColor`};function he(e,t){return m(),i(`svg`,me,[...t[0]||=[a(`path`,{d:`M8 5v14l11-7z`},null,-1)]])}var ge=C(B,[[`render`,he]]);const _e={},ve={viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,"stroke-width":`2`,"stroke-linecap":`round`,"stroke-linejoin":`round`};function ye(e,t){return m(),i(`svg`,ve,[...t[0]||=[a(`rect`,{x:`2`,y:`6`,width:`20`,height:`12`,rx:`2`},null,-1),a(`path`,{d:`M7 14v-4M12 14V8M17 14v-2`},null,-1)]])}var be=C(_e,[[`render`,ye]]);const xe={},Se={viewBox:`0 0 24 24`,fill:`currentColor`};function Ce(e,t){return m(),i(`svg`,Se,[...t[0]||=[a(`path`,{d:`M12 5V1L7 6l5 5V7c3.31 0 6 2.69 6 6s-2.69 6-6 6-6-2.69-6-6H4c0 4.42 3.58 8 8 8s8-3.58 8-8-3.58-8-8-8z`},null,-1)]])}var we=C(xe,[[`render`,Ce]]);const Te={},Ee={viewBox:`0 0 24 24`,fill:`currentColor`};function De(e,t){return m(),i(`svg`,Ee,[...t[0]||=[a(`path`,{d:`M16.5 12A4.5 4.5 0 0 0 14 7.97v2.21l2.45 2.45c.03-.2.05-.41.05-.63zm2.5 0c0 .94-.2 1.82-.54 2.64l1.51 1.51A8.796 8.796 0 0 0 21 12c0-4.28-2.99-7.86-7-8.77v2.06c2.89.86 5 3.54 5 6.71zM4.27 3L3 4.27 7.73 9H3v6h4l5 5v-6.73l4.25 4.25c-.67.52-1.42.93-2.25 1.18v2.06A8.99 8.99 0 0 0 17.73 18L19 19.27 20.27 18 5.27 3 4.27 3zM12 4L9.91 6.09 12 8.18V4z`},null,-1)]])}var Oe=C(Te,[[`render`,De]]);const ke={},Ae={viewBox:`0 0 24 24`,fill:`currentColor`};function V(e,t){return m(),i(`svg`,Ae,[...t[0]||=[a(`path`,{d:`M3 9v6h4l5 5V4L7 9H3zm13.5 3A4.5 4.5 0 0 0 14 7.97v8.05c1.48-.73 2.5-2.25 2.5-4.02z`},null,-1)]])}var je=C(ke,[[`render`,V]]);const Me=[`aria-label`];var Ne=C(c({__name:`PlayButton`,props:{player:{type:[Object,null],required:!1},for:{type:String,required:!1}},setup(e){let r=e,a=x(v(r,`player`),v(r,`for`)),o=t(()=>a.value?.isError?`error`:a.value?.hasEnded?`ended`:a.value?.isPlaying?`playing`:`idle`),s=t(()=>{switch(o.value){case`error`:return`Retry`;case`ended`:return`Replay`;case`playing`:return`Pause`;default:return`Play`}});function c(){let e=a.value;e&&(e.isError?e.retry():e.togglePlay())}return(e,t)=>(m(),i(`button`,{type:`button`,class:`mvp-play-button`,"aria-label":s.value,onClick:c},[g(e.$slots,`default`,{isPlaying:y(a)?.isPlaying??!1,hasEnded:y(a)?.hasEnded??!1,isError:y(a)?.isError??!1},()=>[o.value===`error`||o.value===`ended`?(m(),n(y(we),{key:0})):o.value===`playing`?(m(),n(y(F),{key:1})):(m(),n(y(ge),{key:2}))],!0)],8,Me))}}),[[`__scopeId`,`data-v-218255bd`]]),Pe=c({__name:`VolumeIcon`,props:{isAudible:{type:Boolean,required:!0}},setup(e){return(t,r)=>e.isAudible?(m(),n(y(je),{key:0})):(m(),n(y(Oe),{key:1}))}});const Fe=[`aria-label`];var Ie=C(c({__name:`MuteButton`,props:{player:{type:[Object,null],required:!1},for:{type:String,required:!1}},setup(e){let t=e,n=x(v(t,`player`),v(t,`for`));return(e,t)=>(m(),i(`button`,{type:`button`,class:`mvp-mute-button`,"aria-label":y(n)?.isMuted?`Unmute`:`Mute`,onClick:t[0]||=e=>y(n)?.toggleMute()},[g(e.$slots,`default`,{isMuted:y(n)?.isMuted??!1},()=>[s(Pe,{"is-audible":y(n)?.isAudible??!1},null,8,[`is-audible`])],!0)],8,Fe))}}),[[`__scopeId`,`data-v-1940b598`]]);const Le=[`aria-label`];var Re=C(c({__name:`FullscreenButton`,props:{player:{type:[Object,null],required:!1},for:{type:String,required:!1}},setup(e){let t=e,r=x(v(t,`player`),v(t,`for`));return(e,t)=>(m(),i(`button`,{type:`button`,class:`mvp-fullscreen-button`,"aria-label":y(r)?.isFullscreen?`Exit fullscreen`:`Enter fullscreen`,onClick:t[0]||=e=>y(r)?.toggleFullscreen()},[g(e.$slots,`default`,{isFullscreen:y(r)?.isFullscreen??!1},()=>[y(r)?.isFullscreen?(m(),n(y(D),{key:0})):(m(),n(y(pe),{key:1}))],!0)],8,Le))}}),[[`__scopeId`,`data-v-b93a5017`]]),ze=C(c({__name:`Spinner`,props:{size:{type:Number,required:!1,default:14},borderWidth:{type:Number,required:!1,default:2}},setup(e){return(t,n)=>(m(),i(`div`,{class:`spinner`,style:f({width:`${e.size}px`,height:`${e.size}px`,borderWidth:`${e.borderWidth}px`})},null,4))}}),[[`__scopeId`,`data-v-259be2b2`]]);const Be={key:0,class:`mvp-buffering`};var Ve=C(c({__name:`Buffering`,props:{player:{type:[Object,null],required:!1},for:{type:String,required:!1}},setup(e){let t=e,n=x(v(t,`player`),v(t,`for`));return(e,t)=>y(n)?.isBuffering?(m(),i(`div`,Be,[g(e.$slots,`default`,{},()=>[s(ze)],!0)])):r(`v-if`,!0)}}),[[`__scopeId`,`data-v-454fec7f`]]);const He=[`aria-pressed`];var Ue=C(c({__name:`LoopButton`,props:{player:{type:[Object,null],required:!1},for:{type:String,required:!1}},setup(e){let t=e,n=x(v(t,`player`),v(t,`for`));return(e,t)=>(m(),i(`button`,{type:`button`,class:d([`mvp-loop-button`,{"mvp-loop-button--active":y(n)?.isLooping}]),"aria-pressed":y(n)?.isLooping??!1,"aria-label":`Toggle loop`,onClick:t[0]||=e=>y(n)?.toggleLoop()},[g(e.$slots,`default`,{isLooping:y(n)?.isLooping??!1},()=>[s(y(j))],!0)],10,He))}}),[[`__scopeId`,`data-v-e32eda25`]]);const We=[`aria-pressed`];var Ge=C(c({__name:`PipButton`,props:{player:{type:[Object,null],required:!1},for:{type:String,required:!1}},setup(e){let t=e,n=x(v(t,`player`),v(t,`for`));return(e,t)=>y(n)?.supportsPip?(m(),i(`button`,{key:0,type:`button`,class:d([`mvp-pip-button`,{"mvp-pip-button--active":y(n)?.isPipActive}]),"aria-pressed":y(n)?.isPipActive??!1,"aria-label":`Toggle Picture-in-Picture`,onClick:t[0]||=e=>y(n)?.togglePip()},[g(e.$slots,`default`,{isPipActive:y(n)?.isPipActive??!1},()=>[s(y(z))],!0)],10,We)):r(`v-if`,!0)}}),[[`__scopeId`,`data-v-f654d93e`]]);function Ke(e){function t(){let t=e.captionTracks;if(!t.length)return;let n=t[t.findIndex(t=>t.index===e.activeCaptionIndex)+1];e.setCaptionTrack(n?n.index:null)}function n(){return e.captionTracks.find(t=>t.index===e.activeCaptionIndex)?.label??`Off`}return{cycleCaptionTrack:t,currentCaptionLabel:n}}const qe=[`aria-label`];var Je=C(c({__name:`CaptionsButton`,props:{player:{type:[Object,null],required:!1},for:{type:String,required:!1}},setup(e){let n=e,a=x(v(n,`player`),v(n,`for`)),o=t(()=>a.value?Ke(a.value):null),c=t(()=>o.value?.currentCaptionLabel()??`Off`);function l(){o.value?.cycleCaptionTrack()}return(e,t)=>y(a)?.supportsCaptions?(m(),i(`button`,{key:0,type:`button`,class:d([`mvp-captions-button`,{"mvp-captions-button--active":y(a)?.activeCaptionIndex!==null}]),"aria-label":`Captions: ${c.value}`,onClick:l},[g(e.$slots,`default`,{currentLabel:c.value,tracks:y(a)?.captionTracks??[],activeIndex:y(a)?.activeCaptionIndex??null},()=>[s(y(le))],!0)],10,qe)):r(`v-if`,!0)}}),[[`__scopeId`,`data-v-2572c4f2`]]);function Ye(e){function t(){let t=e.qualityLevels;if(!t.length)return;if(e.isAutoQuality){e.setQuality(t[t.length-1].index);return}let n=t.findIndex(t=>t.index===e.currentQualityIndex);e.setQuality(n<=0?null:t[n-1].index)}function n(){return e.isAutoQuality?`Auto`:e.qualityLevels.find(t=>t.index===e.currentQualityIndex)?.label??`Auto`}return{cycleQuality:t,currentQualityLabel:n}}const Xe=[`aria-label`];var Ze=C(c({__name:`QualityButton`,props:{player:{type:[Object,null],required:!1},for:{type:String,required:!1}},setup(e){let n=e,a=x(v(n,`player`),v(n,`for`)),o=t(()=>a.value?Ye(a.value):null),c=t(()=>o.value?.currentQualityLabel()??`Auto`);function l(){o.value?.cycleQuality()}return(e,t)=>y(a)?.supportsQuality?(m(),i(`button`,{key:0,type:`button`,class:d([`mvp-quality-button`,{"mvp-quality-button--active":!y(a)?.isAutoQuality}]),"aria-label":`Quality: ${c.value}`,onClick:l},[g(e.$slots,`default`,{currentLabel:c.value,levels:y(a)?.qualityLevels??[],currentIndex:y(a)?.currentQualityIndex??null,isAuto:y(a)?.isAutoQuality??!0},()=>[s(y(be))],!0)],10,Xe)):r(`v-if`,!0)}}),[[`__scopeId`,`data-v-d1352878`]]);function Qe(e){function t(){let t=b.indexOf(e.playbackRate);e.setPlaybackRate(b[(t+1)%b.length])}function n(e){return`${e}×`}return{cycleRate:t,fmtRate:n}}const $e=[`aria-label`];var et=C(c({__name:`PlaybackRateButton`,props:{player:{type:[Object,null],required:!1},for:{type:String,required:!1}},setup(e){let n=e,a=x(v(n,`player`),v(n,`for`)),s=t(()=>a.value?Qe(a.value):null),c=t(()=>s.value?s.value.fmtRate(a.value.playbackRate):`1×`);function l(){s.value?.cycleRate()}return(e,t)=>y(a)?.supportsPlaybackRate?(m(),i(`button`,{key:0,type:`button`,class:d([`mvp-playback-rate-button`,{"mvp-playback-rate-button--active":y(a)?.playbackRate!==1}]),"aria-label":`Playback speed: ${c.value}`,onClick:l},[g(e.$slots,`default`,{currentLabel:c.value,rate:y(a)?.playbackRate??1,rates:[...y(b)]},()=>[o(_(c.value),1)],!0)],10,$e)):r(`v-if`,!0)}}),[[`__scopeId`,`data-v-646c9bc1`]]);const tt={class:`mvp-volume-wrap`},nt=[`value`],rt={class:`mvp-volume-pct`};var it=C(c({inheritAttrs:!1,__name:`VolumeSlider`,props:{player:{type:[Object,null],required:!1},for:{type:String,required:!1}},setup(e){let n=e,r=x(v(n,`player`),v(n,`for`)),o=t(()=>{let e=r.value;return e?e.isMuted?0:Math.round(e.vol*100):0});function s(e){r.value?.setVolume(Number(e.target.value)/100)}return(e,t)=>(m(),i(`div`,tt,[a(`input`,u({type:`range`,min:`0`,max:`100`,step:`1`,value:o.value,class:`mvp-volume-slider`},e.$attrs,{style:{"--v":`${o.value}%`},"aria-label":`Volume`,onInput:s}),null,16,nt),g(e.$slots,`default`,{percent:o.value},()=>[a(`span`,rt,_(o.value)+`%`,1)],!0)]))}}),[[`__scopeId`,`data-v-fbb2c86c`]]),at=C(c({__name:`TimeDisplay`,props:{player:{type:[Object,null],required:!1},for:{type:String,required:!1}},setup(t){let n=t,r=x(v(n,`player`),v(n,`for`));return(t,n)=>(m(),i(`span`,{class:d([`mvp-time-display`,{"mvp-time-display--live":y(r)?.isLive}])},[g(t.$slots,`default`,{isLive:y(r)?.isLive??!1,current:y(r)?.current??0,total:y(r)?.total??0,formatTime:y(S)},()=>[y(r)?.isLive?(m(),i(e,{key:0},[n[0]||=a(`span`,{class:`mvp-time-display__dot`},null,-1),n[1]||=o(`Live `,-1)],64)):(m(),i(e,{key:1},[o(_(y(S)(y(r)?.current??0))+` / `+_(y(S)(y(r)?.total??0)),1)],64))],!0)],2))}}),[[`__scopeId`,`data-v-0285c811`]]);const H=l(ae,{shadowRoot:!1}),U=l(Ne,{shadowRoot:!1}),W=l(Ie,{shadowRoot:!1}),G=l(Re,{shadowRoot:!1}),K=l(Ve,{shadowRoot:!1}),q=l(Ue,{shadowRoot:!1}),J=l(Ge,{shadowRoot:!1}),Y=l(Je,{shadowRoot:!1}),X=l(Ze,{shadowRoot:!1}),Z=l(et,{shadowRoot:!1}),Q=l(it,{shadowRoot:!1}),$=l(at,{shadowRoot:!1});customElements.define(`muns-controls-scrubber`,H),customElements.define(`muns-controls-play-button`,U),customElements.define(`muns-controls-mute-button`,W),customElements.define(`muns-controls-fullscreen-button`,G),customElements.define(`muns-controls-buffering`,K),customElements.define(`muns-controls-loop-button`,q),customElements.define(`muns-controls-pip-button`,J),customElements.define(`muns-controls-captions-button`,Y),customElements.define(`muns-controls-quality-button`,X),customElements.define(`muns-controls-playback-rate-button`,Z),customElements.define(`muns-controls-volume-slider`,Q),customElements.define(`muns-controls-time-display`,$);export{K as BufferingElement,Y as CaptionsButtonElement,G as FullscreenButtonElement,q as LoopButtonElement,W as MuteButtonElement,J as PipButtonElement,U as PlayButtonElement,Z as PlaybackRateButtonElement,X as QualityButtonElement,H as ScrubberElement,$ as TimeDisplayElement,Q as VolumeSliderElement};
@@ -0,0 +1 @@
1
+ :where(.mvp-play-button[data-v-218255bd]){all:unset;cursor:pointer;justify-content:center;align-items:center;display:inline-flex}:where(.mvp-play-button svg[data-v-218255bd]){width:1em;height:1em}@keyframes spin-259be2b2{to{transform:rotate(360deg)}}.spinner[data-v-259be2b2]{border-style:solid;border-color:#fffc #ffffff26 #ffffff26;border-radius:50%;animation:.75s linear infinite spin-259be2b2}:where(.mvp-buffering[data-v-454fec7f]){justify-content:center;align-items:center;display:inline-flex}.ppbtn{backdrop-filter:blur(12px);will-change:backdrop-filter;color:#fff;cursor:pointer;background:#ffffff1a;border:1px solid #ffffff2e;border-radius:50%;justify-content:center;align-items:center;transition:background .2s,transform .15s;display:flex}.ppbtn:hover{background:#fff3;transform:scale(1.08)}.ppbtn--lg{background:var(--mvp-btn-bg,#ffffff1a);width:clamp(48px,20cqw,64px);height:clamp(48px,20cqw,64px);color:var(--mvp-btn-color,#fff)}.ppbtn--lg svg{width:clamp(20px,9cqw,28px);height:clamp(20px,9cqw,28px)}.ppbtn--lg:hover{background:var(--mvp-btn-bg,#fff3);filter:brightness(1.12)}.ppbtn--sm{color:#ffffffbf;background:0 0;border:none;border-radius:8px;width:30px;height:30px}.ppbtn--sm svg{width:16px;height:16px}.ppbtn--sm:hover{color:#fff;background:#ffffff1a;transform:none}.hud[data-v-a6d118e7]{align-items:center;gap:clamp(12px,5cqw,20px);display:flex}.hud__play-wrap[data-v-a6d118e7]{display:flex;position:relative}.hud__buf-spinner[data-v-a6d118e7]{margin-top:8px;position:absolute;top:100%;left:calc(50% - 7px)}.hud__spacer[data-v-a6d118e7]{flex-shrink:0;width:clamp(36px,14cqw,44px);height:clamp(36px,14cqw,44px)}.hud__btn[data-v-a6d118e7]{background:var(--mvp-btn-bg,#ffffff1a);backdrop-filter:blur(12px);will-change:backdrop-filter;width:clamp(36px,14cqw,44px);height:clamp(36px,14cqw,44px);color:var(--mvp-btn-color,#fff);cursor:pointer;border:1px solid #ffffff2e;border-radius:50%;justify-content:center;align-items:center;transition:background .2s,transform .15s;display:flex}.hud__btn svg[data-v-a6d118e7]{width:clamp(16px,6cqw,18px);height:clamp(16px,6cqw,18px)}.hud__btn[data-v-a6d118e7]:hover{background:var(--mvp-btn-bg,#fff3);filter:brightness(1.12);transform:scale(1.08)}.hud__btn--active[data-v-a6d118e7]{border-color:var(--mvp-accent,#3b82f6);background:color-mix(in srgb, var(--mvp-accent,#3b82f6) 30%, var(--mvp-btn-bg,#ffffff1a));color:var(--mvp-btn-color,#fff)}.hud__btn--active[data-v-a6d118e7]:hover{background:color-mix(in srgb, var(--mvp-accent,#3b82f6) 45%, var(--mvp-btn-bg,#fff3))}:where(.mvp-fullscreen-button[data-v-b93a5017]){all:unset;cursor:pointer;justify-content:center;align-items:center;display:inline-flex}:where(.mvp-fullscreen-button svg[data-v-b93a5017]){width:1em;height:1em}:where(.mvp-pip-button[data-v-f654d93e]){all:unset;cursor:pointer;opacity:.6;justify-content:center;align-items:center;display:inline-flex}:where(.mvp-pip-button--active[data-v-f654d93e]){opacity:1}:where(.mvp-pip-button svg[data-v-f654d93e]){width:1em;height:1em}.mvp-scrubber-wrap[data-v-043f1bc8]{width:100%;position:relative}:where(.mvp-scrubber[data-v-043f1bc8]){cursor:pointer;accent-color:currentColor;width:100%}.mvp-scrubber__preview[data-v-043f1bc8]{font-variant-numeric:tabular-nums;pointer-events:none;white-space:nowrap;margin-bottom:.4rem;font-size:.75rem;position:absolute;bottom:100%;transform:translate(-50%)}:where(.mvp-time-display[data-v-0285c811]){font-variant-numeric:tabular-nums;align-items:center;gap:.35rem;display:inline-flex}:where(.mvp-time-display__dot[data-v-0285c811]){background:currentColor;border-radius:50%;width:6px;height:6px}.mvp-volume-wrap[data-v-fbb2c86c]{align-items:center;gap:.75rem;width:100%;display:flex}:where(.mvp-volume-slider[data-v-fbb2c86c]){cursor:pointer;accent-color:currentColor;flex:1}:where(.mvp-volume-pct[data-v-fbb2c86c]){font-variant-numeric:tabular-nums;text-align:right;min-width:2rem}.controls__vol-slider{-webkit-appearance:none;appearance:none;cursor:pointer;background:linear-gradient(to right, #fff 0%, #fff var(--v), #ffffff26 var(--v), #ffffff26 100%);outline:none;flex:1;height:5px;transition:height .15s}.controls__vol-slider:hover{height:7px}.controls__vol-slider::-webkit-slider-runnable-track{background:0 0;height:5px}.controls__vol-slider::-webkit-slider-thumb{-webkit-appearance:none;cursor:pointer;background:#fff;border-radius:50%;width:16px;height:16px;margin-top:-5.5px;transition:transform .15s;box-shadow:0 0 0 3px #fff3}.controls__vol-slider:hover::-webkit-slider-thumb{transform:scale(1.2)}.controls__vol-slider::-moz-range-track{background:#ffffff26;height:5px}.controls__vol-slider::-moz-range-progress{background:#fff;height:5px}.controls__vol-slider::-moz-range-thumb{cursor:pointer;background:#fff;border:none;border-radius:50%;width:16px;height:16px;box-shadow:0 0 0 3px #fff3}.controls__vol-header[data-v-10b0ae67]{align-items:center;gap:4px;display:flex}.controls__vol-label[data-v-10b0ae67]{color:#ffffffe6;flex:1;padding:0 4px;font-size:.8rem;font-weight:600}.controls__vol-body[data-v-10b0ae67]{align-items:center;gap:12px;padding:6px 0 4px;display:flex}.controls__vol-pct[data-v-10b0ae67]{font-variant-numeric:tabular-nums;color:#fff9;text-align:right;min-width:32px;font-size:.75rem;font-weight:600}.controls__more-row[data-v-47ca5bfc]{color:#ffffffd9;cursor:pointer;background:0 0;border:none;border-radius:8px;justify-content:space-between;align-items:center;width:100%;padding:8px 4px;font-size:.8rem;font-weight:500;transition:background .15s;display:flex}.controls__more-row[data-v-47ca5bfc]:hover{background:#ffffff14}.controls__more-row-label[data-v-47ca5bfc]{align-items:center;gap:8px;display:flex}.controls__more-row-icon[data-v-47ca5bfc]{flex-shrink:0;width:16px;height:16px}.controls__more-value[data-v-47ca5bfc]{font-variant-numeric:tabular-nums;color:#ffffff80;font-size:.7rem;font-weight:600}.controls__more-row--active .controls__more-value[data-v-47ca5bfc]{color:var(--mvp-accent,#3b82f6)}.controls__more-header[data-v-e89a8208]{flex-shrink:0;align-items:center;gap:4px;display:flex}.controls__more-label[data-v-e89a8208]{color:#ffffffe6;flex:1;padding:0 4px;font-size:.8rem;font-weight:600}.controls__more-body[data-v-e89a8208]{flex-direction:column;min-height:0;padding:2px 0;display:flex;overflow-y:auto}::view-transition-old(controls-popup),::view-transition-new(controls-popup){animation-duration:.22s;animation-timing-function:ease-in-out}.controls__btn{color:#ffffffbf;cursor:pointer;background:0 0;border:none;border-radius:8px;flex-shrink:0;justify-content:center;align-items:center;width:30px;height:30px;transition:background .15s,color .15s;display:flex}.controls__btn svg{width:16px;height:16px}.controls__btn:hover{color:#fff;background:#ffffff1a}.controls--compact .controls__seek,.controls--compact .mvp-scrubber-wrap,.controls--compact .controls__btn--more,.controls--compact .controls__btn--pip,.controls--compact .controls__fs-wrap{display:none}@container player-shell (width<=300px){.controls__seek,.mvp-scrubber-wrap,.controls__btn--more,.controls__btn--pip,.controls__fs-wrap{display:none}}.controls__seek{-webkit-appearance:none;appearance:none;cursor:pointer;box-sizing:border-box;background:padding-box content-box;background-image:linear-gradient(to right, var(--mvp-accent,#3b82f6) 0%, var(--mvp-accent,#3b82f6) var(--p), #ffffff4d var(--p), #ffffff4d var(--b), #ffffff1a var(--b), #ffffff1a 100%);outline:none;width:100%;height:20px;padding:8px 0}.controls__seek::-webkit-slider-runnable-track{background:0 0;height:4px}.controls__seek::-webkit-slider-thumb{-webkit-appearance:none;background:var(--mvp-accent,#3b82f6);width:14px;height:14px;box-shadow:0 0 0 3px color-mix(in srgb, var(--mvp-accent,#3b82f6) 30%, transparent);cursor:pointer;border-radius:50%;margin-top:-5px;transition:transform .15s}.controls__seek:hover::-webkit-slider-thumb{transform:scale(1.25)}.controls__seek::-moz-range-track{background:#ffffff1a;height:4px}.controls__seek::-moz-range-progress{background:var(--mvp-accent,#3b82f6);height:4px}.controls__seek::-moz-range-thumb{background:var(--mvp-accent,#3b82f6);width:14px;height:14px;box-shadow:0 0 0 3px color-mix(in srgb, var(--mvp-accent,#3b82f6) 30%, transparent);cursor:pointer;border:none;border-radius:50%}.controls[data-v-5712aad4]{-webkit-backdrop-filter:blur(20px);background:#0a0a0e;border:1px solid #ffffff1a;border-radius:18px;flex-direction:column;gap:10px;width:min(340px,100% - 32px);max-height:100%;padding:14px 16px 12px;display:flex;overflow:hidden;box-shadow:0 8px 32px #00000080}.controls--fs[data-v-5712aad4]{width:100%}.controls__row[data-v-5712aad4]{align-items:center;gap:4px;display:flex}.controls__time-display[data-v-5712aad4]{color:#fff9;letter-spacing:.02em;flex:1;padding:0 4px;font-size:.7rem;font-weight:500}.controls__time-display.mvp-time-display--live[data-v-5712aad4]{letter-spacing:.04em;text-transform:uppercase;color:#f87171;font-weight:700}.controls__play-wrap[data-v-5712aad4]{flex-shrink:0;justify-content:center;align-items:center;width:30px;height:30px;display:flex}.controls__btn--active[data-v-5712aad4],.controls__btn--pip.mvp-pip-button--active[data-v-5712aad4]{color:var(--mvp-accent,#3b82f6)}.controls__btn--active[data-v-5712aad4]:hover,.controls__btn--pip.mvp-pip-button--active[data-v-5712aad4]:hover{color:var(--mvp-accent,#60a5fa)}.controls__fs-wrap[data-v-5712aad4]{flex-shrink:0;justify-content:center;align-items:center;width:30px;height:30px;display:flex}.overlay__spinner[data-v-f3307b98]{z-index:5;pointer-events:none;justify-content:center;align-items:center;display:flex;position:absolute;inset:0}.fade-enter-active[data-v-f3307b98],.fade-leave-active[data-v-f3307b98]{transition:opacity .25s}.fade-enter-from[data-v-f3307b98],.fade-leave-to[data-v-f3307b98]{opacity:0}.overlay__error[data-v-f3307b98]{z-index:6;pointer-events:none;flex-direction:column;justify-content:center;align-items:center;gap:.75rem;padding:1.5rem;display:flex;position:absolute;inset:0}.overlay__error p[data-v-f3307b98]{color:#ffffffb3;text-align:center;max-width:260px;font-size:.875rem;line-height:1.5}.overlay__retry[data-v-f3307b98]{pointer-events:auto;color:#fff;cursor:pointer;background:#ffffff1a;border:1px solid #ffffff40;border-radius:8px;padding:.4rem 1rem;font-size:.8rem;font-weight:600;transition:background .15s}.overlay__retry[data-v-f3307b98]:hover{background:#fff3}.overlay__hud[data-v-f3307b98]{z-index:4;opacity:0;pointer-events:none;justify-content:center;align-items:center;transition:opacity .3s;display:flex;position:absolute;top:50%;left:0;right:0;transform:translateY(-50%)}.overlay__hud--visible[data-v-f3307b98]{opacity:1;pointer-events:all}.overlay__popup[data-v-f3307b98]{z-index:5;align-items:var(--mvp-popup-align,center);pointer-events:none;justify-content:center;padding:16px;display:flex;position:absolute;inset:0}.overlay__popup[data-v-f3307b98] .controls{pointer-events:auto}.overlay__popup--fs[data-v-f3307b98]{align-items:flex-end}.popup-enter-active[data-v-f3307b98],.popup-leave-active[data-v-f3307b98]{transition:opacity .2s,transform .2s}.popup-enter-from[data-v-f3307b98],.popup-leave-to[data-v-f3307b98]{opacity:0;transform:scale(.95)}.ad-overlay[data-v-23efcc8b]{z-index:21;pointer-events:none;position:absolute;inset:0}.ad-overlay__bar[data-v-23efcc8b]{align-items:center;gap:6px;display:flex;position:absolute;bottom:10px;left:10px}.ad-overlay__badge[data-v-23efcc8b]{color:#fff;text-transform:uppercase;letter-spacing:.06em;background:#0009;border-radius:4px;align-items:center;gap:6px;padding:3px 8px;font-size:.65rem;font-weight:700;display:flex}.ad-overlay__countdown[data-v-23efcc8b]{font-variant-numeric:tabular-nums;text-transform:none;letter-spacing:.02em;opacity:.8;font-weight:500}.ad-overlay__btn[data-v-23efcc8b]{color:#fff;cursor:pointer;pointer-events:auto;background:#0009;border:none;border-radius:4px;justify-content:center;align-items:center;padding:3px;transition:background .15s;display:flex}.ad-overlay__btn[data-v-23efcc8b]:hover{background:#000c}.ad-overlay__btn svg[data-v-23efcc8b]{width:11px;height:11px}.player[data-v-4ab3cf75]{user-select:none;-webkit-tap-highlight-color:transparent;width:100%;max-width:800px;margin:0 auto}[data-mvp-player][data-v-4ab3cf75]{position:absolute;inset:0}.player__shell[data-v-4ab3cf75]{border-radius:var(--mvp-radius,12px);cursor:pointer;background:#000;position:relative;overflow:hidden;container:player-shell/inline-size}.mvp-video--hidden[data-v-4ab3cf75]{opacity:0}.overlay__tap-capture[data-v-4ab3cf75]{z-index:2;position:absolute;inset:0}.player__shell--portrait[data-v-4ab3cf75]{width:auto;max-width:100%;max-height:75dvh;margin:0 auto}.player__native-play[data-v-4ab3cf75]{z-index:3;pointer-events:none;justify-content:center;align-items:center;display:flex;position:absolute;inset:0}.player__native-play[data-v-4ab3cf75]>*{pointer-events:auto}.player__shell[data-v-4ab3cf75]:fullscreen{border-radius:0;width:100vw;height:100dvh}.player__shell:fullscreen .mvp-video[data-v-4ab3cf75],.player__shell:fullscreen [data-mvp-player][data-v-4ab3cf75]{width:100%!important;height:100%!important}.mvp-video{border-radius:var(--mvp-radius,12px);object-fit:cover;background:#000;width:100%;height:100%;position:absolute;inset:0;overflow:hidden}.mvp-dailymotion .mvp-tech>*,.mvp-dailymotion .dailymotion-player-root,.mvp-dailymotion iframe{width:100%!important;height:100%!important;position:absolute!important;inset:0!important}.mvp-vimeo .mvp-tech>div{width:100%!important;height:100%!important;padding:0!important;position:absolute!important;top:0!important;left:0!important}.mvp-vimeo iframe{width:100%!important;height:100%!important;position:absolute!important;top:0!important;left:0!important}[data-mvp-player] .ima-ad-container{pointer-events:none;z-index:20!important;width:100%!important;height:100%!important;position:absolute!important;top:0!important;left:0!important}.mvp-ad-playing .ima-ad-container{pointer-events:auto}.mvp-ad-playing .overlay__hud,.mvp-ad-playing .overlay__spinner{display:none!important}.stage-wrapper[data-v-fb8c86cf]{width:100%;max-width:800px;margin:0 auto}.stage[data-v-fb8c86cf]{width:100%}.stage__dismiss[data-v-fb8c86cf],.stage__scroll-to[data-v-fb8c86cf]{z-index:10;backdrop-filter:blur(6px);color:#fff;cursor:pointer;background:#0009;border:none;border-radius:50%;justify-content:center;align-items:center;width:26px;height:26px;transition:all .15s;display:flex;position:absolute;top:.4rem}.stage__dismiss[data-v-fb8c86cf]:hover,.stage__scroll-to[data-v-fb8c86cf]:hover{background:#000000d9}.stage__dismiss svg[data-v-fb8c86cf],.stage__scroll-to svg[data-v-fb8c86cf]{width:14px;height:14px}.stage__dismiss[data-v-fb8c86cf]{right:.4rem}.stage__scroll-to[data-v-fb8c86cf]{left:.4rem}.stage--minified[data-v-fb8c86cf]{z-index:100;margin:0;position:fixed;overflow:hidden;box-shadow:0 8px 32px #0009}.stage--pin-bottom-right[data-v-fb8c86cf],.stage--pin-bottom-left[data-v-fb8c86cf],.stage--pin-top-right[data-v-fb8c86cf],.stage--pin-top-left[data-v-fb8c86cf]{border-radius:var(--mvp-radius,12px);width:360px;max-width:360px;@media (width<=740px){max-width:300px}}.stage--pin-bottom-right[data-v-fb8c86cf]{bottom:1.25rem;right:1.25rem}.stage--pin-bottom-left[data-v-fb8c86cf]{bottom:1.25rem;left:1.25rem}.stage--pin-top-right[data-v-fb8c86cf]{top:1.25rem;right:1.25rem}.stage--pin-top-left[data-v-fb8c86cf]{top:1.25rem;left:1.25rem}.stage--pin-full-width[data-v-fb8c86cf]{width:100%;max-width:100%;box-shadow:none;border-radius:0;top:0;left:0;right:0}.stage__idle[data-v-fb8c86cf]{border-radius:var(--mvp-radius,12px);cursor:pointer;background:#111;justify-content:center;align-items:center;width:100%;display:flex;position:relative;overflow:hidden}.stage__idle-poster[data-v-fb8c86cf]{object-fit:cover;width:100%;height:100%;position:absolute;inset:0}.stage__idle-scrim[data-v-fb8c86cf]{background:#00000073;transition:background .15s;position:absolute;inset:0}.stage__idle:hover .stage__idle-scrim[data-v-fb8c86cf]{background:#0000004d}.stage__idle-body[data-v-fb8c86cf]{text-align:center;flex-direction:column;align-items:center;gap:.75rem;padding:1rem;display:flex;position:relative}.stage__idle-btn[data-v-fb8c86cf]{backdrop-filter:blur(4px);color:#fff;background:#0009;border-radius:50%;justify-content:center;align-items:center;width:64px;height:64px;transition:transform .15s,background .15s;display:flex}.stage__idle:hover .stage__idle-btn[data-v-fb8c86cf]{background:#000c;transform:scale(1.1)}.stage__idle-btn svg[data-v-fb8c86cf]{width:32px;height:32px}.stage__idle-title[data-v-fb8c86cf]{color:#f1f5f9;text-shadow:0 1px 4px #000c;font-size:1rem;font-weight:600}.stage__idle-empty[data-v-fb8c86cf]{color:#64748b;font-size:.95rem}.placeholder[data-v-dbcb4f82]{width:100%}.placeholder__shell[data-v-dbcb4f82]{border-radius:var(--mvp-radius,12px);cursor:pointer;background:#111;position:relative;overflow:hidden}.placeholder__shell--portrait[data-v-dbcb4f82]{width:auto;max-height:75dvh;margin:0 auto}.placeholder__poster[data-v-dbcb4f82]{object-fit:cover;width:100%;height:100%;position:absolute;inset:0}.placeholder__scrim[data-v-dbcb4f82]{background:#00000059;transition:background .15s;position:absolute;inset:0}.placeholder__shell:hover .placeholder__scrim[data-v-dbcb4f82]{background:#00000026}.placeholder__btn-wrap[data-v-dbcb4f82]{pointer-events:none;justify-content:center;align-items:center;display:flex;position:absolute;inset:0}.placeholder__btn-wrap[data-v-dbcb4f82]>*{pointer-events:auto}.placeholder__footer[data-v-dbcb4f82]{pointer-events:none;background:linear-gradient(#0000 0%,#000c 100%);padding:1.5rem .75rem .6rem;position:absolute;bottom:0;left:0;right:0}.placeholder__title[data-v-dbcb4f82]{color:#f1f5f9;white-space:nowrap;text-overflow:ellipsis;text-shadow:0 1px 3px #0009;font-size:.8rem;font-weight:600;overflow:hidden}
@@ -46,10 +46,12 @@ interface PlayerProps {
46
46
  payload?: Record<string, unknown>;
47
47
  action?: PlayerAction | null;
48
48
  disableTapCapture?: boolean;
49
+ controls?: boolean;
50
+ playWhenInView?: boolean;
49
51
  }
50
52
  type VideoEntry = PlayerProps;
51
53
  //#endregion
52
- //#region src/element/index.d.ts
54
+ //#region src/elements/core.d.ts
53
55
  declare const VideoPlayerElement: import("vue").VueElementConstructor<PlayerProps & {
54
56
  hasPlaylist?: boolean;
55
57
  hasNext?: boolean;