mirror of
https://github.com/umap-project/umap.git
synced 2025-04-29 11:52:38 +02:00
wip(forms): refactor forms templating
This commit is contained in:
parent
e0fadea749
commit
176b8bdbcc
8 changed files with 242 additions and 201 deletions
|
@ -1,3 +1,4 @@
|
||||||
|
.umap-form-inline .formbox,
|
||||||
.umap-form-inline {
|
.umap-form-inline {
|
||||||
display: inline;
|
display: inline;
|
||||||
}
|
}
|
||||||
|
@ -559,7 +560,6 @@ i.info {
|
||||||
clear: both;
|
clear: both;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
display: none;
|
|
||||||
}
|
}
|
||||||
.umap-color-picker span {
|
.umap-color-picker span {
|
||||||
width: 20px;
|
width: 20px;
|
||||||
|
|
|
@ -655,7 +655,7 @@ export class DataLayer extends ServerStored {
|
||||||
{
|
{
|
||||||
label: translate('Data is browsable'),
|
label: translate('Data is browsable'),
|
||||||
handler: 'Switch',
|
handler: 'Switch',
|
||||||
helpEntries: 'browsable',
|
helpEntries: ['browsable'],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import getClass from './fields.js'
|
import getClass from './fields.js'
|
||||||
import * as Utils from '../utils.js'
|
import * as Utils from '../utils.js'
|
||||||
import { SCHEMA } from '../schema.js'
|
import { SCHEMA } from '../schema.js'
|
||||||
|
import { translate } from '../i18n.js'
|
||||||
|
|
||||||
export class Form {
|
export class Form {
|
||||||
constructor(obj, fields, properties) {
|
constructor(obj, fields, properties) {
|
||||||
|
@ -35,9 +36,8 @@ export class Form {
|
||||||
}
|
}
|
||||||
|
|
||||||
buildField(field) {
|
buildField(field) {
|
||||||
field.buildLabel()
|
field.buildTemplate()
|
||||||
field.build()
|
field.build()
|
||||||
field.buildHelpText()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
makeField(field) {
|
makeField(field) {
|
||||||
|
@ -115,6 +115,14 @@ export class Form {
|
||||||
}
|
}
|
||||||
|
|
||||||
finish() {}
|
finish() {}
|
||||||
|
|
||||||
|
getTemplate(helper) {
|
||||||
|
return `
|
||||||
|
<div class="formbox" data-ref=container>
|
||||||
|
${helper.getTemplate()}
|
||||||
|
<small class="help-text" data-ref=helpText></small>
|
||||||
|
</div>`
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class MutatingForm extends Form {
|
export class MutatingForm extends Form {
|
||||||
|
@ -190,6 +198,40 @@ export class MutatingForm extends Form {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getTemplate(helper) {
|
||||||
|
let template
|
||||||
|
if (helper.properties.inheritable) {
|
||||||
|
const extraClassName = helper.get(true) === undefined ? ' undefined' : ''
|
||||||
|
template = `
|
||||||
|
<div class="umap-field-${helper.name} formbox inheritable${extraClassName}">
|
||||||
|
<div class="header" data-ref=header>
|
||||||
|
<a href="#" class="button undefine" data-ref=undefine>${translate('clear')}</a>
|
||||||
|
<a href="#" class="button define" data-ref=define>${translate('define')}</a>
|
||||||
|
<span class="quick-actions show-on-defined" data-ref=actions></span>
|
||||||
|
${helper.getLabelTemplate()}
|
||||||
|
</div>
|
||||||
|
<div class="show-on-defined" data-ref=container>
|
||||||
|
${helper.getTemplate()}
|
||||||
|
<small class="help-text" data-ref=helpText></small>
|
||||||
|
</div>
|
||||||
|
</div>`
|
||||||
|
} else {
|
||||||
|
template = `
|
||||||
|
<div class="formbox umap-field-${helper.name}" data-ref=container>
|
||||||
|
${helper.getLabelTemplate()}
|
||||||
|
${helper.getTemplate()}
|
||||||
|
<small class="help-text" data-ref=helpText></small>
|
||||||
|
</div>`
|
||||||
|
}
|
||||||
|
return template
|
||||||
|
}
|
||||||
|
|
||||||
|
build() {
|
||||||
|
super.build()
|
||||||
|
this._umap.help.parse(this.form)
|
||||||
|
return this.form
|
||||||
|
}
|
||||||
|
|
||||||
finish(helper) {
|
finish(helper) {
|
||||||
helper.input?.blur()
|
helper.input?.blur()
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,53 +25,55 @@ class BaseElement {
|
||||||
this.setProperties(properties)
|
this.setProperties(properties)
|
||||||
this.fieldEls = this.field.split('.')
|
this.fieldEls = this.field.split('.')
|
||||||
this.name = this.builder.getName(field)
|
this.name = this.builder.getName(field)
|
||||||
this.parentNode = this.getParentNode()
|
this.id = `${this.builder.properties.id || Date.now()}.${this.name}`
|
||||||
|
}
|
||||||
|
|
||||||
|
getDefaultProperties() {
|
||||||
|
return {}
|
||||||
}
|
}
|
||||||
|
|
||||||
setProperties(properties) {
|
setProperties(properties) {
|
||||||
this.properties = Object.assign({}, this.properties, properties)
|
this.properties = Object.assign(
|
||||||
|
this.getDefaultProperties(),
|
||||||
|
this.properties,
|
||||||
|
properties
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
onDefine() {}
|
onDefine() {}
|
||||||
|
|
||||||
getParentNode() {
|
buildTemplate() {
|
||||||
const classNames = ['formbox']
|
const template = this.builder.getTemplate(this)
|
||||||
if (this.properties.inheritable) {
|
const [root, elements] = Utils.loadTemplateWithRefs(template)
|
||||||
classNames.push('inheritable')
|
this.root = root
|
||||||
if (this.get(true) === undefined) classNames.push('undefined')
|
this.elements = elements
|
||||||
|
this.container = elements.container
|
||||||
|
this.form.appendChild(this.root)
|
||||||
}
|
}
|
||||||
classNames.push(`umap-field-${this.name}`)
|
|
||||||
const [wrapper, { header, define, undefine, quickContainer, container }] =
|
getTemplate() {
|
||||||
Utils.loadTemplateWithRefs(`
|
return ''
|
||||||
<div>
|
}
|
||||||
<div class="header" data-ref=header>
|
|
||||||
<a href="#" class="button undefine" data-ref=undefine>${translate('clear')}</a>
|
build() {
|
||||||
<a href="#" class="button define" data-ref=define>${translate('define')}</a>
|
if (this.properties.helpText) {
|
||||||
<span class="quick-actions show-on-defined" data-ref=quickContainer></span>
|
this.elements.helpText.textContent = this.properties.helpText
|
||||||
</div>
|
} else {
|
||||||
<div class="show-on-defined" data-ref=container></div>
|
this.elements.helpText.hidden = true
|
||||||
</div>`)
|
}
|
||||||
this.wrapper = wrapper
|
|
||||||
this.wrapper.classList.add(...classNames)
|
if (this.elements.define) {
|
||||||
this.header = header
|
this.elements.define.addEventListener('click', (event) => {
|
||||||
this.form.appendChild(this.wrapper)
|
|
||||||
if (this.properties.inheritable) {
|
|
||||||
define.addEventListener('click', (event) => {
|
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
event.stopPropagation()
|
event.stopPropagation()
|
||||||
this.fetch()
|
this.fetch()
|
||||||
this.onDefine()
|
this.onDefine()
|
||||||
this.wrapper.classList.remove('undefined')
|
this.root.classList.remove('undefined')
|
||||||
})
|
})
|
||||||
undefine.addEventListener('click', () => this.undefine())
|
|
||||||
} else {
|
|
||||||
define.hidden = true
|
|
||||||
undefine.hidden = true
|
|
||||||
}
|
}
|
||||||
|
if (this.elements.undefine) {
|
||||||
this.quickContainer = quickContainer
|
this.elements.undefine.addEventListener('click', () => this.undefine())
|
||||||
this.extendedContainer = container
|
}
|
||||||
return this.extendedContainer
|
|
||||||
}
|
}
|
||||||
|
|
||||||
clear() {
|
clear() {
|
||||||
|
@ -102,44 +104,12 @@ class BaseElement {
|
||||||
this.builder.setter(this.field, this.toJS())
|
this.builder.setter(this.field, this.toJS())
|
||||||
}
|
}
|
||||||
|
|
||||||
getLabelParent() {
|
getLabelTemplate() {
|
||||||
return this.header
|
|
||||||
}
|
|
||||||
|
|
||||||
getHelpTextParent() {
|
|
||||||
return this.parentNode
|
|
||||||
}
|
|
||||||
|
|
||||||
buildLabel() {
|
|
||||||
if (this.properties.label) {
|
|
||||||
const label = this.properties.label
|
const label = this.properties.label
|
||||||
this.label = Utils.loadTemplate(`<label title="${label}">${label}</label>`)
|
const help = this.properties.helpEntries?.join() || ''
|
||||||
const parent = this.getLabelParent()
|
return label
|
||||||
parent.appendChild(this.label)
|
? `<label title="${label}" data-ref=label data-help="${help}">${label}</label>`
|
||||||
if (this.properties.helpEntries) {
|
: ''
|
||||||
this.builder._umap.help.button(this.label, this.properties.helpEntries)
|
|
||||||
} else if (this.properties.helpTooltip) {
|
|
||||||
const info = Utils.loadTemplate('<i class="info"></i>')
|
|
||||||
this.label.appendChild(info)
|
|
||||||
info.addEventListener('mouseover', () => {
|
|
||||||
this.builder._umap.tooltip.open({
|
|
||||||
anchor: info,
|
|
||||||
content: this.properties.helpTooltip,
|
|
||||||
position: 'top',
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
buildHelpText() {
|
|
||||||
if (this.properties.helpText) {
|
|
||||||
const container = Utils.loadTemplate(
|
|
||||||
`<small class="help-text">${Utils.escapeHTML(this.properties.helpText)}</small>`
|
|
||||||
)
|
|
||||||
const parent = this.getHelpTextParent()
|
|
||||||
parent.appendChild(container)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fetch() {}
|
fetch() {}
|
||||||
|
@ -154,35 +124,35 @@ class BaseElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
undefine() {
|
undefine() {
|
||||||
this.wrapper.classList.add('undefined')
|
this.root.classList.add('undefined')
|
||||||
this.clear()
|
this.clear()
|
||||||
this.sync()
|
this.sync()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Fields.Textarea = class extends BaseElement {
|
Fields.Textarea = class extends BaseElement {
|
||||||
build() {
|
getTemplate() {
|
||||||
this.input = Utils.loadTemplate('<textarea></textarea>')
|
return `<textarea placeholder="${this.properties.placeholder || ''}" data-ref=textarea></textarea>`
|
||||||
if (this.properties.className) this.input.classList.add(this.properties.className)
|
|
||||||
if (this.properties.placeholder) {
|
|
||||||
this.input.placeholder = this.properties.placeholder
|
|
||||||
}
|
}
|
||||||
this.parentNode.appendChild(this.input)
|
|
||||||
|
build() {
|
||||||
|
super.build()
|
||||||
|
this.textarea = this.elements.textarea
|
||||||
this.fetch()
|
this.fetch()
|
||||||
this.input.addEventListener('input', () => this.sync())
|
this.textarea.addEventListener('input', () => this.sync())
|
||||||
this.input.addEventListener('keypress', (event) => this.onKeyPress(event))
|
this.textarea.addEventListener('keypress', (event) => this.onKeyPress(event))
|
||||||
}
|
}
|
||||||
|
|
||||||
fetch() {
|
fetch() {
|
||||||
const value = this.toHTML()
|
const value = this.toHTML()
|
||||||
this.initial = value
|
this.initial = value
|
||||||
if (value) {
|
if (value) {
|
||||||
this.input.value = value
|
this.textarea.value = value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
value() {
|
value() {
|
||||||
return this.input.value
|
return this.textarea.value
|
||||||
}
|
}
|
||||||
|
|
||||||
onKeyPress(event) {
|
onKeyPress(event) {
|
||||||
|
@ -195,18 +165,17 @@ Fields.Textarea = class extends BaseElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
Fields.Input = class extends BaseElement {
|
Fields.Input = class extends BaseElement {
|
||||||
|
getTemplate() {
|
||||||
|
return `<input type="${this.type()}" name="${this.name}" placeholder="${this.properties.placeholder || ''}" data-ref=input />`
|
||||||
|
}
|
||||||
|
|
||||||
build() {
|
build() {
|
||||||
this.input = Utils.loadTemplate('<input />')
|
super.build()
|
||||||
this.parentNode.appendChild(this.input)
|
this.input = this.elements.input
|
||||||
this.input.type = this.type()
|
|
||||||
this.input.name = this.name
|
|
||||||
this.input._helper = this
|
this.input._helper = this
|
||||||
if (this.properties.className) {
|
if (this.properties.className) {
|
||||||
this.input.classList.add(this.properties.className)
|
this.input.classList.add(this.properties.className)
|
||||||
}
|
}
|
||||||
if (this.properties.placeholder) {
|
|
||||||
this.input.placeholder = this.properties.placeholder
|
|
||||||
}
|
|
||||||
if (this.properties.min !== undefined) {
|
if (this.properties.min !== undefined) {
|
||||||
this.input.min = this.properties.min
|
this.input.min = this.properties.min
|
||||||
}
|
}
|
||||||
|
@ -254,11 +223,13 @@ Fields.BlurInput = class extends Fields.Input {
|
||||||
return 'blur'
|
return 'blur'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getTemplate() {
|
||||||
|
return `${super.getTemplate()}<span class="button blur-button"></span>`
|
||||||
|
}
|
||||||
|
|
||||||
build() {
|
build() {
|
||||||
this.properties.className = 'blur'
|
this.properties.className = 'blur'
|
||||||
super.build()
|
super.build()
|
||||||
const button = Utils.loadTemplate('<span class="button blur-button"></span>')
|
|
||||||
this.input.parentNode.insertBefore(button, this.input.nextSibling)
|
|
||||||
this.input.addEventListener('focus', () => this.fetch())
|
this.input.addEventListener('focus', () => this.fetch())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -305,31 +276,29 @@ const FloatMixin = (Base) =>
|
||||||
}
|
}
|
||||||
|
|
||||||
Fields.FloatInput = class extends FloatMixin(Fields.Input) {
|
Fields.FloatInput = class extends FloatMixin(Fields.Input) {
|
||||||
// options: {
|
// TODO use public class properties when in baseline
|
||||||
// step: 'any',
|
getDefaultProperties() {
|
||||||
// }
|
return { step: 'any' }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Fields.BlurFloatInput = class extends FloatMixin(Fields.BlurInput) {
|
Fields.BlurFloatInput = class extends FloatMixin(Fields.BlurInput) {
|
||||||
// options: {
|
getDefaultProperties() {
|
||||||
// step: 'any',
|
return { step: 'any' }
|
||||||
// },
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Fields.CheckBox = class extends BaseElement {
|
Fields.CheckBox = class extends BaseElement {
|
||||||
build() {
|
getTemplate() {
|
||||||
const container = Utils.loadTemplate('<div class="checkbox-wrapper"></div>')
|
return `<input type=checkbox name="${this.name}" data-ref=input />`
|
||||||
this.parentNode.appendChild(container)
|
|
||||||
this.input = Utils.loadTemplate('<input />')
|
|
||||||
container.appendChild(this.input)
|
|
||||||
if (this.properties.className) {
|
|
||||||
this.input.classList.add(this.properties.className)
|
|
||||||
}
|
}
|
||||||
this.input.type = 'checkbox'
|
|
||||||
this.input.name = this.name
|
build() {
|
||||||
|
this.input = this.elements.input
|
||||||
this.input._helper = this
|
this.input._helper = this
|
||||||
this.fetch()
|
this.fetch()
|
||||||
this.input.addEventListener('change', () => this.sync())
|
this.input.addEventListener('change', () => this.sync())
|
||||||
|
super.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
fetch() {
|
fetch() {
|
||||||
|
@ -338,7 +307,7 @@ Fields.CheckBox = class extends BaseElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
value() {
|
value() {
|
||||||
return this.wrapper.classList.contains('undefined') ? undefined : this.input.checked
|
return this.root.classList.contains('undefined') ? undefined : this.input.checked
|
||||||
}
|
}
|
||||||
|
|
||||||
toHTML() {
|
toHTML() {
|
||||||
|
@ -351,12 +320,16 @@ Fields.CheckBox = class extends BaseElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
Fields.Select = class extends BaseElement {
|
Fields.Select = class extends BaseElement {
|
||||||
|
getTemplate() {
|
||||||
|
return `<select name="${this.name}" data-ref=select></select>`
|
||||||
|
}
|
||||||
|
|
||||||
build() {
|
build() {
|
||||||
this.select = Utils.loadTemplate(`<select name="${this.name}"></select>`)
|
this.select = this.elements.select
|
||||||
this.parentNode.appendChild(this.select)
|
|
||||||
this.validValues = []
|
this.validValues = []
|
||||||
this.buildOptions()
|
this.buildOptions()
|
||||||
this.select.addEventListener('change', () => this.sync())
|
this.select.addEventListener('change', () => this.sync())
|
||||||
|
super.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
getOptions() {
|
getOptions() {
|
||||||
|
@ -380,7 +353,7 @@ Fields.Select = class extends BaseElement {
|
||||||
const option = Utils.loadTemplate('<option></option>')
|
const option = Utils.loadTemplate('<option></option>')
|
||||||
this.select.appendChild(option)
|
this.select.appendChild(option)
|
||||||
option.value = value
|
option.value = value
|
||||||
option.innerHTML = label
|
option.textContent = label
|
||||||
if (this.toHTML() === value) {
|
if (this.toHTML() === value) {
|
||||||
option.selected = 'selected'
|
option.selected = 'selected'
|
||||||
}
|
}
|
||||||
|
@ -444,21 +417,23 @@ Fields.NullableBoolean = class extends Fields.Select {
|
||||||
}
|
}
|
||||||
|
|
||||||
Fields.EditableText = class extends BaseElement {
|
Fields.EditableText = class extends BaseElement {
|
||||||
|
getTemplate() {
|
||||||
|
return `<span contentEditable class="${this.properties.className || ''}" data-ref=input></span>`
|
||||||
|
}
|
||||||
|
|
||||||
|
buildTemplate() {
|
||||||
|
// No wrapper at all
|
||||||
|
const template = this.getTemplate()
|
||||||
|
this.input = Utils.loadTemplate(template)
|
||||||
|
this.form.appendChild(this.input)
|
||||||
|
}
|
||||||
|
|
||||||
build() {
|
build() {
|
||||||
this.input = Utils.loadTemplate(
|
|
||||||
`<span class="${this.properties.className || ''}"></span>`
|
|
||||||
)
|
|
||||||
this.parentNode.appendChild(this.input)
|
|
||||||
this.input.contentEditable = true
|
|
||||||
this.fetch()
|
this.fetch()
|
||||||
this.input.addEventListener('input', () => this.sync())
|
this.input.addEventListener('input', () => this.sync())
|
||||||
this.input.addEventListener('keypress', (event) => this.onKeyPress(event))
|
this.input.addEventListener('keypress', (event) => this.onKeyPress(event))
|
||||||
}
|
}
|
||||||
|
|
||||||
getParentNode() {
|
|
||||||
return this.form
|
|
||||||
}
|
|
||||||
|
|
||||||
value() {
|
value() {
|
||||||
return this.input.textContent
|
return this.input.textContent
|
||||||
}
|
}
|
||||||
|
@ -480,17 +455,18 @@ Fields.ColorPicker = class extends Fields.Input {
|
||||||
return Utils.COLORS
|
return Utils.COLORS
|
||||||
}
|
}
|
||||||
|
|
||||||
getParentNode() {
|
getDefaultProperties() {
|
||||||
super.getParentNode()
|
return {
|
||||||
return this.quickContainer
|
placeholder: translate('Inherit'),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getTemplate() {
|
||||||
|
return `${super.getTemplate()}<div class="umap-color-picker" hidden data-ref=colors></div>`
|
||||||
}
|
}
|
||||||
|
|
||||||
build() {
|
build() {
|
||||||
super.build()
|
super.build()
|
||||||
this.input.placeholder = this.properties.placeholder || translate('Inherit')
|
|
||||||
this.container = Utils.loadTemplate('<div class="umap-color-picker"></div>')
|
|
||||||
this.extendedContainer.appendChild(this.container)
|
|
||||||
this.container.style.display = 'none'
|
|
||||||
for (const color of this.getColors()) {
|
for (const color of this.getColors()) {
|
||||||
this.addColor(color)
|
this.addColor(color)
|
||||||
}
|
}
|
||||||
|
@ -506,16 +482,21 @@ Fields.ColorPicker = class extends Fields.Input {
|
||||||
}
|
}
|
||||||
|
|
||||||
onFocus() {
|
onFocus() {
|
||||||
this.container.style.display = 'block'
|
this.showPicker()
|
||||||
this.spreadColor()
|
this.spreadColor()
|
||||||
}
|
}
|
||||||
|
|
||||||
onBlur() {
|
showPicker() {
|
||||||
const closePicker = () => {
|
this.elements.colors.hidden = false
|
||||||
this.container.style.display = 'none'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
closePicker() {
|
||||||
|
this.elements.colors.hidden = true
|
||||||
|
}
|
||||||
|
|
||||||
|
onBlur() {
|
||||||
// We must leave time for the click to be listened.
|
// We must leave time for the click to be listened.
|
||||||
window.setTimeout(closePicker, 100)
|
window.setTimeout(() => this.closePicker(), 100)
|
||||||
}
|
}
|
||||||
|
|
||||||
sync() {
|
sync() {
|
||||||
|
@ -530,12 +511,12 @@ Fields.ColorPicker = class extends Fields.Input {
|
||||||
|
|
||||||
addColor(colorName) {
|
addColor(colorName) {
|
||||||
const span = Utils.loadTemplate('<span></span>')
|
const span = Utils.loadTemplate('<span></span>')
|
||||||
this.container.appendChild(span)
|
this.elements.colors.appendChild(span)
|
||||||
span.style.backgroundColor = span.title = colorName
|
span.style.backgroundColor = span.title = colorName
|
||||||
const updateColorInput = () => {
|
const updateColorInput = () => {
|
||||||
this.input.value = colorName
|
this.input.value = colorName
|
||||||
this.sync()
|
this.sync()
|
||||||
this.container.style.display = 'none'
|
this.closePicker()
|
||||||
}
|
}
|
||||||
span.addEventListener('mousedown', updateColorInput)
|
span.addEventListener('mousedown', updateColorInput)
|
||||||
}
|
}
|
||||||
|
@ -680,21 +661,25 @@ Fields.IconUrl = class extends Fields.BlurInput {
|
||||||
return 'hidden'
|
return 'hidden'
|
||||||
}
|
}
|
||||||
|
|
||||||
build() {
|
getTemplate() {
|
||||||
super.build()
|
return `
|
||||||
const [container, { buttons, tabs, body, footer }] = Utils.loadTemplateWithRefs(`
|
|
||||||
<div>
|
<div>
|
||||||
<div data-ref=buttons></div>
|
<div data-ref=buttons></div>
|
||||||
<div class="flat-tabs" data-ref=tabs></div>
|
<div class="flat-tabs" data-ref=tabs></div>
|
||||||
<div class="umap-pictogram-body" data-ref=body></div>
|
<div class="umap-pictogram-body" data-ref=body>
|
||||||
|
${super.getTemplate()}
|
||||||
|
</div>
|
||||||
<div data-ref=footer></div>
|
<div data-ref=footer></div>
|
||||||
</div>
|
</div>
|
||||||
`)
|
`
|
||||||
this.parentNode.appendChild(container)
|
}
|
||||||
this.buttons = buttons
|
|
||||||
this.tabs = tabs
|
build() {
|
||||||
this.body = body
|
super.build()
|
||||||
this.footer = footer
|
this.buttons = this.elements.buttons
|
||||||
|
this.tabs = this.elements.tabs
|
||||||
|
this.body = this.elements.body
|
||||||
|
this.footer = this.elements.footer
|
||||||
this.updatePreview()
|
this.updatePreview()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -936,23 +921,27 @@ Fields.Url = class extends Fields.Input {
|
||||||
}
|
}
|
||||||
|
|
||||||
Fields.Switch = class extends Fields.CheckBox {
|
Fields.Switch = class extends Fields.CheckBox {
|
||||||
getParentNode() {
|
getTemplate() {
|
||||||
super.getParentNode()
|
const label = this.properties.label
|
||||||
if (this.properties.inheritable) return this.quickContainer
|
return `${super.getTemplate()}<label title="${label}" for="${this.id}" data-ref=customLabel>${label}</label>`
|
||||||
return this.extendedContainer
|
|
||||||
}
|
}
|
||||||
|
|
||||||
build() {
|
build() {
|
||||||
super.build()
|
super.build()
|
||||||
if (this.properties.inheritable) {
|
// We have it in our template
|
||||||
this.label = Utils.loadTemplate('<label></label>')
|
if (!this.properties.inheritable) {
|
||||||
|
// We already have the label near the switch,
|
||||||
|
// only show the default label in inheritable mode
|
||||||
|
// as the switch itself may be hidden (until "defined")
|
||||||
|
if (this.elements.label) {
|
||||||
|
this.elements.label.hidden = true
|
||||||
|
this.elements.label.innerHTML = ''
|
||||||
|
this.elements.label.title = ''
|
||||||
}
|
}
|
||||||
this.input.parentNode.appendChild(this.label)
|
}
|
||||||
this.input.parentNode.classList.add('with-switch')
|
this.container.classList.add('with-switch')
|
||||||
const id = `${this.builder.properties.id || Date.now()}.${this.name}`
|
|
||||||
this.label.setAttribute('for', id)
|
|
||||||
this.input.classList.add('switch')
|
this.input.classList.add('switch')
|
||||||
this.input.id = id
|
this.input.id = this.id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -961,22 +950,22 @@ Fields.FacetSearchBase = class extends BaseElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
Fields.FacetSearchChoices = class extends Fields.FacetSearchBase {
|
Fields.FacetSearchChoices = class extends Fields.FacetSearchBase {
|
||||||
build() {
|
getTemplate() {
|
||||||
const [container, { ul, label }] = Utils.loadTemplateWithRefs(`
|
return `
|
||||||
<fieldset class="umap-facet">
|
<fieldset class="umap-facet">
|
||||||
<legend data-ref=label>${Utils.escapeHTML(this.properties.label)}</legend>
|
<legend data-ref=label>${Utils.escapeHTML(this.properties.label)}</legend>
|
||||||
<ul data-ref=ul></ul>
|
<ul data-ref=ul></ul>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
`)
|
`
|
||||||
this.container = container
|
}
|
||||||
this.ul = ul
|
|
||||||
this.label = label
|
build() {
|
||||||
this.parentNode.appendChild(this.container)
|
|
||||||
this.type = this.properties.criteria.type
|
this.type = this.properties.criteria.type
|
||||||
|
|
||||||
const choices = this.properties.criteria.choices
|
const choices = this.properties.criteria.choices
|
||||||
choices.sort()
|
choices.sort()
|
||||||
choices.forEach((value) => this.buildLi(value))
|
choices.forEach((value) => this.buildLi(value))
|
||||||
|
super.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
buildLi(value) {
|
buildLi(value) {
|
||||||
|
@ -993,13 +982,13 @@ Fields.FacetSearchChoices = class extends Fields.FacetSearchBase {
|
||||||
input.checked = this.get().choices.includes(value)
|
input.checked = this.get().choices.includes(value)
|
||||||
input.dataset.value = value
|
input.dataset.value = value
|
||||||
input.addEventListener('change', () => this.sync())
|
input.addEventListener('change', () => this.sync())
|
||||||
this.ul.appendChild(li)
|
this.elements.ul.appendChild(li)
|
||||||
}
|
}
|
||||||
|
|
||||||
toJS() {
|
toJS() {
|
||||||
return {
|
return {
|
||||||
type: this.type,
|
type: this.type,
|
||||||
choices: [...this.ul.querySelectorAll('input:checked')].map(
|
choices: [...this.elements.ul.querySelectorAll('input:checked')].map(
|
||||||
(i) => i.dataset.value
|
(i) => i.dataset.value
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
@ -1019,28 +1008,30 @@ Fields.MinMaxBase = class extends Fields.FacetSearchBase {
|
||||||
return value.valueOf()
|
return value.valueOf()
|
||||||
}
|
}
|
||||||
|
|
||||||
build() {
|
getTemplate() {
|
||||||
const [minLabel, maxLabel] = this.getLabels()
|
const [minLabel, maxLabel] = this.getLabels()
|
||||||
const { min, max, type } = this.properties.criteria
|
const { min, max, type } = this.properties.criteria
|
||||||
const { min: modifiedMin, max: modifiedMax } = this.get()
|
|
||||||
|
|
||||||
const currentMin = modifiedMin !== undefined ? modifiedMin : min
|
|
||||||
const currentMax = modifiedMax !== undefined ? modifiedMax : max
|
|
||||||
this.type = type
|
this.type = type
|
||||||
const inputType = this.getInputType(this.type)
|
const inputType = this.getInputType(this.type)
|
||||||
const minHTML = this.prepareForHTML(min)
|
const minHTML = this.prepareForHTML(min)
|
||||||
const maxHTML = this.prepareForHTML(max)
|
const maxHTML = this.prepareForHTML(max)
|
||||||
const [container, { minInput, maxInput }] = Utils.loadTemplateWithRefs(`
|
return `
|
||||||
<fieldset class="umap-facet">
|
<fieldset class="umap-facet">
|
||||||
<legend>${Utils.escapeHTML(this.properties.label)}</legend>
|
<legend>${Utils.escapeHTML(this.properties.label)}</legend>
|
||||||
<label>${minLabel}<input min="${minHTML}" max="${maxHTML}" step=any type="${inputType}" data-ref=minInput /></label>
|
<label>${minLabel}<input min="${minHTML}" max="${maxHTML}" step=any type="${inputType}" data-ref=minInput /></label>
|
||||||
<label>${maxLabel}<input min="${minHTML}" max="${maxHTML}" step=any type="${inputType}" data-ref=maxInput /></label>
|
<label>${maxLabel}<input min="${minHTML}" max="${maxHTML}" step=any type="${inputType}" data-ref=maxInput /></label>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
`)
|
`
|
||||||
this.container = container
|
}
|
||||||
this.minInput = minInput
|
|
||||||
this.maxInput = maxInput
|
build() {
|
||||||
this.parentNode.appendChild(this.container)
|
this.minInput = this.elements.minInput
|
||||||
|
this.maxInput = this.elements.maxInput
|
||||||
|
const { min, max, type } = this.properties.criteria
|
||||||
|
const { min: modifiedMin, max: modifiedMax } = this.get()
|
||||||
|
|
||||||
|
const currentMin = modifiedMin !== undefined ? modifiedMin : min
|
||||||
|
const currentMax = modifiedMax !== undefined ? modifiedMax : max
|
||||||
if (min != null) {
|
if (min != null) {
|
||||||
// The value stored using setAttribute is not modified by
|
// The value stored using setAttribute is not modified by
|
||||||
// user input, and will be used as initial value when calling
|
// user input, and will be used as initial value when calling
|
||||||
|
@ -1061,6 +1052,7 @@ Fields.MinMaxBase = class extends Fields.FacetSearchBase {
|
||||||
|
|
||||||
this.minInput.addEventListener('change', () => this.sync())
|
this.minInput.addEventListener('change', () => this.sync())
|
||||||
this.maxInput.addEventListener('change', () => this.sync())
|
this.maxInput.addEventListener('change', () => this.sync())
|
||||||
|
super.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleStatus() {
|
toggleStatus() {
|
||||||
|
@ -1174,16 +1166,17 @@ Fields.MultiChoice = class extends BaseElement {
|
||||||
return this.properties.choices || this.choices
|
return this.properties.choices || this.choices
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getTemplate() {
|
||||||
|
return `<div class="${this.getClassName()} by${this.getChoices().length}" data-ref=wrapper></div>`
|
||||||
|
}
|
||||||
|
|
||||||
build() {
|
build() {
|
||||||
const choices = this.getChoices()
|
const choices = this.getChoices()
|
||||||
this.container = Utils.loadTemplate(
|
|
||||||
`<div class="${this.getClassName()} by${choices.length}"></div>`
|
|
||||||
)
|
|
||||||
this.parentNode.appendChild(this.container)
|
|
||||||
for (const [i, [value, label]] of choices.entries()) {
|
for (const [i, [value, label]] of choices.entries()) {
|
||||||
this.addChoice(value, label, i)
|
this.addChoice(value, label, i)
|
||||||
}
|
}
|
||||||
this.fetch()
|
this.fetch()
|
||||||
|
super.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
addChoice(value, label, counter) {
|
addChoice(value, label, counter) {
|
||||||
|
@ -1191,8 +1184,8 @@ Fields.MultiChoice = class extends BaseElement {
|
||||||
const input = Utils.loadTemplate(
|
const input = Utils.loadTemplate(
|
||||||
`<input type="radio" name="${this.name}" id="${id}" value="${value}" />`
|
`<input type="radio" name="${this.name}" id="${id}" value="${value}" />`
|
||||||
)
|
)
|
||||||
this.container.appendChild(input)
|
this.elements.wrapper.appendChild(input)
|
||||||
this.container.appendChild(
|
this.elements.wrapper.appendChild(
|
||||||
Utils.loadTemplate(`<label for="${id}">${label}</label>`)
|
Utils.loadTemplate(`<label for="${id}">${label}</label>`)
|
||||||
)
|
)
|
||||||
input.addEventListener('change', () => this.sync())
|
input.addEventListener('change', () => this.sync())
|
||||||
|
@ -1259,10 +1252,11 @@ Fields.Range = class extends Fields.FloatInput {
|
||||||
}
|
}
|
||||||
|
|
||||||
value() {
|
value() {
|
||||||
return this.wrapper.classList.contains('undefined') ? undefined : super.value()
|
return this.root.classList.contains('undefined') ? undefined : super.value()
|
||||||
}
|
}
|
||||||
|
|
||||||
buildHelpText() {
|
build() {
|
||||||
|
super.build()
|
||||||
let options = ''
|
let options = ''
|
||||||
const step = this.properties.step || 1
|
const step = this.properties.step || 1
|
||||||
const digits = step < 1 ? 1 : 0
|
const digits = step < 1 ? 1 : 0
|
||||||
|
@ -1272,16 +1266,14 @@ Fields.Range = class extends Fields.FloatInput {
|
||||||
i <= this.properties.max;
|
i <= this.properties.max;
|
||||||
i += this.properties.step
|
i += this.properties.step
|
||||||
) {
|
) {
|
||||||
options += `<option value="${i.toFixed(digits)}" label="${i.toFixed(
|
const ii = i.toFixed(digits)
|
||||||
digits
|
options += `<option value="${ii}" label="${ii}"></option>`
|
||||||
)}"></option>`
|
|
||||||
}
|
}
|
||||||
const parent = this.getHelpTextParent()
|
|
||||||
const datalist = Utils.loadTemplate(
|
const datalist = Utils.loadTemplate(
|
||||||
`<datalist class="umap-field-datalist" id="${id}">${options}</datalist>`
|
`<datalist class="umap-field-datalist" id="${id}">${options}</datalist>`
|
||||||
)
|
)
|
||||||
|
this.container.appendChild(datalist)
|
||||||
this.input.setAttribute('list', id)
|
this.input.setAttribute('list', id)
|
||||||
super.buildHelpText()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1292,13 +1284,14 @@ Fields.ManageOwner = class extends BaseElement {
|
||||||
on_select: L.bind(this.onSelect, this),
|
on_select: L.bind(this.onSelect, this),
|
||||||
placeholder: translate("Type new owner's username"),
|
placeholder: translate("Type new owner's username"),
|
||||||
}
|
}
|
||||||
this.autocomplete = new AjaxAutocomplete(this.parentNode, options)
|
this.autocomplete = new AjaxAutocomplete(this.container, options)
|
||||||
const owner = this.toHTML()
|
const owner = this.toHTML()
|
||||||
if (owner)
|
if (owner) {
|
||||||
this.autocomplete.displaySelected({
|
this.autocomplete.displaySelected({
|
||||||
item: { value: owner.id, label: owner.name },
|
item: { value: owner.id, label: owner.name },
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
value() {
|
value() {
|
||||||
return this._value
|
return this._value
|
||||||
|
@ -1322,7 +1315,7 @@ Fields.ManageEditors = class extends BaseElement {
|
||||||
on_unselect: L.bind(this.onUnselect, this),
|
on_unselect: L.bind(this.onUnselect, this),
|
||||||
placeholder: translate("Type editor's username"),
|
placeholder: translate("Type editor's username"),
|
||||||
}
|
}
|
||||||
this.autocomplete = new AjaxAutocompleteMultiple(this.parentNode, options)
|
this.autocomplete = new AjaxAutocompleteMultiple(this.container, options)
|
||||||
this._values = this.toHTML()
|
this._values = this.toHTML()
|
||||||
if (this._values)
|
if (this._values)
|
||||||
for (let i = 0; i < this._values.length; i++)
|
for (let i = 0; i < this._values.length; i++)
|
||||||
|
|
|
@ -228,9 +228,11 @@ export default class Help {
|
||||||
|
|
||||||
parse(container) {
|
parse(container) {
|
||||||
for (const element of container.querySelectorAll('[data-help]')) {
|
for (const element of container.querySelectorAll('[data-help]')) {
|
||||||
|
if (element.dataset.help) {
|
||||||
this.button(element, element.dataset.help.split(','))
|
this.button(element, element.dataset.help.split(','))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_buildEditEntry() {
|
_buildEditEntry() {
|
||||||
const container = DomUtil.create('div', '')
|
const container = DomUtil.create('div', '')
|
||||||
|
|
|
@ -416,9 +416,11 @@ export function loadTemplate(html) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function loadTemplateWithRefs(html) {
|
export function loadTemplateWithRefs(html) {
|
||||||
const element = loadTemplate(html)
|
const template = document.createElement('template')
|
||||||
|
template.innerHTML = html
|
||||||
|
const element = template.content.firstElementChild
|
||||||
const elements = {}
|
const elements = {}
|
||||||
for (const node of element.querySelectorAll('[data-ref]')) {
|
for (const node of template.content.querySelectorAll('[data-ref]')) {
|
||||||
elements[node.dataset.ref] = node
|
elements[node.dataset.ref] = node
|
||||||
}
|
}
|
||||||
return [element, elements]
|
return [element, elements]
|
||||||
|
|
|
@ -101,7 +101,7 @@ def test_can_remove_stroke(live_server, openmap, page, bootstrap):
|
||||||
page.get_by_role("link", name="Toggle edit mode").click()
|
page.get_by_role("link", name="Toggle edit mode").click()
|
||||||
page.get_by_text("Shape properties").click()
|
page.get_by_text("Shape properties").click()
|
||||||
page.locator(".umap-field-stroke .define").first.click()
|
page.locator(".umap-field-stroke .define").first.click()
|
||||||
page.locator(".umap-field-stroke label").first.click()
|
page.locator(".umap-field-stroke .show-on-defined label").first.click()
|
||||||
expect(page.locator(".leaflet-overlay-pane path[stroke='DarkBlue']")).to_have_count(
|
expect(page.locator(".leaflet-overlay-pane path[stroke='DarkBlue']")).to_have_count(
|
||||||
0
|
0
|
||||||
)
|
)
|
||||||
|
|
|
@ -187,9 +187,11 @@ def test_websocket_connection_can_sync_map_properties(
|
||||||
# Zoom control is synced
|
# Zoom control is synced
|
||||||
peerB.get_by_role("link", name="Map advanced properties").click()
|
peerB.get_by_role("link", name="Map advanced properties").click()
|
||||||
peerB.locator("summary").filter(has_text="User interface options").click()
|
peerB.locator("summary").filter(has_text="User interface options").click()
|
||||||
peerB.locator("div").filter(
|
switch = peerB.locator("div.formbox").filter(
|
||||||
has_text=re.compile(r"^Display the zoom control")
|
has_text=re.compile("Display the zoom control")
|
||||||
).locator("label").nth(2).click()
|
)
|
||||||
|
expect(switch).to_be_visible()
|
||||||
|
switch.get_by_text("Never").click()
|
||||||
|
|
||||||
expect(peerA.locator(".leaflet-control-zoom")).to_be_hidden()
|
expect(peerA.locator(".leaflet-control-zoom")).to_be_hidden()
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue