diff options
Diffstat (limited to 'src/marker.ts')
-rw-r--r-- | src/marker.ts | 171 |
1 files changed, 171 insertions, 0 deletions
diff --git a/src/marker.ts b/src/marker.ts new file mode 100644 index 0000000..9f59497 --- /dev/null +++ b/src/marker.ts @@ -0,0 +1,171 @@ +import { h, s } from 'lib/h' +import * as Color from 'lib/color' +import * as ContextMenu from 'lib/contextMenu' +import * as MarkerForm from 'markerForm' +import * as Icons from 'lib/icons' +import * as Modal from 'lib/modal' +import * as State from 'state' +const L = window.L + +interface CreateParams { + id: State.Index, + pos: L.Pos, + color: string, + icon: string, + name: string, + radius: number, + addToMap: (layer: L.Layer | L.FeatureGroup) => void, + removeFromMap: (layer: L.Layer | L.FeatureGroup) => void, +} + +export function add({ id, pos, color, icon, name, radius, addToMap, removeFromMap }: CreateParams) { + const marker = L.marker(pos, { + draggable: true, + autoPan: true, + icon: divIcon({ icon, color, name }), + }) + + const circle = + radius !== 0 + ? L.circle(pos, { radius, color, fillColor: color }) + : undefined + + const layer = + circle !== undefined + ? L.featureGroup([ marker, circle ]) + : L.featureGroup([ marker ]) + + const onUpdate = () => + Modal.show(MarkerForm.view({ + onValidate: (color: string, icon: string, name: string, radius: number) => { + removeFromMap(layer) + add({ id, pos, color, icon, name, radius, addToMap, removeFromMap }) + State.update(id, { pos, color, icon, name, radius }) + Modal.hide() + }, + onCancel: () => Modal.hide(), + color, + icon, + name, + radius, + })) + + marker.addEventListener('contextmenu', e => { + ContextMenu.show( + e.originalEvent, + [ { label: 'Modify', + action: onUpdate, + } + , { label: 'Remove', + action: () => { + removeFromMap(layer) + State.remove(id) + } + } + ] + ) + }) + + marker.addEventListener('drag', e => { + circle && circle.setLatLng(marker.getLatLng()) + }) + + marker.addEventListener('dragend', e => { + const pos = marker.getLatLng() + removeFromMap(layer) + add({ id, pos, color, icon, name, radius, addToMap, removeFromMap }) + State.update(id, { pos, color, icon, name, radius }) + }) + + marker.addEventListener('dblclick', onUpdate) + + addToMap(layer) +} + +interface CreateIconParams { + icon: string, + color: string, + name: string, +} + +function divIcon({ icon, color, name }: CreateIconParams): L.Icon { + const c = Color.parse(color) + const crBlack = Color.contrastRatio({ red: 0, green: 0, blue: 0 }, c) + const crWhite = Color.contrastRatio({ red: 255, green: 255, blue: 255 }, c) + const textCol = crBlack > crWhite ? 'black' : 'white' + const width = 10 + const height = 15 + const stroke = 'black' + const strokeWidth = 0.6 + // Triangle + const t = [ + { x: width * 0.15, y: 7.46 }, + { x: width / 2, y: height }, + { x: width * 0.85, y: 7.46 } + ] + return L.divIcon( + { className: '' + , popupAnchor: [ 0, -34 ] + , html: + h('div', + { className: 'g-Marker' }, + s('svg', + { viewBox: `0 0 ${width} ${height}`, + class: 'g-Marker__Base' + }, + s('circle', + { cx: width / 2, + cy: width / 2, + r: (width - 2 * strokeWidth) / 2, + stroke, + 'stroke-width': strokeWidth, + fill: color + } + ), + s('polygon', + { points: `${t[0].x},${t[0].y} ${t[1].x},${t[1].y} ${t[2].x},${t[2].y}`, + fill: color + } + ), + s('line', + { x1: t[0].x, + y1: t[0].y, + x2: t[1].x, + y2: t[1].y, + stroke, + 'stroke-width': strokeWidth + } + ), + s('line', + { x1: t[1].x, + y1: t[1].y, + x2: t[2].x, + y2: t[2].y, + stroke, + 'stroke-width': strokeWidth + } + ), + ), + Icons.get( + icon, + { class: 'g-Marker__Icon' + , style: `fill: ${textCol}; stroke: ${textCol}` + } + ), + h('div', + { className: 'g-Marker__Title', + style: `color: black; text-shadow: ${textShadow('white', 1, 1)}` + }, + name + ) + ) + } + ) +} + +function textShadow(color: string, w: number, blurr: number): string { + return [[-w, -w], [-w, 0], [-w, w], [0, -w], [0, w], [w, -w], [w, 0], [w, w]] + .map(xs => `${color} ${xs[0]}px ${xs[1]}px ${blurr}px`) + .join(', ') +} + |