mirror of
https://github.com/umap-project/umap.git
synced 2025-04-29 03:42:37 +02:00
chore: refactor web components with templates
This commit is contained in:
parent
cc773e7feb
commit
6bbdec49bf
4 changed files with 119 additions and 77 deletions
|
@ -4,7 +4,7 @@
|
||||||
@import "{% static 'umap/js/components/alerts/alert.css' %}";
|
@import "{% static 'umap/js/components/alerts/alert.css' %}";
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<umap-alert hidden>
|
<template id="umap-alert-template">
|
||||||
<div role="dialog">
|
<div role="dialog">
|
||||||
<div>
|
<div>
|
||||||
<p role="alert"></p>
|
<p role="alert"></p>
|
||||||
|
@ -13,9 +13,11 @@
|
||||||
<i class="icon icon-16 icon-close"></i>
|
<i class="icon icon-16 icon-close"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</umap-alert>
|
</template>
|
||||||
|
|
||||||
<umap-alert-creation hidden>
|
<umap-alert></umap-alert>
|
||||||
|
|
||||||
|
<template id="umap-alert-creation-template">
|
||||||
<div role="dialog">
|
<div role="dialog">
|
||||||
<div>
|
<div>
|
||||||
<p role="alert"></p>
|
<p role="alert"></p>
|
||||||
|
@ -37,15 +39,17 @@
|
||||||
<i class="icon icon-16 icon-close"></i>
|
<i class="icon icon-16 icon-close"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</umap-alert-creation>
|
</template>
|
||||||
|
|
||||||
<umap-alert-choice hidden>
|
<umap-alert-creation></umap-alert-creation>
|
||||||
|
|
||||||
|
<template id="umap-alert-choice-template">
|
||||||
<div role="dialog">
|
<div role="dialog">
|
||||||
<div>
|
<div>
|
||||||
<p role="alert"></p>
|
<p role="alert"></p>
|
||||||
<div id="choice-wrapper">
|
<div id="choice-wrapper">
|
||||||
<form>
|
<form>
|
||||||
<a href="" onclick="document.url" target="_blank">{% translate "See their edits in another tab" %}</a>
|
<a href="#" onclick="document.url" target="_blank">{% translate "See their edits in another tab" %}</a>
|
||||||
<input id="your-changes" type="submit" value="{% translate "Keep your changes and loose theirs" %}">
|
<input id="your-changes" type="submit" value="{% translate "Keep your changes and loose theirs" %}">
|
||||||
<input id="their-changes" type="submit" value="{% translate "Keep their changes and loose yours" %}">
|
<input id="their-changes" type="submit" value="{% translate "Keep their changes and loose yours" %}">
|
||||||
</form>
|
</form>
|
||||||
|
@ -55,15 +59,18 @@
|
||||||
<i class="icon icon-16 icon-close"></i>
|
<i class="icon icon-16 icon-close"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</umap-alert-choice>
|
</template>
|
||||||
|
|
||||||
|
<umap-alert-choice></umap-alert-choice>
|
||||||
|
|
||||||
<script type="module">
|
<script type="module">
|
||||||
|
import { register } from '{% static 'umap/js/components/base.js' %}'
|
||||||
import {
|
import {
|
||||||
uMapAlert,
|
uMapAlert,
|
||||||
uMapAlertCreation,
|
uMapAlertCreation,
|
||||||
uMapAlertChoice
|
uMapAlertChoice
|
||||||
} from '{% static 'umap/js/components/alerts/alert.js' %}'
|
} from '{% static 'umap/js/components/alerts/alert.js' %}'
|
||||||
customElements.define('umap-alert', uMapAlert)
|
register(uMapAlert, 'umap-alert')
|
||||||
customElements.define('umap-alert-creation', uMapAlertCreation)
|
register(uMapAlertCreation, 'umap-alert-creation')
|
||||||
customElements.define('umap-alert-choice', uMapAlertChoice)
|
register(uMapAlertChoice, 'umap-alert-choice')
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,43 +1,57 @@
|
||||||
class uMapAlert extends HTMLElement {
|
import { uMapElement } from '../base.js'
|
||||||
|
|
||||||
|
class uMapAlert extends uMapElement {
|
||||||
|
static get observedAttributes() {
|
||||||
|
return ['open']
|
||||||
|
}
|
||||||
|
|
||||||
|
attributeChangedCallback(name, oldValue, newValue) {
|
||||||
|
switch (name) {
|
||||||
|
case 'open':
|
||||||
|
newValue === 'open' ? this._show() : this._hide()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static info(message, duration = 5000) {
|
static info(message, duration = 5000) {
|
||||||
const event = new CustomEvent('umap:alert', {
|
uMapAlert.emit('alert', { message, duration })
|
||||||
bubbles: true,
|
|
||||||
cancelable: true,
|
|
||||||
detail: { message, duration },
|
|
||||||
})
|
|
||||||
document.dispatchEvent(event)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// biome-ignore lint/style/useNumberNamespace: Number.Infinity returns undefined by default
|
// biome-ignore lint/style/useNumberNamespace: Number.Infinity returns undefined by default
|
||||||
static error(message, duration = Infinity) {
|
static error(message, duration = Infinity) {
|
||||||
const event = new CustomEvent('umap:alert', {
|
uMapAlert.emit('alert', { level: 'error', message, duration })
|
||||||
bubbles: true,
|
|
||||||
cancelable: true,
|
|
||||||
detail: { level: 'error', message, duration },
|
|
||||||
})
|
|
||||||
document.dispatchEvent(event)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super()
|
super()
|
||||||
|
this._hide()
|
||||||
this.container = this.querySelector('[role="dialog"]')
|
this.container = this.querySelector('[role="dialog"]')
|
||||||
this.element = this.container.querySelector('[role="alert"]')
|
this.element = this.container.querySelector('[role="alert"]')
|
||||||
}
|
}
|
||||||
|
|
||||||
_hide() {
|
_hide() {
|
||||||
this.setAttribute('hidden', 'hidden')
|
this.setAttribute('hidden', 'hidden')
|
||||||
|
this.removeAttribute('open')
|
||||||
}
|
}
|
||||||
|
|
||||||
_show() {
|
_show() {
|
||||||
this.removeAttribute('hidden')
|
this.removeAttribute('hidden')
|
||||||
}
|
}
|
||||||
|
|
||||||
_displayAlert(detail) {
|
_handleClose() {
|
||||||
const { level = 'info', duration = 5000, message = '' } = detail
|
this.addEventListener('click', (event) => {
|
||||||
|
if (event.target.closest('[data-close]')) {
|
||||||
|
this._hide()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onAlert(event) {
|
||||||
|
const { level = 'info', duration = 5000, message = '' } = event.detail
|
||||||
this.container.dataset.level = level
|
this.container.dataset.level = level
|
||||||
this.container.dataset.duration = duration
|
this.container.dataset.duration = duration
|
||||||
this.element.textContent = message
|
this.element.textContent = message
|
||||||
this._show()
|
this.setAttribute('open', 'open')
|
||||||
if (Number.isFinite(duration)) {
|
if (Number.isFinite(duration)) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this._hide()
|
this._hide()
|
||||||
|
@ -46,14 +60,8 @@ class uMapAlert extends HTMLElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
this.addEventListener('click', (event) => {
|
this._handleClose()
|
||||||
if (event.target.closest('[data-close]')) {
|
this.listen('alert')
|
||||||
this._hide()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
document.addEventListener('umap:alert', (event) => {
|
|
||||||
this._displayAlert(event.detail)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,12 +73,7 @@ class uMapAlertCreation extends uMapAlert {
|
||||||
editLink = undefined,
|
editLink = undefined,
|
||||||
sendLink = undefined
|
sendLink = undefined
|
||||||
) {
|
) {
|
||||||
const event = new CustomEvent('umap:alert-creation', {
|
uMapAlertCreation.emit('alertCreation', { message, duration, editLink, sendLink })
|
||||||
bubbles: true,
|
|
||||||
cancelable: true,
|
|
||||||
detail: { message, duration, editLink, sendLink },
|
|
||||||
})
|
|
||||||
document.dispatchEvent(event)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
@ -79,15 +82,15 @@ class uMapAlertCreation extends uMapAlert {
|
||||||
this.formWrapper = this.container.querySelector('#form-wrapper')
|
this.formWrapper = this.container.querySelector('#form-wrapper')
|
||||||
}
|
}
|
||||||
|
|
||||||
_displayCreationAlert(detail) {
|
onAlertCreation(event) {
|
||||||
const {
|
const {
|
||||||
level = 'info',
|
level = 'info',
|
||||||
duration = 5000,
|
duration = 5000,
|
||||||
message = '',
|
message = '',
|
||||||
editLink = undefined,
|
editLink = undefined,
|
||||||
sendLink = undefined,
|
sendLink = undefined,
|
||||||
} = detail
|
} = event.detail
|
||||||
uMapAlert.prototype._displayAlert.call(this, { level, duration, message })
|
uMapAlert.prototype.onAlert.call(this, { detail: { level, duration, message } })
|
||||||
this.linkWrapper.querySelector('input[type="url"]').value = editLink
|
this.linkWrapper.querySelector('input[type="url"]').value = editLink
|
||||||
const button = this.linkWrapper.querySelector('input[type="button"]')
|
const button = this.linkWrapper.querySelector('input[type="button"]')
|
||||||
button.addEventListener('click', (event) => {
|
button.addEventListener('click', (event) => {
|
||||||
|
@ -102,21 +105,15 @@ class uMapAlertCreation extends uMapAlert {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
const formData = new FormData(form)
|
const formData = new FormData(form)
|
||||||
const server = new U.ServerRequest()
|
const server = new U.ServerRequest()
|
||||||
this._hide()
|
this.removeAttribute('open')
|
||||||
await server.post(sendLink, {}, formData)
|
await server.post(sendLink, {}, formData)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
this.addEventListener('click', (event) => {
|
this._handleClose()
|
||||||
if (event.target.closest('[data-close]')) {
|
this.listen('alertCreation')
|
||||||
this._hide()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
document.addEventListener('umap:alert-creation', (event) => {
|
|
||||||
this._displayCreationAlert(event.detail)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,12 +123,7 @@ class uMapAlertChoice extends uMapAlert {
|
||||||
// biome-ignore lint/style/useNumberNamespace: Number.Infinity returns undefined by default
|
// biome-ignore lint/style/useNumberNamespace: Number.Infinity returns undefined by default
|
||||||
duration = Infinity
|
duration = Infinity
|
||||||
) {
|
) {
|
||||||
const event = new CustomEvent('umap:alert-choice', {
|
uMapAlertChoice.emit('alertChoice', { level: 'error', message, duration })
|
||||||
bubbles: true,
|
|
||||||
cancelable: true,
|
|
||||||
detail: { level: 'error', message, duration },
|
|
||||||
})
|
|
||||||
document.dispatchEvent(event)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
@ -139,38 +131,27 @@ class uMapAlertChoice extends uMapAlert {
|
||||||
this.choiceWrapper = this.container.querySelector('#choice-wrapper')
|
this.choiceWrapper = this.container.querySelector('#choice-wrapper')
|
||||||
}
|
}
|
||||||
|
|
||||||
_displayChoiceAlert(detail) {
|
onAlertChoice(event) {
|
||||||
const { level = 'info', duration = 5000, message = '' } = detail
|
const { level = 'info', duration = 5000, message = '' } = event.detail
|
||||||
uMapAlert.prototype._displayAlert.call(this, { level, duration, message })
|
uMapAlert.prototype.onAlert.call(this, { detail: { level, duration, message } })
|
||||||
const form = this.choiceWrapper.querySelector('form')
|
const form = this.choiceWrapper.querySelector('form')
|
||||||
form.addEventListener('submit', (event) => {
|
form.addEventListener('submit', (event) => {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
switch (event.submitter.id) {
|
switch (event.submitter.id) {
|
||||||
case 'your-changes':
|
case 'your-changes':
|
||||||
document.dispatchEvent(
|
uMapAlertChoice.emit('alertChoiceOverride')
|
||||||
new CustomEvent('umap:alert-choice-override', {
|
|
||||||
bubbles: true,
|
|
||||||
cancelable: true,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
break
|
break
|
||||||
case 'their-changes':
|
case 'their-changes':
|
||||||
window.location.reload()
|
window.location.reload()
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
this._hide()
|
this.removeAttribute('open')
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
this.addEventListener('click', (event) => {
|
this._handleClose()
|
||||||
if (event.target.closest('[data-close]')) {
|
this.listen('alertChoice')
|
||||||
this._hide()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
document.addEventListener('umap:alert-choice', (event) => {
|
|
||||||
this._displayChoiceAlert(event.detail)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
54
umap/static/umap/js/components/base.js
Normal file
54
umap/static/umap/js/components/base.js
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
export class uMapElement extends HTMLElement {
|
||||||
|
static EVENT_PREFIX = 'umap'
|
||||||
|
|
||||||
|
static emit(type, detail = {}) {
|
||||||
|
const event = new CustomEvent(`${uMapElement.EVENT_PREFIX}:${type}`, {
|
||||||
|
bubbles: true,
|
||||||
|
cancelable: true,
|
||||||
|
detail: detail,
|
||||||
|
})
|
||||||
|
return document.dispatchEvent(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a clone of the content template either using the `template`
|
||||||
|
* attribute or an id mathing the name of the component:
|
||||||
|
*
|
||||||
|
* `umap-alert` component => `umap-alert-template` template id lookup.
|
||||||
|
*/
|
||||||
|
get template() {
|
||||||
|
return document
|
||||||
|
.getElementById(this.getAttribute('template') || `${this.localName}-template`)
|
||||||
|
.content.cloneNode(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super()
|
||||||
|
this.append(this.template)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Special method which allows to easily listen to events
|
||||||
|
* and have automated event to component method binding.
|
||||||
|
*
|
||||||
|
* For instance listening to `alert` will then call `onAlert`.
|
||||||
|
*/
|
||||||
|
handleEvent(event) {
|
||||||
|
event.preventDefault()
|
||||||
|
// From `umap:alert` to `alert`.
|
||||||
|
const eventName = event.type.replace(`${uMapElement.EVENT_PREFIX}:`, '')
|
||||||
|
// From `alert` event type to `onAlert` call against that class.
|
||||||
|
this[`on${eventName.charAt(0).toUpperCase() + eventName.slice(1)}`](event)
|
||||||
|
}
|
||||||
|
|
||||||
|
listen(eventName) {
|
||||||
|
// Using `this` as a listener will call `handleEvent` under the hood.
|
||||||
|
document.addEventListener(`${uMapElement.EVENT_PREFIX}:${eventName}`, this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function register(klass, name) {
|
||||||
|
if ('customElements' in globalThis && !customElements.get(name)) {
|
||||||
|
customElements.define(name, klass)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1711,7 +1711,7 @@ U.DataLayer = L.Evented.extend({
|
||||||
'This situation is tricky, you have to choose carefully which version is pertinent.'
|
'This situation is tricky, you have to choose carefully which version is pertinent.'
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
document.addEventListener('umap:alert-choice-override', async (event) => {
|
document.addEventListener('alertChoiceOverride', async (event) => {
|
||||||
await this._trySave(url, {}, formData)
|
await this._trySave(url, {}, formData)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue