mirror of
https://github.com/umap-project/umap.git
synced 2025-04-29 11:52:38 +02:00
feat: add AutocompleteDatalist and use it for conditional rules
This commit is contained in:
parent
45b9db4242
commit
7baa70d1b6
6 changed files with 72 additions and 3 deletions
|
@ -6,6 +6,7 @@ import {
|
||||||
} from '../../vendors/leaflet/leaflet-src.esm.js'
|
} from '../../vendors/leaflet/leaflet-src.esm.js'
|
||||||
import { translate } from './i18n.js'
|
import { translate } from './i18n.js'
|
||||||
import { Request, ServerRequest } from './request.js'
|
import { Request, ServerRequest } from './request.js'
|
||||||
|
import { escapeHTML, generateId } from './utils.js'
|
||||||
|
|
||||||
export class BaseAutocomplete {
|
export class BaseAutocomplete {
|
||||||
constructor(el, options) {
|
constructor(el, options) {
|
||||||
|
@ -46,7 +47,7 @@ export class BaseAutocomplete {
|
||||||
placeholder: this.options.placeholder,
|
placeholder: this.options.placeholder,
|
||||||
autocomplete: 'off',
|
autocomplete: 'off',
|
||||||
className: this.options.className,
|
className: this.options.className,
|
||||||
name: this.options.name || 'autocomplete'
|
name: this.options.name || 'autocomplete',
|
||||||
})
|
})
|
||||||
DomEvent.on(this.input, 'keydown', this.onKeyDown, this)
|
DomEvent.on(this.input, 'keydown', this.onKeyDown, this)
|
||||||
DomEvent.on(this.input, 'keyup', this.onKeyUp, this)
|
DomEvent.on(this.input, 'keyup', this.onKeyUp, this)
|
||||||
|
@ -350,3 +351,19 @@ export const MultipleMixin = (Base) =>
|
||||||
export class AjaxAutocompleteMultiple extends MultipleMixin(BaseServerAjax) {}
|
export class AjaxAutocompleteMultiple extends MultipleMixin(BaseServerAjax) {}
|
||||||
|
|
||||||
export class AjaxAutocomplete extends SingleMixin(BaseServerAjax) {}
|
export class AjaxAutocomplete extends SingleMixin(BaseServerAjax) {}
|
||||||
|
|
||||||
|
export class AutocompleteDatalist {
|
||||||
|
constructor(input) {
|
||||||
|
this.input = input
|
||||||
|
this.datalist = document.createElement('datalist')
|
||||||
|
this.datalist.id = generateId()
|
||||||
|
this.input.setAttribute('list', this.datalist.id)
|
||||||
|
this.input.parentElement.appendChild(this.datalist)
|
||||||
|
}
|
||||||
|
|
||||||
|
set suggestions(values) {
|
||||||
|
this.datalist.innerHTML = values
|
||||||
|
.map((value) => `<option>${escapeHTML(value)}</option>`)
|
||||||
|
.join('')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ import {
|
||||||
uMapAlertConflict as AlertConflict,
|
uMapAlertConflict as AlertConflict,
|
||||||
uMapAlertCreation as AlertCreation,
|
uMapAlertCreation as AlertCreation,
|
||||||
} from '../components/alerts/alert.js'
|
} from '../components/alerts/alert.js'
|
||||||
import { AjaxAutocomplete, AjaxAutocompleteMultiple } from './autocomplete.js'
|
import { AjaxAutocomplete, AjaxAutocompleteMultiple, AutocompleteDatalist } from './autocomplete.js'
|
||||||
import Browser from './browser.js'
|
import Browser from './browser.js'
|
||||||
import Caption from './caption.js'
|
import Caption from './caption.js'
|
||||||
import Facets from './facets.js'
|
import Facets from './facets.js'
|
||||||
|
@ -33,6 +33,7 @@ window.U = {
|
||||||
AlertConflict,
|
AlertConflict,
|
||||||
AjaxAutocomplete,
|
AjaxAutocomplete,
|
||||||
AjaxAutocompleteMultiple,
|
AjaxAutocompleteMultiple,
|
||||||
|
AutocompleteDatalist,
|
||||||
Browser,
|
Browser,
|
||||||
Caption,
|
Caption,
|
||||||
Dialog,
|
Dialog,
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { DomEvent, DomUtil, stamp } from '../../vendors/leaflet/leaflet-src.esm.js'
|
import { DomEvent, DomUtil, stamp } from '../../vendors/leaflet/leaflet-src.esm.js'
|
||||||
import { translate } from './i18n.js'
|
import { translate } from './i18n.js'
|
||||||
import * as Utils from './utils.js'
|
import * as Utils from './utils.js'
|
||||||
|
import { AutocompleteDatalist } from './autocomplete.js'
|
||||||
|
|
||||||
class Rule {
|
class Rule {
|
||||||
get condition() {
|
get condition() {
|
||||||
|
@ -123,7 +124,18 @@ class Rule {
|
||||||
const builder = new U.FormBuilder(this, options)
|
const builder = new U.FormBuilder(this, options)
|
||||||
const defaultShapeProperties = DomUtil.add('div', '', container)
|
const defaultShapeProperties = DomUtil.add('div', '', container)
|
||||||
defaultShapeProperties.appendChild(builder.build())
|
defaultShapeProperties.appendChild(builder.build())
|
||||||
|
const autocomplete = new AutocompleteDatalist(builder.helpers.condition.input)
|
||||||
|
const properties = this.map.allProperties()
|
||||||
|
autocomplete.suggestions = properties
|
||||||
|
autocomplete.input.addEventListener('input', (event) => {
|
||||||
|
const value = event.target.value
|
||||||
|
if (properties.includes(value)) {
|
||||||
|
autocomplete.suggestions = [`${value}=`, `${value}!=`, `${value}>`, `${value}<`]
|
||||||
|
} else if (value.endsWith('=')) {
|
||||||
|
const key = value.split('!')[0].split('=')[0]
|
||||||
|
autocomplete.suggestions = this.map.sortedValues(key).map((str) => `${value}${str || ''}`)
|
||||||
|
}
|
||||||
|
})
|
||||||
this.map.editPanel.open({ content: container })
|
this.map.editPanel.open({ content: container })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1895,4 +1895,15 @@ U.Map = L.Map.extend({
|
||||||
})
|
})
|
||||||
await this.server.post(sendLink, {}, formData)
|
await this.server.post(sendLink, {}, formData)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
allProperties: function () {
|
||||||
|
return [].concat(...this.datalayers_index.map((dl) => dl._propertiesIndex))
|
||||||
|
},
|
||||||
|
|
||||||
|
sortedValues: function (property) {
|
||||||
|
return []
|
||||||
|
.concat(...this.datalayers_index.map((dl) => dl.sortedValues(property)))
|
||||||
|
.filter((val, idx, arr) => arr.indexOf(val) === idx)
|
||||||
|
.sort(U.Utils.naturalSort)
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
|
@ -1052,6 +1052,13 @@ U.DataLayer = L.Evented.extend({
|
||||||
if (idx !== -1) this._propertiesIndex.splice(idx, 1)
|
if (idx !== -1) this._propertiesIndex.splice(idx, 1)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
sortedValues: function (property) {
|
||||||
|
return Object.values(this._layers)
|
||||||
|
.map((feature) => feature.properties[property])
|
||||||
|
.filter((val, idx, arr) => arr.indexOf(val) === idx)
|
||||||
|
.sort(U.Utils.naturalSort)
|
||||||
|
},
|
||||||
|
|
||||||
addData: function (geojson, sync) {
|
addData: function (geojson, sync) {
|
||||||
try {
|
try {
|
||||||
// Do not fail if remote data is somehow invalid,
|
// Do not fail if remote data is somehow invalid,
|
||||||
|
|
|
@ -199,3 +199,24 @@ def test_can_deactive_rule_from_list(live_server, page, openmap):
|
||||||
page.get_by_role("button", name="Show/hide layer").click()
|
page.get_by_role("button", name="Show/hide layer").click()
|
||||||
colors = getColors(markers)
|
colors = getColors(markers)
|
||||||
assert colors.count("rgb(240, 248, 255)") == 2
|
assert colors.count("rgb(240, 248, 255)") == 2
|
||||||
|
|
||||||
|
|
||||||
|
def test_autocomplete_datalist(live_server, page, openmap):
|
||||||
|
DataLayerFactory(map=openmap, data=DATALAYER_DATA1)
|
||||||
|
page.goto(f"{live_server.url}{openmap.get_absolute_url()}?edit#6/48.948/1.670")
|
||||||
|
page.get_by_role("link", name="Map advanced properties").click()
|
||||||
|
page.locator("summary").filter(has_text="Conditional style rules").click()
|
||||||
|
page.get_by_role("button", name="Add rule").click()
|
||||||
|
panel = page.locator(".panel.right.on")
|
||||||
|
datalist = panel.locator(".umap-field-condition datalist option")
|
||||||
|
expect(datalist).to_have_count(5)
|
||||||
|
values = {option.inner_text() for option in datalist.all()}
|
||||||
|
assert values == {"myboolean", "mytype", "mynumber", "mydate", "name"}
|
||||||
|
page.get_by_placeholder("key=value or key!=value").fill("mytype")
|
||||||
|
expect(datalist).to_have_count(4)
|
||||||
|
values = {option.inner_text() for option in datalist.all()}
|
||||||
|
assert values == {"mytype=", "mytype!=", "mytype>", "mytype<"}
|
||||||
|
page.get_by_placeholder("key=value or key!=value").fill("mytype=")
|
||||||
|
expect(datalist).to_have_count(2)
|
||||||
|
values = {option.inner_text() for option in datalist.all()}
|
||||||
|
assert values == {"mytype=even", "mytype=odd"}
|
||||||
|
|
Loading…
Reference in a new issue