Compare commits

...

16 commits

Author SHA1 Message Date
Yohan Boniface
3b9a0c0951
chore: bump ruff from 0.9.2 to 0.9.3 (#2451)
Some checks failed
Test & Docs / tests (postgresql, 3.10) (push) Has been cancelled
Test & Docs / tests (postgresql, 3.12) (push) Has been cancelled
Test & Docs / lint (push) Has been cancelled
Test & Docs / docs (push) Has been cancelled
2025-01-27 19:05:20 +01:00
Yohan Boniface
cb5e13b218
chore: bump django-environ from 0.11.2 to 0.12.0 (#2454) 2025-01-27 18:41:12 +01:00
Yohan Boniface
c42a2b7129
chore: bump psycopg from 3.2.3 to 3.2.4 (#2453) 2025-01-27 18:40:55 +01:00
Yohan Boniface
2482111d24
chore: bump pydantic from 2.10.5 to 2.10.6 (#2450) 2025-01-27 18:40:31 +01:00
Yohan Boniface
bcd21d3697
feat(forms): add a debounce for Input and Textarea fields (#2445)
fix #2415

I'm a bit afraid this will add more hiccup to the playwright tests,
tough :(
2025-01-27 18:40:11 +01:00
Yohan Boniface
b6b47cc0d0
chore: bump pymdown-extensions from 10.14 to 10.14.1 (#2452) 2025-01-27 18:39:50 +01:00
dependabot[bot]
cb4ea1b1d2
chore: bump django-environ from 0.11.2 to 0.12.0
Bumps [django-environ](https://github.com/joke2k/django-environ) from 0.11.2 to 0.12.0.
- [Release notes](https://github.com/joke2k/django-environ/releases)
- [Changelog](https://github.com/joke2k/django-environ/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/joke2k/django-environ/compare/v0.11.2...v0.12.0)

---
updated-dependencies:
- dependency-name: django-environ
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-27 17:32:18 +00:00
dependabot[bot]
99fff916d5
chore: bump psycopg from 3.2.3 to 3.2.4
Bumps [psycopg](https://github.com/psycopg/psycopg) from 3.2.3 to 3.2.4.
- [Changelog](https://github.com/psycopg/psycopg/blob/master/docs/news.rst)
- [Commits](https://github.com/psycopg/psycopg/compare/3.2.3...3.2.4)

---
updated-dependencies:
- dependency-name: psycopg
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-27 17:31:06 +00:00
dependabot[bot]
151beb6d4c
chore: bump pymdown-extensions from 10.14 to 10.14.1
Bumps [pymdown-extensions](https://github.com/facelessuser/pymdown-extensions) from 10.14 to 10.14.1.
- [Release notes](https://github.com/facelessuser/pymdown-extensions/releases)
- [Commits](https://github.com/facelessuser/pymdown-extensions/compare/10.14...10.14.1)

---
updated-dependencies:
- dependency-name: pymdown-extensions
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-27 17:30:59 +00:00
dependabot[bot]
fe40effaaa
chore: bump ruff from 0.9.2 to 0.9.3
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.9.2 to 0.9.3.
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md)
- [Commits](https://github.com/astral-sh/ruff/compare/0.9.2...0.9.3)

---
updated-dependencies:
- dependency-name: ruff
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-27 17:30:53 +00:00
dependabot[bot]
0e1fa6965d
chore: bump pydantic from 2.10.5 to 2.10.6
Bumps [pydantic](https://github.com/pydantic/pydantic) from 2.10.5 to 2.10.6.
- [Release notes](https://github.com/pydantic/pydantic/releases)
- [Changelog](https://github.com/pydantic/pydantic/blob/main/HISTORY.md)
- [Commits](https://github.com/pydantic/pydantic/compare/v2.10.5...v2.10.6)

---
updated-dependencies:
- dependency-name: pydantic
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-27 17:30:34 +00:00
Yohan Boniface
49ea7ed4a5 feat(forms): add a debounce for Input and Textarea fields
fix #2415
2025-01-27 17:25:56 +01:00
Yohan Boniface
d20943a487
feat: move star button to caption (#2442)
fix #2282


![image](https://github.com/user-attachments/assets/45fda270-035b-4ec2-9ac1-3e2f2798663b)
![Screenshot From 2025-01-23
10-42-23](https://github.com/user-attachments/assets/ef323c82-bb12-41ef-97e6-b21dfeef01ba)
2025-01-27 17:18:28 +01:00
Yohan Boniface
250579eaa2 chore: better styling for star button in caption panel
Co-authored-by: David Larlet <david@larlet.fr>
2025-01-27 16:50:27 +01:00
Yohan Boniface
8603774778 chore: do not use hx for non structural headings 2025-01-24 18:52:24 +01:00
Yohan Boniface
48f9afdedd feat: move star button to caption
fix #2282
2025-01-23 11:25:09 +01:00
29 changed files with 129 additions and 92 deletions

View file

@ -1,5 +1,5 @@
# Force rtfd to use a recent version of mkdocs
mkdocs==1.6.1
pymdown-extensions==10.14
pymdown-extensions==10.14.1
mkdocs-material==9.5.50
mkdocs-static-i18n==1.2.3

View file

@ -1,5 +1,5 @@
# Force rtfd to use a recent version of mkdocs
mkdocs==1.6.1
pymdown-extensions==10.14
pymdown-extensions==10.14.1
mkdocs-material==9.5.50
mkdocs-static-i18n==1.2.3

View file

@ -30,10 +30,10 @@ classifiers = [
dependencies = [
"Django==5.1.5",
"django-agnocomplete==2.2.0",
"django-environ==0.11.2",
"django-environ==0.12.0",
"django-probes==1.7.0",
"Pillow==11.1.0",
"psycopg==3.2.3",
"psycopg==3.2.4",
"requests==2.32.3",
"rcssmin==1.2.0",
"rjsmin==1.2.3",
@ -44,13 +44,13 @@ dependencies = [
[project.optional-dependencies]
dev = [
"hatch==1.14.0",
"ruff==0.9.2",
"ruff==0.9.3",
"djlint==1.36.4",
"mkdocs==1.6.1",
"mkdocs-material==9.5.50",
"mkdocs-static-i18n==1.2.3",
"vermin==1.6.0",
"pymdown-extensions==10.14",
"pymdown-extensions==10.14.1",
"isort==5.13.2",
]
test = [
@ -71,7 +71,7 @@ s3 = [
"django-storages[s3]==1.14.4",
]
sync = [
"pydantic==2.10.5",
"pydantic==2.10.6",
"redis==5.2.1",
]

View file

@ -46,7 +46,7 @@ h3, h4, h5 {
margin-bottom: 14px;
}
p {
line-height: 21px;
line-height: 1.4;
margin-top: 14px;
margin-bottom: 14px;
}

View file

@ -1,7 +1,6 @@
.umap-main-edit-toolbox [type=button] {
color: #fff;
font-size: 1em;
border: none;
background-color: var(--color-darkGray);
width: auto;
margin-bottom: 0;
@ -11,7 +10,7 @@
}
.leaflet-container [type=button].umap-help-link {
padding-bottom: 3px;
padding: 0 var(--text-margin);
background-color: inherit;
}
.leaflet-container .edit-save,
@ -20,8 +19,6 @@
.leaflet-container .connected-peers
{
display: block;
border: none;
border-radius: 20px;
height: 32px;
line-height: 30px;
padding: 0 20px;
@ -37,11 +34,6 @@
color: var(--color-darkGray);
}
.leaflet-container .edit-cancel,
.leaflet-container .edit-disable,
.leaflet-container .connected-peers{
border: 0.5px solid rgba(153, 153, 153, 0.40);
}
.leaflet-container .edit-cancel:hover,
.leaflet-container .edit-disable:hover {
border: 0.5px solid rgba(153, 153, 153, 0.80);
@ -120,7 +112,7 @@
column-gap: 10px;
}
.umap-right-edit-toolbox {
align-items: baseline;
align-items: center;
}
.umap-main-edit-toolbox .logo {

View file

@ -76,15 +76,14 @@ select[multiple="multiple"] {
.button,
[type="button"],
input[type="submit"] {
display: block;
display: flex;
align-items: center;
margin-bottom: 14px;
text-align: center;
border-radius: 2px;
font-weight: normal;
cursor: pointer;
padding: 7px 14px;
min-height: 32px;
line-height: 32px;
padding: 3px 12px;
border: none;
text-decoration: none;
background-color: white;
@ -132,6 +131,11 @@ button.flat:hover,
.dark [type="button"].flat:hover {
text-decoration: underline;
}
.dark button.round,
button.round {
border-radius: 20px;
border: 0.5px solid rgba(153, 153, 153, 0.40);
}
.help-text, .helptext {
display: block;
padding: 7px 7px;
@ -572,17 +576,11 @@ input.blur {
border-start-end-radius: 0;
border-end-end-radius: 0;
}
.blur + .button:before,
.blur + [type="button"]:before {
content: '✔';
}
.blur + .button,
.blur + [type="button"] {
width: 40px;
height: 18px;
display: inline-block;
vertical-align: middle;
line-height: 18px;
border-start-start-radius: 0;
border-end-start-radius: 0;
box-sizing: border-box;
@ -591,6 +589,10 @@ input[type=hidden].blur + .button,
input[type=hidden].blur + [type="button"] {
display: none;
}
.blur-container {
display: flex;
align-items: stretch;
}
.copiable-input {
display: flex;
align-items: end;

View file

@ -17,11 +17,11 @@
background-image: url('../img/24.svg');
--tile: -36px;
height: 36px;
line-height: 36px;
width: 36px;
}
.icon + span {
margin-inline-start: 10px;
margin-inline-start: 5px;
margin-inline-end: 5px;
}
html[dir="rtl"] .icon {
transform: scaleX(-1);
@ -153,6 +153,12 @@ html[dir="rtl"] .icon {
.icon-share {
background-position: 0px calc(var(--tile) * 5);
}
.icon-star {
background-position: var(--tile) calc(var(--tile) * 7);
}
.icon-starred {
background-position: 0 calc(var(--tile) * 7);
}
.icon-table {
background-position: calc(var(--tile) * 2) 0px;
}

View file

@ -42,7 +42,8 @@
padding: var(--panel-gutter);
}
.panel h3 {
line-height: 120%;
display: flex;
align-items: center;
}
.panel .counter::before {
counter-increment: step;

View file

@ -208,5 +208,7 @@
<g id="g2-67" transform="translate(170.12 814.31)" clip-path="url(#clip0_2695_1939)">
<path id="path1-5" d="m8.8453 14.83c-0.28116 0.6439-1.1722 0.6439-1.4533 0l-0.73138-1.6751c-0.65086-1.4907-1.8224-2.6774-3.2837-3.326l-2.0131-0.89358c-0.64004-0.28408-0.64004-1.2152 0-1.4993l1.9502-0.86569c1.4989-0.66535 2.6914-1.8959 3.3312-3.4375l0.74086-1.7852c0.27491-0.66247 1.1902-0.66247 1.4652 0l0.74083 1.7852c0.63972 1.5416 1.8322 2.7722 3.3311 3.4375l1.9503 0.86569c0.64 0.2841 0.64 1.2152 0 1.4993l-2.0131 0.89358c-1.4613 0.64864-2.6328 1.8353-3.2837 3.326zm-5.0624-6.6444c1.9049 0.84555 3.4537 2.2354 4.3357 4.1478 0.88202-1.9124 2.4308-3.3022 4.3356-4.1478-1.9276-0.85565-3.4813-2.3132-4.3356-4.2596-0.85434 1.9463-2.4081 3.4039-4.3357 4.2596zm12.385 10.723 0.2057-0.4714c0.3667-0.8405 1.0271-1.5098 1.8511-1.8758l0.6336-0.2816c0.3428-0.1523 0.3428-0.6504 0-0.8026l-0.5981-0.2658c-0.8453-0.3755-1.5175-1.0695-1.8779-1.9386l-0.2112-0.5094c-0.1473-0.355-0.6381-0.355-0.7853 0l-0.2112 0.5094c-0.3603 0.8691-1.0326 1.5631-1.8778 1.9386l-0.5983 0.2658c-0.3427 0.1522-0.3427 0.6503 0 0.8026l0.6337 0.2816c0.8241 0.366 1.4844 1.0353 1.8511 1.8758l0.2057 0.4714c0.1505 0.3451 0.6283 0.3451 0.7789 0zm-0.8557-3.0358 0.4687-0.4655 0.459 0.4655-0.459 0.4524z" fill="#efefef"/>
</g>
<path id="star" class="sprite" d="m7.6698 998.86 1.3886-5.255-4.0585-3.2468h5.1831l1.8193-5.4949 1.8147 5.4939h5.1829l-4.0615 3.2496 1.3838 5.2564-4.3249-3.0123z" fill="#efefef"/>
<path id="starred" class="sprite" d="m31.67 998.86 1.3886-5.255-4.0585-3.2468h5.1831l1.8193-5.4949 1.8147 5.4939h5.1829l-4.0615 3.2496 1.3838 5.2565-4.3249-3.0123z" fill="none" stroke="#efefef" stroke-linecap="square" stroke-linejoin="round"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 43 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

View file

@ -19,7 +19,7 @@
<rect width="20" height="20" fill="#ffffff" id="rect1" x="0" y="0" />
</clipPath>
</defs>
<sodipodi:namedview id="base" pagecolor="#ffffff" bordercolor="#666666" borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="14.041122" inkscape:cx="165.15774" inkscape:cy="24.998002" inkscape:document-units="px" inkscape:current-layer="layer1" showgrid="true" inkscape:window-width="1920" inkscape:window-height="1011" inkscape:window-x="0" inkscape:window-y="0" inkscape:window-maximized="1" showguides="true" inkscape:guide-bbox="true" inkscape:snap-grids="true" inkscape:snap-to-guides="true" inkscape:showpageshadow="2" inkscape:pagecheckerboard="0" inkscape:deskcolor="#d1d1d1">
<sodipodi:namedview id="base" pagecolor="#ffffff" bordercolor="#666666" borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="14.412751" inkscape:cx="41.352272" inkscape:cy="165.68662" inkscape:document-units="px" inkscape:current-layer="layer1" showgrid="true" inkscape:window-width="1920" inkscape:window-height="1011" inkscape:window-x="0" inkscape:window-y="0" inkscape:window-maximized="1" showguides="true" inkscape:guide-bbox="true" inkscape:snap-grids="true" inkscape:snap-to-guides="true" inkscape:showpageshadow="2" inkscape:pagecheckerboard="0" inkscape:deskcolor="#d1d1d1">
<inkscape:grid type="xygrid" id="grid3004" empspacing="4" visible="true" enabled="true" snapvisiblegridlinesonly="true" originx="0" originy="0" spacingy="1" spacingx="1" units="px" />
<inkscape:grid id="grid1" units="px" originx="0" originy="0" spacingx="24" spacingy="24" empcolor="#203fff" empopacity="0.85490196" color="#3f3fff" opacity="0.1254902" empspacing="1" enabled="true" visible="true" />
</sodipodi:namedview>
@ -219,5 +219,7 @@
<g clip-path="url(#clip0_2695_1939)" id="g2-67" transform="translate(170.11621,814.31159)">
<path d="m 8.84533,14.8298 c -0.28116,0.6439 -1.1722,0.6439 -1.45333,0 l -0.73138,-1.6751 c -0.65086,-1.4907 -1.82238,-2.6774 -3.2837,-3.32604 l -2.0131,-0.89358 c -0.64004,-0.28408 -0.64004,-1.21518 0,-1.49928 l 1.95022,-0.86569 c 1.4989,-0.66535 2.69143,-1.89594 3.33118,-3.43751 l 0.74086,-1.78516 c 0.27491,-0.662471 1.19025,-0.662472 1.46517,0 l 0.74083,1.78517 c 0.63972,1.54156 1.83222,2.77215 3.33112,3.4375 l 1.9503,0.86569 c 0.64,0.2841 0.64,1.2152 0,1.49928 l -2.0131,0.89358 c -1.4613,0.64864 -2.6328,1.83534 -3.28374,3.32604 z m -5.06236,-6.64435 c 1.90486,0.84555 3.45371,2.23535 4.33568,4.14775 0.88202,-1.9124 2.43085,-3.3022 4.33565,-4.14775 -1.9276,-0.85565 -3.4813,-2.31323 -4.33564,-4.25955 -0.85434,1.94633 -2.4081,3.4039 -4.33569,4.25955 z m 12.38483,10.72295 0.2057,-0.4714 c 0.3667,-0.8405 1.0271,-1.5098 1.8511,-1.8758 l 0.6336,-0.2816 c 0.3428,-0.1523 0.3428,-0.6504 0,-0.8026 l -0.5981,-0.2658 c -0.8453,-0.3755 -1.5175,-1.0695 -1.8779,-1.9386 l -0.2112,-0.5094 c -0.1473,-0.355 -0.6381,-0.355 -0.7853,0 l -0.2112,0.5094 c -0.3603,0.8691 -1.0326,1.5631 -1.8778,1.9386 l -0.5983,0.2658 c -0.3427,0.1522 -0.3427,0.6503 0,0.8026 l 0.6337,0.2816 c 0.8241,0.366 1.4844,1.0353 1.8511,1.8758 l 0.2057,0.4714 c 0.1505,0.3451 0.6283,0.3451 0.7789,0 z m -0.8557,-3.0358 0.4687,-0.4655 0.459,0.4655 -0.459,0.4524 z" fill="#efefef" id="path1-5" />
</g>
<path style="fill:#efefef;fill-opacity:1;stroke:none;stroke-width:6.97518;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" inkscape:transform-center-x="-0.0010932573" inkscape:transform-center-y="-0.7377641" d="m 7.6698317,998.8588 1.3886278,-5.25497 -4.0584591,-3.24679 h 5.1830926 l 1.819345,-5.49493 1.81469,5.49392 h 5.182872 l -4.06154,3.24965 1.383849,5.25642 -4.324867,-3.01228 z" id="star" inkscape:connector-curvature="0" sodipodi:nodetypes="ccccccccccc" class="sprite" />
<path style="fill:none;fill-opacity:1;stroke:#efefef;stroke-width:1;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" inkscape:transform-center-x="-0.0010925804" inkscape:transform-center-y="-0.73776941" d="m 31.669832,998.8588 1.388628,-5.25501 -4.05846,-3.24679 h 5.183093 l 1.819345,-5.49493 1.814694,5.49392 h 5.182868 l -4.06154,3.24964 1.383849,5.25647 -4.324874,-3.01232 z" id="starred" inkscape:connector-curvature="0" sodipodi:nodetypes="ccccccccccc" class="sprite" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 75 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 44 KiB

View file

@ -1,5 +1,6 @@
import { translate } from './i18n.js'
import * as Utils from './utils.js'
import { uMapAlert as Alert } from '../components/alerts/alert.js'
const TEMPLATE = `
<div class="umap-caption">
@ -7,8 +8,9 @@ const TEMPLATE = `
<i class="icon icon-16 icon-caption icon-block"></i>
<hgroup>
<h3><span class="map-name" data-ref="name"></span></h3>
<h4 data-ref="author"></h4>
<h5 class="dates" data-ref="dates"></h5>
<p class="dates" data-ref="dates"></p>
<p data-ref="author"></p>
<p><button type="button" class="round" data-ref="star" title="${translate('Star this map')}"><i class="icon icon-16 icon-star map-star"></i><span class="map-stars"></span></button></p>
</hgroup>
</div>
<div class="umap-map-description text" data-ref="description"></div>
@ -35,6 +37,14 @@ export default class Caption extends Utils.WithTemplate {
this._umap = umap
this._leafletMap = leafletMap
this.loadTemplate(TEMPLATE)
this.elements.star.addEventListener('click', async () => {
if (this._umap.properties.user?.id) {
await this._umap.star()
this.refresh()
} else {
Alert.error(translate('You must be logged in'))
}
})
}
isOpen() {
@ -62,10 +72,6 @@ export default class Caption extends Utils.WithTemplate {
this.addDataLayer(datalayer, this.elements.datalayersContainer)
)
this.addCredits()
this._umap.panel.open({ content: this.element }).then(() => {
// Create the legend when the panel is actually on the DOM
this._umap.eachDataLayerReverse((datalayer) => datalayer.renderLegend())
})
if (this._umap.properties.created_at) {
const created_at = translate('Created at {date}', {
date: new Date(this._umap.properties.created_at).toLocaleDateString(),
@ -77,6 +83,11 @@ export default class Caption extends Utils.WithTemplate {
} else {
this.elements.dates.hidden = true
}
this._umap.panel.open({ content: this.element }).then(() => {
// Create the legend when the panel is actually on the DOM
this._umap.eachDataLayerReverse((datalayer) => datalayer.renderLegend())
this._umap.propagate()
})
}
addDataLayer(datalayer, parent) {

View file

@ -132,7 +132,10 @@ Fields.Textarea = class extends BaseElement {
super.build()
this.textarea = this.elements.textarea
this.fetch()
this.textarea.addEventListener('input', () => this.sync())
this.textarea.addEventListener(
'input',
Utils.debounce(() => this.sync(), 300)
)
this.textarea.addEventListener('keypress', (event) => this.onKeyPress(event))
}
@ -179,7 +182,7 @@ Fields.Input = class extends BaseElement {
this.input.step = this.properties.step
}
this.fetch()
this.input.addEventListener(this.getSyncEvent(), () => this.sync())
this.listenForSync()
this.input.addEventListener('keydown', (event) => this.onKeyDown(event))
}
@ -189,8 +192,11 @@ Fields.Input = class extends BaseElement {
this.input.value = value
}
getSyncEvent() {
return 'input'
listenForSync() {
this.input.addEventListener(
'input',
Utils.debounce(() => this.sync(), 300)
)
}
type() {
@ -212,12 +218,12 @@ Fields.Input = class extends BaseElement {
}
Fields.BlurInput = class extends Fields.Input {
getSyncEvent() {
return 'blur'
listenForSync() {
this.input.addEventListener('blur', () => this.sync())
}
getTemplate() {
return `${super.getTemplate()}<span class="button blur-button"></span>`
return `<div class="blur-container">${super.getTemplate()}<button type="button">✔</button></div>`
}
build() {
@ -856,10 +862,10 @@ Fields.IconUrl = class extends Fields.BlurInput {
}
buildInput(parent, value) {
const input = Utils.loadTemplate('<input class="blur" />')
const button = Utils.loadTemplate('<span class="button blur-button"></span>')
parent.appendChild(input)
parent.appendChild(button)
const [element, { input }] = Utils.loadTemplateWithRefs(
'<div class="blur-container"><input class="blur" data-ref="input" /><button type="button">✔</button></div>'
)
parent.appendChild(element)
if (value) input.value = value
input.addEventListener('blur', () => {
// Do not clear this.input when focus-blur

View file

@ -32,7 +32,6 @@ const ControlsMixin = {
'locate',
'measure',
'editinosm',
'star',
'tilelayers',
],
@ -84,7 +83,6 @@ const ControlsMixin = {
this._controls.search = new U.SearchControl()
this._controls.embed = new Control.Embed(this._umap)
this._controls.tilelayersChooser = new U.TileLayerChooser(this)
if (this.options.user?.id) this._controls.star = new U.StarControl(this._umap)
this._controls.editinosm = new Control.EditInOSM({
position: 'topleft',
widgetOptions: {

View file

@ -478,12 +478,6 @@ export const SCHEMA = {
label: translate('Sort key'),
inheritable: true,
},
starControl: {
type: Boolean,
impacts: ['ui'],
nullable: true,
label: translate('Display the star map button'),
},
stroke: {
type: Boolean,
impacts: ['data'],

View file

@ -7,8 +7,8 @@ const TOP_BAR_TEMPLATE = `
<div class="umap-main-edit-toolbox with-transition dark">
<div class="umap-left-edit-toolbox" data-ref="left">
<div class="logo"><a class="" href="/" title="${translate('Go to the homepage')}">uMap</a></div>
<button class="map-name" type="button" data-ref="name"></button>
<button class="share-status" type="button" data-ref="share"></button>
<button class="map-name flat" type="button" data-ref="name"></button>
<button class="share-status flat" type="button" data-ref="share"></button>
</div>
<div class="umap-right-edit-toolbox" data-ref="right">
<button class="connected-peers round" type="button" data-ref="peers">
@ -19,7 +19,7 @@ const TOP_BAR_TEMPLATE = `
<i class="icon icon-16 icon-profile"></i>
<span class="username" data-ref="username"></span>
</button>
<button class="umap-help-link" type="button" title="${translate('Help')}" data-ref="help">${translate('Help')}</button>
<button class="umap-help-link flat" type="button" title="${translate('Help')}" data-ref="help">${translate('Help')}</button>
<button class="edit-cancel round" type="button" data-ref="cancel">
<i class="icon icon-16 icon-restore"></i>
<span class="">${translate('Cancel edits')}</span>

View file

@ -1366,7 +1366,11 @@ export default class Umap extends ServerStored {
},
'properties.starred': () => {
Utils.eachElement('.map-star', (el) => {
el.classList.toggle('starred', this.properties.starred)
el.classList.toggle('icon-starred', this.properties.starred)
el.classList.toggle('icon-star', !this.properties.starred)
})
Utils.eachElement('.map-stars', (el) => {
el.textContent = this.properties.stars || 0
})
},
}
@ -1549,6 +1553,7 @@ export default class Umap extends ServerStored {
return
}
this.properties.starred = data.starred
this.properties.stars = data.stars
Alert.success(
data.starred
? translate('Map has been starred')

View file

@ -471,6 +471,19 @@ export function isWritable(element) {
return false
}
// From https://www.joshwcomeau.com/snippets/javascript/debounce/
export const debounce = (callback, wait) => {
let timeoutId = null
return (...args) => {
window.clearTimeout(timeoutId)
timeoutId = window.setTimeout(() => {
callback.apply(null, args)
}, wait)
}
}
export const COLORS = [
'Black',
'Navy',

View file

@ -491,18 +491,6 @@ U.CaptionControl = L.Control.Button.extend({
},
})
U.StarControl = L.Control.Button.extend({
options: {
position: 'topleft',
title: L._('Star this map'),
className: 'leaflet-control-star map-star umap-control',
},
onClick: function () {
this._umap.star()
},
})
L.Control.Embed = L.Control.Button.extend({
options: {
position: 'topleft',

View file

@ -134,12 +134,6 @@ html[dir="rtl"] .leaflet-tooltip-pane > * {
background-position: -72px -144px;
box-shadow: 0 0 4px 0 black inset;
}
.leaflet-control-star [type="button"] {
background-position: -144px -144px;
}
.leaflet-control-star.starred [type="button"] {
background-position: -108px -144px;
}
.leaflet-control-search [type="button"] {
background-position: -36px -108px;
display: block;
@ -703,6 +697,10 @@ a.umap-control-caption,
.umap-caption .header i.icon {
flex-shrink: 0;
}
.umap-caption hgroup p,
.umap-caption hgroup button {
margin: 0;
}
.umap-browser .main-toolbox {
padding-left: 4px; /* Align with toolbox below */
border-top: 1px solid var(--color-mediumGray);

View file

@ -191,7 +191,7 @@ def test_sortkey_impacts_datalayerindex(map, live_server, page):
page.locator('input[name="sortKey"]').fill("key")
# Click the checkmark to apply the changes
page.locator(".panel .umap-field-sortKey .blur-button").click()
page.locator(".panel .umap-field-sortKey .blur-container button").click()
# Features should be sorted by key (First, Second, Third)
first_listed_feature = page.locator(".umap-browser .datalayer ul > li").nth(0)

View file

@ -24,6 +24,7 @@ def test_layers_list_is_updated(live_server, tilelayer, page):
page.get_by_role("button", name="Add a layer").click()
page.locator('input[name="name"]').click()
page.locator('input[name="name"]').fill("foobar")
page.wait_for_timeout(300) # Time for the input debounce.
page.get_by_role("link", name=f"Import data ({modifier}+I)").click()
# Should still work
page.locator("[name=layer-id]").select_option(label="Import in a new layer")

View file

@ -285,6 +285,7 @@ def test_should_display_alert_on_conflict(context, live_server, datalayer, openm
# Change name on page one and save
page_one.locator(".leaflet-marker-icon").click(modifiers=["Shift"])
page_one.locator('input[name="name"]').fill("name from page one")
page_one.wait_for_timeout(300) # Time for the input debounce.
with page_one.expect_response(re.compile(r".*/datalayer/update/.*")):
page_one.get_by_role("button", name="Save").click()

View file

@ -24,6 +24,7 @@ def test_reseting_map_would_remove_from_save_queue(
page.get_by_role("button", name="Edit", exact=True).click()
page.locator('input[name="name"]').click()
page.locator('input[name="name"]').fill("new datalayer name")
page.wait_for_timeout(300) # Time of the Input debounce
with page.expect_response(re.compile(".*/datalayer/update/.*")):
page.get_by_role("button", name="Save").click()
assert len(requests) == 1

View file

@ -8,20 +8,24 @@ from umap.models import Star
pytestmark = pytest.mark.django_db
def test_star_control_is_visible_if_logged_in(map, live_server, page, login, user):
def test_star_button_is_active_if_logged_in(map, live_server, page, login, user):
login(user)
assert not Star.objects.count()
page.goto(f"{live_server.url}{map.get_absolute_url()}")
page.get_by_title("More controls").click()
control = page.locator(".leaflet-control-star")
expect(control).to_be_visible()
page.get_by_title("About").click()
button = page.locator(".icon-star")
expect(button).to_be_visible()
with page.expect_response(re.compile(".*/star/")):
control.click()
button.click()
expect(button).to_be_hidden()
# Button has changed
expect(page.locator(".icon-starred")).to_be_visible()
assert Star.objects.count() == 1
def test_no_star_control_if_not_logged_in(map, live_server, page):
def test_star_button_inctive_if_not_logged_in(map, live_server, page):
page.goto(f"{live_server.url}{map.get_absolute_url()}")
page.get_by_title("More controls").click()
control = page.locator(".leaflet-control-star")
expect(control).to_be_hidden()
page.get_by_title("About").click()
button = page.locator(".icon-star")
button.click()
expect(page.get_by_text("You must be logged in")).to_be_visible()

View file

@ -74,6 +74,7 @@ def test_table_editor(live_server, openmap, datalayer, page):
page.locator("dialog").get_by_role("button", name="OK").click()
page.locator("td").nth(2).dblclick()
page.locator('input[name="newprop"]').fill("newvalue")
page.wait_for_timeout(300) # Time for the input debounce.
page.keyboard.press("Enter")
page.locator("thead button[data-property=name]").click()
page.get_by_role("button", name="Delete this column").click()

View file

@ -56,6 +56,7 @@ def test_websocket_connection_can_sync_markers(new_page, asgi_live_server, tilel
expect(peerB.get_by_role("button", name="Cancel edits")).to_be_hidden()
peerA.locator("body").type("Synced name")
peerA.locator("body").press("Escape")
peerA.wait_for_timeout(300)
peerB.locator(".leaflet-marker-icon").first.click()
peerB.get_by_role("link", name="Toggle edit mode (⇧+Click)").click()
@ -320,6 +321,7 @@ def test_websocket_connection_can_sync_late_joining_peer(
a_map_el.click(position={"x": 220, "y": 220})
peerA.locator("body").type("First marker")
peerA.locator("body").press("Escape")
peerA.wait_for_timeout(300)
# Add a polygon from peer A
create_polygon = peerA.locator(".leaflet-control-toolbar ").get_by_title(

View file

@ -605,6 +605,7 @@ class MapDetailMixin(SessionMixin):
"schema": Map.extra_schema,
"id": self.get_id(),
"starred": self.is_starred(),
"stars": self.stars(),
"licences": dict((l.name, l.json) for l in Licence.objects.all()),
"umap_version": VERSION,
"featuresHaveOwner": settings.UMAP_DEFAULT_FEATURES_HAVE_OWNERS,
@ -677,6 +678,9 @@ class MapDetailMixin(SessionMixin):
def is_starred(self):
return False
def stars(self):
return 0
def get_geojson(self):
return {
"geometry": {
@ -779,6 +783,9 @@ class MapView(MapDetailMixin, PermissionsMixin, DetailView):
return False
return Star.objects.filter(by=user, map=self.object).exists()
def stars(self):
return Star.objects.filter(map=self.object).count()
class MapDownload(DetailView):
model = Map
@ -1080,7 +1087,9 @@ class ToggleMapStarStatus(View):
else:
Star.objects.create(map=map_inst, by=self.request.user)
status = True
return simple_json_response(starred=status)
return simple_json_response(
starred=status, stars=Star.objects.filter(map=map_inst).count()
)
class MapShortUrl(RedirectView):