@profitlich/template-toolkit 2.10.0 → 2.11.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.
@@ -1,11 +1,16 @@
1
1
  import '@mux/mux-player';
2
+ import config from '../../config.json' with { type: 'json' };
2
3
  import './mux-player.scss';
3
4
 
4
5
  export class MuxPlayer {
5
6
  #lazyLoadObserver;
6
7
  #playPauseObserver;
8
+ #loop;
9
+ #onPlayerCreated;
7
10
 
8
- constructor() {
11
+ constructor({ loop = false, onPlayerCreated = null } = {}) {
12
+ this.#loop = loop;
13
+ this.#onPlayerCreated = onPlayerCreated;
9
14
  this.#lazyLoadObserver = new IntersectionObserver(this.#handleLazyLoad.bind(this));
10
15
  this.#playPauseObserver = new IntersectionObserver(this.#handlePlayPause.bind(this));
11
16
  }
@@ -16,6 +21,40 @@ export class MuxPlayer {
16
21
  });
17
22
  }
18
23
 
24
+ #setupAutoplay(player) {
25
+ // Guard Clause: Führe das Setup nur aus, wenn es noch nicht passiert ist.
26
+ if (player.dataset.isSetup === 'true') {
27
+ return;
28
+ }
29
+
30
+ player.muted = true;
31
+ player.style.setProperty('--controls', 'none');
32
+ if (this.#loop) player.loop = true;
33
+
34
+ // Flag setzen, um zukünftige Ausführungen zu verhindern.
35
+ player.dataset.isSetup = 'true';
36
+
37
+ // Den playPauseObserver starten, sobald die Daten geladen sind.
38
+ if (player.readyState >= 2) { // HAVE_CURRENT_DATA oder höher
39
+ this.#playPauseObserver.observe(player);
40
+ } else {
41
+ player.addEventListener('loadeddata', () => {
42
+ this.#playPauseObserver.observe(player);
43
+ }, { once: true });
44
+ }
45
+ }
46
+
47
+ handleVisibilityChange(player) {
48
+ const isVisible = window.getComputedStyle(player).display !== 'none';
49
+
50
+ if (isVisible && player.hasAttribute('data-autoplay') && player.dataset.isSetup !== 'true') {
51
+ this.#setupAutoplay(player);
52
+ } else if (!isVisible) {
53
+ this.#playPauseObserver.unobserve(player);
54
+ player.pause();
55
+ }
56
+ }
57
+
19
58
  #handleLazyLoad(entries) {
20
59
  for (const entry of entries) {
21
60
  if (entry.isIntersecting) {
@@ -30,69 +69,28 @@ export class MuxPlayer {
30
69
  player.streamType = 'on-demand';
31
70
  player.playsInline = true;
32
71
  player.style.aspectRatio = aspectRatio;
72
+ player.thumbnailTime = 0;
73
+ player.minResolution = config.muxMinResolution || '1080p';
74
+
75
+ if (container.dataset.accentColor) {
76
+ player.accentColor = container.dataset.accentColor;
77
+ }
33
78
 
34
- // Autoplay-Attribut am Player-Element setzen, damit es später gefunden werden kann
35
79
  if (autoplay) {
36
80
  player.setAttribute('data-autoplay', 'true');
37
81
  }
38
82
 
39
- const wrapper = container.closest('[data-video-wrapper]');
40
-
41
- // Wenn ein Wrapper gefunden wird, richte den MutationObserver ein
42
- if (wrapper) {
43
- const visibilityObserver = new MutationObserver(() => {
44
- this.#handleVisibilityChange(player);
45
- });
46
-
47
- // Beobachte den gefundenen Wrapper
48
- visibilityObserver.observe(wrapper, { attributes: true, childList: true, subtree: true });
49
-
50
- // Container durch Player ersetzen BEVOR die Sichtbarkeitsprüfung
51
- container.replaceWith(player);
83
+ container.replaceWith(player);
52
84
 
53
- // Führe eine initiale Prüfung durch, falls es schon sichtbar ist
54
- this.#handleVisibilityChange(player);
85
+ if (this.#onPlayerCreated) {
86
+ this.#onPlayerCreated(player, container);
55
87
  } else if (autoplay) {
56
- // Fallback für normale Autoplay-Videos ohne speziellen Wrapper
57
- player.muted = true;
58
- player.style.setProperty('--controls', 'none');
59
- container.replaceWith(player);
60
-
61
- player.addEventListener('loadeddata', () => {
62
- this.#playPauseObserver.observe(player);
63
- });
64
- } else {
65
- // Für Videos ohne Autoplay
66
- container.replaceWith(player);
88
+ this.handleVisibilityChange(player);
67
89
  }
68
90
  }
69
91
  }
70
92
  }
71
93
 
72
- #handleVisibilityChange(player) {
73
- // Prüfe direkt am Player, ob er durch CSS sichtbar ist
74
- const isVisible = window.getComputedStyle(player).display !== 'none';
75
-
76
- if (isVisible && player.hasAttribute('data-autoplay')) {
77
- // Wenn der Player sichtbar ist und Autoplay haben soll
78
- player.muted = true;
79
- player.style.setProperty('--controls', 'none');
80
-
81
- // Warte auf loadeddata bevor der IntersectionObserver aktiviert wird
82
- if (player.readyState >= 2) { // HAVE_CURRENT_DATA oder höher
83
- this.#playPauseObserver.observe(player);
84
- } else {
85
- player.addEventListener('loadeddata', () => {
86
- this.#playPauseObserver.observe(player);
87
- }, { once: true });
88
- }
89
- } else if (!isVisible) {
90
- // Wenn der Player unsichtbar wird, pausiere ihn und stoppe die Beobachtung
91
- this.#playPauseObserver.unobserve(player);
92
- player.pause();
93
- }
94
- }
95
-
96
94
  #handlePlayPause(entries) {
