diff --git a/umap/static/umap/js/modules/browser.js b/umap/static/umap/js/modules/browser.js
index 6afdf92d..2c7cfdec 100644
--- a/umap/static/umap/js/modules/browser.js
+++ b/umap/static/umap/js/modules/browser.js
@@ -1,5 +1,6 @@
import { DomEvent, DomUtil, stamp } from '../../vendors/leaflet/leaflet-src.esm.js'
import { translate } from './i18n.js'
+import * as Icon from './rendering/icon.js'
export default class Browser {
constructor(map) {
@@ -34,14 +35,14 @@ export default class Browser {
const colorBox = DomUtil.create('i', 'icon icon-16 feature-color', row)
const title = DomUtil.create('span', 'feature-title', row)
const symbol = feature._getIconUrl
- ? U.Icon.prototype.formatUrl(feature._getIconUrl(), feature)
+ ? Icon.formatUrl(feature._getIconUrl(), feature)
: null
title.textContent = feature.getDisplayName() || '—'
const bgcolor = feature.getPreviewColor()
colorBox.style.backgroundColor = bgcolor
if (symbol && symbol !== U.SCHEMA.iconUrl.default) {
- const icon = U.Icon.makeIconElement(symbol, colorBox)
- U.Icon.setIconContrast(icon, colorBox, symbol, bgcolor)
+ const icon = Icon.makeElement(symbol, colorBox)
+ Icon.setContrast(icon, colorBox, symbol, bgcolor)
}
const viewFeature = (e) => {
feature.zoomTo({ ...e, callback: feature.view })
diff --git a/umap/static/umap/js/modules/global.js b/umap/static/umap/js/modules/global.js
index a6c2b341..61a92802 100644
--- a/umap/static/umap/js/modules/global.js
+++ b/umap/static/umap/js/modules/global.js
@@ -25,6 +25,7 @@ import { EditPanel, FullPanel, Panel } from './ui/panel.js'
import Tooltip from './ui/tooltip.js'
import URLs from './urls.js'
import * as Utils from './utils.js'
+import * as Icon from './rendering/icon.js'
import { DataLayer, LAYER_TYPES } from './data/layer.js'
import { DataLayerPermissions, MapPermissions } from './permissions.js'
import { Point, LineString, Polygon } from './data/features.js'
@@ -51,6 +52,7 @@ window.U = {
FullPanel,
Help,
HTTPError,
+ Icon,
Importer,
LAYER_TYPES,
LeafletMarker,
diff --git a/umap/static/umap/js/umap.icon.js b/umap/static/umap/js/modules/rendering/icon.js
similarity index 66%
rename from umap/static/umap/js/umap.icon.js
rename to umap/static/umap/js/modules/rendering/icon.js
index 33c0651e..6a90d480 100644
--- a/umap/static/umap/js/umap.icon.js
+++ b/umap/static/umap/js/modules/rendering/icon.js
@@ -1,15 +1,36 @@
-U.Icon = L.DivIcon.extend({
- statics: {
- RECENT: [],
- },
+import {
+ DomEvent,
+ DomUtil,
+ DivIcon,
+ Icon,
+} from '../../../vendors/leaflet/leaflet-src.esm.js'
+import * as Utils from '../utils.js'
+import { SCHEMA } from '../schema.js'
+
+export function getClass(name) {
+ switch (name) {
+ case 'Circle':
+ return Circle
+ case 'Ball':
+ return Ball
+ case 'Drop':
+ return Drop
+ default:
+ return DefaultIcon
+ }
+}
+
+export const RECENT = []
+
+const BaseIcon = L.DivIcon.extend({
initialize: function (options) {
const default_options = {
iconSize: null, // Made in css
- iconUrl: U.SCHEMA.iconUrl.default,
+ iconUrl: SCHEMA.iconUrl.default,
feature: null,
}
options = L.Util.extend({}, default_options, options)
- L.Icon.prototype.initialize.call(this, options)
+ Icon.prototype.initialize.call(this, options)
this.feature = this.options.feature
if (this.feature?.isReadOnly()) {
this.options.className += ' readonly'
@@ -17,10 +38,10 @@ U.Icon = L.DivIcon.extend({
},
_setRecent: (url) => {
- if (U.Utils.hasVar(url)) return
- if (url === U.SCHEMA.iconUrl.default) return
- if (U.Icon.RECENT.indexOf(url) === -1) {
- U.Icon.RECENT.push(url)
+ if (Utils.hasVar(url)) return
+ if (url === SCHEMA.iconUrl.default) return
+ if (RECENT.indexOf(url) === -1) {
+ RECENT.push(url)
}
},
@@ -32,29 +53,26 @@ U.Icon = L.DivIcon.extend({
} else {
url = this.options[`${name}Url`]
}
- return this.formatUrl(url, this.feature)
+ return formatUrl(url, this.feature)
},
_getColor: function () {
let color
if (this.feature) color = this.feature.getDynamicOption('color')
else if (this.options.color) color = this.options.color
- else color = U.SCHEMA.color.default
+ else color = SCHEMA.color.default
return color
},
_getOpacity: function () {
if (this.feature) return this.feature.getOption('iconOpacity')
- return U.SCHEMA.iconOpacity.default
+ return SCHEMA.iconOpacity.default
},
- formatUrl: (url, feature) =>
- U.Utils.greedyTemplate(url || '', feature ? feature.extendedProperties() : {}),
-
onAdd: () => {},
})
-U.Icon.Default = U.Icon.extend({
+const DefaultIcon = BaseIcon.extend({
default_options: {
iconAnchor: new L.Point(16, 40),
popupAnchor: new L.Point(0, -40),
@@ -64,11 +82,11 @@ U.Icon.Default = U.Icon.extend({
initialize: function (options) {
options = L.Util.extend({}, this.default_options, options)
- U.Icon.prototype.initialize.call(this, options)
+ BaseIcon.prototype.initialize.call(this, options)
},
_setIconStyles: function (img, name) {
- U.Icon.prototype._setIconStyles.call(this, img, name)
+ BaseIcon.prototype._setIconStyles.call(this, img, name)
const color = this._getColor()
const opacity = this._getOpacity()
this.elements.container.style.backgroundColor = color
@@ -80,29 +98,29 @@ U.Icon.Default = U.Icon.extend({
onAdd: function () {
const src = this._getIconUrl('icon')
const bgcolor = this._getColor()
- U.Icon.setIconContrast(this.elements.icon, this.elements.container, src, bgcolor)
+ setContrast(this.elements.icon, this.elements.container, src, bgcolor)
},
createIcon: function () {
this.elements = {}
- this.elements.main = L.DomUtil.create('div')
- this.elements.container = L.DomUtil.create(
+ this.elements.main = DomUtil.create('div')
+ this.elements.container = DomUtil.create(
'div',
'icon_container',
this.elements.main
)
this.elements.main.dataset.feature = this.feature?.id
- this.elements.arrow = L.DomUtil.create('div', 'icon_arrow', this.elements.main)
+ this.elements.arrow = DomUtil.create('div', 'icon_arrow', this.elements.main)
const src = this._getIconUrl('icon')
if (src) {
- this.elements.icon = U.Icon.makeIconElement(src, this.elements.container)
+ this.elements.icon = makeElement(src, this.elements.container)
}
this._setIconStyles(this.elements.main, 'icon')
return this.elements.main
},
})
-U.Icon.Circle = U.Icon.extend({
+const Circle = BaseIcon.extend({
initialize: function (options) {
const default_options = {
popupAnchor: new L.Point(0, -6),
@@ -110,18 +128,18 @@ U.Icon.Circle = U.Icon.extend({
className: 'umap-circle-icon',
}
options = L.Util.extend({}, default_options, options)
- U.Icon.prototype.initialize.call(this, options)
+ BaseIcon.prototype.initialize.call(this, options)
},
_setIconStyles: function (img, name) {
- U.Icon.prototype._setIconStyles.call(this, img, name)
+ BaseIcon.prototype._setIconStyles.call(this, img, name)
this.elements.main.style.backgroundColor = this._getColor()
this.elements.main.style.opacity = this._getOpacity()
},
createIcon: function () {
this.elements = {}
- this.elements.main = L.DomUtil.create('div')
+ this.elements.main = DomUtil.create('div')
this.elements.main.innerHTML = ' '
this._setIconStyles(this.elements.main, 'icon')
this.elements.main.dataset.feature = this.feature?.id
@@ -129,7 +147,7 @@ U.Icon.Circle = U.Icon.extend({
},
})
-U.Icon.Drop = U.Icon.Default.extend({
+const Drop = DefaultIcon.extend({
default_options: {
iconAnchor: new L.Point(16, 42),
popupAnchor: new L.Point(0, -42),
@@ -138,7 +156,7 @@ U.Icon.Drop = U.Icon.Default.extend({
},
})
-U.Icon.Ball = U.Icon.Default.extend({
+const Ball = DefaultIcon.extend({
default_options: {
iconAnchor: new L.Point(8, 30),
popupAnchor: new L.Point(0, -28),
@@ -148,20 +166,20 @@ U.Icon.Ball = U.Icon.Default.extend({
createIcon: function () {
this.elements = {}
- this.elements.main = L.DomUtil.create('div')
- this.elements.container = L.DomUtil.create(
+ this.elements.main = DomUtil.create('div')
+ this.elements.container = DomUtil.create(
'div',
'icon_container',
this.elements.main
)
this.elements.main.dataset.feature = this.feature?.id
- this.elements.arrow = L.DomUtil.create('div', 'icon_arrow', this.elements.main)
+ this.elements.arrow = DomUtil.create('div', 'icon_arrow', this.elements.main)
this._setIconStyles(this.elements.main, 'icon')
return this.elements.main
},
_setIconStyles: function (img, name) {
- U.Icon.prototype._setIconStyles.call(this, img, name)
+ BaseIcon.prototype._setIconStyles.call(this, img, name)
const color = this._getColor('color')
let background
if (L.Browser.ielt9) {
@@ -176,7 +194,7 @@ U.Icon.Ball = U.Icon.Default.extend({
},
})
-U.Icon.Cluster = L.DivIcon.extend({
+export const Cluster = DivIcon.extend({
options: {
iconSize: [40, 40],
},
@@ -187,9 +205,9 @@ U.Icon.Cluster = L.DivIcon.extend({
},
createIcon: function () {
- const container = L.DomUtil.create('div', 'leaflet-marker-icon marker-cluster')
- const div = L.DomUtil.create('div', '', container)
- const span = L.DomUtil.create('span', '', div)
+ const container = DomUtil.create('div', 'leaflet-marker-icon marker-cluster')
+ const div = DomUtil.create('div', '', container)
+ const span = DomUtil.create('span', '', div)
const backgroundColor = this.datalayer.getColor()
span.textContent = this.cluster.getChildCount()
div.style.backgroundColor = backgroundColor
@@ -202,27 +220,28 @@ U.Icon.Cluster = L.DivIcon.extend({
if (this.datalayer.options.cluster?.textColor) {
color = this.datalayer.options.cluster.textColor
}
- return color || L.DomUtil.TextColorFromBackgroundColor(el, backgroundColor)
+ return color || DomUtil.TextColorFromBackgroundColor(el, backgroundColor)
},
})
-U.Icon.isImg = (src) =>
- U.Utils.isPath(src) || U.Utils.isRemoteUrl(src) || U.Utils.isDataImage(src)
+export function isImg(src) {
+ return Utils.isPath(src) || Utils.isRemoteUrl(src) || Utils.isDataImage(src)
+}
-U.Icon.makeIconElement = (src, parent) => {
+export function makeElement(src, parent) {
let icon
- if (U.Icon.isImg(src)) {
- icon = L.DomUtil.create('img')
+ if (isImg(src)) {
+ icon = DomUtil.create('img')
icon.src = src
} else {
- icon = L.DomUtil.create('span')
+ icon = DomUtil.create('span')
icon.textContent = src
}
parent.appendChild(icon)
return icon
}
-U.Icon.setIconContrast = (icon, parent, src, bgcolor) => {
+export function setContrast(icon, parent, src, bgcolor) {
/*
* icon: the element we'll adapt the style, it can be an image or text
* parent: the element we'll consider to decide whether to adapt the style,
@@ -233,14 +252,10 @@ U.Icon.setIconContrast = (icon, parent, src, bgcolor) => {
*/
if (!icon) return
- if (L.DomUtil.contrastedColor(parent, bgcolor)) {
+ if (DomUtil.contrastedColor(parent, bgcolor)) {
// Decide whether to switch svg to white or not, but do it
// only for internal SVG, as invert could do weird things
- if (
- U.Utils.isPath(src) &&
- src.endsWith('.svg') &&
- src !== U.SCHEMA.iconUrl.default
- ) {
+ if (Utils.isPath(src) && src.endsWith('.svg') && src !== SCHEMA.iconUrl.default) {
// Must be called after icon container is added to the DOM
// An image
icon.style.filter = 'invert(1)'
@@ -250,3 +265,7 @@ U.Icon.setIconContrast = (icon, parent, src, bgcolor) => {
}
}
}
+
+export function formatUrl(url, feature) {
+ return Utils.greedyTemplate(url || '', feature ? feature.extendedProperties() : {})
+}
diff --git a/umap/static/umap/js/modules/rendering/layers/cluster.js b/umap/static/umap/js/modules/rendering/layers/cluster.js
index d0623bcf..af206795 100644
--- a/umap/static/umap/js/modules/rendering/layers/cluster.js
+++ b/umap/static/umap/js/modules/rendering/layers/cluster.js
@@ -1,10 +1,10 @@
// WARNING must be loaded dynamically, or at least after leaflet.markercluster
// Uses global L.MarkerCluster and L.MarkerClusterGroup, not exposed as ESM
-// Uses global U.Icon not yet a module
import { translate } from '../../i18n.js'
import { LayerMixin } from './base.js'
import * as Utils from '../../utils.js'
import { Evented } from '../../../../vendors/leaflet/leaflet-src.esm.js'
+import { Cluster as ClusterIcon } from '../icon.js'
const MarkerCluster = L.MarkerCluster.extend({
// Custom class so we can call computeTextColor
@@ -34,7 +34,7 @@ export const Cluster = L.MarkerClusterGroup.extend({
polygonOptions: {
color: this.datalayer.getColor(),
},
- iconCreateFunction: (cluster) => new U.Icon.Cluster(datalayer, cluster),
+ iconCreateFunction: (cluster) => new ClusterIcon(datalayer, cluster),
}
if (this.datalayer.options.cluster?.radius) {
options.maxClusterRadius = this.datalayer.options.cluster.radius
diff --git a/umap/static/umap/js/modules/rendering/template.js b/umap/static/umap/js/modules/rendering/template.js
index cb032dc1..e320356e 100644
--- a/umap/static/umap/js/modules/rendering/template.js
+++ b/umap/static/umap/js/modules/rendering/template.js
@@ -1,6 +1,7 @@
import { DomUtil, DomEvent } from '../../../vendors/leaflet/leaflet-src.esm.js'
import { translate, getLocale } from '../i18n.js'
import * as Utils from '../utils.js'
+import * as Icon from './icon.js'
export default function loadTemplate(name, feature, container) {
let klass = PopupTemplate
@@ -160,9 +161,9 @@ class OSM extends TitleMixin(PopupTemplate) {
const color = feature.getPreviewColor()
title.style.backgroundColor = color
const iconUrl = feature.getDynamicOption('iconUrl')
- const icon = U.Icon.makeIconElement(iconUrl, title)
+ const icon = Icon.makeElement(iconUrl, title)
DomUtil.addClass(icon, 'icon')
- U.Icon.setIconContrast(icon, title, iconUrl, color)
+ Icon.setContrast(icon, title, iconUrl, color)
if (DomUtil.contrastedColor(title, color)) title.style.color = 'white'
DomUtil.add('span', '', title, this.getName(feature))
const street = props['addr:street']
diff --git a/umap/static/umap/js/modules/rendering/ui.js b/umap/static/umap/js/modules/rendering/ui.js
index ad799a59..f86888c6 100644
--- a/umap/static/umap/js/modules/rendering/ui.js
+++ b/umap/static/umap/js/modules/rendering/ui.js
@@ -11,6 +11,7 @@ import {
import { translate } from '../i18n.js'
import { uMapAlert as Alert } from '../../components/alerts/alert.js'
import * as Utils from '../utils.js'
+import * as Icon from './icon.js'
const FeatureMixin = {
initialize: function (feature, latlngs) {
@@ -219,7 +220,7 @@ export const LeafletMarker = Marker.extend({
},
getIcon: function () {
- const Class = U.Icon[this.getIconClass()] || U.Icon.Default
+ const Class = Icon.getClass(this.getIconClass())
return new Class({ feature: this.feature })
},
diff --git a/umap/static/umap/js/umap.forms.js b/umap/static/umap/js/umap.forms.js
index 4288a4c8..cb6760d8 100644
--- a/umap/static/umap/js/umap.forms.js
+++ b/umap/static/umap/js/umap.forms.js
@@ -550,7 +550,7 @@ L.FormBuilder.IconUrl = L.FormBuilder.BlurInput.extend({
// Do not try to render URL with variables
const box = L.DomUtil.create('div', 'umap-pictogram-choice', this.buttons)
L.DomEvent.on(box, 'click', this.onDefine, this)
- const icon = U.Icon.makeIconElement(this.value(), box)
+ const icon = U.Icon.makeElement(this.value(), box)
}
this.button = L.DomUtil.createButton(
'button action-button',
@@ -571,7 +571,7 @@ L.FormBuilder.IconUrl = L.FormBuilder.BlurInput.extend({
if (search && U.Utils.normalize(title).indexOf(search) === -1) return
const className = value === this.value() ? `${baseClass} selected` : baseClass
const container = L.DomUtil.create('div', className, parent)
- U.Icon.makeIconElement(value, container)
+ U.Icon.makeElement(value, container)
container.title = title
L.DomEvent.on(
container,
diff --git a/umap/templates/umap/js.html b/umap/templates/umap/js.html
index 593e8226..9f36ea47 100644
--- a/umap/templates/umap/js.html
+++ b/umap/templates/umap/js.html
@@ -45,7 +45,6 @@
defer>
-