From 839bb4c5d8b9eba00fac740cc407cd48f7901327 Mon Sep 17 00:00:00 2001 From: flammermann Date: Wed, 3 Jan 2024 02:25:10 +0000 Subject: [PATCH] Support date properties in facet search - split type into dataType and inputType --- umap/static/umap/base.css | 4 ++++ umap/static/umap/js/umap.controls.js | 14 ++++++++----- umap/static/umap/js/umap.core.js | 2 +- umap/static/umap/js/umap.features.js | 4 ++-- umap/static/umap/js/umap.forms.js | 30 ++++++++++++++++++---------- umap/static/umap/js/umap.js | 17 ++++++++++++++-- 6 files changed, 50 insertions(+), 21 deletions(-) diff --git a/umap/static/umap/base.css b/umap/static/umap/base.css index ec75c4a0..af8fa881 100644 --- a/umap/static/umap/base.css +++ b/umap/static/umap/base.css @@ -263,6 +263,10 @@ input[type="checkbox"] + label { display: inline; padding: 0 14px; } +input[type="radio"] + label { + display: inline; + padding: 0 14px; +} select + .error, input + .error { display: block; diff --git a/umap/static/umap/js/umap.controls.js b/umap/static/umap/js/umap.controls.js index fc0bced2..a3ddc527 100644 --- a/umap/static/umap/js/umap.controls.js +++ b/umap/static/umap/js/umap.controls.js @@ -669,22 +669,26 @@ const ControlsMixin = { const facetCriteria = {} keys.forEach((key) => { - if (facetKeys[key]["type"] === "date") { + if (facetKeys[key]["dataType"] === "date") { if (!facetCriteria[key]) facetCriteria[key] = { + "dataType": facetKeys[key]["dataType"], + "inputType": facetKeys[key]["inputType"], "min": undefined, "max": undefined } if (!this.facets[key]) this.facets[key] = { - "type": facetKeys[key]["type"], + "dataType": facetKeys[key]["dataType"], "min": undefined, "max": undefined } } else { if (!facetCriteria[key]) facetCriteria[key] = { + "dataType": facetKeys[key]["dataType"], + "inputType": facetKeys[key]["inputType"], "choices": [] } if (!this.facets[key]) this.facets[key] = { - "type": facetKeys[key]["type"], + "dataType": facetKeys[key]["dataType"], "choices": [] } } @@ -694,7 +698,7 @@ const ControlsMixin = { datalayer.eachFeature((feature) => { keys.forEach((key) => { let value = feature.properties[key] - if (facetKeys[key]["type"] === "date") { + if (facetKeys[key]["dataType"] === "date") { value = L.Util.parseDateField(value) if (!!value && (!facetCriteria[key]["min"] || facetCriteria[key]["min"] > value)) { facetCriteria[key]["min"] = value @@ -725,7 +729,7 @@ const ControlsMixin = { const fields = keys.map((key) => [ `facets.${key}`, { - handler: facetKeys[key]["type"] === "date" ? 'FacetSearchDate' : 'FacetSearchCheckbox', + handler: facetCriteria[key]["inputType"] === "datetime-local" ? 'FacetSearchDate' : 'FacetSearchCheckbox', criteria: facetCriteria[key], label: facetKeys[key]["label"] }, diff --git a/umap/static/umap/js/umap.core.js b/umap/static/umap/js/umap.core.js index 9df1c199..055362ed 100644 --- a/umap/static/umap/js/umap.core.js +++ b/umap/static/umap/js/umap.core.js @@ -553,7 +553,7 @@ U.Help = L.Class.extend({ slugKey: L._('The name of the property to use as feature unique identifier.'), filterKey: L._('Comma separated list of properties to use when filtering features'), facetKey: L._( - 'Comma separated list of properties to use for facet search (eg.: mykey,otherkey). To control label, add it after a | (eg.: mykey|My Key,otherkey|Other Key). To control data type, add it after another | (eg.: mykey|My Key|enum,otherkey|Other Key|date). Allowed values for the data type are date and enum (default).' + 'Comma separated list of properties to use for facet search (eg.: mykey,otherkey). To control label, add it after a | (eg.: mykey|My Key,otherkey|Other Key). To control data type, add it after another | (eg.: mykey|My Key|enum,otherkey|Other Key|date). Allowed values for the data type are date and enum (default). To control input field type, add it after another | (eg.: mykey|My Key|enum|checkbox,otherkey|Other Key|date|datetime-local). Allowed values for the input field type are checkbox (default) or radio for data type enum and datetime-local (default) for data type date.' ), interactive: L._( 'If false, the polygon or line will act as a part of the underlying map.' diff --git a/umap/static/umap/js/umap.features.js b/umap/static/umap/js/umap.features.js index d3c14dab..09d23a3b 100644 --- a/umap/static/umap/js/umap.features.js +++ b/umap/static/umap/js/umap.features.js @@ -496,8 +496,8 @@ U.FeatureMixin = { const facets = this.map.facets for (const [property, criteria] of Object.entries(facets)) { let value = this.properties[property] - const type = criteria["type"] - if (type === "date") { + const dataType = criteria["dataType"] + if (dataType === "date") { const min = new Date(criteria["min"]) const max = new Date(criteria["max"]) value = L.Util.parseDateField(value) diff --git a/umap/static/umap/js/umap.forms.js b/umap/static/umap/js/umap.forms.js index 9986b970..15eedd8d 100644 --- a/umap/static/umap/js/umap.forms.js +++ b/umap/static/umap/js/umap.forms.js @@ -748,6 +748,9 @@ L.FormBuilder.FacetSearchCheckbox = L.FormBuilder.Element.extend({ build: function () { this.container = L.DomUtil.create('div', 'umap-facet', this.parentNode) this.ul = L.DomUtil.create('ul', '', this.container) + this.dataType = this.options.criteria["dataType"] + this.inputType = this.options.criteria["inputType"] + const choices = this.options.criteria["choices"] choices.sort() choices.forEach((value) => this.buildLi(value)) @@ -761,18 +764,21 @@ L.FormBuilder.FacetSearchCheckbox = L.FormBuilder.Element.extend({ 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.type = this.inputType + input.name = `${this.inputType}_${this.name}` + input.id = `${this.inputType}_${this.name}_${value}` input.checked = this.get()['choices'].includes(value) input.dataset.value = value - label.htmlFor = `checkbox_${this.name}_${value}` + label.htmlFor = `${this.inputType}_${this.name}_${value}` label.innerHTML = value + L.DomEvent.on(input, 'change', (e) => this.sync()) }, toJS: function () { return { - 'type': 'checkbox', + 'dataType': this.dataType, 'choices': [...this.ul.querySelectorAll('input:checked')].map((i) => i.dataset.value) } }, @@ -782,6 +788,8 @@ L.FormBuilder.FacetSearchDate = L.FormBuilder.Element.extend({ build: function () { this.container = L.DomUtil.create('div', 'umap-facet', this.parentNode); this.table = L.DomUtil.create('table', '', this.container); + this.dataType = this.options.criteria["dataType"]; + this.inputType = this.options.criteria["inputType"]; const min = this.options.criteria['min']; const max = this.options.criteria['max']; @@ -791,13 +799,13 @@ L.FormBuilder.FacetSearchDate = L.FormBuilder.Element.extend({ this.minTdLabel = L.DomUtil.create('td', '', this.minTr); this.minLabel = L.DomUtil.create('label', '', this.minTdLabel); this.minLabel.innerHTML = 'From'; - this.minLabel.htmlFor = `date_${this.name}_min`; + this.minLabel.htmlFor = `${this.inputType}_${this.name}_min`; this.minTdInput = L.DomUtil.create('td', '', this.minTr); this.minInput = L.DomUtil.create('input', '', this.minTdInput); - this.minInput.type = 'datetime-local'; + this.minInput.type = this.inputType; this.minInput.step = '0.001'; - this.minInput.id = `date_${this.name}_min`; + this.minInput.id = `${this.inputType}_${this.name}_min`; this.minInput.valueAsNumber = (min.valueOf() - min.getTimezoneOffset() * 60000);; this.minInput.dataset.value = min; @@ -806,13 +814,13 @@ L.FormBuilder.FacetSearchDate = L.FormBuilder.Element.extend({ this.maxTdLabel = L.DomUtil.create('td', '', this.maxTr); this.maxLabel = L.DomUtil.create('label', '', this.maxTdLabel); this.maxLabel.innerHTML = 'Until'; - this.maxLabel.htmlFor = `date_${this.name}_max`; + this.maxLabel.htmlFor = `${this.inputType}_${this.name}_max`; this.maxTdInput = L.DomUtil.create('td', '', this.maxTr); this.maxInput = L.DomUtil.create('input', '', this.maxTdInput); - this.maxInput.type = 'datetime-local'; + this.maxInput.type = this.inputType; this.maxInput.step = '0.001'; - this.maxInput.id = `date_${this.name}_max`; + this.maxInput.id = `${this.inputType}_${this.name}_max`; this.maxInput.valueAsNumber = (max.valueOf() - max.getTimezoneOffset() * 60000);; this.maxInput.dataset.value = max; @@ -826,7 +834,7 @@ L.FormBuilder.FacetSearchDate = L.FormBuilder.Element.extend({ toJS: function () { return { - 'type': 'date', + 'dataType': this.dataType, 'min': this.minInput.value, 'max': this.maxInput.value, }; diff --git a/umap/static/umap/js/umap.js b/umap/static/umap/js/umap.js index bdd2959d..956d45bd 100644 --- a/umap/static/umap/js/umap.js +++ b/umap/static/umap/js/umap.js @@ -1236,7 +1236,7 @@ U.Map = L.Map.extend({ { handler: 'Input', helpEntries: 'facetKey', - placeholder: L._('Example: key1,key2|Label 2,key3|Label 3|enum'), + placeholder: L._('Example: key1,key2|Label 2,key3|Label 3|enum|checkbox'), label: L._('Facet keys'), }, ], @@ -1846,12 +1846,25 @@ U.Map = L.Map.extend({ }, getFacetKeys: function () { + const allowedTypes = { + "enum": ["checkbox", "radio"], + "date": ["datetime-local"], + } + console.log(this.options.facetKey) return (this.options.facetKey || '').split(',').reduce((acc, curr) => { const els = curr.split('|') acc[els[0]] = { "label": els[1] || els[0], - "type": els[2] || "enum" + "dataType": ( + (els[2] in allowedTypes) ? els[2] : + Object.keys(allowedTypes)[0] + ) } + acc[els[0]]["inputType"] = ( + allowedTypes[acc[els[0]]["dataType"]].includes(els[3]) ? els[3] : + allowedTypes[acc[els[0]]["dataType"]][0] + ) + console.log(acc) return acc }, {}) },