diff --git a/docs-users/fr/tutorials/6-one-click-data-import.md b/docs-users/fr/tutorials/6-one-click-data-import.md index 12064f022..7b4594926 100644 --- a/docs-users/fr/tutorials/6-one-click-data-import.md +++ b/docs-users/fr/tutorials/6-one-click-data-import.md @@ -47,7 +47,7 @@ Voici un bref passage en revu des différents imports proposés et pour finir l ## 1. Importer le contour d’une commune -Cliquez sur l’outil d’importation en bas de la barre de droite, puis descendez jusqu’au cadre « Assistants d’import ». +Cliquez sur l’outil d’importation en bas de la barre de droite, puis cliquez sur le lien « Assistants d’import ». Cliquez sur « Communes France » et sélectionnez la commune souhaitée dans une liste déroulante. Une fois la commune sélectionnée, le format est reconnu automatiquement (geojson) puis le type de calque (cliquer sur « ? » pour savoir quel choix opérer) @@ -64,7 +64,7 @@ Une fois cet import réalisé, tout est réglable : couleur de contour, de fond, ## 2. Importer les contours des départements ou des régions -Cliquez sur l’outil d’importation en bas de la barre de droite, puis descendez jusqu’au cadre « Assistants d’import ». +Cliquez sur l’outil d’importation en bas de la barre de droite, puis cliquez sur le lien « Assistants d’import ». Cliquez sur « Contours nationaux » puis soit départements, soit régions et enfin le type de calque (voir supra l’explication). Tous les départements sont importés : @@ -72,7 +72,7 @@ Cliquez sur « Contours nationaux » puis soit départements, soit régions et ## 3. Importer un point d’intérêt issu de GeoDataMine -Cliquez sur l’outil d’importation en bas de la barre de droite, puis descendez jusqu’au cadre « Assistants d’import ». +Cliquez sur l’outil d’importation en bas de la barre de droite, puis cliquez sur le lien « Assistants d’import ». Cliquez sur « GeoDataMine (thèmes OSM) » et sélectionnez les informations souhaitées, routes, bâtiments, commerces, services publics, … Par exemple, en sélectionnant les points d’eau potable de la CA du Grand Avignon, puis « Copier dans un calque » diff --git a/docs-users/static/tutoriels/importer.gif b/docs-users/static/tutoriels/importer.gif index b0cb9c13d..e5c9167b5 100644 Binary files a/docs-users/static/tutoriels/importer.gif and b/docs-users/static/tutoriels/importer.gif differ diff --git a/umap/static/umap/base.css b/umap/static/umap/base.css index e8321da75..a9f3929b5 100644 --- a/umap/static/umap/base.css +++ b/umap/static/umap/base.css @@ -157,6 +157,9 @@ dt { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); } +.grid-container.by4 { + grid-template-columns: repeat(4, minmax(0, 1fr)); +} .grid-container > * { text-align: center; } diff --git a/umap/static/umap/css/form.css b/umap/static/umap/css/form.css index 912c27978..1d61cab72 100644 --- a/umap/static/umap/css/form.css +++ b/umap/static/umap/css/form.css @@ -78,8 +78,7 @@ input[type="submit"] { border-radius: 2px; font-weight: normal; cursor: pointer; - padding: 7px; - width: 100%; + padding: 7px 14px; min-height: 32px; line-height: 32px; border: none; @@ -92,6 +91,12 @@ input[type="submit"] { color: var(--text-color); border: 1px solid #1b1f20; } +.dark .button.primary:not([disabled]), +.dark [type="button"].primary:not([disabled]) { + background-color: var(--color-brightCyan); + color: var(--color-dark); + border: 1px solid #1b1f20; +} .dark .button:hover, .dark [type="button"]:hover, .dark input[type="submit"]:hover { @@ -100,6 +105,11 @@ input[type="submit"] { .dark a { color: var(--text-color); } +.dark [type="button"][disabled], +.dark input[type="submit"][disabled] { + background-color: var(--color-mediumGray); + cursor: not-allowed; +} button.flat, [type="button"].flat, .dark [type="button"].flat { @@ -602,3 +612,6 @@ input[type=hidden].blur + [type="button"] { input.highlightable:not(:placeholder-shown) { border: 1px solid var(--color-brightCyan); } +.umap-upload [type=url] { + margin-bottom: 0; +} diff --git a/umap/static/umap/css/icon.css b/umap/static/umap/css/icon.css index 43042a540..96bd85e91 100644 --- a/umap/static/umap/css/icon.css +++ b/umap/static/umap/css/icon.css @@ -110,6 +110,9 @@ html[dir="rtl"] .icon { .icon-list { background-position: var(--tile) calc(var(--tile) * 4); } +.icon-magic { + background-position: calc(var(--tile) * 7) 0; +} .icon-marker { background-position: calc(var(--tile) * 3) calc(var(--tile) * 5); } diff --git a/umap/static/umap/img/16-white.svg b/umap/static/umap/img/16-white.svg index 00f6dc672..a9f1484ea 100644 --- a/umap/static/umap/img/16-white.svg +++ b/umap/static/umap/img/16-white.svg @@ -1,4 +1,4 @@ - + @@ -12,6 +12,9 @@ + + + @@ -202,5 +205,8 @@ + + + diff --git a/umap/static/umap/img/16.svg b/umap/static/umap/img/16.svg index 352122700..12221ede7 100644 --- a/umap/static/umap/img/16.svg +++ b/umap/static/umap/img/16.svg @@ -1 +1 @@ -image/svg+xml   +image/svg+xml   diff --git a/umap/static/umap/img/source/16-white.svg b/umap/static/umap/img/source/16-white.svg index 85fcbc168..e0e15e3c7 100644 --- a/umap/static/umap/img/source/16-white.svg +++ b/umap/static/umap/img/source/16-white.svg @@ -1,7 +1,7 @@ - + @@ -13,23 +13,15 @@ - + + + + - + - - - - - - - - - - - - + @@ -40,23 +32,23 @@ - +   - + - + - +   - + @@ -82,7 +74,7 @@ - + @@ -153,53 +145,53 @@ - + - - + + - + - + - + - - - + + + - + - + - - - + + + - + - - - + + + - + - + @@ -208,7 +200,7 @@ - + @@ -224,5 +216,8 @@ + + + diff --git a/umap/static/umap/img/source/16.svg b/umap/static/umap/img/source/16.svg index 2af171a67..9cdf5ab3f 100644 --- a/umap/static/umap/img/source/16.svg +++ b/umap/static/umap/img/source/16.svg @@ -1,4 +1,4 @@ -image/svg+xml   +image/svg+xml   diff --git a/umap/static/umap/js/modules/importer.js b/umap/static/umap/js/modules/importer.js index 7e7343c82..756a1d2e6 100644 --- a/umap/static/umap/js/modules/importer.js +++ b/umap/static/umap/js/modules/importer.js @@ -6,17 +6,14 @@ import Dialog from './ui/dialog.js' import * as Utils from './utils.js' const TEMPLATE = ` +

