mirror of
https://github.com/umap-project/umap.git
synced 2025-04-29 11:52:38 +02:00
feat: very minimal experimental conditional style rules
This commit is contained in:
parent
b94995120a
commit
05eab25da4
5 changed files with 194 additions and 2 deletions
|
@ -5,6 +5,7 @@ import Caption from './caption.js'
|
||||||
import { Panel, EditPanel, FullPanel } from './ui/panel.js'
|
import { Panel, EditPanel, FullPanel } from './ui/panel.js'
|
||||||
import Dialog from './ui/dialog.js'
|
import Dialog from './ui/dialog.js'
|
||||||
import Tooltip from './ui/tooltip.js'
|
import Tooltip from './ui/tooltip.js'
|
||||||
|
import Rules from './rules.js'
|
||||||
import * as Utils from './utils.js'
|
import * as Utils from './utils.js'
|
||||||
import { SCHEMA } from './schema.js'
|
import { SCHEMA } from './schema.js'
|
||||||
import { Request, ServerRequest, RequestError, HTTPError, NOKError } from './request.js'
|
import { Request, ServerRequest, RequestError, HTTPError, NOKError } from './request.js'
|
||||||
|
@ -43,6 +44,7 @@ window.U = {
|
||||||
Panel,
|
Panel,
|
||||||
Request,
|
Request,
|
||||||
RequestError,
|
RequestError,
|
||||||
|
Rules,
|
||||||
SCHEMA,
|
SCHEMA,
|
||||||
ServerRequest,
|
ServerRequest,
|
||||||
SyncEngine,
|
SyncEngine,
|
||||||
|
|
175
umap/static/umap/js/modules/rules.js
Normal file
175
umap/static/umap/js/modules/rules.js
Normal file
|
@ -0,0 +1,175 @@
|
||||||
|
import { DomUtil, DomEvent, stamp } from '../../vendors/leaflet/leaflet-src.esm.js'
|
||||||
|
import * as Utils from './utils.js'
|
||||||
|
import { translate } from './i18n.js'
|
||||||
|
|
||||||
|
class Rule {
|
||||||
|
constructor(map, condition = '', options = {}) {
|
||||||
|
this.map = map
|
||||||
|
this.condition = condition
|
||||||
|
this.parse()
|
||||||
|
this.options = options
|
||||||
|
let isDirty = false
|
||||||
|
Object.defineProperty(this, 'isDirty', {
|
||||||
|
get: () => {
|
||||||
|
return isDirty
|
||||||
|
},
|
||||||
|
set: (status) => {
|
||||||
|
isDirty = status
|
||||||
|
if (status) {
|
||||||
|
this.map.isDirty = status
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
render(fields) {
|
||||||
|
this.map.render(fields)
|
||||||
|
}
|
||||||
|
|
||||||
|
parse() {
|
||||||
|
let vars = []
|
||||||
|
if (this.condition.includes('!=')) {
|
||||||
|
this.operator = (our, other) => our != other
|
||||||
|
vars = this.condition.split('!=')
|
||||||
|
} else if (this.condition.includes('=')) {
|
||||||
|
this.operator = (our, other) => our === other
|
||||||
|
vars = this.condition.split('=')
|
||||||
|
}
|
||||||
|
if (vars.length != 2) this.operator = undefined
|
||||||
|
this.key = vars[0]
|
||||||
|
this.expected = vars[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
match(props) {
|
||||||
|
if (!this.operator) return false
|
||||||
|
return this.operator(this.expected, props[this.key])
|
||||||
|
}
|
||||||
|
|
||||||
|
getMap() {
|
||||||
|
return this.map
|
||||||
|
}
|
||||||
|
|
||||||
|
getOption(option) {
|
||||||
|
return this.options[option]
|
||||||
|
}
|
||||||
|
|
||||||
|
edit() {
|
||||||
|
const options = [
|
||||||
|
[
|
||||||
|
'condition',
|
||||||
|
{
|
||||||
|
handler: 'BlurInput',
|
||||||
|
label: L._('Condition'),
|
||||||
|
placeholder: translate('key=value or key!=value'),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'options.color',
|
||||||
|
'options.iconClass',
|
||||||
|
'options.iconUrl',
|
||||||
|
'options.iconOpacity',
|
||||||
|
'options.opacity',
|
||||||
|
'options.weight',
|
||||||
|
'options.fill',
|
||||||
|
'options.fillColor',
|
||||||
|
'options.fillOpacity',
|
||||||
|
'options.smoothFactor',
|
||||||
|
'options.dashArray',
|
||||||
|
]
|
||||||
|
const container = DomUtil.create('div')
|
||||||
|
const builder = new U.FormBuilder(this, options)
|
||||||
|
const defaultShapeProperties = L.DomUtil.add('div', '', container)
|
||||||
|
defaultShapeProperties.appendChild(builder.build())
|
||||||
|
|
||||||
|
this.map.editPanel.open({ content: container })
|
||||||
|
}
|
||||||
|
|
||||||
|
renderToolbox(container) {
|
||||||
|
const toggle = L.DomUtil.createButtonIcon(
|
||||||
|
container,
|
||||||
|
'icon-eye',
|
||||||
|
L._('Show/hide layer')
|
||||||
|
)
|
||||||
|
const edit = L.DomUtil.createButtonIcon(
|
||||||
|
container,
|
||||||
|
'icon-edit show-on-edit',
|
||||||
|
L._('Edit')
|
||||||
|
)
|
||||||
|
const remove = L.DomUtil.createButtonIcon(
|
||||||
|
container,
|
||||||
|
'icon-delete show-on-edit',
|
||||||
|
L._('Delete layer')
|
||||||
|
)
|
||||||
|
L.DomEvent.on(edit, 'click', this.edit, this)
|
||||||
|
L.DomEvent.on(
|
||||||
|
remove,
|
||||||
|
'click',
|
||||||
|
function () {
|
||||||
|
if (!confirm(L._('Are you sure you want to delete this rule?'))) return
|
||||||
|
this._delete()
|
||||||
|
this.map.editPanel.close()
|
||||||
|
},
|
||||||
|
this
|
||||||
|
)
|
||||||
|
DomUtil.add('span', '', container, this.condition || translate('empty rule'))
|
||||||
|
//L.DomEvent.on(toggle, 'click', this.toggle, this)
|
||||||
|
}
|
||||||
|
|
||||||
|
_delete() {
|
||||||
|
this.map.rules.rules = this.map.rules.rules.filter((rule) => rule != this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class Rules {
|
||||||
|
constructor(map) {
|
||||||
|
this.map = map
|
||||||
|
this.rules = []
|
||||||
|
this.loadRules()
|
||||||
|
}
|
||||||
|
|
||||||
|
loadRules() {
|
||||||
|
if (!this.map.options.rules?.length) return
|
||||||
|
for (const { condition, options } of this.map.options.rules) {
|
||||||
|
if (!condition) continue
|
||||||
|
this.rules.push(new Rule(this.map, condition, options))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
edit(container) {
|
||||||
|
const body = L.DomUtil.createFieldset(
|
||||||
|
container,
|
||||||
|
translate('Conditional style rules')
|
||||||
|
)
|
||||||
|
if (this.rules.length) {
|
||||||
|
const list = DomUtil.create('ul', '', body)
|
||||||
|
for (const rule of this.rules) {
|
||||||
|
rule.renderToolbox(DomUtil.create('li', '', list))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
L.DomUtil.createButton('umap-add', body, translate('Add rule'), this.addRule, this)
|
||||||
|
}
|
||||||
|
|
||||||
|
addRule() {
|
||||||
|
const rule = new Rule(this.map)
|
||||||
|
rule.isDirty = true
|
||||||
|
this.rules.push(rule)
|
||||||
|
rule.edit(map)
|
||||||
|
}
|
||||||
|
|
||||||
|
commit() {
|
||||||
|
this.map.options.rules = this.rules.map((rule) => {
|
||||||
|
return {
|
||||||
|
condition: rule.condition,
|
||||||
|
options: rule.options,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
getOption(option, feature) {
|
||||||
|
for (const rule of this.rules) {
|
||||||
|
if (rule.match(feature.properties)) {
|
||||||
|
if (Utils.usableOption(rule.options, option)) return rule.options[option]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -386,6 +386,10 @@ export const SCHEMA = {
|
||||||
type: Object,
|
type: Object,
|
||||||
impacts: ['remote-data'],
|
impacts: ['remote-data'],
|
||||||
},
|
},
|
||||||
|
rules: {
|
||||||
|
type: Object,
|
||||||
|
impacts: ['data'],
|
||||||
|
},
|
||||||
scaleControl: {
|
scaleControl: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
impacts: ['ui'],
|
impacts: ['ui'],
|
||||||
|
|
|
@ -419,6 +419,7 @@ U.Map = L.Map.extend({
|
||||||
this.importer = new U.Importer(this)
|
this.importer = new U.Importer(this)
|
||||||
this.drop = new U.DropControl(this)
|
this.drop = new U.DropControl(this)
|
||||||
this.share = new U.Share(this)
|
this.share = new U.Share(this)
|
||||||
|
this.rules = new U.Rules(this)
|
||||||
this._controls.tilelayers = new U.TileLayerControl(this)
|
this._controls.tilelayers = new U.TileLayerControl(this)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -823,7 +824,15 @@ U.Map = L.Map.extend({
|
||||||
return U.SCHEMA[option] && U.SCHEMA[option].default
|
return U.SCHEMA[option] && U.SCHEMA[option].default
|
||||||
},
|
},
|
||||||
|
|
||||||
getOption: function (option) {
|
getRuleOption: function (option, feature) {
|
||||||
|
return this.rules.getOption(option, feature)
|
||||||
|
},
|
||||||
|
|
||||||
|
getOption: function (option, feature) {
|
||||||
|
if (feature) {
|
||||||
|
const value = this.getRuleOption(option, feature)
|
||||||
|
if (value !== undefined) return value
|
||||||
|
}
|
||||||
if (U.Utils.usableOption(this.options, option)) return this.options[option]
|
if (U.Utils.usableOption(this.options, option)) return this.options[option]
|
||||||
return this.getDefaultOption(option)
|
return this.getDefaultOption(option)
|
||||||
},
|
},
|
||||||
|
@ -1031,6 +1040,7 @@ U.Map = L.Map.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
saveSelf: async function () {
|
saveSelf: async function () {
|
||||||
|
this.rules.commit()
|
||||||
const geojson = {
|
const geojson = {
|
||||||
type: 'Feature',
|
type: 'Feature',
|
||||||
geometry: this.geometry(),
|
geometry: this.geometry(),
|
||||||
|
@ -1561,6 +1571,7 @@ U.Map = L.Map.extend({
|
||||||
this._editShapeProperties(container)
|
this._editShapeProperties(container)
|
||||||
this._editDefaultProperties(container)
|
this._editDefaultProperties(container)
|
||||||
this._editInteractionsProperties(container)
|
this._editInteractionsProperties(container)
|
||||||
|
this.rules.edit(container)
|
||||||
this._editTilelayer(container)
|
this._editTilelayer(container)
|
||||||
this._editOverlay(container)
|
this._editOverlay(container)
|
||||||
this._editBounds(container)
|
this._editBounds(container)
|
||||||
|
|
|
@ -1496,7 +1496,7 @@ U.DataLayer = L.Evented.extend({
|
||||||
} else if (this.layer && this.layer.defaults && this.layer.defaults[option]) {
|
} else if (this.layer && this.layer.defaults && this.layer.defaults[option]) {
|
||||||
return this.layer.defaults[option]
|
return this.layer.defaults[option]
|
||||||
} else {
|
} else {
|
||||||
return this.map.getOption(option)
|
return this.map.getOption(option, feature)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue