vidply 1.0.34 → 1.0.35
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/dist/dev/{vidply.SoundCloudRenderer-RIA3QKP3.js → vidply.SoundCloudRenderer-HCMKXHSX.js} +1 -3
- package/dist/dev/vidply.SoundCloudRenderer-HCMKXHSX.js.map +7 -0
- package/dist/dev/{vidply.TranscriptManager-T3BVTZHZ.js → vidply.TranscriptManager-EIIN5YOF.js} +2 -2
- package/dist/dev/{vidply.VimeoRenderer-DY2FG7LZ.js → vidply.VimeoRenderer-SLEBCZTT.js} +1 -2
- package/dist/dev/vidply.VimeoRenderer-SLEBCZTT.js.map +7 -0
- package/dist/dev/{vidply.YouTubeRenderer-EVXXE34A.js → vidply.YouTubeRenderer-E6F4UGVF.js} +1 -2
- package/dist/dev/vidply.YouTubeRenderer-E6F4UGVF.js.map +7 -0
- package/dist/dev/{vidply.chunk-74NJTDQI.js → vidply.chunk-AXXU22HR.js} +87 -10
- package/dist/dev/{vidply.chunk-74NJTDQI.js.map → vidply.chunk-AXXU22HR.js.map} +2 -2
- package/dist/dev/vidply.esm.js +32 -50
- package/dist/dev/vidply.esm.js.map +2 -2
- package/dist/legacy/vidply.js +119 -58
- package/dist/legacy/vidply.js.map +3 -3
- package/dist/legacy/vidply.min.js +1 -1
- package/dist/legacy/vidply.min.meta.json +18 -18
- package/dist/prod/vidply.SoundCloudRenderer-D2FNOEG6.min.js +6 -0
- package/dist/prod/{vidply.TranscriptManager-GPAOXEK4.min.js → vidply.TranscriptManager-VXCTCJ7X.min.js} +1 -1
- package/dist/prod/vidply.VimeoRenderer-QELFZVDU.min.js +6 -0
- package/dist/prod/vidply.YouTubeRenderer-ZL6YUHTF.min.js +6 -0
- package/dist/prod/{vidply.chunk-OM7DNW5P.min.js → vidply.chunk-Z6BHMOGK.min.js} +1 -1
- package/dist/prod/vidply.esm.min.js +3 -3
- package/dist/vidply.css +218 -108
- package/dist/vidply.esm.min.meta.json +33 -33
- package/dist/vidply.min.css +1 -1
- package/package.json +3 -3
- package/src/controls/ControlBar.js +3 -0
- package/src/controls/KeyboardManager.js +19 -0
- package/src/core/Player.js +1 -64
- package/src/core/SignLanguageManager.js +18 -4
- package/src/index.js +3 -1
- package/src/renderers/SoundCloudRenderer.js +0 -2
- package/src/renderers/VimeoRenderer.js +0 -1
- package/src/renderers/YouTubeRenderer.js +0 -1
- package/src/styles/vidply.css +218 -108
- package/src/utils/DraggableResizable.js +123 -12
- package/dist/dev/vidply.SoundCloudRenderer-RIA3QKP3.js.map +0 -7
- package/dist/dev/vidply.VimeoRenderer-DY2FG7LZ.js.map +0 -7
- package/dist/dev/vidply.YouTubeRenderer-EVXXE34A.js.map +0 -7
- package/dist/prod/vidply.SoundCloudRenderer-BFV5SSIU.min.js +0 -6
- package/dist/prod/vidply.VimeoRenderer-UQWHQ4LC.min.js +0 -6
- package/dist/prod/vidply.YouTubeRenderer-K7A57ICA.min.js +0 -6
- /package/dist/dev/{vidply.TranscriptManager-T3BVTZHZ.js.map → vidply.TranscriptManager-EIIN5YOF.js.map} +0 -0
|
@@ -145,6 +145,9 @@ export class ControlBar {
|
|
|
145
145
|
// Ensure menu doesn't go off top or bottom
|
|
146
146
|
if (menuRect.top < 10) {
|
|
147
147
|
menu.style.top = '10px';
|
|
148
|
+
// Important: clear bottom constraint, otherwise top+bottom will squeeze the menu height
|
|
149
|
+
// into a tiny strip on some mobile layouts.
|
|
150
|
+
menu.style.bottom = 'auto';
|
|
148
151
|
}
|
|
149
152
|
|
|
150
153
|
if (menuRect.bottom > viewportHeight - 10) {
|
|
@@ -46,6 +46,25 @@ export class KeyboardManager {
|
|
|
46
46
|
if (playlistButton) {
|
|
47
47
|
return; // Let the playlist handle keyboard events
|
|
48
48
|
}
|
|
49
|
+
|
|
50
|
+
// Don't steal arrow keys when the sign-language overlay is in keyboard drag/resize mode.
|
|
51
|
+
// DraggableResizable listens on the overlay itself, but we run in CAPTURE phase, so we must opt out here.
|
|
52
|
+
const signWrapper = activeElement.closest('.vidply-sign-language-wrapper');
|
|
53
|
+
if (signWrapper) {
|
|
54
|
+
const draggable = this.player.signLanguageManager?.draggable;
|
|
55
|
+
if (draggable?.keyboardDragMode || draggable?.keyboardResizeMode) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Same idea for the transcript floating window (it also uses DraggableResizable).
|
|
61
|
+
const transcriptWindow = activeElement.closest('.vidply-transcript-window');
|
|
62
|
+
if (transcriptWindow) {
|
|
63
|
+
const draggable = this.player.transcriptManager?.draggableResizable;
|
|
64
|
+
if (draggable?.keyboardDragMode || draggable?.keyboardResizeMode) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
49
68
|
}
|
|
50
69
|
|
|
51
70
|
const key = e.key;
|
package/src/core/Player.js
CHANGED
|
@@ -588,6 +588,7 @@ export class Player extends EventEmitter {
|
|
|
588
588
|
this.container.classList.add(`${this.options.classPrefix}-responsive`);
|
|
589
589
|
}
|
|
590
590
|
|
|
591
|
+
|
|
591
592
|
// Create video wrapper (for proper positioning of controls)
|
|
592
593
|
this.videoWrapper = DOMUtils.createElement('div', {
|
|
593
594
|
className: `${this.options.classPrefix}-video-wrapper`
|
|
@@ -639,35 +640,10 @@ export class Player extends EventEmitter {
|
|
|
639
640
|
: this.options.height;
|
|
640
641
|
}
|
|
641
642
|
|
|
642
|
-
// If no explicit height is set, ensure video players still have a stable layout box
|
|
643
|
-
// even before any media is loaded (important for deferLoad + playlists).
|
|
644
|
-
// We use the element's width/height attributes (e.g. from TYPO3) as aspect ratio.
|
|
645
|
-
if (this.element.tagName === 'VIDEO' && !this.options.height) {
|
|
646
|
-
const wAttr = parseInt(this.element.getAttribute('width') || '', 10);
|
|
647
|
-
const hAttr = parseInt(this.element.getAttribute('height') || '', 10);
|
|
648
|
-
if (Number.isFinite(wAttr) && Number.isFinite(hAttr) && wAttr > 0 && hAttr > 0) {
|
|
649
|
-
// Only set if not already defined by CSS/inline style
|
|
650
|
-
if (!this.container.style.aspectRatio) {
|
|
651
|
-
this.container.style.aspectRatio = `${wAttr} / ${hAttr}`;
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
// The actual visual box is the videoWrapper (the video element is 100% height).
|
|
655
|
-
// Give the wrapper the same aspect ratio so posters render correctly before metadata is loaded.
|
|
656
|
-
if (this.videoWrapper && !this.videoWrapper.style.aspectRatio) {
|
|
657
|
-
this.videoWrapper.style.aspectRatio = `${wAttr} / ${hAttr}`;
|
|
658
|
-
// Override default CSS height:100% (which depends on parent having a height)
|
|
659
|
-
this.videoWrapper.style.height = 'auto';
|
|
660
|
-
}
|
|
661
|
-
}
|
|
662
|
-
}
|
|
663
|
-
|
|
664
643
|
// Set poster (convert relative paths to absolute URLs)
|
|
665
644
|
if (this.options.poster && this.element.tagName === 'VIDEO') {
|
|
666
645
|
const resolvedPoster = this.resolvePosterPath(this.options.poster);
|
|
667
646
|
this.element.poster = resolvedPoster;
|
|
668
|
-
// If we intentionally have no media loaded yet (e.g. deferLoad/playlist),
|
|
669
|
-
// use poster aspect ratio to size the wrapper so the poster isn't stretched.
|
|
670
|
-
this.applyPosterAspectRatio(resolvedPoster);
|
|
671
647
|
}
|
|
672
648
|
|
|
673
649
|
// Create centered play button overlay (only for video)
|
|
@@ -711,45 +687,6 @@ export class Player extends EventEmitter {
|
|
|
711
687
|
}, { once: true });
|
|
712
688
|
}
|
|
713
689
|
|
|
714
|
-
/**
|
|
715
|
-
* Apply aspect ratio to the video wrapper based on the poster's intrinsic size.
|
|
716
|
-
* This helps render correct poster sizing before media metadata is available.
|
|
717
|
-
*/
|
|
718
|
-
applyPosterAspectRatio(posterUrl) {
|
|
719
|
-
try {
|
|
720
|
-
if (!posterUrl) return;
|
|
721
|
-
if (this.element.tagName !== 'VIDEO') return;
|
|
722
|
-
if (!this.videoWrapper) return;
|
|
723
|
-
|
|
724
|
-
// If user explicitly configured dimensions, don't override.
|
|
725
|
-
if (this.options.width || this.options.height) return;
|
|
726
|
-
|
|
727
|
-
// Avoid repeated work
|
|
728
|
-
if (this._posterAspectAppliedFor === posterUrl) return;
|
|
729
|
-
this._posterAspectAppliedFor = posterUrl;
|
|
730
|
-
|
|
731
|
-
const img = new Image();
|
|
732
|
-
img.decoding = 'async';
|
|
733
|
-
img.onload = () => {
|
|
734
|
-
const w = img.naturalWidth;
|
|
735
|
-
const h = img.naturalHeight;
|
|
736
|
-
if (!w || !h) return;
|
|
737
|
-
|
|
738
|
-
// Apply to wrapper (the actual layout box)
|
|
739
|
-
this.videoWrapper.style.aspectRatio = `${w} / ${h}`;
|
|
740
|
-
this.videoWrapper.style.height = 'auto';
|
|
741
|
-
|
|
742
|
-
// Also apply to container if not explicitly set
|
|
743
|
-
if (this.container && !this.container.style.aspectRatio) {
|
|
744
|
-
this.container.style.aspectRatio = `${w} / ${h}`;
|
|
745
|
-
}
|
|
746
|
-
};
|
|
747
|
-
img.src = posterUrl;
|
|
748
|
-
} catch (e) {
|
|
749
|
-
// ignore
|
|
750
|
-
}
|
|
751
|
-
}
|
|
752
|
-
|
|
753
690
|
createPlayButtonOverlay() {
|
|
754
691
|
// Create complete SVG play button from Icons.js
|
|
755
692
|
this.playButtonOverlay = createPlayOverlay();
|
|
@@ -459,8 +459,11 @@ export class SignLanguageManager {
|
|
|
459
459
|
_setupInteraction() {
|
|
460
460
|
const isMobile = window.innerWidth < 768;
|
|
461
461
|
const isFullscreen = this.player.state.fullscreen;
|
|
462
|
-
|
|
463
|
-
|
|
462
|
+
|
|
463
|
+
// Historically, drag/resize was disabled on mobile unless fullscreen to avoid scroll conflicts.
|
|
464
|
+
// Now that we support touch/pointer dragging with proper `touch-action` handling, enable it
|
|
465
|
+
// by default on iOS/Android as well. Allow opting out via option.
|
|
466
|
+
if (isMobile && !isFullscreen && this.player?.options?.signLanguageDragOnMobile === false) {
|
|
464
467
|
if (this.draggable) {
|
|
465
468
|
this.draggable.destroy();
|
|
466
469
|
this.draggable = null;
|
|
@@ -473,7 +476,9 @@ export class SignLanguageManager {
|
|
|
473
476
|
const classPrefix = this.player.options.classPrefix;
|
|
474
477
|
|
|
475
478
|
this.draggable = new DraggableResizable(this.wrapper, {
|
|
476
|
-
|
|
479
|
+
// Allow dragging from anywhere on the sign-language window (better for touch).
|
|
480
|
+
// We still block dragging when interacting with controls via `onDragStart` below.
|
|
481
|
+
dragHandle: this.wrapper,
|
|
477
482
|
resizeHandles: this.resizeHandles,
|
|
478
483
|
constrainToViewport: true,
|
|
479
484
|
maintainAspectRatio: true,
|
|
@@ -761,9 +766,18 @@ export class SignLanguageManager {
|
|
|
761
766
|
hasTextClass: true,
|
|
762
767
|
onClick: () => {
|
|
763
768
|
this.toggleKeyboardDragMode();
|
|
764
|
-
|
|
769
|
+
// Keep focus off the settings button so arrow keys go to the draggable overlay.
|
|
770
|
+
this.hideSettingsMenu({ focusButton: false });
|
|
771
|
+
// If we just enabled keyboard drag mode, focus the overlay.
|
|
772
|
+
if (this.draggable?.keyboardDragMode) {
|
|
773
|
+
setTimeout(() => {
|
|
774
|
+
this.wrapper?.focus?.({ preventScroll: true });
|
|
775
|
+
}, 20);
|
|
776
|
+
}
|
|
765
777
|
}
|
|
766
778
|
});
|
|
779
|
+
// Allow CSS to hide this option on touch/mobile where dragging is always enabled
|
|
780
|
+
dragOption.setAttribute('data-setting', 'keyboard-drag');
|
|
767
781
|
dragOption.setAttribute('role', 'switch');
|
|
768
782
|
dragOption.setAttribute('aria-checked', 'false');
|
|
769
783
|
this._removeTooltipFromMenuItem(dragOption);
|
package/src/index.js
CHANGED
|
@@ -57,7 +57,9 @@ function parseDataAttributes(dataset) {
|
|
|
57
57
|
'keyboard': 'keyboard',
|
|
58
58
|
'responsive': 'responsive',
|
|
59
59
|
'pipButton': 'pipButton',
|
|
60
|
-
'fullscreenButton': 'fullscreenButton'
|
|
60
|
+
'fullscreenButton': 'fullscreenButton',
|
|
61
|
+
|
|
62
|
+
// Layout
|
|
61
63
|
};
|
|
62
64
|
|
|
63
65
|
// Parse each data attribute
|
|
@@ -125,10 +125,8 @@ export class SoundCloudRenderer {
|
|
|
125
125
|
// Use different aspect ratio for playlists vs single tracks
|
|
126
126
|
// Playlists need more height to show the track list
|
|
127
127
|
if (this.isPlaylist()) {
|
|
128
|
-
this.iframe.style.aspectRatio = '16 / 9'; // More height for playlist
|
|
129
128
|
this.iframe.classList.add('vidply-soundcloud-iframe', 'vidply-soundcloud-playlist');
|
|
130
129
|
} else {
|
|
131
|
-
this.iframe.style.aspectRatio = '16 / 3'; // Banner-like for single track
|
|
132
130
|
this.iframe.classList.add('vidply-soundcloud-iframe');
|
|
133
131
|
}
|
|
134
132
|
this.iframe.style.maxHeight = '100%';
|
|
@@ -70,7 +70,6 @@ export class VimeoRenderer {
|
|
|
70
70
|
this.iframe = document.createElement('div');
|
|
71
71
|
this.iframe.id = `vimeo-player-${Math.random().toString(36).substr(2, 9)}`;
|
|
72
72
|
this.iframe.style.width = '100%';
|
|
73
|
-
this.iframe.style.aspectRatio = '16 / 9';
|
|
74
73
|
this.iframe.style.maxHeight = '100%';
|
|
75
74
|
|
|
76
75
|
this.player.element.parentNode.insertBefore(this.iframe, this.player.element);
|
|
@@ -86,7 +86,6 @@ export class YouTubeRenderer {
|
|
|
86
86
|
this.iframe = document.createElement('div');
|
|
87
87
|
this.iframe.id = `youtube-player-${Math.random().toString(36).substr(2, 9)}`;
|
|
88
88
|
this.iframe.style.width = '100%';
|
|
89
|
-
this.iframe.style.aspectRatio = '16 / 9';
|
|
90
89
|
this.iframe.style.maxHeight = '100%';
|
|
91
90
|
|
|
92
91
|
this.player.element.parentNode.insertBefore(this.iframe, this.player.element);
|