mirror of
https://github.com/umap-project/umap.git
synced 2025-04-28 19:42:36 +02:00
Merge pull request #1971 from umap-project/slideshow-module
chore: move slideshow to a module
This commit is contained in:
commit
665656080c
10 changed files with 225 additions and 263 deletions
69
umap/static/umap/css/slideshow.css
Normal file
69
umap/static/umap/css/slideshow.css
Normal file
|
@ -0,0 +1,69 @@
|
|||
.umap-slideshow-toolbox {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
display: none;
|
||||
}
|
||||
.umap-slideshow-enabled .umap-slideshow-toolbox {
|
||||
display: inline-block;
|
||||
}
|
||||
.umap-slideshow-toolbox li {
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
font-size: 1.5em;
|
||||
background-color: var(--background-color);
|
||||
color: var(--text-color);
|
||||
width: calc(var(--footer-height) * 2);
|
||||
height: var(--footer-height);
|
||||
line-height: var(--footer-height);
|
||||
vertical-align: middle;
|
||||
text-align: center;
|
||||
}
|
||||
.umap-slideshow-toolbox li + li {
|
||||
border-left: 1px solid var(--color-light);
|
||||
}
|
||||
.umap-slideshow-toolbox li:hover {
|
||||
background-color: var(--color-mediumGray);
|
||||
}
|
||||
.umap-slideshow-active .umap-slideshow-toolbox .play,
|
||||
.umap-slideshow-toolbox .play {
|
||||
width: calc(var(--footer-height) * 3);
|
||||
text-align: left;
|
||||
padding-left: 20px;
|
||||
}
|
||||
.umap-slideshow-toolbox .play:after {
|
||||
content: '⏯︎';
|
||||
}
|
||||
.umap-slideshow-active .umap-slideshow-toolbox .play:after {
|
||||
content: '⏸︎';
|
||||
}
|
||||
.umap-slideshow-toolbox .stop:before {
|
||||
content: '⏹';
|
||||
}
|
||||
.umap-slideshow-toolbox .next:before {
|
||||
content: '⏵︎';
|
||||
}
|
||||
.umap-slideshow-toolbox .prev:before {
|
||||
content: '⏴︎';
|
||||
}
|
||||
.umap-slideshow-toolbox .play div {
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
margin: 0px auto;
|
||||
position: relative;
|
||||
top: 5px;
|
||||
border-left: 3px solid rgba(255,255,239,.15);
|
||||
border-right: 3px solid rgba(255,255,255,.15);
|
||||
border-bottom: 3px solid rgba(255,255,255,.15);
|
||||
border-top: 3px solid rgba(255,255,255,.8);
|
||||
border-radius:100%;
|
||||
display: inline-block;
|
||||
visibility: hidden;
|
||||
}
|
||||
@keyframes rotation {
|
||||
from {transform: rotate(0deg);}
|
||||
to {transform: rotate(359deg);}
|
||||
}
|
||||
.umap-slideshow-active .umap-slideshow-toolbox .play .spinner {
|
||||
visibility: visible;
|
||||
}
|
|
@ -13,6 +13,7 @@ import Orderable from './orderable.js'
|
|||
import { HTTPError, NOKError, Request, RequestError, ServerRequest } from './request.js'
|
||||
import Rules from './rules.js'
|
||||
import { SCHEMA } from './schema.js'
|
||||
import Slideshow from './slideshow.js'
|
||||
import { SyncEngine } from './sync/engine.js'
|
||||
import Dialog from './ui/dialog.js'
|
||||
import { EditPanel, FullPanel, Panel } from './ui/panel.js'
|
||||
|
@ -47,6 +48,7 @@ window.U = {
|
|||
Rules,
|
||||
SCHEMA,
|
||||
ServerRequest,
|
||||
Slideshow,
|
||||
SyncEngine,
|
||||
Tooltip,
|
||||
URLs,
|
||||
|
|
137
umap/static/umap/js/modules/slideshow.js
Normal file
137
umap/static/umap/js/modules/slideshow.js
Normal file
|
@ -0,0 +1,137 @@
|
|||
import { WithTemplate } from './utils.js'
|
||||
import { translate } from './i18n.js'
|
||||
|
||||
const TOOLBOX_TEMPLATE = `
|
||||
<ul class="umap-slideshow-toolbox dark">
|
||||
<li class="play" title="${translate('Start slideshow')}" data-ref="play">
|
||||
<div class="spinner" style="animation: none;"></div>
|
||||
</li>
|
||||
<li class="stop" title="${translate('Stop slideshow')}" data-ref="stop"></li>
|
||||
<li class="prev" title="${translate('Zoom to the previous')}" data-ref="previous"></li>
|
||||
<li class="next" title="${translate('Zoom to the next')}" data-ref="next"></li>
|
||||
</ul>
|
||||
`
|
||||
|
||||
export default class Slideshow extends WithTemplate {
|
||||
constructor(map, options) {
|
||||
super()
|
||||
this.map = map
|
||||
this._id = null
|
||||
this.CLASSNAME = 'umap-slideshow-active'
|
||||
this.options = Object.assign(
|
||||
{
|
||||
delay: 5000,
|
||||
autoplay: false,
|
||||
},
|
||||
options
|
||||
)
|
||||
this._current = null
|
||||
|
||||
if (this.options.autoplay) {
|
||||
this.map.onceDataLoaded(function () {
|
||||
this.play()
|
||||
}, this)
|
||||
}
|
||||
this.map.on(
|
||||
'edit:enabled',
|
||||
function () {
|
||||
this.stop()
|
||||
},
|
||||
this
|
||||
)
|
||||
}
|
||||
|
||||
set current(feature) {
|
||||
this._current = feature
|
||||
}
|
||||
|
||||
get current() {
|
||||
if (!this._current) {
|
||||
const datalayer = this.defaultDatalayer()
|
||||
if (datalayer) this._current = datalayer.getFeatureByIndex(0)
|
||||
}
|
||||
return this._current
|
||||
}
|
||||
|
||||
get next() {
|
||||
if (this._current === null) {
|
||||
return this.current
|
||||
}
|
||||
return this.current.getNext()
|
||||
}
|
||||
|
||||
defaultDatalayer() {
|
||||
return this.map.findDataLayer((d) => d.canBrowse())
|
||||
}
|
||||
|
||||
startSpinner() {
|
||||
const time = Number.parseInt(this.options.delay, 10)
|
||||
if (!time) return
|
||||
const css = `rotation ${time / 1000}s infinite linear`
|
||||
const spinner = document.querySelector('.umap-slideshow-toolbox .play .spinner')
|
||||
spinner.style.animation = css
|
||||
}
|
||||
|
||||
stopSpinner() {
|
||||
const spinner = document.querySelector('.umap-slideshow-toolbox .play .spinner')
|
||||
spinner.style.animation = 'none'
|
||||
}
|
||||
|
||||
play() {
|
||||
if (this._id) return
|
||||
if (this.map.editEnabled || !this.map.options.slideshow.active) return
|
||||
L.DomUtil.addClass(document.body, this.CLASSNAME)
|
||||
this._id = window.setInterval(L.bind(this.loop, this), this.options.delay)
|
||||
this.startSpinner()
|
||||
this.loop()
|
||||
}
|
||||
|
||||
loop() {
|
||||
this.current = this.next
|
||||
this.step()
|
||||
}
|
||||
|
||||
pause() {
|
||||
if (this._id) {
|
||||
this.stopSpinner()
|
||||
L.DomUtil.removeClass(document.body, this.CLASSNAME)
|
||||
window.clearInterval(this._id)
|
||||
this._id = null
|
||||
}
|
||||
}
|
||||
|
||||
stop() {
|
||||
this.pause()
|
||||
this.current = null
|
||||
}
|
||||
|
||||
forward() {
|
||||
this.pause()
|
||||
this.current = this.next
|
||||
this.step()
|
||||
}
|
||||
|
||||
backward() {
|
||||
this.pause()
|
||||
if (this.current) this.current = this.current.getPrevious()
|
||||
this.step()
|
||||
}
|
||||
|
||||
step() {
|
||||
if (!this.current) return this.stop()
|
||||
this.current.zoomTo({ easing: this.options.easing })
|
||||
this.current.view()
|
||||
}
|
||||
|
||||
toggle() {
|
||||
this._id ? this.pause() : this.play()
|
||||
}
|
||||
|
||||
renderToolbox(container) {
|
||||
container.appendChild(this.loadTemplate(TOOLBOX_TEMPLATE))
|
||||
this.elements.play.addEventListener('click', this.toggle.bind(this))
|
||||
this.elements.stop.addEventListener('click', this.stop.bind(this))
|
||||
this.elements.previous.addEventListener('click', this.backward.bind(this))
|
||||
this.elements.next.addEventListener('click', this.forward.bind(this))
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
import { translate } from '../i18n.js'
|
||||
import { WithTemplate } from '../utils.js'
|
||||
|
||||
const TEMPLATE = `
|
||||
<dialog data-ref="dialog">
|
||||
|
@ -18,19 +19,6 @@ const TEMPLATE = `
|
|||
</dialog>
|
||||
`
|
||||
|
||||
class WithTemplate {
|
||||
loadTemplate(html) {
|
||||
const template = document.createElement('template')
|
||||
template.innerHTML = html
|
||||
this.element = template.content.firstElementChild
|
||||
this.elements = {}
|
||||
for (const element of this.element.querySelectorAll('[data-ref]')) {
|
||||
this.elements[element.dataset.ref] = element
|
||||
}
|
||||
return this.element
|
||||
}
|
||||
}
|
||||
|
||||
// From https://css-tricks.com/replace-javascript-dialogs-html-dialog-element/
|
||||
export default class Dialog extends WithTemplate {
|
||||
constructor(settings = {}) {
|
||||
|
|
|
@ -377,3 +377,16 @@ export function toggleBadge(element, value) {
|
|||
if (value) element.dataset.badge = value === true ? ' ' : value
|
||||
else delete element.dataset.badge
|
||||
}
|
||||
|
||||
export class WithTemplate {
|
||||
loadTemplate(html) {
|
||||
const template = document.createElement('template')
|
||||
template.innerHTML = html.split('\n').map((line) => line.trim()).join('')
|
||||
this.element = template.content.firstElementChild
|
||||
this.elements = {}
|
||||
for (const element of this.element.querySelectorAll('[data-ref]')) {
|
||||
this.elements[element.dataset.ref] = element
|
||||
}
|
||||
return this.element
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,163 +0,0 @@
|
|||
U.Slideshow = L.Class.extend({
|
||||
statics: {
|
||||
CLASSNAME: 'umap-slideshow-active',
|
||||
},
|
||||
|
||||
options: {
|
||||
delay: 5000,
|
||||
autoplay: false,
|
||||
},
|
||||
|
||||
initialize: function (map, options) {
|
||||
this.setOptions(options)
|
||||
this.map = map
|
||||
this._id = null
|
||||
|
||||
// current feature
|
||||
let current = null
|
||||
try {
|
||||
Object.defineProperty(this, 'current', {
|
||||
get: function () {
|
||||
if (!current) {
|
||||
const datalayer = this.defaultDatalayer()
|
||||
if (datalayer) current = datalayer.getFeatureByIndex(0)
|
||||
}
|
||||
return current
|
||||
},
|
||||
set: (feature) => {
|
||||
current = feature
|
||||
},
|
||||
})
|
||||
} catch (e) {
|
||||
// Certainly IE8, which has a limited version of defineProperty
|
||||
}
|
||||
try {
|
||||
Object.defineProperty(this, 'next', {
|
||||
get: () => {
|
||||
if (!current) {
|
||||
return this.current
|
||||
}
|
||||
return current.getNext()
|
||||
},
|
||||
})
|
||||
} catch (e) {
|
||||
// Certainly IE8, which has a limited version of defineProperty
|
||||
}
|
||||
if (this.options.autoplay) {
|
||||
this.map.onceDataLoaded(function () {
|
||||
this.play()
|
||||
}, this)
|
||||
}
|
||||
this.map.on(
|
||||
'edit:enabled',
|
||||
function () {
|
||||
this.stop()
|
||||
},
|
||||
this
|
||||
)
|
||||
},
|
||||
|
||||
setOptions: function (options) {
|
||||
L.setOptions(this, options)
|
||||
this.timeSpinner()
|
||||
},
|
||||
|
||||
defaultDatalayer: function () {
|
||||
return this.map.findDataLayer((d) => d.canBrowse())
|
||||
},
|
||||
|
||||
timeSpinner: function () {
|
||||
const time = Number.parseInt(this.options.delay, 10)
|
||||
if (!time) return
|
||||
const css = `rotation ${time / 1000}s infinite linear`
|
||||
const spinners = document.querySelectorAll('.umap-slideshow-toolbox .play .spinner')
|
||||
for (let i = 0; i < spinners.length; i++) {
|
||||
spinners[i].style.animation = css
|
||||
spinners[i].style['-webkit-animation'] = css
|
||||
spinners[i].style['-moz-animation'] = css
|
||||
spinners[i].style['-o-animation'] = css
|
||||
}
|
||||
},
|
||||
|
||||
resetSpinners: () => {
|
||||
// Make that animnation is coordinated with user actions
|
||||
const spinners = document.querySelectorAll('.umap-slideshow-toolbox .play .spinner')
|
||||
|
||||
let el
|
||||
let newOne
|
||||
for (let i = 0; i < spinners.length; i++) {
|
||||
el = spinners[i]
|
||||
newOne = el.cloneNode(true)
|
||||
el.parentNode.replaceChild(newOne, el)
|
||||
}
|
||||
},
|
||||
|
||||
play: function () {
|
||||
if (this._id) return
|
||||
if (this.map.editEnabled || !this.map.options.slideshow.active) return
|
||||
L.DomUtil.addClass(document.body, U.Slideshow.CLASSNAME)
|
||||
this._id = window.setInterval(L.bind(this.loop, this), this.options.delay)
|
||||
this.resetSpinners()
|
||||
this.loop()
|
||||
},
|
||||
|
||||
loop: function () {
|
||||
this.current = this.next
|
||||
this.step()
|
||||
},
|
||||
|
||||
pause: function () {
|
||||
if (this._id) {
|
||||
L.DomUtil.removeClass(document.body, U.Slideshow.CLASSNAME)
|
||||
window.clearInterval(this._id)
|
||||
this._id = null
|
||||
}
|
||||
},
|
||||
|
||||
stop: function () {
|
||||
this.pause()
|
||||
this.current = null
|
||||
},
|
||||
|
||||
forward: function () {
|
||||
this.pause()
|
||||
this.current = this.next
|
||||
this.step()
|
||||
},
|
||||
|
||||
backward: function () {
|
||||
this.pause()
|
||||
if (this.current) this.current = this.current.getPrevious()
|
||||
this.step()
|
||||
},
|
||||
|
||||
step: function () {
|
||||
if (!this.current) return this.stop()
|
||||
this.current.zoomTo({ easing: this.options.easing })
|
||||
this.current.view()
|
||||
},
|
||||
|
||||
renderToolbox: function (container) {
|
||||
const box = L.DomUtil.create('ul', 'umap-slideshow-toolbox')
|
||||
const play = L.DomUtil.create('li', 'play', box)
|
||||
const stop = L.DomUtil.create('li', 'stop', box)
|
||||
const prev = L.DomUtil.create('li', 'prev', box)
|
||||
const next = L.DomUtil.create('li', 'next', box)
|
||||
L.DomUtil.create('div', 'spinner', play)
|
||||
play.title = L._('Start slideshow')
|
||||
stop.title = L._('Stop slideshow')
|
||||
next.title = L._('Zoom to the next')
|
||||
prev.title = L._('Zoom to the previous')
|
||||
const toggle = function () {
|
||||
if (this._id) this.pause()
|
||||
else this.play()
|
||||
}
|
||||
L.DomEvent.on(play, 'click', L.DomEvent.stop).on(play, 'click', toggle, this)
|
||||
L.DomEvent.on(stop, 'click', L.DomEvent.stop).on(stop, 'click', this.stop, this)
|
||||
L.DomEvent.on(prev, 'click', L.DomEvent.stop).on(prev, 'click', this.backward, this)
|
||||
L.DomEvent.on(next, 'click', L.DomEvent.stop).on(next, 'click', this.forward, this)
|
||||
container.appendChild(box)
|
||||
this.timeSpinner()
|
||||
return box
|
||||
},
|
||||
})
|
|
@ -677,91 +677,7 @@ ul.photon-autocomplete {
|
|||
.umap-help {
|
||||
font-style: italic;
|
||||
}
|
||||
.umap-slideshow-toolbox {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
display: none;
|
||||
}
|
||||
.umap-slideshow-enabled .umap-slideshow-toolbox {
|
||||
display: inline-block;
|
||||
}
|
||||
.umap-slideshow-toolbox li {
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
font-size: 1.5em;
|
||||
background-color: #464646;
|
||||
color: #fff;
|
||||
width: calc(var(--footer-height) * 2);
|
||||
height: var(--footer-height);
|
||||
line-height: var(--footer-height);
|
||||
vertical-align: middle;
|
||||
text-align: center;
|
||||
}
|
||||
.umap-slideshow-toolbox li + li {
|
||||
border-left: 1px solid #aaa;
|
||||
}
|
||||
.umap-slideshow-toolbox li:hover {
|
||||
background-color: #666;
|
||||
}
|
||||
.umap-slideshow-active .umap-slideshow-toolbox .play,
|
||||
.umap-slideshow-toolbox .play {
|
||||
width: calc(var(--footer-height) * 3);
|
||||
text-align: left;
|
||||
padding-left: 20px;
|
||||
}
|
||||
.umap-slideshow-toolbox .play:after {
|
||||
content: '⏯︎';
|
||||
}
|
||||
.umap-slideshow-active .umap-slideshow-toolbox .play:after {
|
||||
content: '⏸︎';
|
||||
}
|
||||
.umap-slideshow-toolbox .stop:before {
|
||||
content: '⏹';
|
||||
}
|
||||
.umap-slideshow-toolbox .next:before {
|
||||
content: '⏵︎';
|
||||
}
|
||||
.umap-slideshow-toolbox .prev:before {
|
||||
content: '⏴︎';
|
||||
}
|
||||
.umap-slideshow-toolbox .play div {
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
margin: 0px auto;
|
||||
position: relative;
|
||||
top: 5px;
|
||||
-webkit-animation: rotation 5s infinite linear;
|
||||
-moz-animation: rotation 5s infinite linear;
|
||||
-o-animation: rotation 5s infinite linear;
|
||||
animation: rotation 5s infinite linear;
|
||||
border-left: 3px solid rgba(255,255,239,.15);
|
||||
border-right: 3px solid rgba(255,255,255,.15);
|
||||
border-bottom: 3px solid rgba(255,255,255,.15);
|
||||
border-top: 3px solid rgba(255,255,255,.8);
|
||||
border-radius:100%;
|
||||
display: inline-block;
|
||||
visibility: hidden;
|
||||
}
|
||||
@-webkit-keyframes rotation {
|
||||
from {-webkit-transform: rotate(0deg);}
|
||||
to {-webkit-transform: rotate(359deg);}
|
||||
}
|
||||
@-moz-keyframes rotation {
|
||||
from {-moz-transform: rotate(0deg);}
|
||||
to {-moz-transform: rotate(359deg);}
|
||||
}
|
||||
@-o-keyframes rotation {
|
||||
from {-o-transform: rotate(0deg);}
|
||||
to {-o-transform: rotate(359deg);}
|
||||
}
|
||||
@keyframes rotation {
|
||||
from {transform: rotate(0deg);}
|
||||
to {transform: rotate(359deg);}
|
||||
}
|
||||
.umap-slideshow-active .umap-slideshow-toolbox .play .spinner {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.umap-datalayer-version {
|
||||
padding: 5px 0;
|
||||
border-bottom: 1px solid #202425;
|
||||
|
@ -776,7 +692,6 @@ ul.photon-autocomplete {
|
|||
margin-right: 10px;
|
||||
}
|
||||
|
||||
|
||||
.leaflet-toolbar-tip {
|
||||
background-color: var(--color-darkGray);
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
--color-waterMint: #B9F5D2;
|
||||
--color-darkBlue: #263B58;
|
||||
--color-lightGray: #ddd;
|
||||
--color-mediumGray: #3e4444;
|
||||
--color-darkGray: #323737;
|
||||
--color-light: white;
|
||||
--color-limeGreen: #b9f5d2;
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
<link rel="stylesheet" href="{% static 'umap/content.css' %}" />
|
||||
<link rel="stylesheet" href="{% static 'umap/nav.css' %}" />
|
||||
<link rel="stylesheet" href="{% static 'umap/map.css' %}" />
|
||||
<link rel="stylesheet" href="{% static 'umap/css/slideshow.css' %}" />
|
||||
<link rel="stylesheet" href="{% static 'umap/css/panel.css' %}" />
|
||||
<link rel="stylesheet" href="{% static 'umap/css/window.css' %}" />
|
||||
<link rel="stylesheet" href="{% static 'umap/css/tooltip.css' %}" />
|
||||
|
|
|
@ -54,7 +54,6 @@
|
|||
<script src="{% static 'umap/js/umap.datalayer.permissions.js' %}" defer></script>
|
||||
<script src="{% static 'umap/js/umap.layer.js' %}" defer></script>
|
||||
<script src="{% static 'umap/js/umap.controls.js' %}" defer></script>
|
||||
<script src="{% static 'umap/js/umap.slideshow.js' %}" defer></script>
|
||||
<script src="{% static 'umap/js/umap.tableeditor.js' %}" defer></script>
|
||||
<script src="{% static 'umap/js/umap.share.js' %}" defer></script>
|
||||
<script src="{% static 'umap/js/umap.js' %}" defer></script>
|
||||
|
|
Loading…
Reference in a new issue