From dca6f8cb94a454ea86891e9a5d944bf836ae1102 Mon Sep 17 00:00:00 2001
From: James Kafader
Date: Tue, 23 Aug 2022 10:16:41 -0700
Subject: [PATCH] add support for hide/show sidebar. Add support for hover
tooltips. Update animation for edge hover/select
---
src/MapPanel.tsx | 10 +-
src/components/EditingInterface.component.js | 16 +-
src/components/MapCanvas.component.js | 37 +-
src/components/NetworkMap.js | 37 +-
src/components/SideBar.component.js | 3 +-
src/components/css/esmap.css.js | 57 +-
src/components/lib/esmap.js | 214 ++++---
src/components/lib/rubbercement.js | 49 ++
src/css/esmap.css | 140 ----
src/css/leaflet.css | 640 -------------------
src/module.ts | 29 +-
src/types.ts | 1 +
12 files changed, 327 insertions(+), 906 deletions(-)
delete mode 100644 src/css/esmap.css
delete mode 100644 src/css/leaflet.css
diff --git a/src/MapPanel.tsx b/src/MapPanel.tsx
index 4f7280a..931fc4c 100644
--- a/src/MapPanel.tsx
+++ b/src/MapPanel.tsx
@@ -15,12 +15,19 @@ export class MapPanel extends Component {
constructor(props: Props) {
super(props);
- // ref approach... doesn't seem to want to work.
this.mapCanvas = React.createRef();
this.lastOptions = this.props.options;
this.theme = createTheme();
+ PubSub.subscribe('returnMapCenterAndZoom', this.updateCenter);
}
+ updateCenter = (centerData) => {
+ const startLat = centerData.center.lat;
+ const startLng = centerData.center.lng;
+ const startZoom = centerData.zoom;
+ this.props.onOptionsChange({ ...this.props.options, startLat, startLng, startZoom });
+ };
+
// A function to update the map jsons in the Edit panel based on the current map state
// Used in esmap.js
updateMapJson = (mapData) => {
@@ -47,6 +54,7 @@ export class MapPanel extends Component {
'tileSetLayer',
'boundaryLayer',
'labelLayer',
+ 'showSidebar',
'layer1',
'color1',
diff --git a/src/components/EditingInterface.component.js b/src/components/EditingInterface.component.js
index f656ac0..f81ee8b 100644
--- a/src/components/EditingInterface.component.js
+++ b/src/components/EditingInterface.component.js
@@ -20,13 +20,21 @@ class EditingInterface extends BindableHTMLElement {
PubSub.subscribe("clearSelection", ()=>{
this.selection = false;
}, this)
- PubSub.subscribe("toggleNodeEdit", ()=>{
+ PubSub.subscribe("toggleNodeEdit", (value)=>{
this._edgeEditMode = false;
- this.nodeEditMode = !this.nodeEditMode;
+ if(value === null || value === undefined){
+ this.nodeEditMode = !this.nodeEditMode;
+ } else {
+ this.nodeEditMode = value;
+ }
}, this)
- PubSub.subscribe("toggleEdgeEdit", ()=>{
+ PubSub.subscribe("toggleEdgeEdit", (value)=>{
this._nodeEditMode = false;
- this.edgeEditMode = !this.edgeEditMode;
+ if(value === null || value === undefined){
+ this.edgeEditMode = !this.edgeEditMode;
+ } else {
+ this.edgeEditMode = value;
+ }
}, this)
PubSub.subscribe("showEditNodeDialog", (evtData)=>{
this._selectedNode = evtData['object'];
diff --git a/src/components/MapCanvas.component.js b/src/components/MapCanvas.component.js
index 13f4357..3fc881a 100644
--- a/src/components/MapCanvas.component.js
+++ b/src/components/MapCanvas.component.js
@@ -6,6 +6,7 @@ import "./SideBar.component.js"
import * as maplayers from './lib/maplayers.js';
import * as pubsub from './lib/pubsub.js';
import { testJsonSchema } from './lib/utils.js';
+
const PubSub = pubsub.PubSub;
const PrivateMessageBus = pubsub.PrivateMessageBus;
@@ -36,6 +37,15 @@ export class MapCanvas extends HTMLElement {
PubSub.subscribe('updateMapTopology', this.updateMapTopology, this);
PubSub.subscribe('updateMapDimensions', this.updateMapDimensions, this);
PubSub.subscribe('updateTopology', () => { this.updateTopology(this.topology) }, this);
+ PubSub.subscribe('getMapCenterAndZoom', (() => {
+ var self = this;
+ return () => {
+ PubSub.publish("returnMapCenterAndZoom", {
+ center: self.map.leafletMap.getBounds().getCenter(),
+ zoom: self.map.leafletMap.getZoom()
+ })
+ }
+ })());
}
get topology() {
@@ -83,6 +93,20 @@ export class MapCanvas extends HTMLElement {
this._options[k] = options[k];
})
+ if(changed.indexOf('showSidebar') >= 0){
+ var edgeEditMode = this.editingInterface.edgeEditMode;
+ var nodeEditMode = this.editingInterface.nodeEditMode;
+ this.shadow.remove();
+ this.shadow = null;
+ this.render();
+ this.newMap();
+ if(edgeEditMode){
+ PubSub.publish("toggleEdgeEdit", edgeEditMode, this);
+ }
+ if(nodeEditMode){
+ PubSub.publish("toggleNodeEdit", nodeEditMode, this);
+ }
+ }
if (
changed.indexOf('tileSetLayer')>=0 ||
changed.indexOf('boundaryLayer')>=0 ||
@@ -215,10 +239,11 @@ export class MapCanvas extends HTMLElement {
${leafletCss}
+
- `;
+ ${ this.options.showSidebar ? "" : "" }`;
this.mapContainer = this.shadow.querySelector("#map");
this.editingInterface = this.shadow.querySelector("esnet-map-editing-interface");
@@ -227,14 +252,20 @@ export class MapCanvas extends HTMLElement {
this.editingInterface.updateTopology = this.updateTopology;
this.sideBar = this.shadow.querySelector("esnet-map-side-bar");
- this.sideBar.mapCanvas = this;
+ if(this.sideBar){
+ this.sideBar.mapCanvas = this;
+ }
}
this.renderStyle();
if(this.height){
this.mapContainer.style.height = this.height + 'px';
}
if(this.width){
- this.mapContainer.style.width = (this.width * 0.80) - 5 + 'px';
+ if(this.options.showSidebar){
+ this.mapContainer.style.width = (this.width * 0.80) - 5 + 'px';
+ } else {
+ this.mapContainer.style.width = this.width + "px";
+ }
}
if(!this.map && this.options && this.topology){
this.newMap();
diff --git a/src/components/NetworkMap.js b/src/components/NetworkMap.js
index debb7d3..2fd3078 100644
--- a/src/components/NetworkMap.js
+++ b/src/components/NetworkMap.js
@@ -5,22 +5,24 @@ import * as utils from './lib/utils.js';
// these imports are the result of very significant trial and error.
// they allow ES6 browser imports to propagate, but also use static import
-// statements, without which some of these libraries (d3 in particular)
+// statements, without which some of these libraries (d3 in particular)
// will not function. This can also be accomplished using the `import()` function
// but it causes the code to become complexly asynchronous. keeping these as
// static imports allows the best balance of simplicity and functionality
import * as d3_import from './lib/d3.min.js';
// populate either with import or ES6 root-scope version
-const d3 = window['d3'] || d3_import;
+const d3 = window['d3'] || d3_import;
// dynamic import of modules that must be handled this way
var locationService = { "partial": function(){ } }
// require is only defined in the webpack context, not ES6
var L = window['L'];
-if(typeof require !== "undefined"){
+try {
var L = require('./lib/leaflet.js');
- const m = require('@grafana/runtime');
- locationService = m.locationService;
+ /*const m = require('@grafana/runtime');
+ locationService = m.locationService;*/
+} catch (e) {
+ console.log(e);
}
export default class NetworkMap {
@@ -74,30 +76,27 @@ export default class NetworkMap {
setLocation[dashboardVariable] = [
srcVariable + "|=|" + event.nodeA,
dstVariable + "|=|" + event.nodeZ
- ]
+ ]
}
locationService.partial(setLocation, false)
}
- toggleEdgeEdit() {
- if (!!this.esmap.editEdges) {
+ setEdgeEdit(bool){
this.esmap.editNodeMode(false);
+ this.esmap.editEdgeMode(bool);
+ }
+ setNodeEdit(bool){
this.esmap.editEdgeMode(false);
- } else {
- this.esmap.editNodeMode(false);
- this.esmap.editEdgeMode(true);
- }
+ this.esmap.editNodeMode(bool);
+ }
+
+ toggleEdgeEdit() {
+ this.setEdgeEdit(!this.esmap.editEdges);
}
toggleNodeEdit() {
- if (!!this.esmap.editNodes) {
- this.esmap.editEdgeMode(false);
- this.esmap.editNodeMode(false);
- } else {
- this.esmap.editEdgeMode(false);
- this.esmap.editNodeMode(true);
- }
+ this.setNodeEdit(!this.esmap.editNodes);
}
renderMapLayers() {
diff --git a/src/components/SideBar.component.js b/src/components/SideBar.component.js
index ff222bf..19b5299 100644
--- a/src/components/SideBar.component.js
+++ b/src/components/SideBar.component.js
@@ -31,7 +31,8 @@ class SideBar extends BindableHTMLElement {
PubSub.publish("toggleLayer", {"layer": layer, "visible": value}, this);
}
- showTooltip(tooltip){
+ showTooltip(data){
+ var tooltip = data.text;
var tooltipTarget = this.shadow.querySelector("#sidebar-tooltip");
tooltipTarget.style.opacity = 1; // this gets set incorrectly at times.
tooltipTarget.innerHTML = tooltip;
diff --git a/src/components/css/esmap.css.js b/src/components/css/esmap.css.js
index 6d5ed63..b86d7b7 100644
--- a/src/components/css/esmap.css.js
+++ b/src/components/css/esmap.css.js
@@ -1,4 +1,5 @@
-export const esmapCss = `.leaflet-pane > svg path {
+export const esmapCss = `
+.leaflet-pane > svg path {
pointer-events: all;
}
@@ -6,7 +7,13 @@ export const esmapCss = `.leaflet-pane > svg path {
position: relative;
}
+svg path.edge-az {
+ marker-start: url("#arrow");
+}
+svg path.edge-za {
+ marker-start: url("#arrow");
+}
svg circle.node {
/* fill: #999; */
@@ -21,23 +28,23 @@ svg path.edge {
pointer-events: visiblePainted !important;
}
-svg path.animated-edge {
- stroke-linecap: butt;
- stroke-width: 5;
- /* stroke: #aaa; */
+svg path.animated-edge.edge-az {
+ stroke: transparent;
+ stroke-linecap: butt;
fill: none;
- cursor: crosshair;
+ cursor: crosshair;
+}
- stroke-dasharray: 90 10 ;
- stroke-dashoffset: 100;
- animation-name: dash;
- animation-duration: 5s;
- animation-timing-function: steps(25,start);
- animation-delay: 0s;
- animation-direction: forwards;
- animation-iteration-count: infinite;
- animation-play-state: running;
+svg path.animated-edge.edge-za {
+ stroke: transparent;
+ stroke-linecap: butt;
+ fill: none;
+ cursor: crosshair;
+}
+svg path.animated-edge.edge-az.selected {
+}
+svg path.animated-edge.edge-za.selected {
}
@keyframes dash {
@@ -62,6 +69,26 @@ svg circle.controlPoint {
cursor: move;
}
+div.tooltip-hover {
+ position:absolute;
+ z-index:10000;
+ background: white;
+ border-radius:4px;
+ padding:10px;
+ margin:10px;
+ max-width:250px;
+ box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.3);
+}
+
+div.tooltip-hover p:first-of-type {
+ margin-top:0;
+}
+
+div.tooltip-hover p {
+ margin-top: 6px;
+ margin-bottom:0;
+}
+
div.sidebar-tooltip {
position: absolute;
text-align: left;
diff --git a/src/components/lib/esmap.js b/src/components/lib/esmap.js
index 0998c8f..9217c02 100644
--- a/src/components/lib/esmap.js
+++ b/src/components/lib/esmap.js
@@ -6,47 +6,55 @@ const d3 = window['d3'] || d3_import;
import * as React_import from "./react.js";
// populate either with import or ES6 root-scope version
const React = window['React'] || React_import;
-import { render as renderTemplate } from "./templates.js"
-
-function createSvgMarker(svg) {
- //--- setup markers
- var defs = svg.append('svg:defs').attr('id', 'markers');
-
- //--- could not this just be in a template somewhere?
- var marker = defs
- .selectAll('marker')
- .data([
- {
- id: 2,
- name: 'arrow',
- path: 'M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z',
- viewbox: '0 0 20 20',
- },
- ])
- .enter()
- .append('svg:marker')
- .attr('id', function (d) {
- return d.name;
- })
- .attr('markerHeight', 5)
- .attr('markerWidth', 4)
- .attr('markerUnits', 'strokeWidth')
- .attr('orient', 'auto')
- .attr('refX', 10)
- .attr('refY', 10)
- .attr('viewBox', function (d) {
- return d.viewbox;
- })
- .append('svg:path')
- .attr('d', function (d) {
- return d.path;
- })
- .attr('fill', function (d, i) {
- return '#333';
- })
- .attr('stroke', '#555') // color
- .attr('stroke-width', 0.5);
- return marker;
+import { render as renderTemplate } from "./rubbercement.js"
+
+// functions to calculate bearings between two points
+// Converts from degrees to radians.
+function toRadians(degrees) {
+ return degrees * Math.PI / 180;
+};
+
+// Converts from radians to degrees.
+function toDegrees(radians) {
+ return radians * 180 / Math.PI;
+}
+
+function bearingAngle(src, dest){
+ const deltaX = dest[0] - src[0];
+ const deltaY = dest[1] - src[1];
+
+ const slope = deltaY / deltaX;
+ const angleInRads = Math.atan(slope);
+ return toDegrees(angleInRads);
+}
+
+function pathCrawl(path, klass, color, edgeWidth){
+ var svgns = "http://www.w3.org/2000/svg";
+ //path.setAttribute("stroke", "transparent");
+ var totalDashWidth = 10;
+ for(var i=0; i<(path.getTotalLength() / totalDashWidth); i++){
+ var animStart = i/(path.getTotalLength()/totalDashWidth);
+ var animEnd = (i+1)/(path.getTotalLength()/totalDashWidth);
+ if(animStart >= 0 && animEnd < 1){
+ var value = path.getAttribute("d");
+ var dashWidth = edgeWidth * 1.2; // this will vary with the stroke width for the line
+ var rect = document.createElementNS(svgns, "polygon");
+ rect.setAttributeNS(null, "points", `${(totalDashWidth / 5 * 4)-(dashWidth/3)*2},${dashWidth} -${(dashWidth/3) * 2},${dashWidth} 0,0 -${(dashWidth/3) * 2},-${dashWidth} ${(totalDashWidth / 5 * 4)-(dashWidth/3)*2},-${dashWidth} ${(totalDashWidth / 5 * 4)},0`)
+ rect.setAttributeNS(null, "fill", color);
+ rect.setAttributeNS(null, "class", klass);
+ var anim = document.createElementNS(svgns, "animateMotion");
+ anim.setAttributeNS(null, "dur", "0.5s");
+ anim.setAttributeNS(null, "path", value);
+ anim.setAttributeNS(null, "class", "animation");
+ anim.setAttributeNS(null, "repeatCount", "indefinite");
+ anim.setAttributeNS(null, "keyPoints", `${animStart};${animEnd}`);
+ anim.setAttributeNS(null, "keyTimes", "0;1");
+ anim.setAttributeNS(null, "calcMode", "linear");
+ anim.setAttributeNS(null, "rotate", "auto");
+ rect.appendChild(anim);
+ path.parentElement.insertBefore(rect, path);
+ }
+ }
}
function renderEdges(g, data, ref, layerId) {
@@ -67,26 +75,34 @@ function renderEdges(g, data, ref, layerId) {
.attr('stroke', function (d) {
return d.azColor;
})
- .attr('marker-mid', function (d, i) {
- return 'url(#arrow)';
- })
.attr('stroke-width', edgeWidth)
.attr('class', function (d) {
- var connections = " connects-to-"+d.name.split("--").join(" connects-to-");
- return 'edge edge-az edge-az-' + d.name + connections;
+ var name = d.name.replaceAll(" ", "-");
+ var connections = " connects-to-"+name.split("--").join(" connects-to-");
+ var layerClass = ' l'+layerId;
+ return 'edge edge-az edge-az-' + name + connections + layerClass;
})
.attr('text', function (d) {
return d.AZname;
})
.attr('pointer-events', 'visiblePainted')
.on('click', function(event, d){
+ var dashes = document.querySelectorAll(".dash-selected");
+ for(var i=0; iFrom: ${ d.nodeA }
To: ${ d.nodeZ }
Volume: ${ d.AZdisplayValue }
`;
- PubSub.publish("showTooltip", text, ref.svg.node());
+ PubSub.publish("showTooltip", { "event": event, "text": text }, ref.svg.node());
})
- .on('mouseout', function (d, i) {
+ .on('mouseout', function (event, d) {
+ d3.select(this).attr('stroke', d.azColor);
+ var dashes = document.querySelectorAll(".dash-over");
+ for(var i=0; iFrom: ' +
- d.nodeZ +
- 'To: ' +
- d.nodeA +
- '
Volume: ' +
- d.ZAdisplayValue +
- '
';
- PubSub.publish("showTooltip", text, ref.svg.node());
- })
- .on('mouseout', function (d, i) {
- // don't stop animating if this component is selected
+ `From: ${d.nodeZ}
+ To: ${d.nodeA}
+ Volume: ${d.ZAdisplayValue}
`;
+ PubSub.publish("showTooltip", { "event": event, "text": text }, ref.svg.node());
+ })
+ .on('mouseout', function (event, d) {
+ d3.select(this).attr('stroke', d.azColor);
+ var dashes = document.querySelectorAll(".dash-over");
+ for(var i=0; iIn Volume: ${d.inValue}
Out Volume: ${d.outValue}
`;
}
- PubSub.publish("showTooltip", text, ref.svg.node());
+ PubSub.publish("showTooltip", { "event": event, "text": text }, ref.svg.node());
})
.on('mouseleave', function(){
PubSub.publish("hideTooltip", null, ref.svg.node());
@@ -355,8 +391,9 @@ function renderEdgeControl(g, data, ref, layerId) {
return d.controlPointPath;
})
.attr('class', function (d) {
- var connections = " control-for-"+d.name.split("--").join(" control-for-");
- return 'control controlEddge edge-az-' + d.name + connections;
+ var name = d.name.replaceAll(" ", "-");
+ var connections = " control-for-"+name.split("--").join(" control-for-");
+ return 'control controlEddge edge-az-' + name + connections;
})
// still need to figure out how to not zoom when doubleclicking here
@@ -483,7 +520,7 @@ function renderNodes(g, data, ref, layerId) {
Out Volume: ${d.outValue}
`;
}
- PubSub.publish("showTooltip", text, ref.svg.node());
+ PubSub.publish("showTooltip", { "event": event, "text": text }, ref.svg.node());
})
.on('mouseout', function (event, d) {
d3.select(event.target.parentElement).attr("transform", "scale(1.0, 1.0)")
@@ -595,16 +632,31 @@ export class EsMap {
this.data = {};
this.mapLayers = {};
this.lineGen = d3.line().curve(curve);
- this.editEdges = 0;
- this.editNodes = 0;
+ this.editEdges = this.mapCanvas.editingInterface.editEdgeMode;
+ this.editNodes = this.mapCanvas.editingInterface.editNodeMode;
+ console.log(this.editEdges);
+ console.log(this.editNodes);
this.div = div;
this.options = this.mapCanvas.options;
this.lastInteractedObject = null; // the last object that the user interacted with
// used for nudging and deletion via keyboard
this.lastInteractedType = null; // "nodes" or "edges"
- createSvgMarker(this.svg);
-
+ if(!this.mapCanvas.options.showSidebar){
+ PubSub.subscribe("showTooltip",function(data){
+ var elem = document.createElement("div");
+ elem.setAttribute("id", "tooltip-hover");
+ elem.setAttribute("class", "tooltip-hover");
+ elem.innerHTML = data.text;
+ elem.setAttribute("style", `top:${data.event.clientY+10}px; left:${data.event.clientX+10}px;`);
+ document.body.appendChild(elem);
+ },
+ this.svg.node())
+ PubSub.subscribe("hideTooltip",function(){
+ document.querySelector("#tooltip-hover").remove();
+ },
+ this.svg.node())
+ }
//
let self = this;
@@ -621,6 +673,10 @@ export class EsMap {
PubSub.subscribe("updateOptions", updateOptions, this.svg.node());
function clearSelection(){
+ var dashes = document.querySelectorAll(".dash-selected");
+ for(var i=0; i
+ group
+ .toUpperCase()
+ .replace('-', '')
+ .replace('_', '')
+ );
+ }
+}
+
+
+export const types = {
+ "choiceAmong": function(choices, value){
+ return choices.indexOf(value) >= 0;
+ },
+ "boolean": function(value){
+ return choiceAmong(["true", 1, "yes", "True", true], value);
+ },
+ "string": function(value){
+ return String(value);
+ },
+ "number": function(value){
+ return Number(value);
+ },
+ "function": function(value){
+ return value;
+ }
+}
+
+
+const TEMPLATE_REGEX = /\${[^{}]+}/g;
+
+// copied from Matt Browne's answer https://stackoverflow.com/questions/29182244/convert-a-string-to-a-template-string
+// functions renamed slightly for readability
+
+//get the specified property or nested property of an object
+function resolveObjPath(path, obj, fallback = '') {
+ return path.split('.').reduce((res, key) => res[key] || fallback, obj);
+}
+
+export function render(template, variables, fallback) {
+ return template.replace(TEMPLATE_REGEX, (match) => {
+ const path = match.slice(2, -1).trim();
+ return resolveObjPath(path, variables, fallback);
+ });
+}
\ No newline at end of file
diff --git a/src/css/esmap.css b/src/css/esmap.css
deleted file mode 100644
index bdf625f..0000000
--- a/src/css/esmap.css
+++ /dev/null
@@ -1,140 +0,0 @@
-.leaflet-pane > svg path {
- pointer-events: all;
-}
-
-.map-panel {
- position: relative;
-}
-
-
-
-svg circle.node {
- /* fill: #999; */
- stroke: #777;
- stroke-width: 1;
- pointer-events: all;
-}
-
-svg path.edge {
- stroke-linecap: butt;
- fill: none;
- pointer-events: visiblePainted !important;
-}
-
-svg path.animated-edge {
- stroke-linecap: butt;
- stroke-width: 5;
- /* stroke: #aaa; */
- fill: none;
- cursor: crosshair;
-
- stroke-dasharray: 90 10 ;
- stroke-dashoffset: 100;
- animation-name: dash;
- animation-duration: 5s;
- animation-timing-function: steps(25,start);
- animation-delay: 0s;
- animation-direction: forwards;
- animation-iteration-count: infinite;
- animation-play-state: running;
-
-}
-
-@keyframes dash {
- to {
- stroke-dashoffset: 0;
- }
-}
-
-
-svg path.control {
- stroke-dasharray: 8 1;
- stroke-width: 6;
- stroke: #f808;
- fill: none;
- cursor: crosshair;
-}
-
-svg circle.controlPoint {
- stroke: black;
- stroke-width: 1;
- fill: #f80;
- cursor: move;
-}
-
-div.sidebar-tooltip {
- position: absolute;
- text-align: left;
- height: auto;
- font: sans-serif;
- pointer-events: none;
- line-height: 0.9;
-}
-
-.legend-text {
- padding-left: 5px;
- vertical-align: middle;
-}
-
-/* The switch - the box around the slider */
-.switch {
- position: relative;
- display: inline-block;
- width: 45px;
- height: 26px;
-}
-
-/* Hide default HTML checkbox */
-.switch input {
- opacity: 0;
- width: 0;
- height: 0;
- margin-top: 5px;
-}
-
-/* The slider */
-.slider {
- position: absolute;
- cursor: pointer;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- background-color: #ccc;
- -webkit-transition: .4s;
- transition: .4s;
- border-radius: 25px;
-}
-
-.slider:before {
- position: absolute;
- content: "";
- height: 20px;
- width: 20px;
- left: 4px;
- bottom: 3px;
- background-color: white;
- -webkit-transition: .4s;
- transition: .4s;
- border-radius: 50%;
-}
-
-input:checked + .slider {
- background-color: #4EC1E0;
-}
-
-input:focus + .slider {
- box-shadow: 0 0 1px #4EC1E0;
-}
-
-input:checked + .slider:before {
- -webkit-transform: translateX(18px);
- -ms-transform: translateX(18px);
- transform: translateX(18px);
-}
-
-/* this is to bring grafana panel header on top leaflet layers */
-.panel-header:hover {
- z-index: 1000;
-}
-
diff --git a/src/css/leaflet.css b/src/css/leaflet.css
deleted file mode 100644
index dbdbc7d..0000000
--- a/src/css/leaflet.css
+++ /dev/null
@@ -1,640 +0,0 @@
-/* required styles */
-
-.leaflet-pane,
-.leaflet-tile,
-.leaflet-marker-icon,
-.leaflet-marker-shadow,
-.leaflet-tile-container,
-.leaflet-pane > svg,
-.leaflet-pane > canvas,
-.leaflet-zoom-box,
-.leaflet-image-layer,
-.leaflet-layer {
- position: absolute;
- left: 0;
- top: 0;
- }
-.leaflet-container {
- overflow: hidden;
- }
-.leaflet-tile,
-.leaflet-marker-icon,
-.leaflet-marker-shadow {
- -webkit-user-select: none;
- -moz-user-select: none;
- user-select: none;
- -webkit-user-drag: none;
- }
-/* Prevents IE11 from highlighting tiles in blue */
-.leaflet-tile::selection {
- background: transparent;
-}
-/* Safari renders non-retina tile on retina better with this, but Chrome is worse */
-.leaflet-safari .leaflet-tile {
- image-rendering: -webkit-optimize-contrast;
- }
-/* hack that prevents hw layers "stretching" when loading new tiles */
-.leaflet-safari .leaflet-tile-container {
- width: 1600px;
- height: 1600px;
- -webkit-transform-origin: 0 0;
- }
-.leaflet-marker-icon,
-.leaflet-marker-shadow {
- display: block;
- }
-/* .leaflet-container svg: reset svg max-width decleration shipped in Joomla! (joomla.org) 3.x */
-/* .leaflet-container img: map is broken in FF if you have max-width: 100% on tiles */
-.leaflet-container .leaflet-overlay-pane svg,
-.leaflet-container .leaflet-marker-pane img,
-.leaflet-container .leaflet-shadow-pane img,
-.leaflet-container .leaflet-tile-pane img,
-.leaflet-container img.leaflet-image-layer,
-.leaflet-container .leaflet-tile {
- max-width: none !important;
- max-height: none !important;
- }
-
-.leaflet-container.leaflet-touch-zoom {
- -ms-touch-action: pan-x pan-y;
- touch-action: pan-x pan-y;
- }
-.leaflet-container.leaflet-touch-drag {
- -ms-touch-action: pinch-zoom;
- /* Fallback for FF which doesn't support pinch-zoom */
- touch-action: none;
- touch-action: pinch-zoom;
-}
-.leaflet-container.leaflet-touch-drag.leaflet-touch-zoom {
- -ms-touch-action: none;
- touch-action: none;
-}
-.leaflet-container {
- -webkit-tap-highlight-color: transparent;
-}
-.leaflet-container a {
- -webkit-tap-highlight-color: rgba(51, 181, 229, 0.4);
-}
-.leaflet-tile {
- filter: inherit;
- visibility: hidden;
- }
-.leaflet-tile-loaded {
- visibility: inherit;
- }
-.leaflet-zoom-box {
- width: 0;
- height: 0;
- -moz-box-sizing: border-box;
- box-sizing: border-box;
- z-index: 800;
- }
-/* workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=888319 */
-.leaflet-overlay-pane svg {
- -moz-user-select: none;
- }
-
-.leaflet-pane { z-index: 400; }
-
-.leaflet-tile-pane { z-index: 200; }
-.leaflet-overlay-pane { z-index: 400; }
-.leaflet-shadow-pane { z-index: 500; }
-.leaflet-marker-pane { z-index: 600; }
-.leaflet-tooltip-pane { z-index: 650; }
-.leaflet-popup-pane { z-index: 700; }
-
-.leaflet-map-pane canvas { z-index: 100; }
-.leaflet-map-pane svg { z-index: 200; }
-
-.leaflet-vml-shape {
- width: 1px;
- height: 1px;
- }
-.lvml {
- behavior: url(#default#VML);
- display: inline-block;
- position: absolute;
- }
-
-
-/* control positioning */
-
-.leaflet-control {
- position: relative;
- z-index: 800;
- pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */
- pointer-events: auto;
- }
-.leaflet-top,
-.leaflet-bottom {
- position: absolute;
- z-index: 1000;
- pointer-events: none;
- }
-.leaflet-top {
- top: 0;
- }
-.leaflet-right {
- right: 0;
- }
-.leaflet-bottom {
- bottom: 0;
- }
-.leaflet-left {
- left: 0;
- }
-.leaflet-control {
- float: left;
- clear: both;
- }
-.leaflet-right .leaflet-control {
- float: right;
- }
-.leaflet-top .leaflet-control {
- margin-top: 10px;
- }
-.leaflet-bottom .leaflet-control {
- margin-bottom: 10px;
- }
-.leaflet-left .leaflet-control {
- margin-left: 10px;
- }
-.leaflet-right .leaflet-control {
- margin-right: 10px;
- }
-
-
-/* zoom and fade animations */
-
-.leaflet-fade-anim .leaflet-tile {
- will-change: opacity;
- }
-.leaflet-fade-anim .leaflet-popup {
- opacity: 0;
- -webkit-transition: opacity 0.2s linear;
- -moz-transition: opacity 0.2s linear;
- transition: opacity 0.2s linear;
- }
-.leaflet-fade-anim .leaflet-map-pane .leaflet-popup {
- opacity: 1;
- }
-.leaflet-zoom-animated {
- -webkit-transform-origin: 0 0;
- -ms-transform-origin: 0 0;
- transform-origin: 0 0;
- }
-.leaflet-zoom-anim .leaflet-zoom-animated {
- will-change: transform;
- }
-.leaflet-zoom-anim .leaflet-zoom-animated {
- -webkit-transition: -webkit-transform 0.25s cubic-bezier(0,0,0.25,1);
- -moz-transition: -moz-transform 0.25s cubic-bezier(0,0,0.25,1);
- transition: transform 0.25s cubic-bezier(0,0,0.25,1);
- }
-.leaflet-zoom-anim .leaflet-tile,
-.leaflet-pan-anim .leaflet-tile {
- -webkit-transition: none;
- -moz-transition: none;
- transition: none;
- }
-
-.leaflet-zoom-anim .leaflet-zoom-hide {
- visibility: hidden;
- }
-
-
-/* cursors */
-
-.leaflet-interactive {
- cursor: pointer;
- }
-.leaflet-grab {
- cursor: -webkit-grab;
- cursor: -moz-grab;
- cursor: grab;
- }
-.leaflet-crosshair,
-.leaflet-crosshair .leaflet-interactive {
- cursor: crosshair;
- }
-.leaflet-popup-pane,
-.leaflet-control {
- cursor: auto;
- }
-.leaflet-dragging .leaflet-grab,
-.leaflet-dragging .leaflet-grab .leaflet-interactive,
-.leaflet-dragging .leaflet-marker-draggable {
- cursor: move;
- cursor: -webkit-grabbing;
- cursor: -moz-grabbing;
- cursor: grabbing;
- }
-
-/* marker & overlays interactivity */
-.leaflet-marker-icon,
-.leaflet-marker-shadow,
-.leaflet-image-layer,
-.leaflet-pane > svg path,
-.leaflet-tile-container {
- pointer-events: all;
- }
-
-.leaflet-marker-icon.leaflet-interactive,
-.leaflet-image-layer.leaflet-interactive,
-.leaflet-pane > svg path.leaflet-interactive,
-svg.leaflet-image-layer.leaflet-interactive path {
- pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */
- pointer-events: auto;
- }
-
-/* visual tweaks */
-
-.leaflet-container {
- background: #ddd;
- outline: 0;
- }
-.leaflet-container a {
- color: #0078A8;
- }
-.leaflet-container a.leaflet-active {
- outline: 2px solid orange;
- }
-.leaflet-zoom-box {
- border: 2px dotted #38f;
- background: rgba(255,255,255,0.5);
- }
-
-
-/* general typography */
-.leaflet-container {
- font: 12px/1.5 "Helvetica Neue", Arial, Helvetica, sans-serif;
- }
-
-
-/* general toolbar styles */
-
-.leaflet-bar {
- box-shadow: 0 1px 5px rgba(0,0,0,0.65);
- border-radius: 4px;
- }
-.leaflet-bar a,
-.leaflet-bar a:hover {
- background-color: #fff;
- border-bottom: 1px solid #ccc;
- width: 26px;
- height: 26px;
- line-height: 26px;
- display: block;
- text-align: center;
- text-decoration: none;
- color: black;
- }
-.leaflet-bar a,
-.leaflet-control-layers-toggle {
- background-position: 50% 50%;
- background-repeat: no-repeat;
- display: block;
- }
-.leaflet-bar a:hover {
- background-color: #f4f4f4;
- }
-.leaflet-bar a:first-child {
- border-top-left-radius: 4px;
- border-top-right-radius: 4px;
- }
-.leaflet-bar a:last-child {
- border-bottom-left-radius: 4px;
- border-bottom-right-radius: 4px;
- border-bottom: none;
- }
-.leaflet-bar a.leaflet-disabled {
- cursor: default;
- background-color: #f4f4f4;
- color: #bbb;
- }
-
-.leaflet-touch .leaflet-bar a {
- width: 30px;
- height: 30px;
- line-height: 30px;
- }
-.leaflet-touch .leaflet-bar a:first-child {
- border-top-left-radius: 2px;
- border-top-right-radius: 2px;
- }
-.leaflet-touch .leaflet-bar a:last-child {
- border-bottom-left-radius: 2px;
- border-bottom-right-radius: 2px;
- }
-
-/* zoom control */
-
-.leaflet-control-zoom-in,
-.leaflet-control-zoom-out {
- font: bold 18px 'Lucida Console', Monaco, monospace;
- text-indent: 1px;
- }
-
-.leaflet-touch .leaflet-control-zoom-in, .leaflet-touch .leaflet-control-zoom-out {
- font-size: 22px;
- }
-
-
-/* layers control */
-
-.leaflet-control-layers {
- box-shadow: 0 1px 5px rgba(0,0,0,0.4);
- background: #fff;
- border-radius: 5px;
- }
-.leaflet-control-layers-toggle {
- /* background-image: url(../images/layers.png); */
- width: 36px;
- height: 36px;
- }
-.leaflet-retina .leaflet-control-layers-toggle {
- /* background-image: url(../images/layers-2x.png); */
- background-size: 26px 26px;
- }
-.leaflet-touch .leaflet-control-layers-toggle {
- width: 44px;
- height: 44px;
- }
-.leaflet-control-layers .leaflet-control-layers-list,
-.leaflet-control-layers-expanded .leaflet-control-layers-toggle {
- display: none;
- }
-.leaflet-control-layers-expanded .leaflet-control-layers-list {
- display: block;
- position: relative;
- }
-.leaflet-control-layers-expanded {
- padding: 6px 10px 6px 6px;
- color: #333;
- background: #fff;
- }
-.leaflet-control-layers-scrollbar {
- overflow-y: scroll;
- overflow-x: hidden;
- padding-right: 5px;
- }
-.leaflet-control-layers-selector {
- margin-top: 2px;
- position: relative;
- top: 1px;
- }
-.leaflet-control-layers label {
- display: block;
- }
-.leaflet-control-layers-separator {
- height: 0;
- border-top: 1px solid #ddd;
- margin: 5px -10px 5px -6px;
- }
-
-/* Default icon URLs */
-/* .leaflet-default-icon-path {
- background-image: url(images/marker-icon.png);
- } */
-
-
-/* attribution and scale controls */
-
-.leaflet-container .leaflet-control-attribution {
- background: #fff;
- background: rgba(255, 255, 255, 0.7);
- margin: 0;
- }
-.leaflet-control-attribution,
-.leaflet-control-scale-line {
- padding: 0 5px;
- color: #333;
- }
-.leaflet-control-attribution a {
- text-decoration: none;
- }
-.leaflet-control-attribution a:hover {
- text-decoration: underline;
- }
-.leaflet-container .leaflet-control-attribution,
-.leaflet-container .leaflet-control-scale {
- font-size: 11px;
- }
-.leaflet-left .leaflet-control-scale {
- margin-left: 5px;
- }
-.leaflet-bottom .leaflet-control-scale {
- margin-bottom: 5px;
- }
-.leaflet-control-scale-line {
- border: 2px solid #777;
- border-top: none;
- line-height: 1.1;
- padding: 2px 5px 1px;
- font-size: 11px;
- white-space: nowrap;
- overflow: hidden;
- -moz-box-sizing: border-box;
- box-sizing: border-box;
-
- background: #fff;
- background: rgba(255, 255, 255, 0.5);
- }
-.leaflet-control-scale-line:not(:first-child) {
- border-top: 2px solid #777;
- border-bottom: none;
- margin-top: -2px;
- }
-.leaflet-control-scale-line:not(:first-child):not(:last-child) {
- border-bottom: 2px solid #777;
- }
-
-.leaflet-touch .leaflet-control-attribution,
-.leaflet-touch .leaflet-control-layers,
-.leaflet-touch .leaflet-bar {
- box-shadow: none;
- }
-.leaflet-touch .leaflet-control-layers,
-.leaflet-touch .leaflet-bar {
- border: 2px solid rgba(0,0,0,0.2);
- background-clip: padding-box;
- }
-
-
-/* popup */
-
-.leaflet-popup {
- position: absolute;
- text-align: center;
- margin-bottom: 20px;
- }
-.leaflet-popup-content-wrapper {
- padding: 1px;
- text-align: left;
- border-radius: 12px;
- }
-.leaflet-popup-content {
- margin: 13px 19px;
- line-height: 1.4;
- }
-.leaflet-popup-content p {
- margin: 18px 0;
- }
-.leaflet-popup-tip-container {
- width: 40px;
- height: 20px;
- position: absolute;
- left: 50%;
- margin-left: -20px;
- overflow: hidden;
- pointer-events: none;
- }
-.leaflet-popup-tip {
- width: 17px;
- height: 17px;
- padding: 1px;
-
- margin: -10px auto 0;
-
- -webkit-transform: rotate(45deg);
- -moz-transform: rotate(45deg);
- -ms-transform: rotate(45deg);
- transform: rotate(45deg);
- }
-.leaflet-popup-content-wrapper,
-.leaflet-popup-tip {
- background: white;
- color: #333;
- box-shadow: 0 3px 14px rgba(0,0,0,0.4);
- }
-.leaflet-container a.leaflet-popup-close-button {
- position: absolute;
- top: 0;
- right: 0;
- padding: 4px 4px 0 0;
- border: none;
- text-align: center;
- width: 18px;
- height: 14px;
- font: 16px/14px Tahoma, Verdana, sans-serif;
- color: #c3c3c3;
- text-decoration: none;
- font-weight: bold;
- background: transparent;
- }
-.leaflet-container a.leaflet-popup-close-button:hover {
- color: #999;
- }
-.leaflet-popup-scrolled {
- overflow: auto;
- border-bottom: 1px solid #ddd;
- border-top: 1px solid #ddd;
- }
-
-.leaflet-oldie .leaflet-popup-content-wrapper {
- -ms-zoom: 1;
- }
-.leaflet-oldie .leaflet-popup-tip {
- width: 24px;
- margin: 0 auto;
-
- -ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)";
- filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678);
- }
-.leaflet-oldie .leaflet-popup-tip-container {
- margin-top: -1px;
- }
-
-.leaflet-oldie .leaflet-control-zoom,
-.leaflet-oldie .leaflet-control-layers,
-.leaflet-oldie .leaflet-popup-content-wrapper,
-.leaflet-oldie .leaflet-popup-tip {
- border: 1px solid #999;
- }
-
-
-/* div icon */
-
-.leaflet-div-icon {
- background: #fff;
- border: 1px solid #666;
- }
-
-
-/* Tooltip */
-/* Base styles for the element that has a tooltip */
-.leaflet-tooltip {
- position: absolute;
- padding: 6px;
- background-color: #fff;
- border: 1px solid #fff;
- border-radius: 3px;
- color: #222;
- white-space: nowrap;
- -webkit-user-select: none;
- -moz-user-select: none;
- -ms-user-select: none;
- user-select: none;
- pointer-events: none;
- box-shadow: 0 1px 3px rgba(0,0,0,0.4);
- }
-.leaflet-tooltip.leaflet-clickable {
- cursor: pointer;
- pointer-events: auto;
- }
-.leaflet-tooltip-top:before,
-.leaflet-tooltip-bottom:before,
-.leaflet-tooltip-left:before,
-.leaflet-tooltip-right:before {
- position: absolute;
- pointer-events: none;
- border: 6px solid transparent;
- background: transparent;
- content: "";
- }
-
-/* Directions */
-
-.leaflet-tooltip-bottom {
- margin-top: 6px;
-}
-.leaflet-tooltip-top {
- margin-top: -6px;
-}
-.leaflet-tooltip-bottom:before,
-.leaflet-tooltip-top:before {
- left: 50%;
- margin-left: -6px;
- }
-.leaflet-tooltip-top:before {
- bottom: 0;
- margin-bottom: -12px;
- border-top-color: #fff;
- }
-.leaflet-tooltip-bottom:before {
- top: 0;
- margin-top: -12px;
- margin-left: -6px;
- border-bottom-color: #fff;
- }
-.leaflet-tooltip-left {
- margin-left: -6px;
-}
-.leaflet-tooltip-right {
- margin-left: 6px;
-}
-.leaflet-tooltip-left:before,
-.leaflet-tooltip-right:before {
- top: 50%;
- margin-top: -6px;
- }
-.leaflet-tooltip-left:before {
- right: 0;
- margin-right: -12px;
- border-left-color: #fff;
- }
-.leaflet-tooltip-right:before {
- left: 0;
- margin-left: -12px;
- border-right-color: #fff;
- }
diff --git a/src/module.ts b/src/module.ts
index e420e58..f853213 100644
--- a/src/module.ts
+++ b/src/module.ts
@@ -2,6 +2,7 @@ import { FieldConfigProperty, PanelPlugin, FieldOverrideContext, getFieldDisplay
import { MapOptions } from './types';
import { MapPanel } from './MapPanel';
import { CustomTextArea } from './components/CustomTextArea';
+import { CoordinateButton } from './components/CoordinateButton';
const FieldsCategory = ['Choose Fields'];
const LayersCategory = ['Layer options'];
@@ -15,17 +16,32 @@ const layer3Bool = (layer3: boolean) => (config: MapOptions) => config.layer3 ==
// -------------------- Network Map Panel Options --------------------
plugin.setPanelOptions((builder) => {
- builder.addNumberInput({
+ builder.addCustomEditor({
+ id: 'setLatLngZoom',
+ path: 'setLatLngZoom',
+ name: 'Set Default Latitude / Longitude / Zoom',
+ description:
+ 'Set the default Latitude, Longitude and Zoom level to the current map Latitude, Longitude and Zoom level.',
+ settings: { label: 'Set Lat/Lng & Zoom' },
+ editor: CoordinateButton,
+ });
+ builder.addCustomEditor({
+ id: 'startLat',
path: 'startLat',
name: 'Starting Latitude of map',
description: 'This will be the center of the map when it loads. (numbers only)',
- defaultValue: 42,
+ defaultValue: 39,
+ settings: { useTextarea: true, rows: 1 },
+ editor: CustomTextArea,
});
- builder.addNumberInput({
+ builder.addCustomEditor({
+ id: 'startLng',
path: 'startLng',
name: 'Starting Longitude of map',
description: 'This will be the center of the map when it loads. (numbers only)',
- defaultValue: -105,
+ defaultValue: -98,
+ settings: { useTextarea: true, rows: 1 },
+ editor: CustomTextArea,
});
builder.addSliderInput({
path: 'startZoom',
@@ -130,6 +146,11 @@ plugin.setPanelOptions((builder) => {
},
},
});
+ builder.addBooleanSwitch({
+ path: 'showSidebar',
+ name: 'Show Map Sidebar',
+ defaultValue: true,
+ });
// -------------------- Layer Options -------------------
builder.addBooleanSwitch({
diff --git a/src/types.ts b/src/types.ts
index d9266f9..0002f52 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -3,6 +3,7 @@ export interface MapOptions {
color1: string;
color2: string;
color3: string;
+ showSidebar: boolean;
layer1: boolean;
mapjsonL1: string;
nodeHighlightL1: string;