From af6c7fba26a1b58ca6b23d6183e39d7e47139581 Mon Sep 17 00:00:00 2001 From: Yohan Boniface Date: Sat, 7 Jul 2018 14:57:56 +0200 Subject: [PATCH] Allow to use an unicode char as marker icon symbol MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit One can also use a property to have a dynamic value (eg. to display numbers:, 1, 2, 3, 4…). fix #527 --- docs/changelog.md | 1 + umap/static/umap/base.css | 24 ++++++++++++++- umap/static/umap/js/umap.core.js | 2 +- umap/static/umap/js/umap.forms.js | 49 +++++++++++++++++++++++-------- umap/static/umap/js/umap.icon.js | 12 ++++++-- umap/static/umap/map.css | 6 ++++ umap/static/umap/test/Marker.js | 32 ++++++++++++++++++++ umap/static/umap/test/_pre.js | 1 + 8 files changed, 110 insertions(+), 17 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 90faa0fd..584eb651 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -71,6 +71,7 @@ COMMIT; - fixed ClusterMarker text color on Chrome (#547) - allow to clone also markers - only list https ready tilerlayers when page is in https (#567) +- allow to use an unicode character as Marker symbol (#527) diff --git a/umap/static/umap/base.css b/umap/static/umap/base.css index db9f033f..9de276ce 100644 --- a/umap/static/umap/base.css +++ b/umap/static/umap/base.css @@ -553,7 +553,29 @@ i.info { cursor: pointer; float: left; } - +input.blur { + width: calc(100% - 40px); + display: inline-block; + vertical-align: middle; + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +.blur + .button:before { + content: '✔'; +} +.blur + .button { + width: 40px; + height: 18px; + display: inline-block; + vertical-align: middle; + line-height: 18px; + border-top-left-radius: 0; + border-bottom-left-radius: 0; + box-sizing: border-box; +} +input[type=hidden].blur + .button { + display: none; +} /* *********** */ /* Panel */ diff --git a/umap/static/umap/js/umap.core.js b/umap/static/umap/js/umap.core.js index b05aab43..0f1c57e3 100644 --- a/umap/static/umap/js/umap.core.js +++ b/umap/static/umap/js/umap.core.js @@ -410,7 +410,7 @@ L.U.Help = L.Class.extend({ }, formatURL: L._('Supported variables that will be dynamically replaced') + ': {bbox}, {lat}, {lng}, {zoom}, {east}, {north}..., {left}, {top}...', - formatIconURL: L._('You can use feature properties as variables: ex.: with "http://myserver.org/images/{name}.png", the {name} variable will be replaced by the "name" value of each markers.'), + formatIconSymbol: L._('Symbol can be either a unicode caracter or an URL. You can use feature properties as variables: ex.: with "http://myserver.org/images/{name}.png", the {name} variable will be replaced by the "name" value of each marker.'), colorValue: L._('Must be a valid CSS value (eg.: DarkBlue or #123456)'), smoothFactor: L._('How much to simplify the polyline on each zoom level (more = better performance and smoother look, less = more accurate)'), dashArray: L._('A comma separated list of numbers that defines the stroke dash pattern. Ex.: "5, 10, 15".'), diff --git a/umap/static/umap/js/umap.forms.js b/umap/static/umap/js/umap.forms.js index bc8c72c5..5151a2e0 100644 --- a/umap/static/umap/js/umap.forms.js +++ b/umap/static/umap/js/umap.forms.js @@ -52,10 +52,10 @@ L.FormBuilder.Element.include({ this.label = L.DomUtil.create('label', '', this.getLabelParent()); this.label.innerHTML = this.label.title = this.options.label; if (this.options.helpEntries) this.builder.map.help.button(this.label, this.options.helpEntries); - else if (this.options.helpText) { + else if (this.options.helpTooltip) { var info = L.DomUtil.create('i', 'info', this.label); L.DomEvent.on(info, 'mouseover', function () { - this.builder.map.ui.tooltip({anchor: info, content: this.options.helpText, position: 'top'}); + this.builder.map.ui.tooltip({anchor: info, content: this.options.helpTooltip, position: 'top'}); }, this); } } @@ -355,28 +355,51 @@ L.FormBuilder.NullableBoolean = L.FormBuilder.Select.extend({ }); -L.FormBuilder.IconUrl = L.FormBuilder.Input.extend({ + +L.FormBuilder.BlurInput.include({ + + build: function () { + this.options.className = 'blur'; + L.FormBuilder.Input.prototype.build.call(this); + var button = L.DomUtil.create('span', 'button blur-button'); + L.DomUtil.after(this.input, button); + } + +}); + +L.FormBuilder.IconUrl = L.FormBuilder.BlurInput.extend({ type: function () { return 'hidden'; }, build: function () { - L.FormBuilder.Input.prototype.build.call(this); + this.options.helpText = this.builder.map.help.formatIconSymbol; + L.FormBuilder.BlurInput.prototype.build.call(this); this.parentContainer = L.DomUtil.create('div', 'umap-form-iconfield', this.parentNode); this.buttonsContainer = L.DomUtil.create('div', '', this.parentContainer); this.pictogramsContainer = L.DomUtil.create('div', 'umap-pictogram-list', this.parentContainer); this.input.type = 'hidden'; - this.input.placeholder = L._('Url'); + this.input.placeholder = L._('Symbol or url'); this.udpatePreview(); this.on('define', this.fetchIconList); }, + isUrl: function () { + return (this.value().indexOf('/') !== -1); + }, + udpatePreview: function () { - if (this.value() && this.value().indexOf('{') === -1) { // Do not try to render URL with variables - var img = L.DomUtil.create('img', '', L.DomUtil.create('div', 'umap-icon-choice', this.buttonsContainer)); - img.src = this.value(); - L.DomEvent.on(img, 'click', this.fetchIconList, this); + if (this.value()&& this.value().indexOf('{') === -1) { // Do not try to render URL with variables + if (this.isUrl()) { + var img = L.DomUtil.create('img', '', L.DomUtil.create('div', 'umap-icon-choice', this.buttonsContainer)); + img.src = this.value(); + L.DomEvent.on(img, 'click', this.fetchIconList, this); + } else { + var el = L.DomUtil.create('span', '', L.DomUtil.create('div', 'umap-icon-choice', this.buttonsContainer)); + el.textContent = this.value(); + L.DomEvent.on(el, 'click', this.fetchIconList, this); + } } this.button = L.DomUtil.create('a', '', this.buttonsContainer); this.button.innerHTML = this.value() ? L._('Change symbol') : L._('Add symbol'); @@ -432,15 +455,15 @@ L.FormBuilder.IconUrl = L.FormBuilder.Input.extend({ this.udpatePreview(); }, this); var customButton = L.DomUtil.create('a', '', this.pictogramsContainer); - customButton.innerHTML = L._('Set URL'); + customButton.innerHTML = L._('Set symbol'); customButton.href = '#'; customButton.style.display = 'block'; customButton.style.clear = 'both'; - this.builder.map.help.button(customButton, 'formatIconURL'); + this.builder.map.help.button(customButton, 'formatIconSymbol'); L.DomEvent .on(customButton, 'click', L.DomEvent.stop) .on(customButton, 'click', function (e) { - this.input.type = 'url'; + this.input.type = 'text'; this.pictogramsContainer.innerHTML = ''; }, this); }, @@ -699,7 +722,7 @@ L.U.FormBuilder = L.FormBuilder.extend({ smoothFactor: {handler: 'Range', min: 0, max: 10, step: 0.5, label: L._('Simplify'), helpEntries: 'smoothFactor', inheritable: true}, dashArray: {label: L._('dash array'), helpEntries: 'dashArray', inheritable: true}, iconClass: {handler: 'IconClassSwitcher', label: L._('Icon shape'), inheritable: true}, - iconUrl: {handler: 'IconUrl', label: L._('Icon symbol'), inheritable: true}, + iconUrl: {handler: 'IconUrl', label: L._('Icon symbol'), inheritable: true, helpText: L.U.Help.formatIconSymbol}, popupTemplate: {handler: 'PopupTemplate', label: L._('Popup style'), inheritable: true}, popupContentTemplate: {label: L._('Popup content template'), handler: 'Textarea', helpEntries: ['dynamicProperties', 'textFormatting'], placeholder: '# {name}', inheritable: true}, datalayer: {handler: 'DataLayerSwitcher', label: L._('Choose the layer of the feature')}, diff --git a/umap/static/umap/js/umap.icon.js b/umap/static/umap/js/umap.icon.js index f98cc0c4..68170ea3 100644 --- a/umap/static/umap/js/umap.icon.js +++ b/umap/static/umap/js/umap.icon.js @@ -59,9 +59,17 @@ L.U.Icon.Default = L.U.Icon.extend({ this.elements.main = L.DomUtil.create('div'); this.elements.container = L.DomUtil.create('div', 'icon_container', this.elements.main); this.elements.arrow = L.DomUtil.create('div', 'icon_arrow', this.elements.main); - this.elements.img = L.DomUtil.create('img', null, this.elements.container); var src = this._getIconUrl('icon'); - if (src) this.elements.img.src = src; + if (src) { + // An url. + if (src.indexOf('http') === 0 || src.indexOf('/') === 0) { + this.elements.img = L.DomUtil.create('img', null, this.elements.container); + this.elements.img.src = src; + } else { + this.elements.span = L.DomUtil.create('span', null, this.elements.container) + this.elements.span.textContent = src; + } + } this._setColor(); this._setIconStyles(this.elements.main, 'icon'); return this.elements.main; diff --git a/umap/static/umap/map.css b/umap/static/umap/map.css index 361210bf..288eac07 100644 --- a/umap/static/umap/map.css +++ b/umap/static/umap/map.css @@ -1030,6 +1030,12 @@ a.add-datalayer:hover, vertical-align: middle; max-width: 24px !important; } +.umap-div-icon .icon_container span, +.umap-drop-icon .icon_container span { + vertical-align: middle; + color: white; + font-weight: bold; +} .umap-circle-icon { border: 1px solid white; border-radius: 10px 10px 10px 10px; diff --git a/umap/static/umap/test/Marker.js b/umap/static/umap/test/Marker.js index 108d1a0f..9d607621 100644 --- a/umap/static/umap/test/Marker.js +++ b/umap/static/umap/test/Marker.js @@ -31,6 +31,38 @@ describe('L.U.Marker', function () { }); + describe('#iconSymbolChange()', function () { + + it('should change icon symbol', function () { + enableEdit(); + happen.click(qs('div.umap-drop-icon')); + happen.click(qs('ul.leaflet-inplace-toolbar a.umap-toggle-edit')); + changeInputValue(qs('form#umap-feature-shape-properties .umap-field-iconUrl input[name=iconUrl]'), '1'); + assert.equal(qs('div.umap-drop-icon span').textContent, '1'); + changeInputValue(qs('form#umap-feature-shape-properties .umap-field-iconUrl input[name=iconUrl]'), '{name}'); + assert.equal(qs('div.umap-drop-icon span').textContent, 'test'); + clickCancel(); + }); + + }); + + describe('#iconClassChange()', function () { + + it('should change icon class', function () { + enableEdit(); + happen.click(qs('div.umap-drop-icon')); + happen.click(qs('ul.leaflet-inplace-toolbar a.umap-toggle-edit')); + changeSelectValue(qs('form#umap-feature-shape-properties .umap-field-iconClass select[name=iconClass]'), 'Circle'); + assert.notOk(qs('div.umap-drop-icon')); + assert.ok(qs('div.umap-circle-icon')); + happen.click(qs('form#umap-feature-shape-properties .umap-field-iconClass .undefine')); + assert.notOk(qs('div.umap-circle-icon')); + assert.ok(qs('div.umap-drop-icon')); + clickCancel(); + }); + + }); + describe('#clone', function () { it('should clone marker', function () { diff --git a/umap/static/umap/test/_pre.js b/umap/static/umap/test/_pre.js index 386220dc..1bd25878 100644 --- a/umap/static/umap/test/_pre.js +++ b/umap/static/umap/test/_pre.js @@ -44,6 +44,7 @@ var clickCancel = function () { var changeInputValue = function (input, value) { input.value = value; happen.once(input, {type: 'input'}); + happen.once(input, {type: 'blur'}); }; var changeSelectValue = function (path_or_select, value) { if (typeof path_or_select === 'string') path_or_select = qs(path_or_select);