@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 +7 -7
- package/dist/responsive-video.cjs.js +3 -3
- package/dist/responsive-video.cjs.js.map +1 -1
- package/dist/responsive-video.esm.js +3 -3
- package/dist/responsive-video.esm.js.map +1 -1
- package/dist/responsive-video.js +3 -3
- package/dist/responsive-video.js.map +1 -1
- package/dist/responsive-video.min.js +1 -1
- package/package.json +2 -2
- package/src/responsive-video.d.ts +17 -0
- package/src/responsive-video.js +3 -3
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 `
|
|
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
|
-
###
|
|
75
|
+
### Mode Attribute
|
|
76
76
|
|
|
77
|
-
The component sets `
|
|
77
|
+
The component sets a `mode` attribute on itself to indicate which source is currently active:
|
|
78
78
|
|
|
79
79
|
```html
|
|
80
|
-
<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[
|
|
86
|
+
responsive-video[mode="mobile"] {
|
|
87
87
|
/* Mobile-specific styles */
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
responsive-video[
|
|
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 `
|
|
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.
|
|
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.
|
|
151
|
+
this.setAttribute("mode", mode);
|
|
152
152
|
} else {
|
|
153
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
149
|
+
this.setAttribute("mode", mode);
|
|
150
150
|
} else {
|
|
151
|
-
|
|
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.
|
|
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":"AAAA;AACA;AACA;AACA;AACO,MAAM,eAAe,SAAS,WAAW,CAAC;AACjD,EAAE,QAAQ,GAAG,IAAI,CAAC;AAClB,EAAE,WAAW,GAAG,IAAI,CAAC;AACrB,EAAE,UAAU,GAAG,CAAC,CAAC;AACjB,EAAE,YAAY,GAAG,IAAI,CAAC;AACtB;AACA,EAAE,iBAAiB,GAAG;AACtB,IAAI,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;AAChD,IAAI,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE;AACxB,MAAM,OAAO;AACb,KAAK;AACL,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;AACxB,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;AACxB,GAAG;AACH;AACA,EAAE,oBAAoB,GAAG;AACzB,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;AACxB,GAAG;AACH;AACA;AACA;AACA;AACA,EAAE,YAAY,GAAG;AACjB,IAAI,IAAI,OAAO,MAAM,KAAK,WAAW,IAAI,IAAI,CAAC,YAAY,EAAE;AAC5D,MAAM,OAAO;AACb,KAAK;AACL;AACA,IAAI,IAAI,CAAC,YAAY,GAAG,MAAM;AAC9B,MAAM,IAAI,IAAI,CAAC,UAAU,EAAE;AAC3B,QAAQ,OAAO;AACf,OAAO;AACP,MAAM,IAAI,CAAC,UAAU,GAAG,qBAAqB,CAAC,MAAM;AACpD,QAAQ,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC;AAC5B,QAAQ,IAAI,CAAC,YAAY,EAAE,CAAC;AAC5B,OAAO,CAAC,CAAC;AACT,KAAK,CAAC;AACN;AACA,IAAI,MAAM,CAAC,gBAAgB,CAAC,QAAQ,EAAE,IAAI,CAAC,YAAY,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;AAC5E,GAAG;AACH;AACA;AACA;AACA;AACA,EAAE,YAAY,GAAG;AACjB,IAAI,IAAI,OAAO,MAAM,KAAK,WAAW,IAAI,IAAI,CAAC,YAAY,EAAE;AAC5D,MAAM,MAAM,CAAC,mBAAmB,CAAC,QAAQ,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;AAC9D,KAAK;AACL,IAAI,IAAI,IAAI,CAAC,UAAU,EAAE;AACzB,MAAM,oBAAoB,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;AAC5C,MAAM,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC;AAC1B,KAAK;AACL,IAAI,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;AAC7B,GAAG;AACH;AACA;AACA;AACA;AACA,EAAE,YAAY,GAAG;AACjB,IAAI,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE;AACxB,MAAM,OAAO;AACb,KAAK;AACL,IAAI,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE;AACvC,MAAM,OAAO;AACb,KAAK;AACL;AACA,IAAI,MAAM,UAAU,GAAG,IAAI,CAAC,cAAc,EAAE,CAAC;AAC7C,IAAI,MAAM,WAAW,GAAG,IAAI,CAAC,YAAY,CAAC,cAAc,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;AACxE,IAAI,MAAM,YAAY,GAAG,IAAI,CAAC,YAAY,CAAC,eAAe,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;AAC1E,IAAI,MAAM,YAAY,GAAG,IAAI,CAAC,YAAY,CAAC,eAAe,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;AAC1E,IAAI,MAAM,aAAa,GAAG,IAAI,CAAC,YAAY,CAAC,gBAAgB,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;AAC5E,IAAI,MAAM,aAAa,GAAG,MAAM,CAAC,UAAU,IAAI,CAAC,CAAC;AACjD;AACA,IAAI,IAAI,QAAQ,GAAG,IAAI,CAAC;AACxB,IAAI,IAAI,SAAS,GAAG,EAAE,CAAC;AACvB,IAAI,IAAI,UAAU,GAAG,EAAE,CAAC;AACxB;AACA,IAAI,IAAI,aAAa,IAAI,UAAU,IAAI,YAAY,EAAE;AACrD,MAAM,QAAQ,GAAG,SAAS,CAAC;AAC3B,MAAM,SAAS,GAAG,YAAY,CAAC;AAC/B,MAAM,UAAU,GAAG,aAAa,CAAC;AACjC,KAAK,MAAM,IAAI,WAAW,EAAE;AAC5B,MAAM,QAAQ,GAAG,QAAQ,CAAC;AAC1B,MAAM,SAAS,GAAG,WAAW,CAAC;AAC9B,MAAM,UAAU,GAAG,YAAY,CAAC;AAChC,KAAK,MAAM,IAAI,YAAY,EAAE;AAC7B;AACA,MAAM,QAAQ,GAAG,SAAS,CAAC;AAC3B,MAAM,SAAS,GAAG,YAAY,CAAC;AAC/B,MAAM,UAAU,GAAG,aAAa,CAAC;AACjC,KAAK;AACL;AACA,IAAI,IAAI,CAAC,SAAS,EAAE;AACpB,MAAM,OAAO;AACb,KAAK;AACL;AACA,IAAI,IAAI,SAAS,KAAK,IAAI,CAAC,WAAW,EAAE;AACxC,MAAM,OAAO;AACb,KAAK;AACL;AACA,IAAI,IAAI,CAAC,YAAY,CAAC,SAAS,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC;AACvD,GAAG;AACH;AACA,EAAE,cAAc,GAAG;AACnB,IAAI,MAAM,IAAI,GAAG,IAAI,CAAC,YAAY,CAAC,YAAY,CAAC,CAAC;AACjD,IAAI,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;AAC5C,IAAI,OAAO,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,MAAM,GAAG,CAAC,GAAG,MAAM,GAAG,GAAG,CAAC;AAChE,GAAG;AACH;AACA,EAAE,YAAY,CAAC,QAAQ,EAAE,SAAS,EAAE,IAAI,EAAE;AAC1C,IAAI,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC;AAChC,IAAI,IAAI,CAAC,KAAK,EAAE;AAChB,MAAM,OAAO;AACb,KAAK;AACL;AACA,IAAI,MAAM,UAAU,GAAG,KAAK,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;AACjD,IAAI,IAAI,UAAU,KAAK,QAAQ,EAAE;AACjC,MAAM,IAAI,CAAC,WAAW,GAAG,QAAQ,CAAC;AAClC,MAAM,IAAI,IAAI,EAAE;AAChB,QAAQ,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;AACxC,OAAO;AACP,MAAM,OAAO;AACb,KAAK;AACL;AACA,IAAI,KAAK,CAAC,YAAY,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;AACxC;AACA,IAAI,IAAI,SAAS,EAAE;AACnB,MAAM,KAAK,CAAC,YAAY,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;AAC9C,KAAK,MAAM;AACX,MAAM,KAAK,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC;AACtC,KAAK;AACL;AACA,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;AACjB;AACA,IAAI,IAAI,KAAK,CAAC,QAAQ,IAAI,OAAO,KAAK,CAAC,IAAI,KAAK,UAAU,EAAE;AAC5D,MAAM,MAAM,WAAW,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;AACvC,MAAM,IAAI,WAAW,IAAI,OAAO,WAAW,CAAC,KAAK,KAAK,UAAU,EAAE;AAClE,QAAQ,WAAW,CAAC,KAAK,CAAC,MAAM;AAChC;AACA,SAAS,CAAC,CAAC;AACX,OAAO;AACP,KAAK;AACL;AACA,IAAI,IAAI,CAAC,WAAW,GAAG,QAAQ,CAAC;AAChC,IAAI,IAAI,IAAI,EAAE;AACd,MAAM,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;AACtC,KAAK,MAAM;AACX,MAAM,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;AACnC,KAAK;AACL,GAAG;AACH,CAAC;AACD;AACA,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,kBAAkB,CAAC,EAAE;AAC7C,EAAE,cAAc,CAAC,MAAM,CAAC,kBAAkB,EAAE,eAAe,CAAC,CAAC;AAC7D;;;;"}
|
package/dist/responsive-video.js
CHANGED
|
@@ -126,7 +126,7 @@
|
|
|
126
126
|
if (currentSrc === videoUrl) {
|
|
127
127
|
this.#currentSrc = videoUrl;
|
|
128
128
|
if (mode) {
|
|
129
|
-
this.
|
|
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.
|
|
155
|
+
this.setAttribute("mode", mode);
|
|
156
156
|
} else {
|
|
157
|
-
|
|
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.
|
|
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&&
|
|
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.
|
|
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
|
+
}
|
package/src/responsive-video.js
CHANGED
|
@@ -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.
|
|
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.
|
|
149
|
+
this.setAttribute("mode", mode);
|
|
150
150
|
} else {
|
|
151
|
-
|
|
151
|
+
this.removeAttribute("mode");
|
|
152
152
|
}
|
|
153
153
|
}
|
|
154
154
|
}
|