mirror of
https://github.com/umap-project/umap.git
synced 2025-04-28 19:42:36 +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' %}";
|
||||
</style>
|
||||
|
||||
<umap-alert hidden>
|
||||
<template id="umap-alert-template">
|
||||
<div role="dialog">
|
||||
<div>
|
||||
<p role="alert"></p>
|
||||
|
@ -13,9 +13,11 @@
|
|||
<i class="icon icon-16 icon-close"></i>
|
||||
</button>
|
||||
</div>
|
||||
</umap-alert>
|
||||
</template>
|
||||
|
||||
<umap-alert-creation hidden>
|
||||
<umap-alert></umap-alert>
|
||||
|
||||
<template id="umap-alert-creation-template">
|
||||
<div role="dialog">
|
||||
<div>
|
||||
<p role="alert"></p>
|
||||
|
@ -37,15 +39,17 @@
|
|||
<i class="icon icon-16 icon-close"></i>
|
||||
</button>
|
||||
</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>
|
||||
<p role="alert"></p>
|
||||
<div id="choice-wrapper">
|
||||
<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="their-changes" type="submit" value="{% translate "Keep their changes and loose yours" %}">
|
||||
</form>
|
||||
|
@ -55,15 +59,18 @@
|
|||
<i class="icon icon-16 icon-close"></i>
|
||||
</button>
|
||||
</div>
|
||||
</umap-alert-choice>
|
||||
</template>
|
||||
|
||||
<umap-alert-choice></umap-alert-choice>
|
||||
|
||||
<script type="module">
|
||||
import { register } from '{% static 'umap/js/components/base.js' %}'
|
||||
import {
|
||||
uMapAlert,
|
||||
uMapAlertCreation,
|
||||
uMapAlertChoice
|
||||
} from '{% static 'umap/js/components/alerts/alert.js' %}'
|
||||
customElements.define('umap-alert', uMapAlert)
|
||||
customElements.define('umap-alert-creation', uMapAlertCreation)
|
||||
customElements.define('umap-alert-choice', uMapAlertChoice)
|
||||
register(uMapAlert, 'umap-alert')
|
||||
register(uMapAlertCreation, 'umap-alert-creation')
|
||||
register(uMapAlertChoice, 'umap-alert-choice')
|
||||
</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) {
|
||||
const event = new CustomEvent('umap:alert', {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
detail: { message, duration },
|
||||
})
|
||||
document.dispatchEvent(event)
|
||||
uMapAlert.emit('alert', { message, duration })
|
||||
}
|
||||
|
||||
// biome-ignore lint/style/useNumberNamespace: Number.Infinity returns undefined by default
|
||||
static error(message, duration = Infinity) {
|
||||
const event = new CustomEvent('umap:alert', {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
detail: { level: 'error', message, duration },
|
||||
})
|
||||
document.dispatchEvent(event)
|
||||
uMapAlert.emit('alert', { level: 'error', message, duration })
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
this._hide()
|
||||
this.container = this.querySelector('[role="dialog"]')
|
||||
this.element = this.container.querySelector('[role="alert"]')
|
||||
}
|
||||
|
||||
_hide() {
|
||||
this.setAttribute('hidden', 'hidden')
|
||||
this.removeAttribute('open')
|
||||
}
|
||||
|
||||
_show() {
|
||||
this.removeAttribute('hidden')
|
||||
}
|
||||
|
||||
_displayAlert(detail) {
|
||||
const { level = 'info', duration = 5000, message = '' } = detail
|
||||
_handleClose() {
|
||||
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.duration = duration
|
||||
this.element.textContent = message
|
||||
this._show()
|
||||
this.setAttribute('open', 'open')
|
||||
if (Number.isFinite(duration)) {
|
||||
setTimeout(() => {
|
||||
this._hide()
|
||||
|
@ -46,14 +60,8 @@ class uMapAlert extends HTMLElement {
|
|||
}
|
||||
|
||||
connectedCallback() {
|
||||
this.addEventListener('click', (event) => {
|
||||
if (event.target.closest('[data-close]')) {
|
||||
this._hide()
|
||||
}
|
||||
})
|
||||
document.addEventListener('umap:alert', (event) => {
|
||||
this._displayAlert(event.detail)
|
||||
})
|
||||
this._handleClose()
|
||||
this.listen('alert')
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -65,12 +73,7 @@ class uMapAlertCreation extends uMapAlert {
|
|||
editLink = undefined,
|
||||
sendLink = undefined
|
||||
) {
|
||||
const event = new CustomEvent('umap:alert-creation', {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
detail: { message, duration, editLink, sendLink },
|
||||
})
|
||||
document.dispatchEvent(event)
|
||||
uMapAlertCreation.emit('alertCreation', { message, duration, editLink, sendLink })
|
||||
}
|
||||
|
||||
constructor() {
|
||||
|
@ -79,15 +82,15 @@ class uMapAlertCreation extends uMapAlert {
|
|||
this.formWrapper = this.container.querySelector('#form-wrapper')
|
||||
}
|
||||
|
||||
_displayCreationAlert(detail) {
|
||||
onAlertCreation(event) {
|
||||
const {
|
||||
level = 'info',
|
||||
duration = 5000,
|
||||
message = '',
|
||||
editLink = undefined,
|
||||
sendLink = undefined,
|
||||
} = detail
|
||||
uMapAlert.prototype._displayAlert.call(this, { level, duration, message })
|
||||
} = event.detail
|
||||
uMapAlert.prototype.onAlert.call(this, { detail: { level, duration, message } })
|
||||
this.linkWrapper.querySelector('input[type="url"]').value = editLink
|
||||
const button = this.linkWrapper.querySelector('input[type="button"]')
|
||||
button.addEventListener('click', (event) => {
|
||||
|
@ -102,21 +105,15 @@ class uMapAlertCreation extends uMapAlert {
|
|||
event.preventDefault()
|
||||
const formData = new FormData(form)
|
||||
const server = new U.ServerRequest()
|
||||
this._hide()
|
||||
this.removeAttribute('open')
|
||||
await server.post(sendLink, {}, formData)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
this.addEventListener('click', (event) => {
|
||||
if (event.target.closest('[data-close]')) {
|
||||
this._hide()
|
||||
}
|
||||
})
|
||||
document.addEventListener('umap:alert-creation', (event) => {
|
||||
this._displayCreationAlert(event.detail)
|
||||
})
|
||||
this._handleClose()
|
||||
this.listen('alertCreation')
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -126,12 +123,7 @@ class uMapAlertChoice extends uMapAlert {
|
|||
// biome-ignore lint/style/useNumberNamespace: Number.Infinity returns undefined by default
|
||||
duration = Infinity
|
||||
) {
|
||||
const event = new CustomEvent('umap:alert-choice', {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
detail: { level: 'error', message, duration },
|
||||
})
|
||||
document.dispatchEvent(event)
|
||||
uMapAlertChoice.emit('alertChoice', { level: 'error', message, duration })
|
||||
}
|
||||
|
||||
constructor() {
|
||||
|
@ -139,38 +131,27 @@ class uMapAlertChoice extends uMapAlert {
|
|||
this.choiceWrapper = this.container.querySelector('#choice-wrapper')
|
||||
}
|
||||
|
||||
_displayChoiceAlert(detail) {
|
||||
const { level = 'info', duration = 5000, message = '' } = detail
|
||||
uMapAlert.prototype._displayAlert.call(this, { level, duration, message })
|
||||
onAlertChoice(event) {
|
||||
const { level = 'info', duration = 5000, message = '' } = event.detail
|
||||
uMapAlert.prototype.onAlert.call(this, { detail: { level, duration, message } })
|
||||
const form = this.choiceWrapper.querySelector('form')
|
||||
form.addEventListener('submit', (event) => {
|
||||
event.preventDefault()
|
||||
switch (event.submitter.id) {
|
||||
case 'your-changes':
|
||||
document.dispatchEvent(
|
||||
new CustomEvent('umap:alert-choice-override', {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
})
|
||||
)
|
||||
uMapAlertChoice.emit('alertChoiceOverride')
|
||||
break
|
||||
case 'their-changes':
|
||||
window.location.reload()
|
||||
break
|
||||
}
|
||||
this._hide()
|
||||
this.removeAttribute('open')
|
||||
})
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
this.addEventListener('click', (event) => {
|
||||
if (event.target.closest('[data-close]')) {
|
||||
this._hide()
|
||||
}
|
||||
})
|
||||
document.addEventListener('umap:alert-choice', (event) => {
|
||||
this._displayChoiceAlert(event.detail)
|
||||
})
|
||||
this._handleClose()
|
||||
this.listen('alertChoice')
|
||||
}
|
||||
}
|
||||
|
||||
|
|
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.'
|
||||
)
|
||||
)
|
||||
document.addEventListener('umap:alert-choice-override', async (event) => {
|
||||
document.addEventListener('alertChoiceOverride', async (event) => {
|
||||
await this._trySave(url, {}, formData)
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue