@magic-spells/responsive-video 0.1.0 → 0.2.0

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
@@ -49,7 +49,7 @@ The component automatically:
49
49
  2. Loads the appropriate video source and poster image
50
50
  3. Applies the `src` and `poster` to the child `<video>` element
51
51
  4. Re-evaluates when the window resizes
52
- 5. Sets `data-active-mode` to "mobile" or "desktop" for styling hooks
52
+ 5. Sets `mode` attribute to "mobile" or "desktop" for styling hooks
53
53
 
54
54
  ## API
55
55
 
@@ -72,22 +72,22 @@ The component automatically:
72
72
  - If no matching video exists → component does nothing
73
73
  - The component looks for the first `<video>` element in its light DOM
74
74
 
75
- ### Data Attributes
75
+ ### Mode Attribute
76
76
 
77
- The component sets `data-active-mode` on itself to indicate which source is currently active:
77
+ The component sets a `mode` attribute on itself to indicate which source is currently active:
78
78
 
79
79
  ```html
80
- <responsive-video data-active-mode="mobile">...</responsive-video>
80
+ <responsive-video mode="mobile">...</responsive-video>
81
81
  ```
82
82
 
83
83
  You can use this for conditional styling:
84
84
 
85
85
  ```css
86
- responsive-video[data-active-mode="mobile"] {
86
+ responsive-video[mode="mobile"] {
87
87
  /* Mobile-specific styles */
88
88
  }
89
89
 
90
- responsive-video[data-active-mode="desktop"] {
90
+ responsive-video[mode="desktop"] {
91
91
  aspect-ratio: 16/9;
92
92
  }
93
93
  ```
@@ -114,7 +114,7 @@ When the source needs to change:
114
114
  2. Updates the `poster` attribute (if a poster URL is provided for the active mode)
115
115
  3. Calls `video.load()` to initiate loading
116
116
  4. Attempts to auto-play if `video.autoplay` is true (catches and ignores errors for muted autoplay requirements)
117
- 5. Updates `data-active-mode` to reflect the active source ("mobile" or "desktop")
117
+ 5. Updates `mode` attribute to reflect the active source ("mobile" or "desktop")
118
118
 
119
119
  ### Private Implementation Details
120
120
 
@@ -122,7 +122,7 @@ class ResponsiveVideo extends HTMLElement {
122
122
  if (currentSrc === videoUrl) {
123
123
  this.#currentSrc = videoUrl;
124
124
  if (mode) {
125
- this.dataset.activeMode = mode;
125
+ this.setAttribute("mode", mode);
126
126
  }
127
127
  return;
128
128
  }
@@ -148,9 +148,9 @@ class ResponsiveVideo extends HTMLElement {
148
148
 
149
149
  this.#currentSrc = videoUrl;
150
150
  if (mode) {
151
- this.dataset.activeMode = mode;
151
+ this.setAttribute("mode", mode);
152
152
  } else {
153
- delete this.dataset.activeMode;
153
+ this.removeAttribute("mode");
154
154
  }
155
155
  }
156
156
  }
@@ -1 +1 @@
1
- {"version":3,"file":"responsive-video.cjs.js","sources":["../src/responsive-video.js"],"sourcesContent":["/**\n * Responsive video loader that swaps sources based on viewport width.\n * Only the matching source is applied so mobile users never download the desktop asset (and vice versa).\n */\nexport class ResponsiveVideo extends HTMLElement {\n #videoEl = null;\n #currentSrc = null;\n #resizeRaf = 0;\n #boundResize = null;\n\n connectedCallback() {\n this.#videoEl = this.querySelector(\"video\");\n if (!this.#videoEl) {\n return;\n }\n this.attachEvents();\n this.updateSource();\n }\n\n disconnectedCallback() {\n this.detachEvents();\n }\n\n /**\n * Sets up listeners that keep the video source aligned with the viewport.\n */\n attachEvents() {\n if (typeof window === \"undefined\" || this.#boundResize) {\n return;\n }\n\n this.#boundResize = () => {\n if (this.#resizeRaf) {\n return;\n }\n this.#resizeRaf = requestAnimationFrame(() => {\n this.#resizeRaf = 0;\n this.updateSource();\n });\n };\n\n window.addEventListener(\"resize\", this.#boundResize, { passive: true });\n }\n\n /**\n * Removes listeners and cancels pending work.\n */\n detachEvents() {\n if (typeof window !== \"undefined\" && this.#boundResize) {\n window.removeEventListener(\"resize\", this.#boundResize);\n }\n if (this.#resizeRaf) {\n cancelAnimationFrame(this.#resizeRaf);\n this.#resizeRaf = 0;\n }\n this.#boundResize = null;\n }\n\n /**\n * Calculates which source should be active and updates the video if needed.\n */\n updateSource() {\n if (!this.#videoEl) {\n return;\n }\n if (typeof window === \"undefined\") {\n return;\n }\n\n const breakpoint = this.#getBreakpoint();\n const mobileVideo = this.getAttribute(\"mobile-video\")?.trim() || \"\";\n const desktopVideo = this.getAttribute(\"desktop-video\")?.trim() || \"\";\n const mobilePoster = this.getAttribute(\"mobile-poster\")?.trim() || \"\";\n const desktopPoster = this.getAttribute(\"desktop-poster\")?.trim() || \"\";\n const viewportWidth = window.innerWidth || 0;\n\n let nextMode = null;\n let nextVideo = \"\";\n let nextPoster = \"\";\n\n if (viewportWidth >= breakpoint && desktopVideo) {\n nextMode = \"desktop\";\n nextVideo = desktopVideo;\n nextPoster = desktopPoster;\n } else if (mobileVideo) {\n nextMode = \"mobile\";\n nextVideo = mobileVideo;\n nextPoster = mobilePoster;\n } else if (desktopVideo) {\n // Fallback to desktop when mobile video is missing.\n nextMode = \"desktop\";\n nextVideo = desktopVideo;\n nextPoster = desktopPoster;\n }\n\n if (!nextVideo) {\n return;\n }\n\n if (nextVideo === this.#currentSrc) {\n return;\n }\n\n this.#applySource(nextVideo, nextPoster, nextMode);\n }\n\n #getBreakpoint() {\n const attr = this.getAttribute(\"breakpoint\");\n const parsed = parseInt(attr ?? \"\", 10);\n return Number.isFinite(parsed) && parsed > 0 ? parsed : 768;\n }\n\n #applySource(videoUrl, posterUrl, mode) {\n const video = this.#videoEl;\n if (!video) {\n return;\n }\n\n const currentSrc = video.getAttribute(\"src\");\n if (currentSrc === videoUrl) {\n this.#currentSrc = videoUrl;\n if (mode) {\n this.dataset.activeMode = mode;\n }\n return;\n }\n\n video.setAttribute(\"src\", videoUrl);\n\n if (posterUrl) {\n video.setAttribute(\"poster\", posterUrl);\n } else {\n video.removeAttribute(\"poster\");\n }\n\n video.load();\n\n if (video.autoplay && typeof video.play === \"function\") {\n const playPromise = video.play();\n if (playPromise && typeof playPromise.catch === \"function\") {\n playPromise.catch(() => {\n // Most browsers require muted autoplay; ignore failures quietly.\n });\n }\n }\n\n this.#currentSrc = videoUrl;\n if (mode) {\n this.dataset.activeMode = mode;\n } else {\n delete this.dataset.activeMode;\n }\n }\n}\n\nif (!customElements.get(\"responsive-video\")) {\n customElements.define(\"responsive-video\", ResponsiveVideo);\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"responsive-video.cjs.js","sources":["../src/responsive-video.js"],"sourcesContent":["/**\n * Responsive video loader that swaps sources based on viewport width.\n * Only the matching source is applied so mobile users never download the desktop asset (and vice versa).\n */\nexport class ResponsiveVideo extends HTMLElement {\n #videoEl = null;\n #currentSrc = null;\n #resizeRaf = 0;\n #boundResize = null;\n\n connectedCallback() {\n this.#videoEl = this.querySelector(\"video\");\n if (!this.#videoEl) {\n return;\n }\n this.attachEvents();\n this.updateSource();\n }\n\n disconnectedCallback() {\n this.detachEvents();\n }\n\n /**\n * Sets up listeners that keep the video source aligned with the viewport.\n */\n attachEvents() {\n if (typeof window === \"undefined\" || this.#boundResize) {\n return;\n }\n\n this.#boundResize = () => {\n if (this.#resizeRaf) {\n return;\n }\n this.#resizeRaf = requestAnimationFrame(() => {\n this.#resizeRaf = 0;\n this.updateSource();\n });\n };\n\n window.addEventListener(\"resize\", this.#boundResize, { passive: true });\n }\n\n /**\n * Removes listeners and cancels pending work.\n */\n detachEvents() {\n if (typeof window !== \"undefined\" && this.#boundResize) {\n window.removeEventListener(\"resize\", this.#boundResize);\n }\n if (this.#resizeRaf) {\n cancelAnimationFrame(this.#resizeRaf);\n this.#resizeRaf = 0;\n }\n this.#boundResize = null;\n }\n\n /**\n * Calculates which source should be active and updates the video if needed.\n */\n updateSource() {\n if (!this.#videoEl) {\n return;\n }\n if (typeof window === \"undefined\") {\n return;\n }\n\n const breakpoint = this.#getBreakpoint();\n const mobileVideo = this.getAttribute(\"mobile-video\")?.trim() || \"\";\n const desktopVideo = this.getAttribute(\"desktop-video\")?.trim() || \"\";\n const mobilePoster = this.getAttribute(\"mobile-poster\")?.trim() || \"\";\n const desktopPoster = this.getAttribute(\"desktop-poster\")?.trim() || \"\";\n const viewportWidth = window.innerWidth || 0;\n\n let nextMode = null;\n let nextVideo = \"\";\n let nextPoster = \"\";\n\n if (viewportWidth >= breakpoint && desktopVideo) {\n nextMode = \"desktop\";\n nextVideo = desktopVideo;\n nextPoster = desktopPoster;\n } else if (mobileVideo) {\n nextMode = \"mobile\";\n nextVideo = mobileVideo;\n nextPoster = mobilePoster;\n } else if (desktopVideo) {\n // Fallback to desktop when mobile video is missing.\n nextMode = \"desktop\";\n nextVideo = desktopVideo;\n nextPoster = desktopPoster;\n }\n\n if (!nextVideo) {\n return;\n }\n\n if (nextVideo === this.#currentSrc) {\n return;\n }\n\n this.#applySource(nextVideo, nextPoster, nextMode);\n }\n\n #getBreakpoint() {\n const attr = this.getAttribute(\"breakpoint\");\n const parsed = parseInt(attr ?? \"\", 10);\n return Number.isFinite(parsed) && parsed > 0 ? parsed : 768;\n }\n\n #applySource(videoUrl, posterUrl, mode) {\n const video = this.#videoEl;\n if (!video) {\n return;\n }\n\n const currentSrc = video.getAttribute(\"src\");\n if (currentSrc === videoUrl) {\n this.#currentSrc = videoUrl;\n if (mode) {\n this.setAttribute(\"mode\", mode);\n }\n return;\n }\n\n video.setAttribute(\"src\", videoUrl);\n\n if (posterUrl) {\n video.setAttribute(\"poster\", posterUrl);\n } else {\n video.removeAttribute(\"poster\");\n }\n\n video.load();\n\n if (video.autoplay && typeof video.play === \"function\") {\n const playPromise = video.play();\n if (playPromise && typeof playPromise.catch === \"function\") {\n playPromise.catch(() => {\n // Most browsers require muted autoplay; ignore failures quietly.\n });\n }\n }\n\n this.#currentSrc = videoUrl;\n if (mode) {\n this.setAttribute(\"mode\", mode);\n } else {\n this.removeAttribute(\"mode\");\n }\n }\n}\n\nif (!customElements.get(\"responsive-video\")) {\n customElements.define(\"responsive-video\", ResponsiveVideo);\n}\n"],"names":[],"mappings":"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"}
@@ -120,7 +120,7 @@ class ResponsiveVideo extends HTMLElement {
120
120
  if (currentSrc === videoUrl) {
121
121
  this.#currentSrc = videoUrl;
122
122
  if (mode) {
123
- this.dataset.activeMode = mode;
123
+ this.setAttribute("mode", mode);
124
124
  }
125
125
  return;
126
126
  }
@@ -146,9 +146,9 @@ class ResponsiveVideo extends HTMLElement {
146
146
 
147
147
  this.#currentSrc = videoUrl;
148
148
  if (mode) {
149
- this.dataset.activeMode = mode;
149
+ this.setAttribute("mode", mode);
150
150
  } else {
151
- delete this.dataset.activeMode;
151
+ this.removeAttribute("mode");
152
152
  }
153
153
  }
154
154
  }
@@ -1 +1 @@
1
- {"version":3,"file":"responsive-video.esm.js","sources":["../src/responsive-video.js"],"sourcesContent":["/**\n * Responsive video loader that swaps sources based on viewport width.\n * Only the matching source is applied so mobile users never download the desktop asset (and vice versa).\n */\nexport class ResponsiveVideo extends HTMLElement {\n #videoEl = null;\n #currentSrc = null;\n #resizeRaf = 0;\n #boundResize = null;\n\n connectedCallback() {\n this.#videoEl = this.querySelector(\"video\");\n if (!this.#videoEl) {\n return;\n }\n this.attachEvents();\n this.updateSource();\n }\n\n disconnectedCallback() {\n this.detachEvents();\n }\n\n /**\n * Sets up listeners that keep the video source aligned with the viewport.\n */\n attachEvents() {\n if (typeof window === \"undefined\" || this.#boundResize) {\n return;\n }\n\n this.#boundResize = () => {\n if (this.#resizeRaf) {\n return;\n }\n this.#resizeRaf = requestAnimationFrame(() => {\n this.#resizeRaf = 0;\n this.updateSource();\n });\n };\n\n window.addEventListener(\"resize\", this.#boundResize, { passive: true });\n }\n\n /**\n * Removes listeners and cancels pending work.\n */\n detachEvents() {\n if (typeof window !== \"undefined\" && this.#boundResize) {\n window.removeEventListener(\"resize\", this.#boundResize);\n }\n if (this.#resizeRaf) {\n cancelAnimationFrame(this.#resizeRaf);\n this.#resizeRaf = 0;\n }\n this.#boundResize = null;\n }\n\n /**\n * Calculates which source should be active and updates the video if needed.\n */\n updateSource() {\n if (!this.#videoEl) {\n return;\n }\n if (typeof window === \"undefined\") {\n return;\n }\n\n const breakpoint = this.#getBreakpoint();\n const mobileVideo = this.getAttribute(\"mobile-video\")?.trim() || \"\";\n const desktopVideo = this.getAttribute(\"desktop-video\")?.trim() || \"\";\n const mobilePoster = this.getAttribute(\"mobile-poster\")?.trim() || \"\";\n const desktopPoster = this.getAttribute(\"desktop-poster\")?.trim() || \"\";\n const viewportWidth = window.innerWidth || 0;\n\n let nextMode = null;\n let nextVideo = \"\";\n let nextPoster = \"\";\n\n if (viewportWidth >= breakpoint && desktopVideo) {\n nextMode = \"desktop\";\n nextVideo = desktopVideo;\n nextPoster = desktopPoster;\n } else if (mobileVideo) {\n nextMode = \"mobile\";\n nextVideo = mobileVideo;\n nextPoster = mobilePoster;\n } else if (desktopVideo) {\n // Fallback to desktop when mobile video is missing.\n nextMode = \"desktop\";\n nextVideo = desktopVideo;\n nextPoster = desktopPoster;\n }\n\n if (!nextVideo) {\n return;\n }\n\n if (nextVideo === this.#currentSrc) {\n return;\n }\n\n this.#applySource(nextVideo, nextPoster, nextMode);\n }\n\n #getBreakpoint() {\n const attr = this.getAttribute(\"breakpoint\");\n const parsed = parseInt(attr ?? \"\", 10);\n return Number.isFinite(parsed) && parsed > 0 ? parsed : 768;\n }\n\n #applySource(videoUrl, posterUrl, mode) {\n const video = this.#videoEl;\n if (!video) {\n return;\n }\n\n const currentSrc = video.getAttribute(\"src\");\n if (currentSrc === videoUrl) {\n this.#currentSrc = videoUrl;\n if (mode) {\n this.dataset.activeMode = mode;\n }\n return;\n }\n\n video.setAttribute(\"src\", videoUrl);\n\n if (posterUrl) {\n video.setAttribute(\"poster\", posterUrl);\n } else {\n video.removeAttribute(\"poster\");\n }\n\n video.load();\n\n if (video.autoplay && typeof video.play === \"function\") {\n const playPromise = video.play();\n if (playPromise && typeof playPromise.catch === \"function\") {\n playPromise.catch(() => {\n // Most browsers require muted autoplay; ignore failures quietly.\n });\n }\n }\n\n this.#currentSrc = videoUrl;\n if (mode) {\n this.dataset.activeMode = mode;\n } else {\n delete this.dataset.activeMode;\n }\n }\n}\n\nif (!customElements.get(\"responsive-video\")) {\n customElements.define(\"responsive-video\", ResponsiveVideo);\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"responsive-video.esm.js","sources":["../src/responsive-video.js"],"sourcesContent":["/**\n * Responsive video loader that swaps sources based on viewport width.\n * Only the matching source is applied so mobile users never download the desktop asset (and vice versa).\n */\nexport class ResponsiveVideo extends HTMLElement {\n #videoEl = null;\n #currentSrc = null;\n #resizeRaf = 0;\n #boundResize = null;\n\n connectedCallback() {\n this.#videoEl = this.querySelector(\"video\");\n if (!this.#videoEl) {\n return;\n }\n this.attachEvents();\n this.updateSource();\n }\n\n disconnectedCallback() {\n this.detachEvents();\n }\n\n /**\n * Sets up listeners that keep the video source aligned with the viewport.\n */\n attachEvents() {\n if (typeof window === \"undefined\" || this.#boundResize) {\n return;\n }\n\n this.#boundResize = () => {\n if (this.#resizeRaf) {\n return;\n }\n this.#resizeRaf = requestAnimationFrame(() => {\n this.#resizeRaf = 0;\n this.updateSource();\n });\n };\n\n window.addEventListener(\"resize\", this.#boundResize, { passive: true });\n }\n\n /**\n * Removes listeners and cancels pending work.\n */\n detachEvents() {\n if (typeof window !== \"undefined\" && this.#boundResize) {\n window.removeEventListener(\"resize\", this.#boundResize);\n }\n if (this.#resizeRaf) {\n cancelAnimationFrame(this.#resizeRaf);\n this.#resizeRaf = 0;\n }\n this.#boundResize = null;\n }\n\n /**\n * Calculates which source should be active and updates the video if needed.\n */\n updateSource() {\n if (!this.#videoEl) {\n return;\n }\n if (typeof window === \"undefined\") {\n return;\n }\n\n const breakpoint = this.#getBreakpoint();\n const mobileVideo = this.getAttribute(\"mobile-video\")?.trim() || \"\";\n const desktopVideo = this.getAttribute(\"desktop-video\")?.trim() || \"\";\n const mobilePoster = this.getAttribute(\"mobile-poster\")?.trim() || \"\";\n const desktopPoster = this.getAttribute(\"desktop-poster\")?.trim() || \"\";\n const viewportWidth = window.innerWidth || 0;\n\n let nextMode = null;\n let nextVideo = \"\";\n let nextPoster = \"\";\n\n if (viewportWidth >= breakpoint && desktopVideo) {\n nextMode = \"desktop\";\n nextVideo = desktopVideo;\n nextPoster = desktopPoster;\n } else if (mobileVideo) {\n nextMode = \"mobile\";\n nextVideo = mobileVideo;\n nextPoster = mobilePoster;\n } else if (desktopVideo) {\n // Fallback to desktop when mobile video is missing.\n nextMode = \"desktop\";\n nextVideo = desktopVideo;\n nextPoster = desktopPoster;\n }\n\n if (!nextVideo) {\n return;\n }\n\n if (nextVideo === this.#currentSrc) {\n return;\n }\n\n this.#applySource(nextVideo, nextPoster, nextMode);\n }\n\n #getBreakpoint() {\n const attr = this.getAttribute(\"breakpoint\");\n const parsed = parseInt(attr ?? \"\", 10);\n return Number.isFinite(parsed) && parsed > 0 ? parsed : 768;\n }\n\n #applySource(videoUrl, posterUrl, mode) {\n const video = this.#videoEl;\n if (!video) {\n return;\n }\n\n const currentSrc = video.getAttribute(\"src\");\n if (currentSrc === videoUrl) {\n this.#currentSrc = videoUrl;\n if (mode) {\n this.setAttribute(\"mode\", mode);\n }\n return;\n }\n\n video.setAttribute(\"src\", videoUrl);\n\n if (posterUrl) {\n video.setAttribute(\"poster\", posterUrl);\n } else {\n video.removeAttribute(\"poster\");\n }\n\n video.load();\n\n if (video.autoplay && typeof video.play === \"function\") {\n const playPromise = video.play();\n if (playPromise && typeof playPromise.catch === \"function\") {\n playPromise.catch(() => {\n // Most browsers require muted autoplay; ignore failures quietly.\n });\n }\n }\n\n this.#currentSrc = videoUrl;\n if (mode) {\n this.setAttribute(\"mode\", mode);\n } else {\n this.removeAttribute(\"mode\");\n }\n }\n}\n\nif (!customElements.get(\"responsive-video\")) {\n customElements.define(\"responsive-video\", ResponsiveVideo);\n}\n"],"names":[],"mappings":"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"}
@@ -126,7 +126,7 @@
126
126
  if (currentSrc === videoUrl) {
127
127
  this.#currentSrc = videoUrl;
128
128
  if (mode) {
129
- this.dataset.activeMode = mode;
129
+ this.setAttribute("mode", mode);
130
130
  }
131
131
  return;
132
132
  }
@@ -152,9 +152,9 @@
152
152
 
153
153
  this.#currentSrc = videoUrl;
154
154
  if (mode) {
155
- this.dataset.activeMode = mode;
155
+ this.setAttribute("mode", mode);
156
156
  } else {
157
- delete this.dataset.activeMode;
157
+ this.removeAttribute("mode");
158
158
  }
159
159
  }
160
160
  }
@@ -1 +1 @@
1
- {"version":3,"file":"responsive-video.js","sources":["../src/responsive-video.js"],"sourcesContent":["/**\n * Responsive video loader that swaps sources based on viewport width.\n * Only the matching source is applied so mobile users never download the desktop asset (and vice versa).\n */\nexport class ResponsiveVideo extends HTMLElement {\n #videoEl = null;\n #currentSrc = null;\n #resizeRaf = 0;\n #boundResize = null;\n\n connectedCallback() {\n this.#videoEl = this.querySelector(\"video\");\n if (!this.#videoEl) {\n return;\n }\n this.attachEvents();\n this.updateSource();\n }\n\n disconnectedCallback() {\n this.detachEvents();\n }\n\n /**\n * Sets up listeners that keep the video source aligned with the viewport.\n */\n attachEvents() {\n if (typeof window === \"undefined\" || this.#boundResize) {\n return;\n }\n\n this.#boundResize = () => {\n if (this.#resizeRaf) {\n return;\n }\n this.#resizeRaf = requestAnimationFrame(() => {\n this.#resizeRaf = 0;\n this.updateSource();\n });\n };\n\n window.addEventListener(\"resize\", this.#boundResize, { passive: true });\n }\n\n /**\n * Removes listeners and cancels pending work.\n */\n detachEvents() {\n if (typeof window !== \"undefined\" && this.#boundResize) {\n window.removeEventListener(\"resize\", this.#boundResize);\n }\n if (this.#resizeRaf) {\n cancelAnimationFrame(this.#resizeRaf);\n this.#resizeRaf = 0;\n }\n this.#boundResize = null;\n }\n\n /**\n * Calculates which source should be active and updates the video if needed.\n */\n updateSource() {\n if (!this.#videoEl) {\n return;\n }\n if (typeof window === \"undefined\") {\n return;\n }\n\n const breakpoint = this.#getBreakpoint();\n const mobileVideo = this.getAttribute(\"mobile-video\")?.trim() || \"\";\n const desktopVideo = this.getAttribute(\"desktop-video\")?.trim() || \"\";\n const mobilePoster = this.getAttribute(\"mobile-poster\")?.trim() || \"\";\n const desktopPoster = this.getAttribute(\"desktop-poster\")?.trim() || \"\";\n const viewportWidth = window.innerWidth || 0;\n\n let nextMode = null;\n let nextVideo = \"\";\n let nextPoster = \"\";\n\n if (viewportWidth >= breakpoint && desktopVideo) {\n nextMode = \"desktop\";\n nextVideo = desktopVideo;\n nextPoster = desktopPoster;\n } else if (mobileVideo) {\n nextMode = \"mobile\";\n nextVideo = mobileVideo;\n nextPoster = mobilePoster;\n } else if (desktopVideo) {\n // Fallback to desktop when mobile video is missing.\n nextMode = \"desktop\";\n nextVideo = desktopVideo;\n nextPoster = desktopPoster;\n }\n\n if (!nextVideo) {\n return;\n }\n\n if (nextVideo === this.#currentSrc) {\n return;\n }\n\n this.#applySource(nextVideo, nextPoster, nextMode);\n }\n\n #getBreakpoint() {\n const attr = this.getAttribute(\"breakpoint\");\n const parsed = parseInt(attr ?? \"\", 10);\n return Number.isFinite(parsed) && parsed > 0 ? parsed : 768;\n }\n\n #applySource(videoUrl, posterUrl, mode) {\n const video = this.#videoEl;\n if (!video) {\n return;\n }\n\n const currentSrc = video.getAttribute(\"src\");\n if (currentSrc === videoUrl) {\n this.#currentSrc = videoUrl;\n if (mode) {\n this.dataset.activeMode = mode;\n }\n return;\n }\n\n video.setAttribute(\"src\", videoUrl);\n\n if (posterUrl) {\n video.setAttribute(\"poster\", posterUrl);\n } else {\n video.removeAttribute(\"poster\");\n }\n\n video.load();\n\n if (video.autoplay && typeof video.play === \"function\") {\n const playPromise = video.play();\n if (playPromise && typeof playPromise.catch === \"function\") {\n playPromise.catch(() => {\n // Most browsers require muted autoplay; ignore failures quietly.\n });\n }\n }\n\n this.#currentSrc = videoUrl;\n if (mode) {\n this.dataset.activeMode = mode;\n } else {\n delete this.dataset.activeMode;\n }\n }\n}\n\nif (!customElements.get(\"responsive-video\")) {\n customElements.define(\"responsive-video\", ResponsiveVideo);\n}\n"],"names":[],"mappings":";;;;;;EAAA;EACA;EACA;EACA;EACO,MAAM,eAAe,SAAS,WAAW,CAAC;EACjD,EAAE,QAAQ,GAAG,IAAI,CAAC;EAClB,EAAE,WAAW,GAAG,IAAI,CAAC;EACrB,EAAE,UAAU,GAAG,CAAC,CAAC;EACjB,EAAE,YAAY,GAAG,IAAI,CAAC;AACtB;EACA,EAAE,iBAAiB,GAAG;EACtB,IAAI,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;EAChD,IAAI,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE;EACxB,MAAM,OAAO;EACb,KAAK;EACL,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;EACxB,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;EACxB,GAAG;AACH;EACA,EAAE,oBAAoB,GAAG;EACzB,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;EACxB,GAAG;AACH;EACA;EACA;EACA;EACA,EAAE,YAAY,GAAG;EACjB,IAAI,IAAI,OAAO,MAAM,KAAK,WAAW,IAAI,IAAI,CAAC,YAAY,EAAE;EAC5D,MAAM,OAAO;EACb,KAAK;AACL;EACA,IAAI,IAAI,CAAC,YAAY,GAAG,MAAM;EAC9B,MAAM,IAAI,IAAI,CAAC,UAAU,EAAE;EAC3B,QAAQ,OAAO;EACf,OAAO;EACP,MAAM,IAAI,CAAC,UAAU,GAAG,qBAAqB,CAAC,MAAM;EACpD,QAAQ,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC;EAC5B,QAAQ,IAAI,CAAC,YAAY,EAAE,CAAC;EAC5B,OAAO,CAAC,CAAC;EACT,KAAK,CAAC;AACN;EACA,IAAI,MAAM,CAAC,gBAAgB,CAAC,QAAQ,EAAE,IAAI,CAAC,YAAY,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;EAC5E,GAAG;AACH;EACA;EACA;EACA;EACA,EAAE,YAAY,GAAG;EACjB,IAAI,IAAI,OAAO,MAAM,KAAK,WAAW,IAAI,IAAI,CAAC,YAAY,EAAE;EAC5D,MAAM,MAAM,CAAC,mBAAmB,CAAC,QAAQ,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;EAC9D,KAAK;EACL,IAAI,IAAI,IAAI,CAAC,UAAU,EAAE;EACzB,MAAM,oBAAoB,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;EAC5C,MAAM,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC;EAC1B,KAAK;EACL,IAAI,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;EAC7B,GAAG;AACH;EACA;EACA;EACA;EACA,EAAE,YAAY,GAAG;EACjB,IAAI,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE;EACxB,MAAM,OAAO;EACb,KAAK;EACL,IAAI,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE;EACvC,MAAM,OAAO;EACb,KAAK;AACL;EACA,IAAI,MAAM,UAAU,GAAG,IAAI,CAAC,cAAc,EAAE,CAAC;EAC7C,IAAI,MAAM,WAAW,GAAG,IAAI,CAAC,YAAY,CAAC,cAAc,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;EACxE,IAAI,MAAM,YAAY,GAAG,IAAI,CAAC,YAAY,CAAC,eAAe,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;EAC1E,IAAI,MAAM,YAAY,GAAG,IAAI,CAAC,YAAY,CAAC,eAAe,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;EAC1E,IAAI,MAAM,aAAa,GAAG,IAAI,CAAC,YAAY,CAAC,gBAAgB,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;EAC5E,IAAI,MAAM,aAAa,GAAG,MAAM,CAAC,UAAU,IAAI,CAAC,CAAC;AACjD;EACA,IAAI,IAAI,QAAQ,GAAG,IAAI,CAAC;EACxB,IAAI,IAAI,SAAS,GAAG,EAAE,CAAC;EACvB,IAAI,IAAI,UAAU,GAAG,EAAE,CAAC;AACxB;EACA,IAAI,IAAI,aAAa,IAAI,UAAU,IAAI,YAAY,EAAE;EACrD,MAAM,QAAQ,GAAG,SAAS,CAAC;EAC3B,MAAM,SAAS,GAAG,YAAY,CAAC;EAC/B,MAAM,UAAU,GAAG,aAAa,CAAC;EACjC,KAAK,MAAM,IAAI,WAAW,EAAE;EAC5B,MAAM,QAAQ,GAAG,QAAQ,CAAC;EAC1B,MAAM,SAAS,GAAG,WAAW,CAAC;EAC9B,MAAM,UAAU,GAAG,YAAY,CAAC;EAChC,KAAK,MAAM,IAAI,YAAY,EAAE;EAC7B;EACA,MAAM,QAAQ,GAAG,SAAS,CAAC;EAC3B,MAAM,SAAS,GAAG,YAAY,CAAC;EAC/B,MAAM,UAAU,GAAG,aAAa,CAAC;EACjC,KAAK;AACL;EACA,IAAI,IAAI,CAAC,SAAS,EAAE;EACpB,MAAM,OAAO;EACb,KAAK;AACL;EACA,IAAI,IAAI,SAAS,KAAK,IAAI,CAAC,WAAW,EAAE;EACxC,MAAM,OAAO;EACb,KAAK;AACL;EACA,IAAI,IAAI,CAAC,YAAY,CAAC,SAAS,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC;EACvD,GAAG;AACH;EACA,EAAE,cAAc,GAAG;EACnB,IAAI,MAAM,IAAI,GAAG,IAAI,CAAC,YAAY,CAAC,YAAY,CAAC,CAAC;EACjD,IAAI,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;EAC5C,IAAI,OAAO,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,MAAM,GAAG,CAAC,GAAG,MAAM,GAAG,GAAG,CAAC;EAChE,GAAG;AACH;EACA,EAAE,YAAY,CAAC,QAAQ,EAAE,SAAS,EAAE,IAAI,EAAE;EAC1C,IAAI,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC;EAChC,IAAI,IAAI,CAAC,KAAK,EAAE;EAChB,MAAM,OAAO;EACb,KAAK;AACL;EACA,IAAI,MAAM,UAAU,GAAG,KAAK,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;EACjD,IAAI,IAAI,UAAU,KAAK,QAAQ,EAAE;EACjC,MAAM,IAAI,CAAC,WAAW,GAAG,QAAQ,CAAC;EAClC,MAAM,IAAI,IAAI,EAAE;EAChB,QAAQ,IAAI,CAAC,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;EACvC,OAAO;EACP,MAAM,OAAO;EACb,KAAK;AACL;EACA,IAAI,KAAK,CAAC,YAAY,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;AACxC;EACA,IAAI,IAAI,SAAS,EAAE;EACnB,MAAM,KAAK,CAAC,YAAY,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;EAC9C,KAAK,MAAM;EACX,MAAM,KAAK,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC;EACtC,KAAK;AACL;EACA,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;AACjB;EACA,IAAI,IAAI,KAAK,CAAC,QAAQ,IAAI,OAAO,KAAK,CAAC,IAAI,KAAK,UAAU,EAAE;EAC5D,MAAM,MAAM,WAAW,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;EACvC,MAAM,IAAI,WAAW,IAAI,OAAO,WAAW,CAAC,KAAK,KAAK,UAAU,EAAE;EAClE,QAAQ,WAAW,CAAC,KAAK,CAAC,MAAM;EAChC;EACA,SAAS,CAAC,CAAC;EACX,OAAO;EACP,KAAK;AACL;EACA,IAAI,IAAI,CAAC,WAAW,GAAG,QAAQ,CAAC;EAChC,IAAI,IAAI,IAAI,EAAE;EACd,MAAM,IAAI,CAAC,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;EACrC,KAAK,MAAM;EACX,MAAM,OAAO,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC;EACrC,KAAK;EACL,GAAG;EACH,CAAC;AACD;EACA,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,kBAAkB,CAAC,EAAE;EAC7C,EAAE,cAAc,CAAC,MAAM,CAAC,kBAAkB,EAAE,eAAe,CAAC,CAAC;EAC7D;;;;;;;;"}
1
+ {"version":3,"file":"responsive-video.js","sources":["../src/responsive-video.js"],"sourcesContent":["/**\n * Responsive video loader that swaps sources based on viewport width.\n * Only the matching source is applied so mobile users never download the desktop asset (and vice versa).\n */\nexport class ResponsiveVideo extends HTMLElement {\n #videoEl = null;\n #currentSrc = null;\n #resizeRaf = 0;\n #boundResize = null;\n\n connectedCallback() {\n this.#videoEl = this.querySelector(\"video\");\n if (!this.#videoEl) {\n return;\n }\n this.attachEvents();\n this.updateSource();\n }\n\n disconnectedCallback() {\n this.detachEvents();\n }\n\n /**\n * Sets up listeners that keep the video source aligned with the viewport.\n */\n attachEvents() {\n if (typeof window === \"undefined\" || this.#boundResize) {\n return;\n }\n\n this.#boundResize = () => {\n if (this.#resizeRaf) {\n return;\n }\n this.#resizeRaf = requestAnimationFrame(() => {\n this.#resizeRaf = 0;\n this.updateSource();\n });\n };\n\n window.addEventListener(\"resize\", this.#boundResize, { passive: true });\n }\n\n /**\n * Removes listeners and cancels pending work.\n */\n detachEvents() {\n if (typeof window !== \"undefined\" && this.#boundResize) {\n window.removeEventListener(\"resize\", this.#boundResize);\n }\n if (this.#resizeRaf) {\n cancelAnimationFrame(this.#resizeRaf);\n this.#resizeRaf = 0;\n }\n this.#boundResize = null;\n }\n\n /**\n * Calculates which source should be active and updates the video if needed.\n */\n updateSource() {\n if (!this.#videoEl) {\n return;\n }\n if (typeof window === \"undefined\") {\n return;\n }\n\n const breakpoint = this.#getBreakpoint();\n const mobileVideo = this.getAttribute(\"mobile-video\")?.trim() || \"\";\n const desktopVideo = this.getAttribute(\"desktop-video\")?.trim() || \"\";\n const mobilePoster = this.getAttribute(\"mobile-poster\")?.trim() || \"\";\n const desktopPoster = this.getAttribute(\"desktop-poster\")?.trim() || \"\";\n const viewportWidth = window.innerWidth || 0;\n\n let nextMode = null;\n let nextVideo = \"\";\n let nextPoster = \"\";\n\n if (viewportWidth >= breakpoint && desktopVideo) {\n nextMode = \"desktop\";\n nextVideo = desktopVideo;\n nextPoster = desktopPoster;\n } else if (mobileVideo) {\n nextMode = \"mobile\";\n nextVideo = mobileVideo;\n nextPoster = mobilePoster;\n } else if (desktopVideo) {\n // Fallback to desktop when mobile video is missing.\n nextMode = \"desktop\";\n nextVideo = desktopVideo;\n nextPoster = desktopPoster;\n }\n\n if (!nextVideo) {\n return;\n }\n\n if (nextVideo === this.#currentSrc) {\n return;\n }\n\n this.#applySource(nextVideo, nextPoster, nextMode);\n }\n\n #getBreakpoint() {\n const attr = this.getAttribute(\"breakpoint\");\n const parsed = parseInt(attr ?? \"\", 10);\n return Number.isFinite(parsed) && parsed > 0 ? parsed : 768;\n }\n\n #applySource(videoUrl, posterUrl, mode) {\n const video = this.#videoEl;\n if (!video) {\n return;\n }\n\n const currentSrc = video.getAttribute(\"src\");\n if (currentSrc === videoUrl) {\n this.#currentSrc = videoUrl;\n if (mode) {\n this.setAttribute(\"mode\", mode);\n }\n return;\n }\n\n video.setAttribute(\"src\", videoUrl);\n\n if (posterUrl) {\n video.setAttribute(\"poster\", posterUrl);\n } else {\n video.removeAttribute(\"poster\");\n }\n\n video.load();\n\n if (video.autoplay && typeof video.play === \"function\") {\n const playPromise = video.play();\n if (playPromise && typeof playPromise.catch === \"function\") {\n playPromise.catch(() => {\n // Most browsers require muted autoplay; ignore failures quietly.\n });\n }\n }\n\n this.#currentSrc = videoUrl;\n if (mode) {\n this.setAttribute(\"mode\", mode);\n } else {\n this.removeAttribute(\"mode\");\n }\n }\n}\n\nif (!customElements.get(\"responsive-video\")) {\n customElements.define(\"responsive-video\", ResponsiveVideo);\n}\n"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).ResponsiveVideo={})}(this,function(e){"use strict";class ResponsiveVideo extends HTMLElement{#e=null;#t=null;#i=0;#s=null;connectedCallback(){this.#e=this.querySelector("video"),this.#e&&(this.attachEvents(),this.updateSource())}disconnectedCallback(){this.detachEvents()}attachEvents(){"undefined"==typeof window||this.#s||(this.#s=()=>{this.#i||(this.#i=requestAnimationFrame(()=>{this.#i=0,this.updateSource()}))},window.addEventListener("resize",this.#s,{passive:!0}))}detachEvents(){"undefined"!=typeof window&&this.#s&&window.removeEventListener("resize",this.#s),this.#i&&(cancelAnimationFrame(this.#i),this.#i=0),this.#s=null}updateSource(){if(!this.#e)return;if("undefined"==typeof window)return;const e=this.#o(),t=this.getAttribute("mobile-video")?.trim()||"",i=this.getAttribute("desktop-video")?.trim()||"",s=this.getAttribute("mobile-poster")?.trim()||"",o=this.getAttribute("desktop-poster")?.trim()||"";let n=null,r="",d="";(window.innerWidth||0)>=e&&i?(n="desktop",r=i,d=o):t?(n="mobile",r=t,d=s):i&&(n="desktop",r=i,d=o),r&&r!==this.#t&&this.#n(r,d,n)}#o(){const e=this.getAttribute("breakpoint"),t=parseInt(e??"",10);return Number.isFinite(t)&&t>0?t:768}#n(e,t,i){const s=this.#e;if(!s)return;if(s.getAttribute("src")===e)return this.#t=e,void(i&&(this.dataset.activeMode=i));if(s.setAttribute("src",e),t?s.setAttribute("poster",t):s.removeAttribute("poster"),s.load(),s.autoplay&&"function"==typeof s.play){const e=s.play();e&&"function"==typeof e.catch&&e.catch(()=>{})}this.#t=e,i?this.dataset.activeMode=i:delete this.dataset.activeMode}}customElements.get("responsive-video")||customElements.define("responsive-video",ResponsiveVideo),e.ResponsiveVideo=ResponsiveVideo});
1
+ !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).ResponsiveVideo={})}(this,function(e){"use strict";class ResponsiveVideo extends HTMLElement{#e=null;#t=null;#i=0;#s=null;connectedCallback(){this.#e=this.querySelector("video"),this.#e&&(this.attachEvents(),this.updateSource())}disconnectedCallback(){this.detachEvents()}attachEvents(){"undefined"==typeof window||this.#s||(this.#s=()=>{this.#i||(this.#i=requestAnimationFrame(()=>{this.#i=0,this.updateSource()}))},window.addEventListener("resize",this.#s,{passive:!0}))}detachEvents(){"undefined"!=typeof window&&this.#s&&window.removeEventListener("resize",this.#s),this.#i&&(cancelAnimationFrame(this.#i),this.#i=0),this.#s=null}updateSource(){if(!this.#e)return;if("undefined"==typeof window)return;const e=this.#o(),t=this.getAttribute("mobile-video")?.trim()||"",i=this.getAttribute("desktop-video")?.trim()||"",s=this.getAttribute("mobile-poster")?.trim()||"",o=this.getAttribute("desktop-poster")?.trim()||"";let n=null,r="",d="";(window.innerWidth||0)>=e&&i?(n="desktop",r=i,d=o):t?(n="mobile",r=t,d=s):i&&(n="desktop",r=i,d=o),r&&r!==this.#t&&this.#n(r,d,n)}#o(){const e=this.getAttribute("breakpoint"),t=parseInt(e??"",10);return Number.isFinite(t)&&t>0?t:768}#n(e,t,i){const s=this.#e;if(!s)return;if(s.getAttribute("src")===e)return this.#t=e,void(i&&this.setAttribute("mode",i));if(s.setAttribute("src",e),t?s.setAttribute("poster",t):s.removeAttribute("poster"),s.load(),s.autoplay&&"function"==typeof s.play){const e=s.play();e&&"function"==typeof e.catch&&e.catch(()=>{})}this.#t=e,i?this.setAttribute("mode",i):this.removeAttribute("mode")}}customElements.get("responsive-video")||customElements.define("responsive-video",ResponsiveVideo),e.ResponsiveVideo=ResponsiveVideo});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@magic-spells/responsive-video",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Responsive video and poster image swapping web component.",
5
5
  "author": "Cory Schulz",
6
6
  "license": "MIT",
@@ -20,7 +20,7 @@
20
20
  "sideEffects": true,
21
21
  "repository": {
22
22
  "type": "git",
23
- "url": "https://github.com/magic-spells/responsive-video"
23
+ "url": "git+https://github.com/magic-spells/responsive-video.git"
24
24
  },
25
25
  "homepage": "https://github.com/magic-spells/responsive-video#readme",
26
26
  "bugs": {
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Responsive video loader that swaps sources based on viewport width.
3
+ * Only the matching source is applied so mobile users never download the desktop asset (and vice versa).
4
+ */
5
+ export declare class ResponsiveVideo extends HTMLElement {
6
+ connectedCallback(): void;
7
+ disconnectedCallback(): void;
8
+ attachEvents(): void;
9
+ detachEvents(): void;
10
+ updateSource(): void;
11
+ }
12
+
13
+ declare global {
14
+ interface HTMLElementTagNameMap {
15
+ "responsive-video": ResponsiveVideo;
16
+ }
17
+ }
@@ -120,7 +120,7 @@ export class ResponsiveVideo extends HTMLElement {
120
120
  if (currentSrc === videoUrl) {
121
121
  this.#currentSrc = videoUrl;
122
122
  if (mode) {
123
- this.dataset.activeMode = mode;
123
+ this.setAttribute("mode", mode);
124
124
  }
125
125
  return;
126
126
  }
@@ -146,9 +146,9 @@ export class ResponsiveVideo extends HTMLElement {
146
146
 
147
147
  this.#currentSrc = videoUrl;
148
148
  if (mode) {
149
- this.dataset.activeMode = mode;
149
+ this.setAttribute("mode", mode);
150
150
  } else {
151
- delete this.dataset.activeMode;
151
+ this.removeAttribute("mode");
152
152
  }
153
153
  }
154
154
  }