Merge pull request #2057 from umap-project/icon-to-modules

chore: move icon.js to modules
This commit is contained in:
Yohan Boniface 2024-08-13 11:10:32 +02:00 committed by GitHub
commit 5a33709cc9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 86 additions and 63 deletions

View file

@ -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 })

View file

@ -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,

View file

@ -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() : {})
}

View file

@ -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

View file

@ -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']

View file

@ -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 })
},

View file

@ -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,

View file

@ -45,7 +45,6 @@
defer></script>
<script src="{% static 'umap/js/umap.core.js' %}" defer></script>
<script src="{% static 'umap/js/umap.forms.js' %}" defer></script>
<script src="{% static 'umap/js/umap.icon.js' %}" defer></script>
<script src="{% static 'umap/js/umap.controls.js' %}" defer></script>
<script src="{% static 'umap/js/umap.js' %}" defer></script>
<script src="{% static 'umap/js/components/fragment.js' %}" defer></script>