Support date properties in facet search
This commit is contained in:
parent
099a34f56a
commit
a2e3d6f327
5 changed files with 121 additions and 23 deletions
|
@ -663,22 +663,50 @@ const ControlsMixin = {
|
||||||
_openFacet: function () {
|
_openFacet: function () {
|
||||||
const container = L.DomUtil.create('div', 'umap-facet-search'),
|
const container = L.DomUtil.create('div', 'umap-facet-search'),
|
||||||
title = L.DomUtil.add('h3', 'umap-filter-title', container, L._('Facet search')),
|
title = L.DomUtil.add('h3', 'umap-filter-title', container, L._('Facet search')),
|
||||||
keys = Object.keys(this.getFacetKeys())
|
facetKeys = this.getFacetKeys(),
|
||||||
|
keys = Object.keys(facetKeys)
|
||||||
|
|
||||||
const knownValues = {}
|
const facetCriteria = {}
|
||||||
|
|
||||||
keys.forEach((key) => {
|
keys.forEach((key) => {
|
||||||
knownValues[key] = []
|
if (facetKeys[key]["type"] === "date") {
|
||||||
if (!this.facets[key]) this.facets[key] = []
|
if (!facetCriteria[key]) facetCriteria[key] = {
|
||||||
|
"min": undefined,
|
||||||
|
"max": undefined
|
||||||
|
}
|
||||||
|
if (!this.facets[key]) this.facets[key] = {
|
||||||
|
"type": facetKeys[key]["type"],
|
||||||
|
"min": undefined,
|
||||||
|
"max": undefined
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!facetCriteria[key]) facetCriteria[key] = {
|
||||||
|
"choices": []
|
||||||
|
}
|
||||||
|
if (!this.facets[key]) this.facets[key] = {
|
||||||
|
"type": facetKeys[key]["type"],
|
||||||
|
"choices": []
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
this.eachBrowsableDataLayer((datalayer) => {
|
this.eachBrowsableDataLayer((datalayer) => {
|
||||||
datalayer.eachFeature((feature) => {
|
datalayer.eachFeature((feature) => {
|
||||||
keys.forEach((key) => {
|
keys.forEach((key) => {
|
||||||
let value = feature.properties[key]
|
let value = feature.properties[key]
|
||||||
if (typeof value !== 'undefined' && !knownValues[key].includes(value)) {
|
if (facetKeys[key]["type"] === "date") {
|
||||||
knownValues[key].push(value)
|
value = feature.parseDateField(value)
|
||||||
}
|
if (!!value && (!facetCriteria[key]["min"] || facetCriteria[key]["min"] > value)) {
|
||||||
|
facetCriteria[key]["min"] = value
|
||||||
|
}
|
||||||
|
if (!!value && (!facetCriteria[key]["max"] || facetCriteria[key]["max"] < value)) {
|
||||||
|
facetCriteria[key]["max"] = value
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!!value && !facetCriteria[key]["choices"].includes(value)) {
|
||||||
|
facetCriteria[key]["choices"].push(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -694,12 +722,12 @@ const ControlsMixin = {
|
||||||
this.ui.alert({ content: L._('No results for these facets'), level: 'info' })
|
this.ui.alert({ content: L._('No results for these facets'), level: 'info' })
|
||||||
}
|
}
|
||||||
|
|
||||||
const fields = keys.map((current) => [
|
const fields = keys.map((key) => [
|
||||||
`facets.${current}`,
|
`facets.${key}`,
|
||||||
{
|
{
|
||||||
handler: 'FacetSearch',
|
handler: facetKeys[key]["type"] === "date" ? 'FacetSearchDate' : 'FacetSearchCheckbox',
|
||||||
choices: knownValues[current],
|
criteria: facetCriteria[key],
|
||||||
label: this.getFacetKeys()[current],
|
label: facetKeys[key]["label"]
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
const builder = new U.FormBuilder(this, fields, {
|
const builder = new U.FormBuilder(this, fields, {
|
||||||
|
|
|
@ -541,7 +541,7 @@ U.Help = L.Class.extend({
|
||||||
slugKey: L._('The name of the property to use as feature unique identifier.'),
|
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'),
|
filterKey: L._('Comma separated list of properties to use when filtering features'),
|
||||||
facetKey: L._(
|
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)'
|
'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 type, add it after another | (eg.: mykey|My Key|checkbox,otherkey|Other Key|date)'
|
||||||
),
|
),
|
||||||
interactive: L._(
|
interactive: L._(
|
||||||
'If false, the polygon or line will act as a part of the underlying map.'
|
'If false, the polygon or line will act as a part of the underlying map.'
|
||||||
|
|
|
@ -492,12 +492,30 @@ U.FeatureMixin = {
|
||||||
return false
|
return false
|
||||||
},
|
},
|
||||||
|
|
||||||
|
parseDateField: function (value) {
|
||||||
|
if (parseFloat(value).toString() === value.toString()) {
|
||||||
|
value = parseFloat(value);
|
||||||
|
if (value < 10000000000) {
|
||||||
|
value = value * 1000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new Date(value);
|
||||||
|
},
|
||||||
|
|
||||||
matchFacets: function () {
|
matchFacets: function () {
|
||||||
const facets = this.map.facets
|
const facets = this.map.facets
|
||||||
for (const [property, expected] of Object.entries(facets)) {
|
for (const [property, criteria] of Object.entries(facets)) {
|
||||||
if (expected.length) {
|
let value = this.properties[property]
|
||||||
let value = this.properties[property]
|
const type = criteria["type"]
|
||||||
if (!value || !expected.includes(value)) return false
|
if (type === "date") {
|
||||||
|
const min = new Date(criteria["min"])
|
||||||
|
const max = new Date(criteria["max"])
|
||||||
|
value = this.parseDateField(value)
|
||||||
|
if (!!min && (!value || min > value)) return false
|
||||||
|
if (!!max && (!value || max < value)) return false
|
||||||
|
} else {
|
||||||
|
const choices = criteria["choices"]
|
||||||
|
if (choices.length && (!value || !choices.includes(value))) return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
|
|
|
@ -744,11 +744,11 @@ L.FormBuilder.Switch = L.FormBuilder.CheckBox.extend({
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
L.FormBuilder.FacetSearch = L.FormBuilder.Element.extend({
|
L.FormBuilder.FacetSearchCheckbox = L.FormBuilder.Element.extend({
|
||||||
build: function () {
|
build: function () {
|
||||||
this.container = L.DomUtil.create('div', 'umap-facet', this.parentNode)
|
this.container = L.DomUtil.create('div', 'umap-facet', this.parentNode)
|
||||||
this.ul = L.DomUtil.create('ul', '', this.container)
|
this.ul = L.DomUtil.create('ul', '', this.container)
|
||||||
const choices = this.options.choices
|
const choices = this.options.criteria["choices"]
|
||||||
choices.sort()
|
choices.sort()
|
||||||
choices.forEach((value) => this.buildLi(value))
|
choices.forEach((value) => this.buildLi(value))
|
||||||
},
|
},
|
||||||
|
@ -763,7 +763,7 @@ L.FormBuilder.FacetSearch = L.FormBuilder.Element.extend({
|
||||||
label = L.DomUtil.create('label', '', property_li)
|
label = L.DomUtil.create('label', '', property_li)
|
||||||
input.type = 'checkbox'
|
input.type = 'checkbox'
|
||||||
input.id = `checkbox_${this.name}_${value}`
|
input.id = `checkbox_${this.name}_${value}`
|
||||||
input.checked = this.get().includes(value)
|
input.checked = this.get()['choices'].includes(value)
|
||||||
input.dataset.value = value
|
input.dataset.value = value
|
||||||
label.htmlFor = `checkbox_${this.name}_${value}`
|
label.htmlFor = `checkbox_${this.name}_${value}`
|
||||||
label.innerHTML = value
|
label.innerHTML = value
|
||||||
|
@ -771,10 +771,59 @@ L.FormBuilder.FacetSearch = L.FormBuilder.Element.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
toJS: function () {
|
toJS: function () {
|
||||||
return [...this.ul.querySelectorAll('input:checked')].map((i) => i.dataset.value)
|
return {
|
||||||
|
'type': 'checkbox',
|
||||||
|
'choices': [...this.ul.querySelectorAll('input:checked')].map((i) => i.dataset.value)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
L.FormBuilder.FacetSearchDate = L.FormBuilder.Element.extend({
|
||||||
|
build: function () {
|
||||||
|
this.container = L.DomUtil.create('div', 'umap-facet', this.parentNode);
|
||||||
|
const min = this.options.criteria['min'];
|
||||||
|
const max = this.options.criteria['max'];
|
||||||
|
|
||||||
|
// Create labels for min and max inputs
|
||||||
|
this.minLabel = L.DomUtil.create('label', '', this.container);
|
||||||
|
this.minLabel.innerHTML = 'Start';
|
||||||
|
this.minLabel.htmlFor = `date_${this.name}_min`;
|
||||||
|
|
||||||
|
this.minInput = L.DomUtil.create('input', '', this.container);
|
||||||
|
this.minInput.type = 'datetime-local';
|
||||||
|
this.minInput.step = '0.001';
|
||||||
|
this.minInput.id = `date_${this.name}_min`;
|
||||||
|
this.minInput.valueAsNumber = (min.valueOf() - min.getTimezoneOffset() * 60000);;
|
||||||
|
this.minInput.dataset.value = min;
|
||||||
|
|
||||||
|
this.maxLabel = L.DomUtil.create('label', '', this.container);
|
||||||
|
this.maxLabel.innerHTML = 'End';
|
||||||
|
this.maxLabel.htmlFor = `date_${this.name}_max`;
|
||||||
|
|
||||||
|
this.maxInput = L.DomUtil.create('input', '', this.container);
|
||||||
|
this.maxInput.type = 'datetime-local';
|
||||||
|
this.maxInput.step = '0.001';
|
||||||
|
this.maxInput.id = `date_${this.name}_max`;
|
||||||
|
this.maxInput.valueAsNumber = (max.valueOf() - max.getTimezoneOffset() * 60000);;
|
||||||
|
this.maxInput.dataset.value = max;
|
||||||
|
|
||||||
|
L.DomEvent.on(this.minInput, 'change', (e) => this.sync());
|
||||||
|
L.DomEvent.on(this.maxInput, 'change', (e) => this.sync());
|
||||||
|
},
|
||||||
|
|
||||||
|
buildLabel: function () {
|
||||||
|
this.label = L.DomUtil.add('h5', '', this.parentNode, this.options.label)
|
||||||
|
},
|
||||||
|
|
||||||
|
toJS: function () {
|
||||||
|
return {
|
||||||
|
'type': 'date',
|
||||||
|
'min': this.minInput.value,
|
||||||
|
'max': this.maxInput.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',
|
||||||
|
|
|
@ -1236,7 +1236,7 @@ U.Map = L.Map.extend({
|
||||||
{
|
{
|
||||||
handler: 'Input',
|
handler: 'Input',
|
||||||
helpEntries: 'facetKey',
|
helpEntries: 'facetKey',
|
||||||
placeholder: L._('Example: key1,key2,key3'),
|
placeholder: L._('Example: key1,key2|Label 2,key3|Label 3|checkbox'),
|
||||||
label: L._('Facet keys'),
|
label: L._('Facet keys'),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -1848,7 +1848,10 @@ U.Map = L.Map.extend({
|
||||||
getFacetKeys: function () {
|
getFacetKeys: function () {
|
||||||
return (this.options.facetKey || '').split(',').reduce((acc, curr) => {
|
return (this.options.facetKey || '').split(',').reduce((acc, curr) => {
|
||||||
const els = curr.split('|')
|
const els = curr.split('|')
|
||||||
acc[els[0]] = els[1] || els[0]
|
acc[els[0]] = {
|
||||||
|
"label": els[1] || els[0],
|
||||||
|
"type": els[2] || "checkbox"
|
||||||
|
}
|
||||||
return acc
|
return acc
|
||||||
}, {})
|
}, {})
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in a new issue