Add Pictogram.category and list pictos grouped by category

This commit is contained in:
Yohan Boniface 2023-10-30 21:07:55 +01:00
parent 4643930870
commit 8c774fb7b3
5 changed files with 71 additions and 31 deletions

View file

@ -0,0 +1,17 @@
# Generated by Django 4.2.2 on 2023-10-30 17:22
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("umap", "0015_alter_pictogram_pictogram"),
]
operations = [
migrations.AddField(
model_name="pictogram",
name="category",
field=models.CharField(blank=True, max_length=300, null=True),
),
]

View file

@ -284,6 +284,7 @@ class Pictogram(NamedModel):
""" """
attribution = models.CharField(max_length=300) attribution = models.CharField(max_length=300)
category = models.CharField(max_length=300, null=True, blank=True)
pictogram = models.FileField(upload_to="pictogram") pictogram = models.FileField(upload_to="pictogram")
@property @property
@ -292,6 +293,7 @@ class Pictogram(NamedModel):
"id": self.pk, "id": self.pk,
"attribution": self.attribution, "attribution": self.attribution,
"name": self.name, "name": self.name,
"category": self.category,
"src": self.pictogram.url, "src": self.pictogram.url,
} }

View file

@ -521,16 +521,14 @@ i.info {
margin-top: -8px; margin-top: -8px;
padding: 0 5px; padding: 0 5px;
} }
.umap-icon-list, .umap-pictogram-list { .umap-pictogram-grid {
clear: both; display: flex;
flex-wrap: wrap;
} }
.umap-icon-choice { .umap-pictogram-choice {
display: block;
float: left;
width: 30px; width: 30px;
height: 30px; height: 30px;
line-height: 30px; line-height: 30px;
position: relative;
cursor: pointer; cursor: pointer;
background-image: url('./img/icon-bg.png'); background-image: url('./img/icon-bg.png');
text-align: center; text-align: center;
@ -538,16 +536,16 @@ i.info {
margin-bottom: 5px; margin-bottom: 5px;
margin-right: 5px; margin-right: 5px;
} }
.umap-icon-choice img { .umap-pictogram-choice img {
vertical-align: middle; vertical-align: middle;
max-width: 24px; max-width: 24px;
} }
.umap-icon-choice:hover, .umap-pictogram-choice:hover,
.umap-icon-choice.selected, .umap-pictogram-choice.selected,
.umap-color-picker span:hover { .umap-color-picker span:hover {
box-shadow: 0 0 4px 0 black; box-shadow: 0 0 4px 0 black;
} }
.umap-icon-choice .leaflet-marker-icon { .umap-pictogram-choice .leaflet-marker-icon {
bottom: 0; bottom: 0;
left: 30px; left: 30px;
position: absolute; position: absolute;

View file

@ -201,6 +201,16 @@ L.Util.greedyTemplate = (str, data, ignore) => {
) )
} }
L.Util.naturalSort = (a, b) => {
return a
.toString()
.toLowerCase()
.localeCompare(b.toString().toLowerCase(), L.lang || 'en', {
sensitivity: 'base',
numeric: true,
})
}
L.Util.sortFeatures = (features, sortKey) => { L.Util.sortFeatures = (features, sortKey) => {
const sortKeys = (sortKey || 'name').split(',') const sortKeys = (sortKey || 'name').split(',')
@ -214,19 +224,9 @@ L.Util.sortFeatures = (features, sortKey) => {
let score let score
const valA = a.properties[sortKey] || '' const valA = a.properties[sortKey] || ''
const valB = b.properties[sortKey] || '' const valB = b.properties[sortKey] || ''
if (!valA) { if (!valA) score = -1
score = -1 else if (!valB) score = 1
} else if (!valB) { else score = L.Util.naturalSort(valA, valB)
score = 1
} else {
score = valA
.toString()
.toLowerCase()
.localeCompare(valB.toString().toLowerCase(), L.lang || 'en', {
sensitivity: 'base',
numeric: true,
})
}
if (score === 0 && sortKeys[i + 1]) return sort(a, b, i + 1) if (score === 0 && sortKeys[i + 1]) return sort(a, b, i + 1)
return score * reverse return score * reverse
} }

View file

@ -547,7 +547,7 @@ L.FormBuilder.IconUrl = L.FormBuilder.BlurInput.extend({
const img = L.DomUtil.create( const img = L.DomUtil.create(
'img', 'img',
'', '',
L.DomUtil.create('div', 'umap-icon-choice', this.buttonsContainer) L.DomUtil.create('div', 'umap-pictogram-choice', this.buttonsContainer)
) )
img.src = this.value() img.src = this.value()
L.DomEvent.on(img, 'click', this.fetchIconList, this) L.DomEvent.on(img, 'click', this.fetchIconList, this)
@ -555,7 +555,7 @@ L.FormBuilder.IconUrl = L.FormBuilder.BlurInput.extend({
const el = L.DomUtil.create( const el = L.DomUtil.create(
'span', 'span',
'', '',
L.DomUtil.create('div', 'umap-icon-choice', this.buttonsContainer) L.DomUtil.create('div', 'umap-pictogram-choice', this.buttonsContainer)
) )
el.textContent = this.value() el.textContent = this.value()
L.DomEvent.on(el, 'click', this.fetchIconList, this) L.DomEvent.on(el, 'click', this.fetchIconList, this)
@ -570,11 +570,11 @@ L.FormBuilder.IconUrl = L.FormBuilder.BlurInput.extend({
) )
}, },
addIconPreview: function (pictogram) { addIconPreview: function (pictogram, parent) {
const baseClass = 'umap-icon-choice', const baseClass = 'umap-pictogram-choice',
value = pictogram.src, value = pictogram.src,
className = value === this.value() ? `${baseClass} selected` : baseClass, className = value === this.value() ? `${baseClass} selected` : baseClass,
container = L.DomUtil.create('div', className, this.pictogramsContainer), container = L.DomUtil.create('div', className, parent),
img = L.DomUtil.create('img', '', container) img = L.DomUtil.create('img', '', container)
img.src = value img.src = value
if (pictogram.name && pictogram.attribution) { if (pictogram.name && pictogram.attribution) {
@ -602,7 +602,7 @@ L.FormBuilder.IconUrl = L.FormBuilder.BlurInput.extend({
}, },
search: function (e) { search: function (e) {
const icons = [...this.parentNode.querySelectorAll('.umap-icon-choice')], const icons = [...this.parentNode.querySelectorAll('.umap-pictogram-choice')],
search = this.searchInput.value.toLowerCase() search = this.searchInput.value.toLowerCase()
icons.forEach((el) => { icons.forEach((el) => {
if (el.title.toLowerCase().indexOf(search) != -1) el.style.display = 'block' if (el.title.toLowerCase().indexOf(search) != -1) el.style.display = 'block'
@ -610,13 +610,36 @@ L.FormBuilder.IconUrl = L.FormBuilder.BlurInput.extend({
}) })
}, },
addCategory: function (category, items) {
const parent = L.DomUtil.create(
'div',
'umap-pictogram-category',
this.pictogramsContainer
),
title = L.DomUtil.add('h6', '', parent, category),
grid = L.DomUtil.create('div', 'umap-pictogram-grid', parent)
for (let item of items) {
this.addIconPreview(item, grid)
}
},
buildIconList: function (data) { buildIconList: function (data) {
this.searchInput = L.DomUtil.create('input', '', this.pictogramsContainer) this.searchInput = L.DomUtil.create('input', '', this.pictogramsContainer)
this.searchInput.type = 'search' this.searchInput.type = 'search'
this.searchInput.placeholder = L._('Search') this.searchInput.placeholder = L._('Search')
L.DomEvent.on(this.searchInput, 'input', this.search, this) L.DomEvent.on(this.searchInput, 'input', this.search, this)
for (const idx in data.pictogram_list) { const categories = {}
this.addIconPreview(data.pictogram_list[idx]) let category
for (const props of data.pictogram_list) {
category = props.category || L._('Generic')
categories[category] = categories[category] || []
categories[category].push(props)
}
const sorted = Object.entries(categories).toSorted(([a], [b]) =>
L.Util.naturalSort(a, b)
)
for (let [category, items] of sorted) {
this.addCategory(category, items)
} }
const closeButton = L.DomUtil.createButton( const closeButton = L.DomUtil.createButton(
'button action-button', 'button action-button',