From f293f9c929a1502575ff4935a48ab95197d3ac83 Mon Sep 17 00:00:00 2001 From: Yohan Boniface Date: Fri, 5 Jul 2024 17:35:08 +0200 Subject: [PATCH] chore: move slideshow to a module --- umap/static/umap/css/slideshow.css | 69 ++++++++++ umap/static/umap/js/modules/global.js | 2 + umap/static/umap/js/modules/slideshow.js | 137 +++++++++++++++++++ umap/static/umap/js/modules/ui/dialog.js | 14 +- umap/static/umap/js/modules/utils.js | 13 ++ umap/static/umap/js/umap.slideshow.js | 163 ----------------------- umap/static/umap/map.css | 87 +----------- umap/static/umap/vars.css | 1 + umap/templates/umap/css.html | 1 + umap/templates/umap/js.html | 1 - 10 files changed, 225 insertions(+), 263 deletions(-) create mode 100644 umap/static/umap/css/slideshow.css create mode 100644 umap/static/umap/js/modules/slideshow.js delete mode 100644 umap/static/umap/js/umap.slideshow.js diff --git a/umap/static/umap/css/slideshow.css b/umap/static/umap/css/slideshow.css new file mode 100644 index 00000000..795c77da --- /dev/null +++ b/umap/static/umap/css/slideshow.css @@ -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; +} diff --git a/umap/static/umap/js/modules/global.js b/umap/static/umap/js/modules/global.js index c4eebe03..9e71921c 100644 --- a/umap/static/umap/js/modules/global.js +++ b/umap/static/umap/js/modules/global.js @@ -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, diff --git a/umap/static/umap/js/modules/slideshow.js b/umap/static/umap/js/modules/slideshow.js new file mode 100644 index 00000000..51b4a523 --- /dev/null +++ b/umap/static/umap/js/modules/slideshow.js @@ -0,0 +1,137 @@ +import { WithTemplate } from './utils.js' +import { translate } from './i18n.js' + +const TOOLBOX_TEMPLATE = ` + +` + +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)) + } +} diff --git a/umap/static/umap/js/modules/ui/dialog.js b/umap/static/umap/js/modules/ui/dialog.js index 58343cd6..6ad1291f 100644 --- a/umap/static/umap/js/modules/ui/dialog.js +++ b/umap/static/umap/js/modules/ui/dialog.js @@ -1,4 +1,5 @@ import { translate } from '../i18n.js' +import { WithTemplate } from '../utils.js' const TEMPLATE = ` @@ -18,19 +19,6 @@ const TEMPLATE = ` ` -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 = {}) { diff --git a/umap/static/umap/js/modules/utils.js b/umap/static/umap/js/modules/utils.js index 0af6d206..c1427fd0 100644 --- a/umap/static/umap/js/modules/utils.js +++ b/umap/static/umap/js/modules/utils.js @@ -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 + } +} diff --git a/umap/static/umap/js/umap.slideshow.js b/umap/static/umap/js/umap.slideshow.js deleted file mode 100644 index 0122c0fd..00000000 --- a/umap/static/umap/js/umap.slideshow.js +++ /dev/null @@ -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 - }, -}) diff --git a/umap/static/umap/map.css b/umap/static/umap/map.css index 60a76d30..baf8fa3b 100644 --- a/umap/static/umap/map.css +++ b/umap/static/umap/map.css @@ -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); } diff --git a/umap/static/umap/vars.css b/umap/static/umap/vars.css index 6d99d17b..9e6a88b0 100644 --- a/umap/static/umap/vars.css +++ b/umap/static/umap/vars.css @@ -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; diff --git a/umap/templates/umap/css.html b/umap/templates/umap/css.html index 64a65b58..1fccc8cc 100644 --- a/umap/templates/umap/css.html +++ b/umap/templates/umap/css.html @@ -28,6 +28,7 @@ + diff --git a/umap/templates/umap/js.html b/umap/templates/umap/js.html index 8e650367..4bdaeaeb 100644 --- a/umap/templates/umap/js.html +++ b/umap/templates/umap/js.html @@ -54,7 +54,6 @@ -