@hanifhan1f/vidstack 1.12.14 → 1.12.18

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.
@@ -0,0 +1 @@
1
+ import{ao as m,e as c}from"./vidstack-DLVdcWrK.js";import{c as p,a as r}from"./vidstack-BGhRKayG.js";import"./vidstack-DWjB11vV.js";import"./vidstack-CS2aNc61.js";import"./vidstack-DPO7J4-v.js";import"./vidstack-DKqYI_HJ.js";import"./vidstack-C_AxqLKV.js";import"./vidstack-DRH_1tFW.js";import"./vidstack-BfBBPhXV.js";import"./vidstack-Bxv1Qnxe.js";import"https://cdn.vidstack.io/icons";const d=r('<svg viewBox="0 0 32 32" fill="none" aria-hidden="true" xmlns="http://www.w3.org/2000/svg"></svg>');function l(o,s){const n=p(d);n.innerHTML=m,o.append(n);const e=document.createElement("span");e.classList.add("vds-google-cast-info"),o.append(e);const t=document.createElement("span");t.classList.add("vds-google-cast-device-name"),c(()=>{const{remotePlaybackInfo:i}=s,a=i();return a?.deviceName&&(t.textContent=a.deviceName,e.append("Google Cast on ",t)),()=>{e.textContent=""}})}export{l as insertContent};
@@ -1 +1 @@
1
- import"./chunks/vidstack-QW5tTAS4.js";import"https://cdn.vidstack.io/icons";import"./chunks/vidstack-DLVdcWrK.js";import"./chunks/vidstack-DWjB11vV.js";import"./chunks/vidstack-CS2aNc61.js";import"./chunks/vidstack-DPO7J4-v.js";import"./chunks/vidstack-DKqYI_HJ.js";import"./chunks/vidstack-C_AxqLKV.js";import"./chunks/vidstack-DRH_1tFW.js";import"./chunks/vidstack-BfBBPhXV.js";import"./chunks/vidstack-Bxv1Qnxe.js";
1
+ import"./chunks/vidstack-BGhRKayG.js";import"https://cdn.vidstack.io/icons";import"./chunks/vidstack-DLVdcWrK.js";import"./chunks/vidstack-DWjB11vV.js";import"./chunks/vidstack-CS2aNc61.js";import"./chunks/vidstack-DPO7J4-v.js";import"./chunks/vidstack-DKqYI_HJ.js";import"./chunks/vidstack-C_AxqLKV.js";import"./chunks/vidstack-DRH_1tFW.js";import"./chunks/vidstack-BfBBPhXV.js";import"./chunks/vidstack-Bxv1Qnxe.js";
@@ -260,11 +260,17 @@ function DefaultBufferingIndicator() {
260
260
  </div>
261
261
  `;
262
262
  }
263
+ function formatMinutesLeftLabel(secondsLeft) {
264
+ if (secondsLeft <= 0) return null;
265
+ if (secondsLeft < 60) return "<1m left";
266
+ return `${Math.floor(secondsLeft / 60)}m left`;
267
+ }
263
268
  function DefaultEpisodesSidebar($open, onClose) {
264
- const { episodes, episodesTitle, smallWhen: smWhen } = useDefaultLayoutContext(), { fullscreen } = useMediaState();
269
+ const { episodes, episodesTitle, smallWhen: smWhen } = useDefaultLayoutContext(), { fullscreen, currentTime, duration } = useMediaState();
265
270
  return $signal(() => {
266
271
  const list = episodes() ?? [];
267
272
  if (!fullscreen() && !smWhen() || !list.length) return null;
273
+ const ct = currentTime(), dur = duration();
268
274
  return html`
269
275
  <div
270
276
  class="vds-episodes-backdrop"
@@ -296,11 +302,28 @@ function DefaultEpisodesSidebar($open, onClose) {
296
302
  </header>
297
303
  <div class="vds-episodes-list" role="list">
298
304
  ${list.map((episode, index) => {
299
- const episodeName = episode.episodeTitle || `Episode ${episode.episodeNumber ?? index + 1}`, runtimeText = Number.isFinite(episode.runtime) ? `${episode.runtime}m` : null, seasonEpLabel = episode.seasonNumber != null && episode.episodeNumber != null ? `S${String(episode.seasonNumber).padStart(2, "0")} \xB7 E${String(
305
+ const episodeName = episode.episodeTitle || `Episode ${episode.episodeNumber ?? index + 1}`, seasonEpLabel = episode.seasonNumber != null && episode.episodeNumber != null ? `S${String(episode.seasonNumber).padStart(2, "0")} \xB7 E${String(
300
306
  episode.episodeNumber
301
- ).padStart(2, "0")}` : episodeName;
307
+ ).padStart(2, "0")}` : episodeName, isActive = episode.isActive === true;
308
+ let runtimeText = null;
309
+ if (isActive && dur > 0) {
310
+ const secsLeft = Math.max(0, dur - ct);
311
+ runtimeText = formatMinutesLeftLabel(secsLeft);
312
+ } else if (episode.timeLeft != null && episode.timeLeft > 0) {
313
+ runtimeText = `${episode.timeLeft}m left`;
314
+ } else if (Number.isFinite(episode.runtime) && (episode.runtime ?? 0) > 0) {
315
+ runtimeText = `${episode.runtime}m`;
316
+ }
317
+ let progressPct = 0;
318
+ if (isActive && dur > 0) {
319
+ progressPct = Math.min(100, ct / dur * 100);
320
+ } else if (episode.progressPercent != null && episode.progressPercent > 0) {
321
+ progressPct = Math.min(100, episode.progressPercent);
322
+ }
323
+ const showSubtitle = !!episode.episodeTitle && episode.episodeTitle.trim() !== "" && episode.episodeTitle !== episode.title;
302
324
  const onEpisodeSelect = (event) => {
303
325
  event.stopPropagation();
326
+ if (isActive) return;
304
327
  event.currentTarget?.dispatchEvent(
305
328
  new CustomEvent("vds-episode-select", {
306
329
  bubbles: true,
@@ -313,6 +336,8 @@ function DefaultEpisodesSidebar($open, onClose) {
313
336
  return html`
314
337
  <article
315
338
  class="vds-episode-item"
339
+ data-active=${isActive ? "true" : "false"}
340
+ aria-current=${isActive ? "true" : void 0}
316
341
  role="button"
317
342
  tabindex="0"
318
343
  aria-label=${episode.title || episodeName}
@@ -331,6 +356,19 @@ function DefaultEpisodesSidebar($open, onClose) {
331
356
  decoding="async"
332
357
  />
333
358
  ` : html`<div class="vds-episode-thumb vds-episode-thumb-placeholder"></div>`}
359
+ ${isActive ? html`
360
+ <div class="vds-episode-active-overlay" aria-hidden="true">
361
+ <span class="vds-episode-playing-badge">Now playing</span>
362
+ </div>
363
+ ` : null}
364
+ ${progressPct > 0 ? html`
365
+ <div class="vds-episode-progress-track" aria-hidden="true">
366
+ <div
367
+ class="vds-episode-progress-fill"
368
+ style=${`width: ${progressPct}%;`}
369
+ ></div>
370
+ </div>
371
+ ` : null}
334
372
  </div>
335
373
  <div class="vds-episode-body">
336
374
  <div class="vds-episode-meta-row">
@@ -340,7 +378,9 @@ function DefaultEpisodesSidebar($open, onClose) {
340
378
  <h4 class="vds-episode-title" title=${episode.title || ""}>
341
379
  ${episode.title || "-"}
342
380
  </h4>
343
- <p class="vds-episode-subtitle" title=${episodeName}>${episodeName}</p>
381
+ ${showSubtitle ? html`<p class="vds-episode-subtitle" title=${episode.episodeTitle}>
382
+ ${episode.episodeTitle}
383
+ </p>` : null}
344
384
  ${episode.overview ? html`<p class="vds-episode-desc" title=${episode.overview}>
345
385
  ${episode.overview}
346
386
  </p>` : null}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@hanifhan1f/vidstack",
3
3
  "description": "UI component library for building high-quality, accessible video and audio experiences on the web.",
4
- "version": "1.12.14",
4
+ "version": "1.12.18",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "types": "index.d.ts",
@@ -956,6 +956,75 @@
956
956
  opacity: 0.98;
957
957
  }
958
958
 
959
+ .vds-video-layout .vds-episode-item[data-active='true'] {
960
+ position: relative;
961
+ cursor: default;
962
+ background: linear-gradient(90deg, rgb(229 9 20 / 12%) 0%, rgb(255 255 255 / 4%) 42%);
963
+ border-radius: 12px;
964
+ padding-inline: 8px;
965
+ margin-inline: -6px;
966
+ outline: 1px solid rgb(229 9 20 / 35%);
967
+ box-shadow: inset 3px 0 0 #e50914;
968
+ }
969
+
970
+ .vds-video-layout .vds-episode-item:not([data-active='true']) {
971
+ cursor: pointer;
972
+ }
973
+
974
+ .vds-video-layout .vds-episode-item[data-active='true'] .vds-episode-thumb {
975
+ filter: brightness(0.78);
976
+ }
977
+
978
+ .vds-video-layout .vds-episode-item[data-active='true'] .vds-episode-label {
979
+ color: #ff6b6b;
980
+ }
981
+
982
+ .vds-video-layout .vds-episode-item:focus-visible {
983
+ outline: 2px solid rgb(255 255 255 / 55%);
984
+ outline-offset: 2px;
985
+ }
986
+
987
+ .vds-video-layout .vds-episode-item[data-active='true']:focus-visible {
988
+ outline-color: rgb(229 9 20 / 70%);
989
+ }
990
+
991
+ .vds-video-layout .vds-episode-active-overlay {
992
+ position: absolute;
993
+ inset: 0;
994
+ display: flex;
995
+ align-items: center;
996
+ justify-content: center;
997
+ background: linear-gradient(180deg, rgb(0 0 0 / 15%) 0%, rgb(0 0 0 / 45%) 100%);
998
+ pointer-events: none;
999
+ }
1000
+
1001
+ .vds-video-layout .vds-episode-playing-badge {
1002
+ background: #e50914;
1003
+ color: #fff;
1004
+ font-size: 10px;
1005
+ font-weight: 700;
1006
+ padding: 4px 8px;
1007
+ border-radius: 6px;
1008
+ text-transform: uppercase;
1009
+ letter-spacing: 0.06em;
1010
+ box-shadow: 0 4px 14px rgb(0 0 0 / 35%);
1011
+ }
1012
+
1013
+ .vds-video-layout .vds-episode-progress-track {
1014
+ position: absolute;
1015
+ bottom: 0;
1016
+ left: 0;
1017
+ right: 0;
1018
+ height: 4px;
1019
+ background-color: rgb(255 255 255 / 22%);
1020
+ }
1021
+
1022
+ .vds-video-layout .vds-episode-progress-fill {
1023
+ height: 100%;
1024
+ background: linear-gradient(90deg, #e50914 0%, #ff4d4d 100%);
1025
+ transition: width 0.35s linear;
1026
+ }
1027
+
959
1028
  .vds-video-layout .vds-episode-thumb {
960
1029
  display: block;
961
1030
  width: 152px;
@@ -260,11 +260,17 @@ function DefaultBufferingIndicator() {
260
260
  </div>
261
261
  `;
262
262
  }
263
+ function formatMinutesLeftLabel(secondsLeft) {
264
+ if (secondsLeft <= 0) return null;
265
+ if (secondsLeft < 60) return "<1m left";
266
+ return `${Math.floor(secondsLeft / 60)}m left`;
267
+ }
263
268
  function DefaultEpisodesSidebar($open, onClose) {
264
- const { episodes, episodesTitle, smallWhen: smWhen } = useDefaultLayoutContext(), { fullscreen } = useMediaState();
269
+ const { episodes, episodesTitle, smallWhen: smWhen } = useDefaultLayoutContext(), { fullscreen, currentTime, duration } = useMediaState();
265
270
  return $signal(() => {
266
271
  const list = episodes() ?? [];
267
272
  if (!fullscreen() && !smWhen() || !list.length) return null;
273
+ const ct = currentTime(), dur = duration();
268
274
  return html`
269
275
  <div
270
276
  class="vds-episodes-backdrop"
@@ -296,11 +302,28 @@ function DefaultEpisodesSidebar($open, onClose) {
296
302
  </header>
297
303
  <div class="vds-episodes-list" role="list">
298
304
  ${list.map((episode, index) => {
299
- const episodeName = episode.episodeTitle || `Episode ${episode.episodeNumber ?? index + 1}`, runtimeText = Number.isFinite(episode.runtime) ? `${episode.runtime}m` : null, seasonEpLabel = episode.seasonNumber != null && episode.episodeNumber != null ? `S${String(episode.seasonNumber).padStart(2, "0")} \xB7 E${String(
305
+ const episodeName = episode.episodeTitle || `Episode ${episode.episodeNumber ?? index + 1}`, seasonEpLabel = episode.seasonNumber != null && episode.episodeNumber != null ? `S${String(episode.seasonNumber).padStart(2, "0")} \xB7 E${String(
300
306
  episode.episodeNumber
301
- ).padStart(2, "0")}` : episodeName;
307
+ ).padStart(2, "0")}` : episodeName, isActive = episode.isActive === true;
308
+ let runtimeText = null;
309
+ if (isActive && dur > 0) {
310
+ const secsLeft = Math.max(0, dur - ct);
311
+ runtimeText = formatMinutesLeftLabel(secsLeft);
312
+ } else if (episode.timeLeft != null && episode.timeLeft > 0) {
313
+ runtimeText = `${episode.timeLeft}m left`;
314
+ } else if (Number.isFinite(episode.runtime) && (episode.runtime ?? 0) > 0) {
315
+ runtimeText = `${episode.runtime}m`;
316
+ }
317
+ let progressPct = 0;
318
+ if (isActive && dur > 0) {
319
+ progressPct = Math.min(100, ct / dur * 100);
320
+ } else if (episode.progressPercent != null && episode.progressPercent > 0) {
321
+ progressPct = Math.min(100, episode.progressPercent);
322
+ }
323
+ const showSubtitle = !!episode.episodeTitle && episode.episodeTitle.trim() !== "" && episode.episodeTitle !== episode.title;
302
324
  const onEpisodeSelect = (event) => {
303
325
  event.stopPropagation();
326
+ if (isActive) return;
304
327
  event.currentTarget?.dispatchEvent(
305
328
  new CustomEvent("vds-episode-select", {
306
329
  bubbles: true,
@@ -313,6 +336,8 @@ function DefaultEpisodesSidebar($open, onClose) {
313
336
  return html`
314
337
  <article
315
338
  class="vds-episode-item"
339
+ data-active=${isActive ? "true" : "false"}
340
+ aria-current=${isActive ? "true" : void 0}
316
341
  role="button"
317
342
  tabindex="0"
318
343
  aria-label=${episode.title || episodeName}
@@ -331,6 +356,19 @@ function DefaultEpisodesSidebar($open, onClose) {
331
356
  decoding="async"
332
357
  />
333
358
  ` : html`<div class="vds-episode-thumb vds-episode-thumb-placeholder"></div>`}
359
+ ${isActive ? html`
360
+ <div class="vds-episode-active-overlay" aria-hidden="true">
361
+ <span class="vds-episode-playing-badge">Now playing</span>
362
+ </div>
363
+ ` : null}
364
+ ${progressPct > 0 ? html`
365
+ <div class="vds-episode-progress-track" aria-hidden="true">
366
+ <div
367
+ class="vds-episode-progress-fill"
368
+ style=${`width: ${progressPct}%;`}
369
+ ></div>
370
+ </div>
371
+ ` : null}
334
372
  </div>
335
373
  <div class="vds-episode-body">
336
374
  <div class="vds-episode-meta-row">
@@ -340,7 +378,9 @@ function DefaultEpisodesSidebar($open, onClose) {
340
378
  <h4 class="vds-episode-title" title=${episode.title || ""}>
341
379
  ${episode.title || "-"}
342
380
  </h4>
343
- <p class="vds-episode-subtitle" title=${episodeName}>${episodeName}</p>
381
+ ${showSubtitle ? html`<p class="vds-episode-subtitle" title=${episode.episodeTitle}>
382
+ ${episode.episodeTitle}
383
+ </p>` : null}
344
384
  ${episode.overview ? html`<p class="vds-episode-desc" title=${episode.overview}>
345
385
  ${episode.overview}
346
386
  </p>` : null}