@glomex/glomex-dialog
Advanced tools
Comparing version 1.0.0 to 1.0.1
@@ -381,8 +381,68 @@ const NON_VISIBLE_WIDTH = window.innerWidth < 720 ? 320 : 640; | ||
const dockTarget = this.getAttribute('dock-target'); | ||
return (dockTarget && document.querySelector(dockTarget)) || this.shadowRoot.querySelector('.dock-target'); | ||
let dockTargetElement; | ||
if (dockTarget) { | ||
dockTargetElement = document.querySelector(dockTarget); | ||
const intersection = getViewportIntersection(dockTargetElement); | ||
if (intersection && intersection.width > 0 && intersection.height > 0) { | ||
return dockTargetElement; | ||
} | ||
} | ||
return this.shadowRoot.querySelector('.dock-target'); | ||
} | ||
} | ||
/* | ||
* Viewport intersection logic partially copied from | ||
* https://github.com/ampproject/amphtml/blob/bf50181843d8520cd017f6fab94740c6727416a5/extensions/amp-video-docking/0.1/amp-video-docking.js | ||
*/ | ||
function getViewportIntersection(elem) { | ||
const viewportRect = { | ||
width: window.innerWidth, | ||
height: window.innerHeight, | ||
left: 0, | ||
top: 0 | ||
}; | ||
const rect = elem.getBoundingClientRect(); | ||
return rectIntersection(viewportRect, rect); | ||
} | ||
function layoutRectLtwh(left, top, width, height) { | ||
return { | ||
left, | ||
top, | ||
width, | ||
height, | ||
bottom: top + height, | ||
right: left + width, | ||
x: left, | ||
y: top, | ||
}; | ||
} | ||
function rectIntersection() { | ||
let x0 = -Infinity; | ||
let x1 = Infinity; | ||
let y0 = -Infinity; | ||
let y1 = Infinity; | ||
for (let i = 0; i < arguments.length; i++) { | ||
const current = arguments[i]; | ||
if (!current) { | ||
continue; | ||
} | ||
x0 = Math.max(x0, current.left); | ||
x1 = Math.min(x1, current.left + current.width); | ||
y0 = Math.max(y0, current.top); | ||
y1 = Math.min(y1, current.top + current.height); | ||
if (x1 < x0 || y1 < y0) { | ||
return null; | ||
} | ||
} | ||
if (x1 == Infinity) { | ||
return null; | ||
} | ||
return layoutRectLtwh(x0, y0, x1 - x0, y1 - y0); | ||
} | ||
if (!window.customElements.get('glomex-dialog')) { | ||
window.customElements.define('glomex-dialog', GlomexDialogElement); | ||
} |
{ | ||
"name": "@glomex/glomex-dialog", | ||
"version": "1.0.0", | ||
"version": "1.0.1", | ||
"description": "A dialog web component that allows docking a video player or putting it in a lightbox", | ||
@@ -5,0 +5,0 @@ "type": "module", |
293
README.md
# glomex-dialog | ||
A dialog web component that allows docking a video player or putting it in a lightbox | ||
See an example here: http://unpkg.com/@glomex/glomex-dialog/example.html | ||
A dialog web component that allows docking a video player or putting it in a lightbox. | ||
It allows implementing a similar feature as https://amp.dev/documentation/examples/multimedia-animations/advanced_video_docking/ but without AMP. | ||
See an example here: http://unpkg.com/@glomex/glomex-dialog/index.html | ||
## Usage | ||
@@ -12,6 +14,13 @@ | ||
</script> | ||
<!-- adjusting mode to "dock" or "lightbox" --> | ||
<glomex-dialog id="inlinePlayer" mode="inline" dock-target-inset="0px 10px auto auto"> | ||
<!-- | ||
mode: "" | "inline" | "dock" | "lightbox" | ||
--> | ||
<glomex-dialog mode="inline"> | ||
<!-- | ||
"dialog-overlay" is optional: | ||
enables drag'n'drop feature when defined | ||
--> | ||
<div slot="dialog-overlay"></div> | ||
<div slot="dialog-element"> | ||
<a-video-player></a-video-player> | ||
<!-- Your HTML that should be docked / put into lightbox --> | ||
</div> | ||
@@ -21,2 +30,276 @@ </glomex-dialog> | ||
## Examples | ||
### Inline mode | ||
```js preact | ||
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</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"> | ||
<div slot="dialog-element"> | ||
<div style="position: relative;"> | ||
<div class="placeholder-16x9"></div> | ||
<video | ||
class="video-element" | ||
controls | ||
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>` | ||
} | ||
``` | ||
### 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. | ||
```js preact | ||
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</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 | ||
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>` | ||
} | ||
``` | ||
~~~html | ||
<glomex-dialog mode="inline" dock-target="#some-css-selector"> | ||
<!-- ... --> | ||
</glomex-dialog> | ||
~~~ | ||
### Hidden | ||
```js preact | ||
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="" 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 ref=${dialog}> | ||
<div slot="dialog-element"> | ||
<div style="position: relative;"> | ||
<div class="placeholder-16x9"></div> | ||
<video | ||
class="video-element" | ||
controls | ||
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>` | ||
} | ||
``` | ||
~~~html | ||
<!-- without a defined mode attribute --> | ||
<glomex-dialog> | ||
<!-- ... --> | ||
</glomex-dialog> | ||
~~~ | ||
### With IntersectionObserver and custom position | ||
This example auto docks the video element when the player gets scrolled out of view. | ||
```js preact | ||
import { html, render, useRef, useEffect } from 'docup' | ||
export default () => { | ||
const select = useRef(); | ||
const dialog = useRef(); | ||
const onButtonClick = () => { | ||
dialog.current.setAttribute('mode', select.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 && glomexDialog.getAttribute('mode') !== '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] | ||
}); | ||
if (dialog.current) { | ||
observer.observe(dialog.current); | ||
dialog.current.addEventListener('toggledialog', (evt) => { | ||
if (evt.detail.mode === 'inline' && currentIntersectionRatio !== 1) { | ||
onceVisible = false; | ||
} | ||
}); | ||
} | ||
}, [dialog]); | ||
return html` | ||
<p> | ||
<select ref=${select}> | ||
<option value="" 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 ref=${dialog} mode="inline" dock-target-inset="50px 10px auto auto"> | ||
<div slot="dialog-element"> | ||
<div style="position: relative;"> | ||
<div class="placeholder-16x9"></div> | ||
<video | ||
class="video-element" | ||
controls | ||
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>` | ||
} | ||
``` | ||
~~~html | ||
<!-- The intersection-observer-code is custom in the above example --> | ||
<glomex-dialog mode="inline" dock-target-inset="50px 10px auto auto"> | ||
<!-- ... --> | ||
</glomex-dialog> | ||
~~~ | ||
### Allow drag'n'drop | ||
```js preact | ||
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</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"> | ||
<div slot="dialog-overlay"></div> | ||
<div slot="dialog-element"> | ||
<div style="position: relative;"> | ||
<div class="placeholder-16x9"></div> | ||
<video | ||
class="video-element" | ||
controls | ||
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>` | ||
} | ||
``` | ||
~~~html | ||
<glomex-dialog mode="inline" dock-target-inset="50px 10px auto auto"> | ||
<!-- when this is defined it automatically makes the element draggable --> | ||
<!-- allows to place custom elements in the dialog-overlay --> | ||
<div slot="dialog-overlay"></div> | ||
<!-- ... --> | ||
</glomex-dialog> | ||
~~~ | ||
## API | ||
@@ -23,0 +306,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
8
469
337
62420