feat: show last used pictograms in list

This feature was planned in the initial rework of the pictogram
form UI, but not yet done. Some recent discussion in the OSM French
forum reactivated the need for it.

cf https://forum.openstreetmap.fr/t/marker-and-marker-colors/21051
This commit is contained in:
Yohan Boniface 2024-02-12 14:09:23 +01:00
parent 188a535f7d
commit 126e47eef9
3 changed files with 48 additions and 25 deletions

View file

@ -537,9 +537,13 @@ L.FormBuilder.IconUrl = L.FormBuilder.BlurInput.extend({
this.on('define', this.onDefine)
},
onDefine: function () {
onDefine: async function () {
this.buttons.innerHTML = ''
this.footer.innerHTML = ''
const [{ pictogram_list }, response, error] = await this.builder.map.server.get(
this.builder.map.options.urls.pictogram_list_json
)
if (!error) this.pictogram_list = pictogram_list
this.buildTabs()
const value = this.value()
if (!value || L.Util.isPath(value)) this.showSymbolsTab()
@ -624,12 +628,11 @@ L.FormBuilder.IconUrl = L.FormBuilder.BlurInput.extend({
search = L.Util.normalize(this.searchInput.value),
title = pictogram.attribution
? `${pictogram.name} — © ${pictogram.attribution}`
: pictogram.name
: pictogram.name || pictogram.src
if (search && L.Util.normalize(title).indexOf(search) === -1) return
const className = value === this.value() ? `${baseClass} selected` : baseClass,
container = L.DomUtil.create('div', className, parent),
img = L.DomUtil.create('img', '', container)
img.src = value
container = L.DomUtil.create('div', className, parent)
U.Icon.makeIconElement(value, container)
container.title = title
L.DomEvent.on(
container,
@ -664,6 +667,15 @@ L.FormBuilder.IconUrl = L.FormBuilder.BlurInput.extend({
if (status) this.grid.appendChild(parent)
},
showLastUsed: function () {
if (U.Icon.LAST_USED.length) {
const items = U.Icon.LAST_USED.map((src) => ({
src,
}))
this.addCategory(L._('Used in this map'), items)
}
},
buildSymbolsList: function () {
this.grid.innerHTML = ''
const categories = {}
@ -676,6 +688,7 @@ L.FormBuilder.IconUrl = L.FormBuilder.BlurInput.extend({
const sorted = Object.entries(categories).toSorted(([a], [b]) =>
L.Util.naturalSort(a, b)
)
this.showLastUsed()
for (let [category, items] of sorted) {
this.addCategory(category, items)
}
@ -692,17 +705,7 @@ L.FormBuilder.IconUrl = L.FormBuilder.BlurInput.extend({
this.searchInput.placeholder = L._('Search')
this.grid = L.DomUtil.create('div', '', this.body)
L.DomEvent.on(this.searchInput, 'input', this.buildSymbolsList, this)
if (this.pictogram_list) {
this.buildSymbolsList()
} else {
const [{ pictogram_list }, response, error] = await this.builder.map.server.get(
this.builder.map.options.urls.pictogram_list_json
)
if (!error) {
this.pictogram_list = pictogram_list
this.buildSymbolsList()
}
}
this.buildSymbolsList()
},
showCharsTab: function () {
@ -989,10 +992,7 @@ L.FormBuilder.ManageEditors = L.FormBuilder.Element.extend({
on_select: L.bind(this.onSelect, this),
on_unselect: L.bind(this.onUnselect, this),
}
this.autocomplete = new U.AutoComplete.Ajax.SelectMultiple(
this.parentNode,
options
)
this.autocomplete = new U.AutoComplete.Ajax.SelectMultiple(this.parentNode, options)
this._values = this.toHTML()
if (this._values)
for (let i = 0; i < this._values.length; i++)

View file

@ -1,4 +1,7 @@
U.Icon = L.DivIcon.extend({
statics: {
LAST_USED: [],
},
initialize: function (map, options) {
this.map = map
const default_options = {
@ -14,11 +17,22 @@ U.Icon = L.DivIcon.extend({
}
},
_setLastUsed: function (url) {
if (L.Util.hasVar(url)) return
if (url === this.map.options.default_iconUrl) return
if (U.Icon.LAST_USED.indexOf(url) === -1) {
U.Icon.LAST_USED.push(url)
}
},
_getIconUrl: function (name) {
let url
if (this.feature && this.feature._getIconUrl(name))
if (this.feature && this.feature._getIconUrl(name)) {
url = this.feature._getIconUrl(name)
else url = this.options[`${name}Url`]
this._setLastUsed(url)
} else {
url = this.options[`${name}Url`]
}
return this.formatUrl(url, this.feature)
},
@ -216,7 +230,7 @@ U.Icon.setIconContrast = function (icon, parent, src, bgcolor) {
* src: the raw "icon" value, can be an URL, a path, text, emoticon, etc.
* bgcolor: the background color, used for caching and in case we cannot guess the
* parent background color
*/
*/
if (!icon) return
if (L.DomUtil.contrastedColor(parent, bgcolor)) {

View file

@ -94,7 +94,7 @@ def test_can_change_picto_at_datalayer_level(map, live_server, page, pictos):
expect(undefine).to_be_hidden()
define.click()
symbols = page.locator(".umap-pictogram-choice")
expect(symbols).to_have_count(2)
expect(symbols).to_have_count(3)
search = page.locator(".umap-pictogram-body input")
search.type("circle")
expect(symbols).to_have_count(1)
@ -128,7 +128,7 @@ def test_can_change_picto_at_marker_level(map, live_server, page, pictos):
expect(undefine).to_be_hidden()
define.click()
symbols = page.locator(".umap-pictogram-choice")
expect(symbols).to_have_count(2)
expect(symbols).to_have_count(3) # Include "Last used" picto
search = page.locator(".umap-pictogram-body input")
search.type("circle")
expect(symbols).to_have_count(1)
@ -157,6 +157,10 @@ def test_can_use_remote_url_as_picto(map, live_server, page, pictos):
define = page.locator(".umap-field-iconUrl .define")
expect(define).to_be_visible()
define.click()
expect(page.get_by_text("Used in this map")).to_be_hidden()
# Only default symbols
symbols = page.locator(".umap-pictogram-choice")
expect(symbols).to_have_count(2)
url_tab = page.get_by_role("button", name="URL")
input_el = page.get_by_placeholder("Add image URL")
expect(input_el).to_be_hidden()
@ -177,6 +181,11 @@ def test_can_use_remote_url_as_picto(map, live_server, page, pictos):
modify.click()
# Should be on URL tab
expect(input_el).to_be_visible()
# Symbol should be visible in symbols b
symbols_tab = page.get_by_role("button", name="Symbol")
symbols_tab.click()
expect(page.get_by_text("Used in this map")).to_be_visible()
expect(symbols).to_have_count(3)
def test_can_use_char_as_picto(map, live_server, page, pictos):