mirror of
https://github.com/umap-project/umap.git
synced 2025-04-28 19:42:36 +02:00
194 lines
5.3 KiB
JavaScript
194 lines
5.3 KiB
JavaScript
import { translate } from '../i18n.js'
|
|
import { WithTemplate } from '../utils.js'
|
|
|
|
const TEMPLATE = `
|
|
<dialog data-ref="dialog">
|
|
<form method="dialog" data-ref="form">
|
|
<ul class="buttons">
|
|
<li><i class="icon icon-16 icon-close" data-close></i></li>
|
|
</ul>
|
|
<h3 data-ref="message" id="${Math.round(Date.now()).toString(36)}"></h3>
|
|
<fieldset data-ref="fieldset" role="document">
|
|
<div data-ref="template"></div>
|
|
</fieldset>
|
|
<menu>
|
|
<button type="button" data-ref="cancel" data-close value="cancel"></button>
|
|
<button type="submit" class="button" data-ref="accept" value="accept"></button>
|
|
</menu>
|
|
</form>
|
|
</dialog>
|
|
`
|
|
|
|
// From https://css-tricks.com/replace-javascript-dialogs-html-dialog-element/
|
|
export default class Dialog extends WithTemplate {
|
|
constructor(settings = {}) {
|
|
super()
|
|
this.settings = Object.assign(
|
|
{
|
|
accept: translate('OK'),
|
|
cancel: translate('Cancel'),
|
|
className: '',
|
|
message: '',
|
|
template: '',
|
|
},
|
|
settings
|
|
)
|
|
this.init()
|
|
}
|
|
|
|
collectFormData(formData) {
|
|
const object = {}
|
|
formData.forEach((value, key) => {
|
|
if (!Reflect.has(object, key)) {
|
|
object[key] = value
|
|
return
|
|
}
|
|
if (!Array.isArray(object[key])) {
|
|
object[key] = [object[key]]
|
|
}
|
|
object[key].push(value)
|
|
})
|
|
return object
|
|
}
|
|
|
|
getFocusable() {
|
|
return [
|
|
...this.dialog.querySelectorAll(
|
|
'button,[href],select,textarea,input:not([type="hidden"]),[tabindex]:not([tabindex="-1"])'
|
|
),
|
|
]
|
|
}
|
|
|
|
init() {
|
|
this.dialogSupported = typeof HTMLDialogElement === 'function'
|
|
this.dialog = this.loadTemplate(TEMPLATE)
|
|
this.dialog.dataset.component = this.dialogSupported ? 'dialog' : 'no-dialog'
|
|
document.body.appendChild(this.dialog)
|
|
|
|
this.focusable = []
|
|
|
|
this.dialog.setAttribute('aria-labelledby', this.elements.message.id)
|
|
this.dialog.addEventListener('click', (event) => {
|
|
if (event.target.closest('[data-close]')) {
|
|
this.close()
|
|
}
|
|
})
|
|
if (!this.dialogSupported) {
|
|
this.elements.form.addEventListener('submit', (event) => {
|
|
event.preventDefault()
|
|
this.dialog.returnValue = 'accept'
|
|
this.close()
|
|
this.dialog.returnValue = undefined
|
|
})
|
|
}
|
|
this.dialog.addEventListener('keydown', (e) => {
|
|
if (e.key === 'Enter') {
|
|
if (!this.dialogSupported) {
|
|
e.preventDefault()
|
|
this.elements.form.requestSubmit()
|
|
}
|
|
}
|
|
if (e.key === 'Escape') {
|
|
e.stopPropagation()
|
|
this.close()
|
|
}
|
|
})
|
|
}
|
|
|
|
currentZIndex() {
|
|
return Math.max(
|
|
...Array.from(document.querySelectorAll('dialog')).map((el) => {
|
|
const zIndex = window.getComputedStyle(el).getPropertyValue('z-index')
|
|
if (zIndex === 'auto') return 0
|
|
return zIndex || 0
|
|
})
|
|
)
|
|
}
|
|
|
|
open(settings = {}) {
|
|
const dialog = Object.assign({}, this.settings, settings)
|
|
this.dialog.className = 'umap-dialog window'
|
|
if (dialog.className) {
|
|
this.dialog.classList.add(...dialog.className.split(' '))
|
|
}
|
|
this.elements.accept.textContent = dialog.accept
|
|
this.elements.accept.hidden = !dialog.accept
|
|
this.elements.cancel.textContent = dialog.cancel
|
|
this.elements.cancel.hidden = !dialog.cancel
|
|
this.elements.message.textContent = dialog.message
|
|
this.elements.message.hidden = !dialog.message
|
|
this.elements.target = dialog.target || ''
|
|
this.elements.template.innerHTML = ''
|
|
if (dialog.template?.nodeType === 1) {
|
|
this.elements.template.appendChild(dialog.template)
|
|
} else {
|
|
this.elements.template.innerHTML = dialog.template || ''
|
|
}
|
|
|
|
this.focusable = this.getFocusable()
|
|
this.hasFormData = this.elements.fieldset.elements.length > 0
|
|
|
|
const currentZIndex = this.currentZIndex()
|
|
if (currentZIndex) {
|
|
this.dialog.style.zIndex = currentZIndex + 1
|
|
}
|
|
|
|
this.toggle(true)
|
|
|
|
if (this.hasFormData) this.focusable[0].focus()
|
|
else this.elements.accept.focus()
|
|
|
|
return this.waitForUser()
|
|
}
|
|
|
|
close() {
|
|
this.toggle(false)
|
|
}
|
|
|
|
toggle(open = false) {
|
|
if (this.dialogSupported) {
|
|
if (open) this.dialog.show()
|
|
else this.dialog.close()
|
|
} else {
|
|
this.dialog.hidden = !open
|
|
if (this.elements.target && !open) {
|
|
this.elements.target.focus()
|
|
}
|
|
if (!open) {
|
|
this.dialog.dispatchEvent(new CustomEvent('close'))
|
|
}
|
|
}
|
|
}
|
|
|
|
waitForUser() {
|
|
return new Promise((resolve) => {
|
|
this.dialog.addEventListener(
|
|
'close',
|
|
(event) => {
|
|
if (this.dialog.returnValue === 'accept') {
|
|
const value = this.hasFormData
|
|
? this.collectFormData(new FormData(this.elements.form))
|
|
: true
|
|
resolve(value)
|
|
}
|
|
},
|
|
{ once: true }
|
|
)
|
|
})
|
|
}
|
|
|
|
alert(config) {
|
|
return this.open(
|
|
Object.assign({}, config, { cancel: false, message, template: false })
|
|
)
|
|
}
|
|
|
|
confirm(message, config = {}) {
|
|
return this.open(Object.assign({}, config, { message, template: false }))
|
|
}
|
|
|
|
prompt(message, fallback = '', config = {}) {
|
|
const template = `<input type="text" name="prompt" value="${fallback}">`
|
|
return this.open(Object.assign({}, config, { message, template }))
|
|
}
|
|
}
|