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;