Use Leaflet.FormBuilder to build advanced filters form

This commit is contained in:
Yohan Boniface 2023-08-02 07:59:37 +02:00
parent 56cb4b44d0
commit 9abbfbc01e
3 changed files with 69 additions and 81 deletions

View file

@ -789,113 +789,68 @@ L.U.Map.include({
L.DomEvent.on(filter, 'input', appendAll, this) L.DomEvent.on(filter, 'input', appendAll, this)
L.DomEvent.on(filter, 'input', resetLayers, this) L.DomEvent.on(filter, 'input', resetLayers, this)
this.ui.openPanel({ data: { html: browserContainer }, actions: [this._aboutLink()] }) this.ui.openPanel({
data: { html: browserContainer },
actions: [this._aboutLink()],
})
}, },
_openFilter: function () { _openFilter: function () {
const filterContainer = L.DomUtil.create('div', 'umap-filter-data'), const container = L.DomUtil.create('div', 'umap-filter-data'),
title = L.DomUtil.add( title = L.DomUtil.add('h3', 'umap-filter-title', container, this.options.name),
'h3',
'umap-filter-title',
filterContainer,
this.options.name
),
propertiesContainer = L.DomUtil.create( propertiesContainer = L.DomUtil.create(
'div', 'div',
'umap-filter-properties', 'umap-filter-properties',
filterContainer container
), ),
advancedFilterKeys = this.getAdvancedFilterKeys() keys = this.getAdvancedFilterKeys()
const advancedFiltersFull = {} const knownValues = {}
let filtersAlreadyLoaded = true if (!this.options.advancedFilters) this.options.advancedFilters = {}
if (!this.options.advancedFilters) {
this.options.advancedFilters = {} keys.forEach((key) => {
filtersAlreadyLoaded = false knownValues[key] = []
} if (!this.options.advancedFilters[key])
advancedFilterKeys.forEach((property) => { this.options.advancedFilters[key] = []
advancedFiltersFull[property] = []
if (!filtersAlreadyLoaded || !this.options.advancedFilters[property]) {
this.options.advancedFilters[property] = []
}
}) })
this.eachBrowsableDataLayer((datalayer) => { this.eachBrowsableDataLayer((datalayer) => {
datalayer.eachFeature((feature) => { datalayer.eachFeature((feature) => {
advancedFilterKeys.forEach((property) => { keys.forEach((key) => {
if (feature.properties[property]) { let value = feature.properties[key]
if (!advancedFiltersFull[property].includes(feature.properties[property])) { if (typeof value !== 'undefined' && !knownValues[key].includes(value)) {
advancedFiltersFull[property].push(feature.properties[property]) knownValues[key].push(value)
}
} }
}) })
}) })
}) })
const addPropertyValue = function (property, value) {
const property_li = L.DomUtil.create('li', ''),
filter_check = L.DomUtil.create('input', '', property_li),
filter_label = L.DomUtil.create('label', '', property_li)
filter_check.type = 'checkbox'
filter_check.id = `checkbox_${property}_${value}`
filter_check.checked =
this.options.advancedFilters[property] &&
this.options.advancedFilters[property].includes(value)
filter_check.dataset.property = property
filter_check.dataset.value = value
filter_label.htmlFor = `checkbox_${property}_${value}`
filter_label.innerHTML = value
L.DomEvent.on(
filter_check,
'change',
function (e) {
const property = e.srcElement.dataset.property
const value = e.srcElement.dataset.value
if (e.srcElement.checked) {
this.options.advancedFilters[property].push(value)
} else {
this.options.advancedFilters[property].splice(
this.options.advancedFilters[property].indexOf(value),
1
)
}
L.bind(filterFeatures, this)()
},
this
)
return property_li
}
const addProperty = function (property) {
const container = L.DomUtil.create(
'div',
'property-container',
propertiesContainer
),
headline = L.DomUtil.add('h5', '', container, property)
const ul = L.DomUtil.create('ul', '', container)
const orderedValues = advancedFiltersFull[property]
orderedValues.sort()
orderedValues.forEach((value) => {
ul.appendChild(L.bind(addPropertyValue, this)(property, value))
})
}
const filterFeatures = function () { const filterFeatures = function () {
let found = false let found = false
this.eachBrowsableDataLayer((datalayer) => { this.eachBrowsableDataLayer((datalayer) => {
datalayer.resetLayer(true) datalayer.resetLayer(true)
if (datalayer.hasDataVisible()) found = true if (datalayer.hasDataVisible()) found = true
}) })
// TODO: display a results counter in the panel instead.
if (!found) if (!found)
this.ui.alert({ content: L._('No results for these filters'), level: 'info' }) this.ui.alert({ content: L._('No results for these filters'), level: 'info' })
} }
propertiesContainer.innerHTML = '' const fields = keys.map((current) => [
advancedFilterKeys.forEach((property) => { `options.advancedFilters.${current}`,
L.bind(addProperty, this)(property) {
handler: 'AdvancedFilter',
choices: knownValues[current],
},
])
const builder = new L.U.FormBuilder(this, fields, {
makeDirty: false,
callback: filterFeatures,
callbackContext: this,
}) })
container.appendChild(builder.build())
this.ui.openPanel({ data: { html: filterContainer }, actions: [this._aboutLink()] }) this.ui.openPanel({ data: { html: container }, actions: [this._aboutLink()] })
}, },
_aboutLink: function () { _aboutLink: function () {

View file

@ -87,7 +87,12 @@ L.U.FeatureMixin = {
edit: function (e) { edit: function (e) {
if (!this.map.editEnabled || this.isReadOnly()) return if (!this.map.editEnabled || this.isReadOnly()) return
const container = L.DomUtil.create('div', 'umap-feature-container') const container = L.DomUtil.create('div', 'umap-feature-container')
L.DomUtil.add('h3', `umap-feature-properties ${this.getClassName()}`, container, L._('Feature properties')) L.DomUtil.add(
'h3',
`umap-feature-properties ${this.getClassName()}`,
container,
L._('Feature properties')
)
let builder = new L.U.FormBuilder(this, ['datalayer'], { let builder = new L.U.FormBuilder(this, ['datalayer'], {
callback: function () { callback: function () {

View file

@ -684,6 +684,34 @@ L.FormBuilder.Switch = L.FormBuilder.CheckBox.extend({
}, },
}) })
L.FormBuilder.AdvancedFilter = L.FormBuilder.Element.extend({
build: function () {
this.container = L.DomUtil.create('div', 'property-container', this.parentNode)
this.headline = L.DomUtil.add('h5', '', this.container, this.name)
this.ul = L.DomUtil.create('ul', '', this.container)
const choices = this.options.choices
choices.sort()
choices.forEach((value) => this.buildLi(value))
},
buildLi: function (value) {
const property_li = L.DomUtil.create('li', '', this.ul),
input = L.DomUtil.create('input', '', property_li),
label = L.DomUtil.create('label', '', property_li)
input.type = 'checkbox'
input.id = `checkbox_${this.name}_${value}`
input.checked = this.get().includes(value)
input.dataset.value = value
label.htmlFor = `checkbox_${this.name}_${value}`
label.innerHTML = value
L.DomEvent.on(input, 'change', (e) => this.sync())
},
toJS: function () {
return [...this.ul.querySelectorAll('input:checked')].map(i => i.dataset.value)
},
})
L.FormBuilder.MultiChoice = L.FormBuilder.Element.extend({ L.FormBuilder.MultiChoice = L.FormBuilder.Element.extend({
default: 'null', default: 'null',
className: 'umap-multiplechoice', className: 'umap-multiplechoice',
@ -1125,7 +1153,7 @@ L.U.FormBuilder = L.FormBuilder.extend({
setter: function (field, value) { setter: function (field, value) {
L.FormBuilder.prototype.setter.call(this, field, value) L.FormBuilder.prototype.setter.call(this, field, value)
this.obj.isDirty = true if (this.options.makeDirty !== false) this.obj.isDirty = true
}, },
finish: function () { finish: function () {