diff --git a/umap/static/umap/js/modules/autocomplete.js b/umap/static/umap/js/modules/autocomplete.js index 31d5d196..e693d6be 100644 --- a/umap/static/umap/js/modules/autocomplete.js +++ b/umap/static/umap/js/modules/autocomplete.js @@ -6,6 +6,7 @@ import { } from '../../vendors/leaflet/leaflet-src.esm.js' import { translate } from './i18n.js' import { Request, ServerRequest } from './request.js' +import { escapeHTML, generateId } from './utils.js' export class BaseAutocomplete { constructor(el, options) { @@ -46,7 +47,7 @@ export class BaseAutocomplete { placeholder: this.options.placeholder, autocomplete: 'off', 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, 'keyup', this.onKeyUp, this) @@ -350,3 +351,19 @@ export const MultipleMixin = (Base) => export class AjaxAutocompleteMultiple extends MultipleMixin(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) => ``) + .join('') + } +} diff --git a/umap/static/umap/js/modules/global.js b/umap/static/umap/js/modules/global.js index fe2ef751..1f62f927 100644 --- a/umap/static/umap/js/modules/global.js +++ b/umap/static/umap/js/modules/global.js @@ -3,7 +3,7 @@ import { uMapAlertConflict as AlertConflict, uMapAlertCreation as AlertCreation, } from '../components/alerts/alert.js' -import { AjaxAutocomplete, AjaxAutocompleteMultiple } from './autocomplete.js' +import { AjaxAutocomplete, AjaxAutocompleteMultiple, AutocompleteDatalist } from './autocomplete.js' import Browser from './browser.js' import Caption from './caption.js' import Facets from './facets.js' @@ -30,6 +30,7 @@ window.U = { AlertConflict, AjaxAutocomplete, AjaxAutocompleteMultiple, + AutocompleteDatalist, Browser, Caption, Dialog, diff --git a/umap/static/umap/js/modules/ui/dialog.js b/umap/static/umap/js/modules/ui/dialog.js index 798ef167..25f869fd 100644 --- a/umap/static/umap/js/modules/ui/dialog.js +++ b/umap/static/umap/js/modules/ui/dialog.js @@ -181,8 +181,8 @@ export default class Dialog { return this.open(Object.assign({}, config, { message, template: false })) } - prompt(message, fallback = '', config = {}) { - const template = `` + prompt(message, config = {}) { + const template = `` return this.open(Object.assign({}, config, { message, template })) } } diff --git a/umap/static/umap/js/umap.layer.js b/umap/static/umap/js/umap.layer.js index 857befe0..852e4b07 100644 --- a/umap/static/umap/js/umap.layer.js +++ b/umap/static/umap/js/umap.layer.js @@ -919,6 +919,13 @@ U.DataLayer = L.Evented.extend({ 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) { try { // Do not fail if remote data is somehow invalid, diff --git a/umap/static/umap/js/umap.tableeditor.js b/umap/static/umap/js/umap.tableeditor.js index 509d246d..c9b4c7fe 100644 --- a/umap/static/umap/js/umap.tableeditor.js +++ b/umap/static/umap/js/umap.tableeditor.js @@ -101,14 +101,29 @@ U.TableEditor = L.Class.extend({ }) }, - refine: function () { - const promise = new U.Prompt().open({ - className: 'dark', - title: L._('Deleting rows matching condition'), + deleteRows: function () { + const dialog = this.datalayer.map.dialog + const promise = dialog.prompt(L._('Deleting rows matching condition')) + + const autocomplete = new U.AutocompleteDatalist( + dialog.dialog.querySelector('[name=prompt]') + ) + autocomplete.suggestions = this.datalayer._propertiesIndex + autocomplete.input.addEventListener('input', (event) => { + const value = event.target.value + if (this.datalayer._propertiesIndex.includes(value)) { + autocomplete.suggestions = [`${value}=`, `${value}!=`, `${value}>`, `${value}<`] + } else if (value.endsWith('=')) { + const key = value.split('!')[0].split('=')[0] + autocomplete.suggestions = this.datalayer + .sortedValues(key) + .map((str) => `${value}${str || ''}`) + } }) - promise.then((raw) => { - if (!raw) return - const rule = new U.Rule(raw) + + promise.then(({ prompt }) => { + if (!prompt) return + const rule = new U.Rule(prompt) const matched = [] this.datalayer.eachLayer((feature) => { if (rule.match(feature.properties)) { @@ -148,7 +163,7 @@ U.TableEditor = L.Class.extend({ L.DomUtil.createIcon(refineButton, 'icon-add'), refineButton.firstChild ) - L.DomEvent.on(refineButton, 'click', this.refine, this) + L.DomEvent.on(refineButton, 'click', this.deleteRows, this) this.datalayer.map.fullPanel.open({ content: this.table, className: 'umap-table-editor',