From 39f38a9cdf62866407b4a88ee8c21f9ac10d8b50 Mon Sep 17 00:00:00 2001 From: Yohan Boniface Date: Wed, 26 Feb 2025 19:32:16 +0100 Subject: [PATCH 01/11] feat: add Map.tags and allow to edit from client cf #2283 --- umap/forms.py | 3 +-- umap/migrations/0027_map_tags.py | 23 +++++++++++++++++++++ umap/models.py | 5 ++++- umap/settings/base.py | 14 +++++++++++++ umap/static/umap/js/modules/form/builder.js | 2 ++ umap/static/umap/js/modules/form/fields.js | 23 +++++++++++++++++++-- umap/static/umap/js/modules/schema.js | 3 +++ umap/static/umap/js/modules/umap.js | 7 +++++++ umap/tests/integration/test_edit_map.py | 15 ++++++++++++++ 9 files changed, 90 insertions(+), 5 deletions(-) create mode 100644 umap/migrations/0027_map_tags.py diff --git a/umap/forms.py b/umap/forms.py index e982799e..b9503d76 100644 --- a/umap/forms.py +++ b/umap/forms.py @@ -4,7 +4,6 @@ from django.contrib.auth import get_user_model from django.contrib.gis.geos import Point from django.forms.utils import ErrorList from django.template.defaultfilters import slugify -from django.utils.translation import gettext_lazy as _ from .models import DataLayer, Map, Team @@ -92,7 +91,7 @@ class MapSettingsForm(forms.ModelForm): return self.cleaned_data["center"] class Meta: - fields = ("settings", "name", "center", "slug") + fields = ("settings", "name", "center", "slug", "tags") model = Map diff --git a/umap/migrations/0027_map_tags.py b/umap/migrations/0027_map_tags.py new file mode 100644 index 00000000..e55d45e2 --- /dev/null +++ b/umap/migrations/0027_map_tags.py @@ -0,0 +1,23 @@ +# Generated by Django 5.1.6 on 2025-02-26 16:18 + +import django.contrib.postgres.fields +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("umap", "0026_datalayer_modified_at_datalayer_share_status"), + ] + + operations = [ + migrations.AddField( + model_name="map", + name="tags", + field=django.contrib.postgres.fields.ArrayField( + base_field=models.CharField(max_length=200), + blank=True, + default=list, + size=None, + ), + ), + ] diff --git a/umap/models.py b/umap/models.py index 36404794..66851787 100644 --- a/umap/models.py +++ b/umap/models.py @@ -4,6 +4,7 @@ import uuid from django.conf import settings from django.contrib.auth.models import User from django.contrib.gis.db import models +from django.contrib.postgres.fields import ArrayField from django.core.files.base import File from django.core.files.storage import storages from django.core.signing import Signer @@ -236,6 +237,7 @@ class Map(NamedModel): settings = models.JSONField( blank=True, null=True, verbose_name=_("settings"), default=dict ) + tags = ArrayField(models.CharField(max_length=200), blank=True, default=list) objects = models.Manager() public = PublicManager() @@ -420,7 +422,8 @@ class Map(NamedModel): return { "iconUrl": { "default": "%sumap/img/marker.svg" % settings.STATIC_URL, - } + }, + "tags": {"choices": settings.UMAP_TAGS}, } diff --git a/umap/settings/base.py b/umap/settings/base.py index ecb51326..ac1ff865 100644 --- a/umap/settings/base.py +++ b/umap/settings/base.py @@ -6,6 +6,7 @@ from email.utils import parseaddr import environ from django.conf.locale import LANG_INFO +from django.utils.translation import gettext_lazy as _ import umap as project_module @@ -290,6 +291,19 @@ UMAP_HOME_FEED = "latest" UMAP_IMPORTERS = {} UMAP_HOST_INFOS = {} UMAP_LABEL_KEYS = ["name", "title"] +UMAP_TAGS = ( + ("art", _("Art and Culture")), + ("bike", _("Bike")), + ("environment", _("Environment")), + ("education", _("Education")), + ("food", _("Food and Agriculture")), + ("history", _("History")), + ("public", _("Public sector")), + ("sport", _("Sport and Leisure")), + ("travel", _("Travel")), + ("trekking", _("Trekking")), + ("tourism", _("Tourism")), +) UMAP_READONLY = env("UMAP_READONLY", default=False) UMAP_GZIP = True diff --git a/umap/static/umap/js/modules/form/builder.js b/umap/static/umap/js/modules/form/builder.js index 88698d39..70d0926c 100644 --- a/umap/static/umap/js/modules/form/builder.js +++ b/umap/static/umap/js/modules/form/builder.js @@ -138,6 +138,8 @@ export class MutatingForm extends Form { } else if (properties.type === Number) { if (properties.step) properties.handler = 'Range' else properties.handler = 'IntInput' + } else if (properties.type === Array) { + properties.handler = 'CheckBoxes' } else if (properties.choices) { const text_length = properties.choices.reduce( (acc, [_, label]) => acc + label.length, diff --git a/umap/static/umap/js/modules/form/fields.js b/umap/static/umap/js/modules/form/fields.js index 5a8be8d6..8d9903b2 100644 --- a/umap/static/umap/js/modules/form/fields.js +++ b/umap/static/umap/js/modules/form/fields.js @@ -324,6 +324,24 @@ Fields.CheckBox = class extends BaseElement { } } +Fields.CheckBoxes = class extends BaseElement { + build() { + const initial = this.get() || [] + for (const [value, label] of this.properties.choices) { + const tpl = `` + const [root, { input }] = Utils.loadTemplateWithRefs(tpl) + this.container.appendChild(root) + input.checked = initial.includes(value) + input.addEventListener('change', () => this.sync()) + } + super.build() + } + + value() { + return Array.from(this.root.querySelectorAll('input:checked')).map((el) => el.value) + } +} + Fields.Select = class extends BaseElement { getTemplate() { return `` @@ -1296,12 +1314,13 @@ Fields.ManageEditors = class extends BaseElement { placeholder: translate("Type editor's username"), } this.autocomplete = new AjaxAutocompleteMultiple(this.container, options) - this._values = this.toHTML() - if (this._values) + this._values = this.toHTML() || [] + if (this._values) { for (let i = 0; i < this._values.length; i++) this.autocomplete.displaySelected({ item: { value: this._values[i].id, label: this._values[i].name }, }) + } } value() { diff --git a/umap/static/umap/js/modules/schema.js b/umap/static/umap/js/modules/schema.js index 24a44d0b..1deac31b 100644 --- a/umap/static/umap/js/modules/schema.js +++ b/umap/static/umap/js/modules/schema.js @@ -516,6 +516,9 @@ export const SCHEMA = { helpEntries: ['sync'], default: false, }, + tags: { + type: Array, + }, team: { type: Object, }, diff --git a/umap/static/umap/js/modules/umap.js b/umap/static/umap/js/modules/umap.js index 82733692..426cc30a 100644 --- a/umap/static/umap/js/modules/umap.js +++ b/umap/static/umap/js/modules/umap.js @@ -755,6 +755,12 @@ export default class Umap { const form = builder.build() container.appendChild(form) + const tags = DomUtil.createFieldset(container, translate('Tags')) + const tagsFields = ['properties.tags'] + const tagsBuilder = new MutatingForm(this, tagsFields, { + umap: this, + }) + tags.appendChild(tagsBuilder.build()) const credits = DomUtil.createFieldset(container, translate('Credits')) const creditsFields = [ 'properties.licence', @@ -1168,6 +1174,7 @@ export default class Umap { const formData = new FormData() formData.append('name', this.properties.name) formData.append('center', JSON.stringify(this.geometry())) + formData.append('tags', this.properties.tags || []) formData.append('settings', JSON.stringify(geojson)) const uri = this.urls.get('map_save', { map_id: this.id }) const [data, _, error] = await this.server.post(uri, {}, formData) diff --git a/umap/tests/integration/test_edit_map.py b/umap/tests/integration/test_edit_map.py index 7bc6ca5f..a64542ae 100644 --- a/umap/tests/integration/test_edit_map.py +++ b/umap/tests/integration/test_edit_map.py @@ -226,3 +226,18 @@ def test_hover_tooltip_setting_should_be_persistent(live_server, map, page): - text: always never on hover """) expect(page.locator(".umap-field-showLabel input[value=null]")).to_be_checked() + + +def test_can_edit_map_tags(live_server, map, page): + map.settings["properties"]["tags"] = ["art"] + map.edit_status = Map.ANONYMOUS + map.save() + page.goto(f"{live_server.url}{map.get_absolute_url()}?edit") + page.get_by_role("button", name="Edit map name and caption").click() + page.get_by_text("Tags").click() + expect(page.get_by_label("Art and Culture")).to_be_checked() + page.get_by_label("Bike").check() + with page.expect_response(re.compile("./update/settings/.*")): + page.get_by_role("button", name="Save").click() + saved = Map.objects.get(pk=map.pk) + assert saved.tags == ["art", "bike"] From 1344fe6b465ec3b6fda009535945ef0a22f91fee Mon Sep 17 00:00:00 2001 From: Yohan Boniface Date: Thu, 27 Feb 2025 09:51:11 +0100 Subject: [PATCH 02/11] feat: basic filtering by tags --- docs/config/settings.md | 5 ++++- umap/context_processors.py | 1 + umap/templates/umap/search_bar.html | 12 ++++++++++-- umap/tests/test_views.py | 24 ++++++++++++++++++++++++ umap/views.py | 8 +++++++- 5 files changed, 46 insertions(+), 4 deletions(-) diff --git a/docs/config/settings.md b/docs/config/settings.md index 5211f7b5..c6b103f6 100644 --- a/docs/config/settings.md +++ b/docs/config/settings.md @@ -323,7 +323,10 @@ CREATE EXTENSION btree_gin; ALTER TEXT SEARCH CONFIGURATION umapdict ALTER MAPPING FOR hword, hword_part, word WITH unaccent, simple; # Now create the index -CREATE INDEX IF NOT EXISTS search_idx ON umap_map USING GIN(to_tsvector('umapdict', COALESCE(name, ''::character varying)::text), share_status); +CREATE INDEX IF NOT EXISTS search_idx ON umap_map USING GIN(to_tsvector('umapdict', COALESCE(name, ''::character varying)::text), share_status, tags); + +# You should also create an index for tag filtering: +CREATE INDEX IF NOT EXISTS tags_idx ON umap_map USING GIN(share_status, tags); ``` Then set: diff --git a/umap/context_processors.py b/umap/context_processors.py index fcabcfb4..6064a152 100644 --- a/umap/context_processors.py +++ b/umap/context_processors.py @@ -14,6 +14,7 @@ def settings(request): "UMAP_DEMO_SITE": djsettings.UMAP_DEMO_SITE, "UMAP_HOST_INFOS": djsettings.UMAP_HOST_INFOS, "UMAP_ALLOW_EDIT_PROFILE": djsettings.UMAP_ALLOW_EDIT_PROFILE, + "UMAP_TAGS": djsettings.UMAP_TAGS, } diff --git a/umap/templates/umap/search_bar.html b/umap/templates/umap/search_bar.html index 701a0fb0..c3f22b0e 100644 --- a/umap/templates/umap/search_bar.html +++ b/umap/templates/umap/search_bar.html @@ -5,14 +5,22 @@
-
+
-
+
+ +
+
diff --git a/umap/tests/test_views.py b/umap/tests/test_views.py index 81ef0cc2..e13a9c9a 100644 --- a/umap/tests/test_views.py +++ b/umap/tests/test_views.py @@ -486,3 +486,27 @@ def test_cannot_search_deleted_map(client, map): url = reverse("search") response = client.get(url + "?q=Blé") assert "Blé dur" not in response.content.decode() + + +@pytest.mark.django_db +def test_filter_by_tag(client, map): + # Very basic search, that do not deal with accent nor case. + # See install.md for how to have a smarter dict + index. + map.name = "Blé dur" + map.tags = ["bike"] + map.save() + url = reverse("search") + response = client.get(url + "?tags=bike") + assert "Blé dur" in response.content.decode() + + +@pytest.mark.django_db +def test_can_combine_search_and_filter(client, map): + # Very basic search, that do not deal with accent nor case. + # See install.md for how to have a smarter dict + index. + map.name = "Blé dur" + map.tags = ["bike"] + map.save() + url = reverse("search") + response = client.get(url + "?q=dur&tags=bike") + assert "Blé dur" in response.content.decode() diff --git a/umap/views.py b/umap/views.py index 0d05a75c..aed882bf 100644 --- a/umap/views.py +++ b/umap/views.py @@ -334,12 +334,18 @@ class TeamMaps(PaginatorMixin, DetailView): class SearchMixin: def get_search_queryset(self, **kwargs): q = self.request.GET.get("q") + tags = [t for t in self.request.GET.getlist("tags") if t] + qs = Map.objects.all() if q: vector = SearchVector("name", config=settings.UMAP_SEARCH_CONFIGURATION) query = SearchQuery( q, config=settings.UMAP_SEARCH_CONFIGURATION, search_type="websearch" ) - return Map.objects.annotate(search=vector).filter(search=query) + qs = qs.annotate(search=vector).filter(search=query) + if tags: + qs = qs.filter(tags__contains=tags) + if q or tags: + return qs class Search(PaginatorMixin, TemplateView, PublicMapsMixin, SearchMixin): From c6ed896a6d6df14ae779b0e16ab538e2471e5ffe Mon Sep 17 00:00:00 2001 From: Yohan Boniface Date: Thu, 6 Mar 2025 18:51:04 +0100 Subject: [PATCH 03/11] feat: associate icons to tags Co-authored-by: David Larlet --- umap/settings/base.py | 12 +++++++++--- umap/static/umap/img/tags/arts.svg | 1 + umap/static/umap/img/tags/business.svg | 1 + umap/static/umap/img/tags/cycling.svg | 1 + umap/static/umap/img/tags/education.svg | 1 + umap/static/umap/img/tags/environment.svg | 1 + umap/static/umap/img/tags/food.svg | 1 + umap/static/umap/img/tags/geopolitics.svg | 1 + umap/static/umap/img/tags/health.svg | 1 + umap/static/umap/img/tags/hiking.svg | 1 + umap/static/umap/img/tags/history.svg | 1 + umap/static/umap/img/tags/public.svg | 1 + umap/static/umap/img/tags/science.svg | 1 + umap/static/umap/img/tags/shopping.svg | 1 + umap/static/umap/img/tags/sport.svg | 1 + umap/static/umap/img/tags/tourism.svg | 1 + umap/static/umap/img/tags/transports.svg | 1 + umap/static/umap/img/tags/travel.svg | 1 + umap/static/umap/js/modules/form/builder.js | 1 + umap/static/umap/js/modules/form/fields.js | 21 +++++++++++++++++++-- umap/static/umap/map.css | 14 ++++++++++++++ 21 files changed, 60 insertions(+), 5 deletions(-) create mode 100644 umap/static/umap/img/tags/arts.svg create mode 100644 umap/static/umap/img/tags/business.svg create mode 100644 umap/static/umap/img/tags/cycling.svg create mode 100644 umap/static/umap/img/tags/education.svg create mode 100644 umap/static/umap/img/tags/environment.svg create mode 100644 umap/static/umap/img/tags/food.svg create mode 100644 umap/static/umap/img/tags/geopolitics.svg create mode 100644 umap/static/umap/img/tags/health.svg create mode 100644 umap/static/umap/img/tags/hiking.svg create mode 100644 umap/static/umap/img/tags/history.svg create mode 100644 umap/static/umap/img/tags/public.svg create mode 100644 umap/static/umap/img/tags/science.svg create mode 100644 umap/static/umap/img/tags/shopping.svg create mode 100644 umap/static/umap/img/tags/sport.svg create mode 100644 umap/static/umap/img/tags/tourism.svg create mode 100644 umap/static/umap/img/tags/transports.svg create mode 100644 umap/static/umap/img/tags/travel.svg diff --git a/umap/settings/base.py b/umap/settings/base.py index ac1ff865..e741b5b8 100644 --- a/umap/settings/base.py +++ b/umap/settings/base.py @@ -292,16 +292,22 @@ UMAP_IMPORTERS = {} UMAP_HOST_INFOS = {} UMAP_LABEL_KEYS = ["name", "title"] UMAP_TAGS = ( - ("art", _("Art and Culture")), - ("bike", _("Bike")), + ("arts", _("Art and Culture")), + ("cycling", _("Cycling")), + ("business", _("Business")), ("environment", _("Environment")), ("education", _("Education")), ("food", _("Food and Agriculture")), + ("geopolitics", _("Geopolitics")), + ("health", _("Health")), + ("hiking", _("Hiking")), ("history", _("History")), ("public", _("Public sector")), + ("science", _("Science")), + ("shopping", _("Shopping")), ("sport", _("Sport and Leisure")), ("travel", _("Travel")), - ("trekking", _("Trekking")), + ("transports", _("Transports")), ("tourism", _("Tourism")), ) diff --git a/umap/static/umap/img/tags/arts.svg b/umap/static/umap/img/tags/arts.svg new file mode 100644 index 00000000..bfbb0a4e --- /dev/null +++ b/umap/static/umap/img/tags/arts.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/umap/static/umap/img/tags/business.svg b/umap/static/umap/img/tags/business.svg new file mode 100644 index 00000000..3d013b87 --- /dev/null +++ b/umap/static/umap/img/tags/business.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/umap/static/umap/img/tags/cycling.svg b/umap/static/umap/img/tags/cycling.svg new file mode 100644 index 00000000..b2758b26 --- /dev/null +++ b/umap/static/umap/img/tags/cycling.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/umap/static/umap/img/tags/education.svg b/umap/static/umap/img/tags/education.svg new file mode 100644 index 00000000..a1275493 --- /dev/null +++ b/umap/static/umap/img/tags/education.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/umap/static/umap/img/tags/environment.svg b/umap/static/umap/img/tags/environment.svg new file mode 100644 index 00000000..b2cc25b0 --- /dev/null +++ b/umap/static/umap/img/tags/environment.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/umap/static/umap/img/tags/food.svg b/umap/static/umap/img/tags/food.svg new file mode 100644 index 00000000..c99d0a0d --- /dev/null +++ b/umap/static/umap/img/tags/food.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/umap/static/umap/img/tags/geopolitics.svg b/umap/static/umap/img/tags/geopolitics.svg new file mode 100644 index 00000000..c78ca4d3 --- /dev/null +++ b/umap/static/umap/img/tags/geopolitics.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/umap/static/umap/img/tags/health.svg b/umap/static/umap/img/tags/health.svg new file mode 100644 index 00000000..1a85bc33 --- /dev/null +++ b/umap/static/umap/img/tags/health.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/umap/static/umap/img/tags/hiking.svg b/umap/static/umap/img/tags/hiking.svg new file mode 100644 index 00000000..a9998188 --- /dev/null +++ b/umap/static/umap/img/tags/hiking.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/umap/static/umap/img/tags/history.svg b/umap/static/umap/img/tags/history.svg new file mode 100644 index 00000000..e11ac259 --- /dev/null +++ b/umap/static/umap/img/tags/history.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/umap/static/umap/img/tags/public.svg b/umap/static/umap/img/tags/public.svg new file mode 100644 index 00000000..0b0a22a3 --- /dev/null +++ b/umap/static/umap/img/tags/public.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/umap/static/umap/img/tags/science.svg b/umap/static/umap/img/tags/science.svg new file mode 100644 index 00000000..62455547 --- /dev/null +++ b/umap/static/umap/img/tags/science.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/umap/static/umap/img/tags/shopping.svg b/umap/static/umap/img/tags/shopping.svg new file mode 100644 index 00000000..743c09b9 --- /dev/null +++ b/umap/static/umap/img/tags/shopping.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/umap/static/umap/img/tags/sport.svg b/umap/static/umap/img/tags/sport.svg new file mode 100644 index 00000000..a439d17d --- /dev/null +++ b/umap/static/umap/img/tags/sport.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/umap/static/umap/img/tags/tourism.svg b/umap/static/umap/img/tags/tourism.svg new file mode 100644 index 00000000..6c9253a9 --- /dev/null +++ b/umap/static/umap/img/tags/tourism.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/umap/static/umap/img/tags/transports.svg b/umap/static/umap/img/tags/transports.svg new file mode 100644 index 00000000..acaaf9d7 --- /dev/null +++ b/umap/static/umap/img/tags/transports.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/umap/static/umap/img/tags/travel.svg b/umap/static/umap/img/tags/travel.svg new file mode 100644 index 00000000..3a506124 --- /dev/null +++ b/umap/static/umap/img/tags/travel.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/umap/static/umap/js/modules/form/builder.js b/umap/static/umap/js/modules/form/builder.js index 70d0926c..05a79247 100644 --- a/umap/static/umap/js/modules/form/builder.js +++ b/umap/static/umap/js/modules/form/builder.js @@ -127,6 +127,7 @@ export class MutatingForm extends Form { facetKey: 'PropertyInput', slugKey: 'PropertyInput', labelKey: 'PropertyInput', + tags: 'TagsEditor', } for (const [key, defaults] of Object.entries(SCHEMA)) { const properties = Object.assign({}, defaults) diff --git a/umap/static/umap/js/modules/form/fields.js b/umap/static/umap/js/modules/form/fields.js index 8d9903b2..ce8fd33d 100644 --- a/umap/static/umap/js/modules/form/fields.js +++ b/umap/static/umap/js/modules/form/fields.js @@ -325,11 +325,16 @@ Fields.CheckBox = class extends BaseElement { } Fields.CheckBoxes = class extends BaseElement { + getInputTemplate(value, label) { + return `` + } + build() { const initial = this.get() || [] for (const [value, label] of this.properties.choices) { - const tpl = `` - const [root, { input }] = Utils.loadTemplateWithRefs(tpl) + const [root, { input }] = Utils.loadTemplateWithRefs( + this.getInputTemplate(value, label) + ) this.container.appendChild(root) input.checked = initial.includes(value) input.addEventListener('change', () => this.sync()) @@ -342,6 +347,18 @@ Fields.CheckBoxes = class extends BaseElement { } } +Fields.TagsEditor = class extends Fields.CheckBoxes { + getInputTemplate(value, label) { + const path = SCHEMA.iconUrl.default.replace('marker.svg', `tags/${value}.svg`) + return ` + + ` + } +} + Fields.Select = class extends BaseElement { getTemplate() { return `` diff --git a/umap/static/umap/map.css b/umap/static/umap/map.css index 64d036cd..d995302a 100644 --- a/umap/static/umap/map.css +++ b/umap/static/umap/map.css @@ -935,6 +935,20 @@ a.umap-control-caption, display: block; } +/* **** */ +/* Tags */ +/* **** */ + +.tag-icon { + width: 20px; + height: 20px; + margin-bottom: -4px; + margin-right: 3px; +} +.dark .tag-icon { + filter: invert(1); +} + /* *************************** */ /* Overriding leaflet defaults */ /* *************************** */ From 06f963c07fa935f9da886e27952ee352abb9080d Mon Sep 17 00:00:00 2001 From: Yohan Boniface Date: Fri, 28 Mar 2025 17:05:08 +0100 Subject: [PATCH 04/11] chore: align search form elements Co-authored-by: David Larlet --- umap/static/umap/content.css | 4 ++++ umap/static/umap/css/bar.css | 1 + umap/static/umap/css/form.css | 5 +---- umap/templates/umap/search_bar.html | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/umap/static/umap/content.css b/umap/static/umap/content.css index 64687adf..b1573b0c 100644 --- a/umap/static/umap/content.css +++ b/umap/static/umap/content.css @@ -16,6 +16,10 @@ input::-webkit-input-placeholder, ::-webkit-input-placeholder { input:-moz-placeholder, :-moz-placeholder { color: #a5a5a5; } +.search-form { + display: flex; + align-items: baseline; +} /* **************** */ diff --git a/umap/static/umap/css/bar.css b/umap/static/umap/css/bar.css index 4dc36f6d..fe0410b6 100644 --- a/umap/static/umap/css/bar.css +++ b/umap/static/umap/css/bar.css @@ -203,6 +203,7 @@ line-height: initial; height: initial; width: auto; + padding: 0 var(--text-margin); } .umap-caption-bar-enabled { --current-footer-height: var(--footer-height); diff --git a/umap/static/umap/css/form.css b/umap/static/umap/css/form.css index 65ae68e5..ae1b0c57 100644 --- a/umap/static/umap/css/form.css +++ b/umap/static/umap/css/form.css @@ -61,10 +61,7 @@ textarea { select { border: 1px solid #222; width: 100%; - height: 28px; - line-height: 28px; - margin-top: 5px; - margin-bottom: var(--box-margin); + padding: var(--button-padding); } .dark select { color: #efefef; diff --git a/umap/templates/umap/search_bar.html b/umap/templates/umap/search_bar.html index c3f22b0e..c4efe694 100644 --- a/umap/templates/umap/search_bar.html +++ b/umap/templates/umap/search_bar.html @@ -4,7 +4,7 @@ {% trans "Search maps" as default_placeholder %}
-
+
Date: Thu, 3 Apr 2025 11:04:14 +0200 Subject: [PATCH 05/11] chore: fix tags related test --- umap/tests/integration/test_edit_map.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/umap/tests/integration/test_edit_map.py b/umap/tests/integration/test_edit_map.py index a64542ae..571829bf 100644 --- a/umap/tests/integration/test_edit_map.py +++ b/umap/tests/integration/test_edit_map.py @@ -229,15 +229,15 @@ def test_hover_tooltip_setting_should_be_persistent(live_server, map, page): def test_can_edit_map_tags(live_server, map, page): - map.settings["properties"]["tags"] = ["art"] + map.settings["properties"]["tags"] = ["arts"] map.edit_status = Map.ANONYMOUS map.save() page.goto(f"{live_server.url}{map.get_absolute_url()}?edit") page.get_by_role("button", name="Edit map name and caption").click() page.get_by_text("Tags").click() expect(page.get_by_label("Art and Culture")).to_be_checked() - page.get_by_label("Bike").check() + page.get_by_label("Cycling").check() with page.expect_response(re.compile("./update/settings/.*")): page.get_by_role("button", name="Save").click() saved = Map.objects.get(pk=map.pk) - assert saved.tags == ["art", "bike"] + assert saved.tags == ["arts", "cycling"] From db72bfad8dc92e9d301a27beae3b351b56be098f Mon Sep 17 00:00:00 2001 From: Yohan Boniface Date: Thu, 3 Apr 2025 11:04:30 +0200 Subject: [PATCH 06/11] wip: sort tags by label, not by value --- umap/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/umap/models.py b/umap/models.py index 66851787..fa083d42 100644 --- a/umap/models.py +++ b/umap/models.py @@ -423,7 +423,7 @@ class Map(NamedModel): "iconUrl": { "default": "%sumap/img/marker.svg" % settings.STATIC_URL, }, - "tags": {"choices": settings.UMAP_TAGS}, + "tags": {"choices": sorted(settings.UMAP_TAGS, key=lambda i: i[0])}, } From e24695b68c33c7099b18d32854b662601a6a9a5d Mon Sep 17 00:00:00 2001 From: Yohan Boniface Date: Thu, 3 Apr 2025 14:51:04 +0200 Subject: [PATCH 07/11] wip: display tags in maps list --- umap/models.py | 4 ++++ umap/static/umap/content.css | 12 +++++++++++- umap/templates/umap/map_list.html | 7 +++++++ 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/umap/models.py b/umap/models.py index fa083d42..58f5d4a7 100644 --- a/umap/models.py +++ b/umap/models.py @@ -417,6 +417,10 @@ class Map(NamedModel): datalayer.clone(map_inst=new) return new + def get_tags_display(self): + labels = dict(settings.UMAP_TAGS) + return [(t, labels.get(t, t)) for t in self.tags] + @classproperty def extra_schema(self): return { diff --git a/umap/static/umap/content.css b/umap/static/umap/content.css index b1573b0c..d55b58a7 100644 --- a/umap/static/umap/content.css +++ b/umap/static/umap/content.css @@ -171,7 +171,17 @@ h2.tabs a:hover { .more_button { min-height: var(--map-fragment-height); } - +.tag-list { + margin-top: var(--text-margin); + margin-bottom: var(--text-margin); +} +.tag-list li { + border: 1px solid var(--color-darkCyan); + border-radius: 3vmin; + display: inline-block; + padding: var(--button-padding-small); + white-space: nowrap; +} /* **************************** */ /* colors */ diff --git a/umap/templates/umap/map_list.html b/umap/templates/umap/map_list.html index 16e559f3..bdce48a8 100644 --- a/umap/templates/umap/map_list.html +++ b/umap/templates/umap/map_list.html @@ -4,6 +4,13 @@
{% map_fragment map_inst prefix=prefix page=request.GET.p %}
+ {% if map_inst.tags %} +
    + {% for tag, label in map_inst.get_tags_display %} +
  • {{ label }}
  • + {% endfor %} +
+ {% endif %}

{{ map_inst.name }}

{% with author=map_inst.get_author %} {% if author %} From 80a969917a684cde6505f7c544618049f28ba9c3 Mon Sep 17 00:00:00 2001 From: Yohan Boniface Date: Thu, 3 Apr 2025 17:51:20 +0200 Subject: [PATCH 08/11] chore: simplify search form CSS and HTML Co-authored-by: David Larlet --- umap/static/umap/content.css | 17 +++++++++++++++ umap/templates/umap/search_bar.html | 32 ++++++++++++----------------- 2 files changed, 30 insertions(+), 19 deletions(-) diff --git a/umap/static/umap/content.css b/umap/static/umap/content.css index d55b58a7..3ba0329b 100644 --- a/umap/static/umap/content.css +++ b/umap/static/umap/content.css @@ -19,6 +19,18 @@ input:-moz-placeholder, :-moz-placeholder { .search-form { display: flex; align-items: baseline; + gap: calc(var(--gutter) / 2); + max-width: 800px; + margin: 0 auto; +} +.search-form select { + max-width: 200px; +} +.search-form input[type=submit] { + min-width: 200px; +} +.flex-break { + justify-content: center; } @@ -555,4 +567,9 @@ dialog::backdrop { .mhide { display: none; } + .flex-break { + flex-direction: column; + align-items: center; + } + } diff --git a/umap/templates/umap/search_bar.html b/umap/templates/umap/search_bar.html index c4efe694..17588e70 100644 --- a/umap/templates/umap/search_bar.html +++ b/umap/templates/umap/search_bar.html @@ -4,25 +4,19 @@ {% trans "Search maps" as default_placeholder %}
- -
- -
-
- -
-
- -
+ + + +
From 8c90535105744702ab06b56037288b6b40cf2c94 Mon Sep 17 00:00:00 2001 From: Yohan Boniface Date: Thu, 3 Apr 2025 17:51:37 +0200 Subject: [PATCH 09/11] chore: better CSS for tags in maps list Co-authored-by: David Larlet --- umap/static/umap/content.css | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/umap/static/umap/content.css b/umap/static/umap/content.css index 3ba0329b..907736ef 100644 --- a/umap/static/umap/content.css +++ b/umap/static/umap/content.css @@ -186,13 +186,25 @@ h2.tabs a:hover { .tag-list { margin-top: var(--text-margin); margin-bottom: var(--text-margin); + display: flex; + flex-wrap: wrap; + gap: calc(var(--gutter) / 2 ); + justify-content: center; } .tag-list li { - border: 1px solid var(--color-darkCyan); + border: 1px solid var(--color-darkBlue); border-radius: 3vmin; display: inline-block; padding: var(--button-padding-small); +} +.tag-list li a { + color: var(--color-darkBlue); + max-width: 125px; + display: inline-block; + overflow: hidden; white-space: nowrap; + text-overflow: ellipsis; + vertical-align: middle; } /* **************************** */ From 54420568af13677424792aaee3c73074f316dd83 Mon Sep 17 00:00:00 2001 From: Yohan Boniface Date: Thu, 3 Apr 2025 18:20:50 +0200 Subject: [PATCH 10/11] chore: refine map fragment card CSS Co-authored-by: David Larlet --- umap/static/umap/content.css | 44 ++++++++++++++++--------------- umap/templates/umap/map_list.html | 29 +++++++++++--------- 2 files changed, 39 insertions(+), 34 deletions(-) diff --git a/umap/static/umap/content.css b/umap/static/umap/content.css index 907736ef..820b54c3 100644 --- a/umap/static/umap/content.css +++ b/umap/static/umap/content.css @@ -76,26 +76,8 @@ body.login header { .demo_map .map_fragment { height: var(--map-fragment-height); } -.map_list .legend { - padding-top: 7px; - margin-bottom: 28px; - text-align: center; - font-size: 1.2em; -} -.map_list .legend a { - color: #222; - font-weight: bold; -} -.map_list .legend em, -.map_list .legend em a { - color: #444; - font-weight: normal; -} -.map_list hr { - display: none; -} -.map_list .wide + hr { - display: block; +.grid-container hgroup { + text-align: left; } .umap-features-list ul { margin-top: 14px; @@ -189,7 +171,6 @@ h2.tabs a:hover { display: flex; flex-wrap: wrap; gap: calc(var(--gutter) / 2 ); - justify-content: center; } .tag-list li { border: 1px solid var(--color-darkBlue); @@ -206,6 +187,27 @@ h2.tabs a:hover { text-overflow: ellipsis; vertical-align: middle; } +.card { + border: 1px solid var(--color-lightGray); + border-radius: var(--border-radius); + padding: var(--box-padding); + display: flex; + flex-direction: column; +} +.card .button { + margin-bottom: 0; +} +.card hgroup { + display: flex; + flex-direction: column; + justify-content: space-between; + margin-bottom: 0; + flex-grow: 1; + gap: var(--gutter); +} +.card h3 { + margin-bottom: 0; +} /* **************************** */ /* colors */ diff --git a/umap/templates/umap/map_list.html b/umap/templates/umap/map_list.html index bdce48a8..67e6df8d 100644 --- a/umap/templates/umap/map_list.html +++ b/umap/templates/umap/map_list.html @@ -1,22 +1,25 @@ {% load umap_tags i18n %} {% for map_inst in maps %} -
+
{% map_fragment map_inst prefix=prefix page=request.GET.p %}
- {% if map_inst.tags %} -
    - {% for tag, label in map_inst.get_tags_display %} -
  • {{ label }}
  • - {% endfor %} -
- {% endif %} -

{{ map_inst.name }}

- {% with author=map_inst.get_author %} - {% if author %} -

{% trans "by" %} {{ author }}

+
+ {% if map_inst.tags %} +
    + {% for tag, label in map_inst.get_tags_display %} +
  • {{ label }}
  • + {% endfor %} +
{% endif %} - {% endwith %} +

{{ map_inst.name }}

+ {% with author=map_inst.get_author %} + {% if author %} +

{% trans "by" %} {{ author }}

+ {% endif %} + {% endwith %} +
+ {% translate "See the map" %}
{% endfor %} From 4fd57e0c116432d5ffbcd1befd558eb9040e0d0e Mon Sep 17 00:00:00 2001 From: Yohan Boniface Date: Thu, 3 Apr 2025 18:25:13 +0200 Subject: [PATCH 11/11] chore: remove tag icons for now Co-authored-by: David Larlet --- umap/static/umap/img/tags/arts.svg | 1 - umap/static/umap/img/tags/business.svg | 1 - umap/static/umap/img/tags/cycling.svg | 1 - umap/static/umap/img/tags/education.svg | 1 - umap/static/umap/img/tags/environment.svg | 1 - umap/static/umap/img/tags/food.svg | 1 - umap/static/umap/img/tags/geopolitics.svg | 1 - umap/static/umap/img/tags/health.svg | 1 - umap/static/umap/img/tags/hiking.svg | 1 - umap/static/umap/img/tags/history.svg | 1 - umap/static/umap/img/tags/public.svg | 1 - umap/static/umap/img/tags/science.svg | 1 - umap/static/umap/img/tags/shopping.svg | 1 - umap/static/umap/img/tags/sport.svg | 1 - umap/static/umap/img/tags/tourism.svg | 1 - umap/static/umap/img/tags/transports.svg | 1 - umap/static/umap/img/tags/travel.svg | 1 - umap/static/umap/js/modules/form/builder.js | 1 - umap/static/umap/js/modules/form/fields.js | 12 ------------ umap/static/umap/map.css | 14 -------------- 20 files changed, 44 deletions(-) delete mode 100644 umap/static/umap/img/tags/arts.svg delete mode 100644 umap/static/umap/img/tags/business.svg delete mode 100644 umap/static/umap/img/tags/cycling.svg delete mode 100644 umap/static/umap/img/tags/education.svg delete mode 100644 umap/static/umap/img/tags/environment.svg delete mode 100644 umap/static/umap/img/tags/food.svg delete mode 100644 umap/static/umap/img/tags/geopolitics.svg delete mode 100644 umap/static/umap/img/tags/health.svg delete mode 100644 umap/static/umap/img/tags/hiking.svg delete mode 100644 umap/static/umap/img/tags/history.svg delete mode 100644 umap/static/umap/img/tags/public.svg delete mode 100644 umap/static/umap/img/tags/science.svg delete mode 100644 umap/static/umap/img/tags/shopping.svg delete mode 100644 umap/static/umap/img/tags/sport.svg delete mode 100644 umap/static/umap/img/tags/tourism.svg delete mode 100644 umap/static/umap/img/tags/transports.svg delete mode 100644 umap/static/umap/img/tags/travel.svg diff --git a/umap/static/umap/img/tags/arts.svg b/umap/static/umap/img/tags/arts.svg deleted file mode 100644 index bfbb0a4e..00000000 --- a/umap/static/umap/img/tags/arts.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/umap/static/umap/img/tags/business.svg b/umap/static/umap/img/tags/business.svg deleted file mode 100644 index 3d013b87..00000000 --- a/umap/static/umap/img/tags/business.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/umap/static/umap/img/tags/cycling.svg b/umap/static/umap/img/tags/cycling.svg deleted file mode 100644 index b2758b26..00000000 --- a/umap/static/umap/img/tags/cycling.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/umap/static/umap/img/tags/education.svg b/umap/static/umap/img/tags/education.svg deleted file mode 100644 index a1275493..00000000 --- a/umap/static/umap/img/tags/education.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/umap/static/umap/img/tags/environment.svg b/umap/static/umap/img/tags/environment.svg deleted file mode 100644 index b2cc25b0..00000000 --- a/umap/static/umap/img/tags/environment.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/umap/static/umap/img/tags/food.svg b/umap/static/umap/img/tags/food.svg deleted file mode 100644 index c99d0a0d..00000000 --- a/umap/static/umap/img/tags/food.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/umap/static/umap/img/tags/geopolitics.svg b/umap/static/umap/img/tags/geopolitics.svg deleted file mode 100644 index c78ca4d3..00000000 --- a/umap/static/umap/img/tags/geopolitics.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/umap/static/umap/img/tags/health.svg b/umap/static/umap/img/tags/health.svg deleted file mode 100644 index 1a85bc33..00000000 --- a/umap/static/umap/img/tags/health.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/umap/static/umap/img/tags/hiking.svg b/umap/static/umap/img/tags/hiking.svg deleted file mode 100644 index a9998188..00000000 --- a/umap/static/umap/img/tags/hiking.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/umap/static/umap/img/tags/history.svg b/umap/static/umap/img/tags/history.svg deleted file mode 100644 index e11ac259..00000000 --- a/umap/static/umap/img/tags/history.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/umap/static/umap/img/tags/public.svg b/umap/static/umap/img/tags/public.svg deleted file mode 100644 index 0b0a22a3..00000000 --- a/umap/static/umap/img/tags/public.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/umap/static/umap/img/tags/science.svg b/umap/static/umap/img/tags/science.svg deleted file mode 100644 index 62455547..00000000 --- a/umap/static/umap/img/tags/science.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/umap/static/umap/img/tags/shopping.svg b/umap/static/umap/img/tags/shopping.svg deleted file mode 100644 index 743c09b9..00000000 --- a/umap/static/umap/img/tags/shopping.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/umap/static/umap/img/tags/sport.svg b/umap/static/umap/img/tags/sport.svg deleted file mode 100644 index a439d17d..00000000 --- a/umap/static/umap/img/tags/sport.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/umap/static/umap/img/tags/tourism.svg b/umap/static/umap/img/tags/tourism.svg deleted file mode 100644 index 6c9253a9..00000000 --- a/umap/static/umap/img/tags/tourism.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/umap/static/umap/img/tags/transports.svg b/umap/static/umap/img/tags/transports.svg deleted file mode 100644 index acaaf9d7..00000000 --- a/umap/static/umap/img/tags/transports.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/umap/static/umap/img/tags/travel.svg b/umap/static/umap/img/tags/travel.svg deleted file mode 100644 index 3a506124..00000000 --- a/umap/static/umap/img/tags/travel.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/umap/static/umap/js/modules/form/builder.js b/umap/static/umap/js/modules/form/builder.js index 05a79247..70d0926c 100644 --- a/umap/static/umap/js/modules/form/builder.js +++ b/umap/static/umap/js/modules/form/builder.js @@ -127,7 +127,6 @@ export class MutatingForm extends Form { facetKey: 'PropertyInput', slugKey: 'PropertyInput', labelKey: 'PropertyInput', - tags: 'TagsEditor', } for (const [key, defaults] of Object.entries(SCHEMA)) { const properties = Object.assign({}, defaults) diff --git a/umap/static/umap/js/modules/form/fields.js b/umap/static/umap/js/modules/form/fields.js index ce8fd33d..7c13a2a9 100644 --- a/umap/static/umap/js/modules/form/fields.js +++ b/umap/static/umap/js/modules/form/fields.js @@ -347,18 +347,6 @@ Fields.CheckBoxes = class extends BaseElement { } } -Fields.TagsEditor = class extends Fields.CheckBoxes { - getInputTemplate(value, label) { - const path = SCHEMA.iconUrl.default.replace('marker.svg', `tags/${value}.svg`) - return ` - - ` - } -} - Fields.Select = class extends BaseElement { getTemplate() { return `` diff --git a/umap/static/umap/map.css b/umap/static/umap/map.css index d995302a..64d036cd 100644 --- a/umap/static/umap/map.css +++ b/umap/static/umap/map.css @@ -935,20 +935,6 @@ a.umap-control-caption, display: block; } -/* **** */ -/* Tags */ -/* **** */ - -.tag-icon { - width: 20px; - height: 20px; - margin-bottom: -4px; - margin-right: 3px; -} -.dark .tag-icon { - filter: invert(1); -} - /* *************************** */ /* Overriding leaflet defaults */ /* *************************** */