97
95
  for (const entry of entries) {
98
96
  const player = entry.target;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@profitlich/template-toolkit",
3
- "version": "2.10.0",
3
+ "version": "2.11.0",
4
4
  "description": "Shared SCSS layout system, JS utilities, components and build scripts for profitlich template repos",
5
5
  "type": "module",
6
6
  "exports": {
@@ -16,6 +16,7 @@
16
16
  "./utils/MailAdresses": "./utils/MailAdresses.js",
17
17
  "./utils/MediaQueries": "./utils/MediaQueries.js",
18
18
  "./utils/Vh100": "./utils/Vh100.js",
19
+ "./utils/images-loaded/ImagesLoaded": "./utils/images-loaded/ImagesLoaded.js",
19
20
  "./scripts/copy-files": "./scripts/copy-files.js",
20
21
  "./scripts/deploy": "./scripts/deploy.js",
21
22
  "./vite/jsonToScss": "./vite/jsonToScss.js"
@@ -0,0 +1,44 @@
1
+ import './images-loaded.scss';
2
+
3
+ /**
4
+ * Steuert das sanfte Einblenden von Bildern, die nativ (loading="lazy") geladen wurden.
5
+ * Fügt dem Bild das Attribut 'loaded="true"' hinzu, sobald der Ladevorgang erfolgreich abgeschlossen ist.
6
+ */
7
+ export class ImagesLoaded { // <-- Export hinzufügen
8
+ #selector;
9
+ #attributeName;
10
+ #attributeValue;
11
+
12
+ constructor(selector, loadedAttribute = 'data-loaded', loadedValue = 'true') {
13
+ this.#selector = selector;
14
+ this.#attributeName = loadedAttribute;
15
+ this.#attributeValue = loadedValue;
16
+
17
+ // Führt die Initialisierung erst nach dem vollständigen Laden des DOM aus.
18
+ document.addEventListener('DOMContentLoaded', this.#init.bind(this));
19
+ }
20
+
21
+ #init() {
22
+ const images = document.querySelectorAll(this.#selector);
23
+ images.forEach(img => this.#setupImage(img));
24
+ }
25
+
26
+ #setupImage(img) {
27
+ // Prüfen auf geladenen Zustand und gültige Dimensionen
28
+ if (img.complete && img.naturalHeight > 0) {
29
+ this.#signalLoaded(img);
30
+ } else {
31
+ // Ansonsten auf das erfolgreiche Lade-Event warten
32
+ img.addEventListener('load', () => this.#signalLoaded(img));
33
+
34
+ // Fehlerbehandlung (optional)
35
+ img.addEventListener('error', () => {
36
+ console.warn(`[ImagesLoaded] Ladevorgang fehlgeschlagen für: ${img.src}`);
37
+ });
38
+ }
39
+ }
40
+
41
+ #signalLoaded(img) {
42
+ img.setAttribute(this.#attributeName, this.#attributeValue);
43
+ }
44
+ }
@@ -0,0 +1,8 @@
1
+ img {
2
+ opacity: 0;
3
+
4
+ &[data-loaded="true"] {
5
+ opacity: 1;
6
+ transition: opacity 0.3s cubic-bezier(0.76, 0, 0.24, 1);
7
+ }
8
+ }