${translate('Import data')}

${translate('Choose data')} - - + +
${translate( @@ -36,28 +33,40 @@ const TEMPLATE = `
${translate('Choose import mode')}
- + +
` -export default class Importer { +const GRID_TEMPLATE = ` +
+

${translate('Import helpers')}

+

${translate('Import helpers will fill the URL field for you.')}

+ +
+` + +export default class Importer extends Utils.WithTemplate { constructor(umap) { + super() this._umap = umap this.TYPES = ['geojson', 'csv', 'gpx', 'kml', 'osm', 'georss', 'umap'] this.IMPORTERS = [] this.loadImporters() - this.dialog = new Dialog() + this.dialog = new Dialog({ className: 'importers dark' }) } loadImporters() { - for (const [name, config] of Object.entries(this._umap.properties.importers || {})) { + for (const [name, config] of Object.entries( + this._umap.properties.importers || {} + )) { const register = (mod) => { this.IMPORTERS.push(new mod.Importer(this._umap, config)) } @@ -112,6 +121,11 @@ export default class Importer { return this.qs('textarea').value } + set raw(value) { + this.qs('textarea').value = value + this.onChange() + } + get clear() { return Boolean(this.qs('[name=clear]').checked) } @@ -144,20 +158,26 @@ export default class Importer { ) } + showImporters() { + if (!this.IMPORTERS.length) return + const [element, { grid }] = Utils.loadTemplateWithRefs(GRID_TEMPLATE) + for (const plugin of this.IMPORTERS.sort((a, b) => (a.id > b.id ? 1 : -1))) { + const button = Utils.loadTemplate( + `
  • ` + ) + button.addEventListener('click', () => plugin.open(this)) + grid.appendChild(button) + } + this.dialog.open({ template: element, cancel: false, accept: false }) + } + build() { - this.container = DomUtil.create('div', 'umap-upload') - this.container.innerHTML = TEMPLATE + this.container = this.loadTemplate(TEMPLATE) if (this.IMPORTERS.length) { - const parent = this.container.querySelector('.importers ul') - for (const plugin of this.IMPORTERS.sort((a, b) => (a.id > b.id ? 1 : -1))) { - L.DomUtil.createButton( - plugin.id, - DomUtil.element({ tagName: 'li', parent }), - plugin.name, - () => plugin.open(this) - ) - } - this.qs('.importers').toggleAttribute('hidden', false) + // TODO use this.elements instead of this.qs + const button = this.qs('[data-ref=importersButton]') + button.addEventListener('click', () => this.showImporters()) + button.toggleAttribute('hidden', false) } for (const type of this.TYPES) { DomUtil.element({ @@ -168,7 +188,7 @@ export default class Importer { }) } this._umap.help.parse(this.container) - DomEvent.on(this.qs('[name=submit]'), 'click', this.submit, this) + this.qs('[name=submit]').addEventListener('click', () => this.submit()) DomEvent.on(this.qs('[type=file]'), 'change', this.onFileChange, this) for (const element of this.container.querySelectorAll('[onchange]')) { DomEvent.on(element, 'change', this.onChange, this) @@ -183,6 +203,7 @@ export default class Importer { ) this.qs('[name=layer-name]').toggleAttribute('hidden', Boolean(this.layerId)) this.qs('#clear').toggleAttribute('hidden', !this.layerId) + this.qs('[name=submit').toggleAttribute('disabled', !this.canSubmit()) } onFileChange(e) { @@ -204,6 +225,7 @@ export default class Importer { this.url = null this.format = undefined this.layerName = null + this.raw = null const layerSelect = this.qs('[name="layer-id"]') layerSelect.innerHTML = '' this._umap.eachDataLayerReverse((datalayer) => { @@ -236,6 +258,17 @@ export default class Importer { this.qs('[type=file]').showPicker() } + canSubmit() { + if (!this.format) return false + const hasFiles = Boolean(this.files.length) + const hasRaw = Boolean(this.raw) + const hasUrl = Boolean(this.url) + const hasAction = Boolean(this.action) + if (!hasFiles && !hasRaw && !hasUrl) return false + if (this.url) return hasAction + return true + } + submit() { let hasErrors if (this.format === 'umap') { diff --git a/umap/tests/integration/test_import.py b/umap/tests/integration/test_import.py index dd4b8a7ca..11413eaab 100644 --- a/umap/tests/integration/test_import.py +++ b/umap/tests/integration/test_import.py @@ -607,6 +607,7 @@ def test_overpass_import_with_bbox(page, live_server, tilelayer, settings): } page.goto(f"{live_server.url}/map/new/") page.get_by_role("link", name="Import data").click() + page.get_by_role("button", name="Import helpers").click() page.get_by_role("button", name="Overpass").click() page.get_by_placeholder("amenity=drinking_water").fill("building") page.get_by_role("button", name="Choose this data").click() @@ -657,6 +658,7 @@ def handle(route): page.route(re.compile("https://foobar.io/api.*"), handle) page.goto(f"{live_server.url}/map/new/") page.get_by_role("link", name="Import data").click() + page.get_by_role("button", name="Import helpers").click() page.get_by_role("button", name="Overpass").click() page.get_by_placeholder("amenity=drinking_water").fill("building") page.get_by_placeholder("Type area name, or let empty").click() @@ -669,6 +671,7 @@ def handle(route): expect(page.get_by_placeholder("Provide an URL here")).to_have_value( "https://my.overpass.io/interpreter?data=[out:json];nwr[building](area:3601393025);out geom;" ) + page.get_by_role("button", name="Import helpers").click() page.get_by_role("button", name="Overpass").click() expect(page.locator("#area")).to_contain_text( "Bray-sur-Seine, Seine-et-Marne, Île-de-France, France" @@ -710,6 +713,7 @@ def handle(route): page.goto(f"{live_server.url}/map/new/") expect(page.locator(".leaflet-marker-icon")).to_be_hidden() page.get_by_role("link", name="Import data").click() + page.get_by_role("button", name="Import helpers").click() page.get_by_role("button", name="Datasets").click() page.get_by_role("dialog").get_by_role("combobox").select_option( "https://remote.org/data.json"