glomex-dialog
A dialog web component that allows docking a video player or putting it in a lightbox.
It allows implementing a similar feature as amp-video-docking but without using AMP.
This component only works in modern browsers that support Web Components with Shadow DOM.
For a formatted view of this document, please visit: http://unpkg.com/@glomex/glomex-dialog/index.html
Testing
Checkout this project, do npm install
, npm start
and visit http://localhost:5000.
Usage
<script type="module">
import 'http://unpkg.com/@glomex/glomex-dialog';
</script>
<style>
glomex-dialog {
--placeholder-background-color: red;
}
</style>
<glomex-dialog mode="inline">
<div slot="dock-background"></div>
<div slot="placeholder"></div>
<div slot="dialog-element">
</div>
</glomex-dialog>
Examples
Inline mode
With custom background color.
import { html, render, useRef } from 'docup'
export default () => {
const select = useRef();
const dialog = useRef();
const onButtonClick = () => {
dialog.current.setAttribute('mode', select.current.value);
};
return html`
<style>
glomex-dialog#inlineDialog {
--placeholder-background-color: red;
}
</style>
<p>
<select ref=${select}>
<option value="hidden">hidden</option>
<option value="inline" selected>inline</option>
<option value="dock">dock</option>
<option value="lightbox">lightbox</option>
<option value="custom-value">custom-value</option>
</select>
<button onClick=${onButtonClick} class="button">Switch Dialog Mode</button>
</p>
<glomex-dialog id="inlineDialog" ref=${dialog} mode="inline">
<div slot="dialog-element">
<div style="position: relative;">
<div class="placeholder-16x9"></div>
<video
class="video-element"
controls
playsinline
webkit-playsinline
preload="none"
src="https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"
poster="https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/images/BigBuckBunny.jpg">
</video>
</div>
</div>
</glomex-dialog>`
}
<style>
glomex-dialog {
--placeholder-background-color: red;
}
</style>
<glomex-dialog mode="inline">
</glomex-dialog>
Downscale the dock mode
import { html, render, useRef } from 'docup'
export default () => {
const select = useRef();
const dialog = useRef();
const onButtonClick = () => {
dialog.current.setAttribute('mode', select.current.value);
};
return html`
<p>
<select ref=${select}>
<option value="hidden">hidden</option>
<option value="inline" selected>inline</option>
<option value="dock">dock</option>
<option value="lightbox">lightbox</option>
</select>
<button onClick=${onButtonClick} class="button">Switch Dialog Mode</button>
</p>
<glomex-dialog ref=${dialog} mode="inline" dock-downscale>
<div slot="dialog-element">
<div style="position: relative;">
<div class="placeholder-16x9"></div>
<video
class="video-element"
controls
playsinline
webkit-playsinline
preload="none"
src="https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"
poster="https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/images/BigBuckBunny.jpg">
</video>
</div>
</div>
</glomex-dialog>`
}
<glomex-dialog mode="inline" dock-downscale>
</glomex-dialog>
Alternative dock target
Can render into an alternative dock target when this dock target is visible during dock transition.
Otherwise it uses the default dock target.
import { html, render, useRef } from 'docup'
const sidebar = document.querySelector('.sidebar');
const alternativeDockTarget = document.createElement('div');
alternativeDockTarget.setAttribute('id', 'alternative-dock-target');
alternativeDockTarget.style.background = '#999';
alternativeDockTarget.width = '100%';
alternativeDockTarget.pointerEvents = 'none';
alternativeDockTarget.innerHTML = '<div class="placeholder-16x9"></div>';
sidebar.appendChild(alternativeDockTarget);
export default () => {
const select = useRef();
const dialog = useRef();
const onButtonClick = () => {
dialog.current.setAttribute('mode', select.current.value);
};
return html`
<p>
<select ref=${select}>
<option value="hidden">hidden</option>
<option value="inline" selected>inline</option>
<option value="dock">dock</option>
<option value="lightbox">lightbox</option>
</select>
<button onClick=${onButtonClick} class="button">Switch Dialog Mode</button>
</p>
<glomex-dialog ref=${dialog} dock-target="#alternative-dock-target" mode="inline">
<div slot="dialog-element">
<div style="position: relative;">
<div class="placeholder-16x9"></div>
<video
class="video-element"
controls
playsinline
webkit-playsinline
preload="none"
src="https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"
poster="https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/images/BigBuckBunny.jpg">
</video>
</div>
</div>
</glomex-dialog>`
}
<glomex-dialog mode="inline" dock-target="#some-css-selector">
</glomex-dialog>
Hidden
import { html, render, useRef } from 'docup'
export default () => {
const select = useRef();
const dialog = useRef();
const onButtonClick = () => {
dialog.current.setAttribute('mode', select.current.value);
};
return html`
<p>
<select ref=${select}>
<option value="hidden" selected>hidden</option>
<option value="inline">inline</option>
<option value="dock">dock</option>
<option value="lightbox">lightbox</option>
</select>
<button onClick=${onButtonClick} class="button">Switch Dialog Mode</button>
</p>
<glomex-dialog mode="hidden" ref=${dialog}>
<div slot="dialog-element">
<div style="position: relative;">
<div class="placeholder-16x9"></div>
<video
class="video-element"
controls
playsinline
webkit-playsinline
preload="none"
src="https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"
poster="https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/images/BigBuckBunny.jpg">
</video>
</div>
</div>
</glomex-dialog>`
}
<glomex-dialog mode="hidden">
</glomex-dialog>
Using rotate-to-fullscreen
You can enable this setting, so that the dialog goes into fullscreen on mobile phones (up until 480px width) when rotated to landscape in lightbox mode.
import { html, render, useRef } from 'docup'
export default () => {
const select = useRef();
const dialog = useRef();
const onButtonClick = () => {
dialog.current.setAttribute('mode', select.current.value);
};
return html`
<p>
<select ref=${select}>
<option value="hidden">hidden</option>
<option value="inline">inline</option>
<option value="dock">dock</option>
<option value="lightbox" selected>lightbox</option>
</select>
<button onClick=${onButtonClick} class="button">Switch Dialog Mode</button>
</p>
<style>
glomex-dialog[mode="lightbox"][fullscreen] video {
height: 100vh;
min-height: 100%;
}
glomex-dialog[mode="lightbox"][fullscreen] .placeholder-16x9 {
padding-top: unset;
}
</style>
<glomex-dialog id="with-rotate-to-fullscreen" mode="inline" ref=${dialog} rotate-to-fullscreen>
<div slot="dialog-element">
<div style="position: relative;">
<div class="placeholder-16x9"></div>
<video
class="video-element"
controls
playsinline
webkit-playsinline
preload="none"
src="https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"
poster="https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/images/BigBuckBunny.jpg">
</video>
</div>
</div>
</glomex-dialog>`
}
<glomex-dialog rotate-to-fullscreen>
</glomex-dialog>
With custom placeholder
import { html, render, useRef } from 'docup'
export default () => {
const select = useRef();
const dialog = useRef();
const onButtonClick = () => {
dialog.current.setAttribute('mode', select.current.value);
};
return html`
<style>
#myPlaceholder:defined {
display: block;
width: 100%;
height: 100%;
background: green;
}
</style>
<p>
<select ref=${select}>
<option value="hidden">hidden</option>
<option value="inline" selected>inline</option>
<option value="dock">dock</option>
<option value="lightbox">lightbox</option>
</select>
<button onClick=${onButtonClick} class="button">Switch Dialog Mode</button>
</p>
<glomex-dialog id="inlineDialog" ref=${dialog} mode="inline">
<div slot="placeholder" id="myPlaceholder"></div>
<div slot="dialog-element">
<div class="placeholder-16x9"></div>
<video
class="video-element"
controls
playsinline
webkit-playsinline
preload="none"
src="https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"
poster="https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/images/BigBuckBunny.jpg">
</video>
</div>
</glomex-dialog>`
}
<style>
#myPlaceholder:defined {
display: block;
width: 100%;
height: 100%;
background: green;
}
</style>
<glomex-dialog mode="inline">
<div id="myPlaceholder" slot="placeholder"></div>
</glomex-dialog>
Provide own dock overlay
import { html, render, useRef } from 'docup'
export default () => {
const select = useRef();
const dialog = useRef();
const video = useRef();
const onButtonClick = () => {
dialog.current.setAttribute('mode', select.current.value);
};
const onPlayButtonClick = (event) => {
video.current.play();
event.preventDefault();
};
const onPauseButtonClick = (event) => {
video.current.pause();
event.preventDefault();
}
return html`
<p>
<style>
glomex-dialog .custom-overlay {
opacity: 0;
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
cursor: move;
}
glomex-dialog[mode=dock] .custom-overlay:hover {
display: block;
opacity: 1;
}
glomex-dialog .custom-overlay:hover {
background-color: rgba(200, 200, 200, 0.7);
}
glomex-dialog .controls {
display: flex;
justify-content: space-evenly;
align-items: center;
height: 100%;
}
glomex-dialog .play-button, glomex-dialog .pause-button {
color: black;
font-size: 2em;
cursor: pointer;
}
glomex-dialog .play-button:hover, glomex-dialog .pause-button:hover {
color: white;
font-size: 2em;
cursor: pointer;
}
</style>
<select ref=${select}>
<option value="hidden">hidden</option>
<option value="inline" selected>inline</option>
<option value="dock">dock</option>
<option value="lightbox">lightbox</option>
</select>
<button onClick=${onButtonClick} class="button">Switch Dialog Mode</button>
</p>
<glomex-dialog ref=${dialog} mode="inline" dock-target-inset="0px auto auto 0px">
<div slot="dock-background" class="custom-overlay">
<div class="controls">
<button class="play-button" onClick=${onPlayButtonClick}>▶</button>
<button class="pause-button" onClick=${onPauseButtonClick}>■</button>
</div>
</div>
<div slot="dialog-element">
<div style="position: relative;">
<div class="placeholder-16x9"></div>
<video
ref=${video}
class="video-element"
controls
playsinline
webkit-playsinline
preload="none"
src="https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"
poster="https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/images/BigBuckBunny.jpg">
</video>
</div>
</div>
</glomex-dialog>`
}
<style>
glomex-dialog .custom-overlay {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
cursor: move;
}
glomex-dialog .custom-overlay:hover {
background-color: rgba(200, 200, 200, 0.7);
}
</style>
<glomex-dialog mode="inline" dock-target-inset="50px 10px auto auto">
<div slot="dock-background" class="custom-overlay">
</div>
</glomex-dialog>
Custom dialog layout
import { html, render, useRef } from 'docup'
export default () => {
const select = useRef();
const dialog = useRef();
const onButtonClick = () => {
dialog.current.setAttribute('mode', select.current.value);
};
return html`
<style>
glomex-dialog .title {
display: none;
color: white;
background: red;
whitespace: no-wrap;
}
glomex-dialog[mode=dock] .title,
glomex-dialog[mode=lightbox] .title {
display: block;
font-size: 1em;
}
glomex-dialog[mode=dock] .backdrop,
glomex-dialog[mode=lightbox] .backdrop {
border: 10px solid red;
background: red;
}
</style>
<p>
<select ref=${select}>
<option value="hidden">hidden</option>
<option value="inline" selected>inline</option>
<option value="dock">dock</option>
<option value="lightbox">lightbox</option>
</select>
<button onClick=${onButtonClick} class="button">Switch Dialog Mode</button>
</p>
<glomex-dialog ref=${dialog} mode="inline" dock-aspect-ratio="16:11" dock-target-inset="auto 0px 0px auto">
<div slot="dialog-element">
<div class="backdrop">
<div class="title">Super Duper Title</div>
<div style="position: relative;">
<div class="placeholder-16x9"></div>
<video
class="video-element"
controls
playsinline
webkit-playsinline
preload="none"
src="https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"
poster="https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/images/BigBuckBunny.jpg">
</video>
</div>
</div>
</div>
</glomex-dialog>`
}
<style>
glomex-dialog[mode=dock] .backdrop,
glomex-dialog[mode=lightbox] .backdrop {
background: red;
border: 10px solid red;
}
</style>
<glomex-dialog mode="inline" dock-aspect-ratio="16:10" dock-target-inset="50px 10px auto auto">
<div class="backdrop">
<div class="title">Some Title</div>
</div>
</glomex-dialog>
With IntersectionObserver and dock-mode = sticky
This example auto docks the video element when the player gets scrolled out of view (similar to position: sticky
).
import { html, render, useRef, useEffect } from 'docup'
export default () => {
const select = useRef();
const dialog = useRef();
const dockMode = useRef();
const onButtonClick = () => {
dialog.current.setAttribute('mode', select.current.value);
};
const onDockModeChange = () => {
dialog.current.setAttribute('dock-mode', dockMode.current.value);
}
let onceVisible = false;
let currentIntersectionRatio;
useEffect(() => {
const observer = new IntersectionObserver((entries) => {
const glomexDialog = dialog.current;
currentIntersectionRatio = entries[0].intersectionRatio;
if (!onceVisible) {
onceVisible = entries[0].intersectionRatio === 1;
return;
}
const currentMode = glomexDialog.getAttribute('mode');
if (currentMode === 'lightbox' || !currentMode) {
return;
}
if (entries[0].intersectionRatio < 1 && (
currentMode !== 'dock'
)) {
glomexDialog.setAttribute('mode', 'dock');
} else if (entries[0].intersectionRatio === 1) {
glomexDialog.setAttribute('mode', 'inline');
}
}, {
threshold: [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1],
rootMargin: '-48px 0px 0px 0px'
});
if (dialog.current) {
observer.observe(dialog.current);
dialog.current.addEventListener('modechange', (evt) => {
if (evt.detail.mode === 'inline' && currentIntersectionRatio !== 1) {
onceVisible = false;
}
});
}
}, [dialog]);
return html`
<p>
<label>
Mode
<select style="margin-left:1em;" ref=${select}>
<option value="hidden">hidden</option>
<option value="inline">inline</option>
<option value="dock" selected>dock</option>
<option value="sticky">sticky</option>
<option value="lightbox">lightbox</option>
</select>
</label>
<label style="margin-left:1em;" >
Dock-Mode
<select onChange="${onDockModeChange}" style="margin-left:1em;" ref=${dockMode}>
<option value="">none</option>
<option value="sticky" selected>sticky</option>
</select>
</label>
<button onClick=${onButtonClick} class="button">Switch Dialog Mode</button>
</p>
<glomex-dialog ref=${dialog} mode="inline" dock-mode="sticky" dock-target-inset="48px 10px auto auto" dock-sticky-target-top="48">
<div slot="dialog-element">
<div style="position: relative;">
<div class="placeholder-16x9"></div>
<video
class="video-element"
controls
playsinline
webkit-playsinline
preload="none"
src="https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"
poster="https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/images/BigBuckBunny.jpg">
</video>
</div>
</div>
</glomex-dialog>`
}
<glomex-dialog
mode="inline"
dock-target-inset="50px 10px auto auto"
dock-mode="sticky"
dock-sticky-target-top="48"
>
</glomex-dialog>
API
Attributes
Attribute |
---|
aspect-ratio |
dock-aspect-ratio |
dock-target |
dock-target-inset |
mode |
Methods
Method | Type |
---|
refreshDockDialog | (): void |
Events
Event | Type |
---|
modechange | CustomEvent<{ mode: string; }> |
dockchange | CustomEvent<{ scale: number; }> |
License
Apache 2.0 License