Merge pull request #4856 from Budibase/feature/map-component
Feature/map component
This commit is contained in:
commit
f11ee00acd
|
@ -28,5 +28,8 @@
|
||||||
],
|
],
|
||||||
"rules": {
|
"rules": {
|
||||||
"no-self-assign": "off"
|
"no-self-assign": "off"
|
||||||
|
},
|
||||||
|
"globals": {
|
||||||
|
"GeolocationPositionError": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,7 +82,8 @@
|
||||||
"link",
|
"link",
|
||||||
"icon",
|
"icon",
|
||||||
"embed",
|
"embed",
|
||||||
"markdownviewer"
|
"markdownviewer",
|
||||||
|
"embeddedmap"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -2524,6 +2524,82 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"embeddedmap": {
|
||||||
|
"name": "Embedded Map",
|
||||||
|
"icon": "Location",
|
||||||
|
"styles": ["size"],
|
||||||
|
"editable": true,
|
||||||
|
"draggable": false,
|
||||||
|
"illegalChildren": [
|
||||||
|
"section"
|
||||||
|
],
|
||||||
|
"settings": [
|
||||||
|
{
|
||||||
|
"type": "dataProvider",
|
||||||
|
"label": "Provider",
|
||||||
|
"key": "dataProvider"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "boolean",
|
||||||
|
"label": "Enable Fullscreen",
|
||||||
|
"key": "fullScreenEnabled",
|
||||||
|
"defaultValue": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "boolean",
|
||||||
|
"label": "Enable Location",
|
||||||
|
"key": "locationEnabled",
|
||||||
|
"defaultValue": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "boolean",
|
||||||
|
"label": "Enable Zoom",
|
||||||
|
"key": "zoomEnabled",
|
||||||
|
"defaultValue": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "number",
|
||||||
|
"label": "Zoom Level (0-100)",
|
||||||
|
"key": "zoomLevel",
|
||||||
|
"defaultValue": 72,
|
||||||
|
"max" : 100,
|
||||||
|
"min" : 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "field",
|
||||||
|
"label": "Latitude Key",
|
||||||
|
"key": "latitudeKey"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "field",
|
||||||
|
"label": "Longitude Key",
|
||||||
|
"key": "longitudeKey"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "field",
|
||||||
|
"label": "Title Key",
|
||||||
|
"key": "titleKey"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"label": "Tile URL",
|
||||||
|
"key": "tileURL",
|
||||||
|
"defaultValue": "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"label": "Default Location (lat,lng)",
|
||||||
|
"key": "defaultLocation",
|
||||||
|
"defaultValue": "51.5072,-0.1276"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"label": "Map Attribution",
|
||||||
|
"key": "mapAttribution",
|
||||||
|
"defaultValue": "OpenStreetMap contributors"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"attachmentfield": {
|
"attachmentfield": {
|
||||||
"name": "Attachment",
|
"name": "Attachment",
|
||||||
"icon": "Attach",
|
"icon": "Attach",
|
||||||
|
|
|
@ -33,8 +33,11 @@
|
||||||
"apexcharts": "^3.22.1",
|
"apexcharts": "^3.22.1",
|
||||||
"dayjs": "^1.10.5",
|
"dayjs": "^1.10.5",
|
||||||
"downloadjs": "1.4.7",
|
"downloadjs": "1.4.7",
|
||||||
|
"leaflet": "^1.7.1",
|
||||||
"regexparam": "^1.3.0",
|
"regexparam": "^1.3.0",
|
||||||
"rollup-plugin-polyfill-node": "^0.8.0",
|
"rollup-plugin-polyfill-node": "^0.8.0",
|
||||||
|
"sanitize-html": "^2.7.0",
|
||||||
|
"screenfull": "^6.0.1",
|
||||||
"shortid": "^2.2.15",
|
"shortid": "^2.2.15",
|
||||||
"svelte": "^3.38.2",
|
"svelte": "^3.38.2",
|
||||||
"svelte-apexcharts": "^1.0.2",
|
"svelte-apexcharts": "^1.0.2",
|
||||||
|
|
|
@ -103,7 +103,12 @@
|
||||||
($builderStore.previewType === "layout" || insideScreenslot) &&
|
($builderStore.previewType === "layout" || insideScreenslot) &&
|
||||||
!isBlock
|
!isBlock
|
||||||
$: editing = editable && selected && $builderStore.editMode
|
$: editing = editable && selected && $builderStore.editMode
|
||||||
$: draggable = !inDragPath && interactive && !isLayout && !isScreen
|
$: draggable =
|
||||||
|
!inDragPath &&
|
||||||
|
interactive &&
|
||||||
|
!isLayout &&
|
||||||
|
!isScreen &&
|
||||||
|
definition?.draggable !== false
|
||||||
$: droppable = interactive && !isLayout && !isScreen
|
$: droppable = interactive && !isLayout && !isScreen
|
||||||
|
|
||||||
// Empty components are those which accept children but do not have any.
|
// Empty components are those which accept children but do not have any.
|
||||||
|
|
|
@ -0,0 +1,297 @@
|
||||||
|
<script>
|
||||||
|
import L from "leaflet"
|
||||||
|
import sanitizeHtml from "sanitize-html"
|
||||||
|
import "leaflet/dist/leaflet.css"
|
||||||
|
import { Helpers } from "@budibase/bbui"
|
||||||
|
import { getContext } from "svelte"
|
||||||
|
import {
|
||||||
|
FullScreenControl,
|
||||||
|
LocationControl,
|
||||||
|
initMapControls,
|
||||||
|
} from "./EmbeddedMapControls"
|
||||||
|
|
||||||
|
initMapControls()
|
||||||
|
|
||||||
|
export let dataProvider
|
||||||
|
export let error
|
||||||
|
export let zoomLevel
|
||||||
|
export let zoomEnabled = true
|
||||||
|
export let latitudeKey = null
|
||||||
|
export let longitudeKey = null
|
||||||
|
export let titleKey = null
|
||||||
|
export let fullScreenEnabled = true
|
||||||
|
export let locationEnabled = true
|
||||||
|
export let defaultLocation
|
||||||
|
export let tileURL = "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
|
||||||
|
export let mapAttribution
|
||||||
|
|
||||||
|
const { styleable, notificationStore } = getContext("sdk")
|
||||||
|
const component = getContext("component")
|
||||||
|
const embeddedMapId = `${Helpers.uuid()}-wrapper`
|
||||||
|
|
||||||
|
let cachedDeviceCoordinates
|
||||||
|
const fallbackCoordinates = [51.5072, -0.1276] //London
|
||||||
|
|
||||||
|
let mapInstance
|
||||||
|
let mapMarkerGroup = new L.FeatureGroup()
|
||||||
|
let mapMarkers = []
|
||||||
|
|
||||||
|
let minZoomLevel = 0
|
||||||
|
let maxZoomLevel = 18
|
||||||
|
let adjustedZoomLevel = !Number.isInteger(zoomLevel)
|
||||||
|
? 72
|
||||||
|
: Math.round(zoomLevel * (maxZoomLevel / 100))
|
||||||
|
|
||||||
|
$: zoomControlUpdated(mapInstance, zoomEnabled)
|
||||||
|
$: locationControlUpdated(mapInstance, locationEnabled)
|
||||||
|
$: fullScreenControlUpdated(mapInstance, fullScreenEnabled)
|
||||||
|
$: updateMapDimensions(
|
||||||
|
mapInstance,
|
||||||
|
$component.styles.normal.width,
|
||||||
|
$component.styles.normal.height
|
||||||
|
)
|
||||||
|
$: addMapMarkers(
|
||||||
|
mapInstance,
|
||||||
|
dataProvider?.rows,
|
||||||
|
latitudeKey,
|
||||||
|
longitudeKey,
|
||||||
|
titleKey
|
||||||
|
)
|
||||||
|
$: if (typeof mapInstance === "object" && mapMarkers.length > 0) {
|
||||||
|
mapInstance.setZoom(0)
|
||||||
|
mapInstance.fitBounds(mapMarkerGroup.getBounds(), {
|
||||||
|
paddingTopLeft: [0, 24],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateMapDimensions = mapInstance => {
|
||||||
|
if (typeof mapInstance !== "object") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
mapInstance.invalidateSize()
|
||||||
|
}
|
||||||
|
|
||||||
|
let isValidLatitude = value => {
|
||||||
|
return !isNaN(value) && value > -90 && value < 90
|
||||||
|
}
|
||||||
|
|
||||||
|
let isValidLongitude = value => {
|
||||||
|
return !isNaN(value) && value > -180 && value < 180
|
||||||
|
}
|
||||||
|
|
||||||
|
const parseDefaultLocation = defaultLocation => {
|
||||||
|
if (typeof defaultLocation !== "string") {
|
||||||
|
return fallbackCoordinates
|
||||||
|
}
|
||||||
|
let defaultLocationParts = defaultLocation.split(",")
|
||||||
|
if (defaultLocationParts.length !== 2) {
|
||||||
|
return fallbackCoordinates
|
||||||
|
}
|
||||||
|
|
||||||
|
let parsedDefaultLatitude = parseFloat(defaultLocationParts[0].trim())
|
||||||
|
let parsedDefaultLongitude = parseFloat(defaultLocationParts[1].trim())
|
||||||
|
|
||||||
|
return isValidLatitude(parsedDefaultLatitude) === true &&
|
||||||
|
isValidLongitude(parsedDefaultLongitude) === true
|
||||||
|
? [parsedDefaultLatitude, parsedDefaultLongitude]
|
||||||
|
: fallbackCoordinates
|
||||||
|
}
|
||||||
|
|
||||||
|
$: defaultCoordinates =
|
||||||
|
mapMarkers.length > 0
|
||||||
|
? parseDefaultLocation(defaultLocation)
|
||||||
|
: fallbackCoordinates
|
||||||
|
|
||||||
|
// Map Button Controls
|
||||||
|
let locationControl = new LocationControl({
|
||||||
|
position: "bottomright",
|
||||||
|
onLocationFail: err => {
|
||||||
|
if (err.code === GeolocationPositionError.PERMISSION_DENIED) {
|
||||||
|
notificationStore.actions.error(
|
||||||
|
"Location requests not permitted. Ensure location is enabled"
|
||||||
|
)
|
||||||
|
} else if (err.code === GeolocationPositionError.POSITION_UNAVAILABLE) {
|
||||||
|
notificationStore.actions.warning(
|
||||||
|
"Location could not be retrieved. Try again"
|
||||||
|
)
|
||||||
|
} else if (err.code === GeolocationPositionError.TIMEOUT) {
|
||||||
|
notificationStore.actions.warning(
|
||||||
|
"Location request timed out. Try again"
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
notificationStore.actions.error("Unknown location error")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onLocationSuccess: pos => {
|
||||||
|
cachedDeviceCoordinates = pos
|
||||||
|
if (typeof mapInstance === "object") {
|
||||||
|
mapInstance.setView(cachedDeviceCoordinates, 15)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
let fullScreenControl = new FullScreenControl({
|
||||||
|
position: "topright",
|
||||||
|
})
|
||||||
|
let zoomControl = L.control.zoom({
|
||||||
|
position: "bottomright",
|
||||||
|
})
|
||||||
|
|
||||||
|
const locationControlUpdated = (mapInstance, locationEnabled) => {
|
||||||
|
if (typeof mapInstance !== "object") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (locationEnabled) {
|
||||||
|
locationControl.addTo(mapInstance)
|
||||||
|
} else {
|
||||||
|
mapInstance.removeControl(locationControl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fullScreenControlUpdated = (mapInstance, fullScreenEnabled) => {
|
||||||
|
if (typeof mapInstance !== "object") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (fullScreenEnabled) {
|
||||||
|
fullScreenControl.addTo(mapInstance)
|
||||||
|
} else {
|
||||||
|
mapInstance.removeControl(fullScreenControl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const zoomControlUpdated = (mapInstance, zoomEnabled) => {
|
||||||
|
if (typeof mapInstance !== "object") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (zoomEnabled) {
|
||||||
|
zoomControl.addTo(mapInstance)
|
||||||
|
mapInstance.scrollWheelZoom.enable()
|
||||||
|
} else {
|
||||||
|
mapInstance.removeControl(zoomControl)
|
||||||
|
mapInstance.scrollWheelZoom.disable()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Map icon and marker configuration
|
||||||
|
const mapIconMarkup =
|
||||||
|
'<div><svg width="26px" height="26px" class="spectrum-Icon" focusable="false" stroke="#b12b27" stroke-width="1%">' +
|
||||||
|
'<use xlink:href="#spectrum-icon-18-Location" /></svg></div>'
|
||||||
|
const mapIcon = L.divIcon({
|
||||||
|
html: mapIconMarkup,
|
||||||
|
className: "embedded-map-marker",
|
||||||
|
iconSize: [26, 26],
|
||||||
|
iconAnchor: [13, 26],
|
||||||
|
popupAnchor: [0, -13],
|
||||||
|
})
|
||||||
|
const mapMarkerOptions = {
|
||||||
|
icon: mapIcon,
|
||||||
|
draggable: false,
|
||||||
|
alt: "Location Marker",
|
||||||
|
}
|
||||||
|
let mapOptions = {
|
||||||
|
fullScreen: false,
|
||||||
|
zoomControl: false,
|
||||||
|
scrollWheelZoom: zoomEnabled,
|
||||||
|
minZoomLevel,
|
||||||
|
maxZoomLevel,
|
||||||
|
}
|
||||||
|
|
||||||
|
const addMapMarkers = (mapInstance, rows, latKey, lngKey, titleKey) => {
|
||||||
|
if (typeof mapInstance !== "object" || !rows || !latKey || !lngKey) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
mapMarkerGroup.clearLayers()
|
||||||
|
|
||||||
|
const validRows = rows.filter(row => {
|
||||||
|
return isValidLatitude(row[latKey]) && isValidLongitude(row[lngKey])
|
||||||
|
})
|
||||||
|
|
||||||
|
validRows.forEach(row => {
|
||||||
|
let markerCoords = [row[latKey], row[lngKey]]
|
||||||
|
|
||||||
|
let marker = L.marker(markerCoords, mapMarkerOptions).addTo(mapInstance)
|
||||||
|
let markerContent = generateMarkerPopupContent(
|
||||||
|
row[latKey],
|
||||||
|
row[lngKey],
|
||||||
|
row[titleKey]
|
||||||
|
)
|
||||||
|
|
||||||
|
marker.bindPopup(markerContent).addTo(mapMarkerGroup)
|
||||||
|
|
||||||
|
//https://github.com/Leaflet/Leaflet/issues/7331
|
||||||
|
marker.on("click", function () {
|
||||||
|
this.openPopup()
|
||||||
|
})
|
||||||
|
|
||||||
|
mapMarkers = [...mapMarkers, marker]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const generateMarkerPopupContent = (latitude, longitude, text) => {
|
||||||
|
return text || latitude + "," + longitude
|
||||||
|
}
|
||||||
|
|
||||||
|
const initMap = () => {
|
||||||
|
const initCoords = defaultCoordinates
|
||||||
|
|
||||||
|
mapInstance = L.map(embeddedMapId, mapOptions)
|
||||||
|
mapMarkerGroup.addTo(mapInstance)
|
||||||
|
|
||||||
|
const cleanAttribution = sanitizeHtml(mapAttribution, {
|
||||||
|
allowedTags: ["a"],
|
||||||
|
allowedAttributes: {
|
||||||
|
a: ["href", "target"],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
L.tileLayer(tileURL, {
|
||||||
|
attribution: "© " + cleanAttribution,
|
||||||
|
zoom: adjustedZoomLevel,
|
||||||
|
}).addTo(mapInstance)
|
||||||
|
|
||||||
|
//Initialise the map
|
||||||
|
mapInstance.setView(initCoords, adjustedZoomLevel)
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapAction = () => {
|
||||||
|
initMap()
|
||||||
|
return {
|
||||||
|
destroy() {
|
||||||
|
mapInstance.remove()
|
||||||
|
mapInstance = undefined
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="embedded-map-wrapper map-default" use:styleable={$component.styles}>
|
||||||
|
{#if error}
|
||||||
|
<div>{error}</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<div id={embeddedMapId} class="embedded embedded-map" use:mapAction />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.embedded-map-wrapper {
|
||||||
|
background-color: #f1f1f1;
|
||||||
|
}
|
||||||
|
.map-default {
|
||||||
|
min-height: 180px;
|
||||||
|
min-width: 200px;
|
||||||
|
}
|
||||||
|
.embedded-map :global(.leaflet-top),
|
||||||
|
.embedded-map :global(.leaflet-bottom) {
|
||||||
|
z-index: 998;
|
||||||
|
}
|
||||||
|
.embedded-map :global(.embedded-map-marker) {
|
||||||
|
color: #ee3b35;
|
||||||
|
}
|
||||||
|
.embedded-map :global(.embedded-map-control) {
|
||||||
|
font-size: 22px;
|
||||||
|
}
|
||||||
|
.embedded-map {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,192 @@
|
||||||
|
import L from "leaflet"
|
||||||
|
import screenfull from "screenfull"
|
||||||
|
|
||||||
|
const createButton = function (html, title, className, container, fn) {
|
||||||
|
let link = L.DomUtil.create("a", className, container)
|
||||||
|
link.innerHTML = html
|
||||||
|
link.href = "#"
|
||||||
|
link.title = title
|
||||||
|
|
||||||
|
link.setAttribute("role", "button")
|
||||||
|
link.setAttribute("aria-label", title)
|
||||||
|
|
||||||
|
L.DomEvent.disableClickPropagation(link)
|
||||||
|
L.DomEvent.on(link, "click", L.DomEvent.stop)
|
||||||
|
L.DomEvent.on(link, "click", fn, this)
|
||||||
|
L.DomEvent.on(link, "click", this._refocusOnMap, this)
|
||||||
|
|
||||||
|
return link
|
||||||
|
}
|
||||||
|
|
||||||
|
// Full Screen Control
|
||||||
|
|
||||||
|
const FullScreenControl = L.Control.extend({
|
||||||
|
options: {
|
||||||
|
position: "topright",
|
||||||
|
fullScreenContent:
|
||||||
|
'<span class="embedded-map-control embedded-map-location-icon">' +
|
||||||
|
'<svg width="16px" height="16px" class="spectrum-Icon" focusable="false">' +
|
||||||
|
'<use xlink:href="#spectrum-icon-18-FullScreen" /></svg><span>',
|
||||||
|
fullScreenTitle: "Enter Fullscreen",
|
||||||
|
},
|
||||||
|
onAdd: function () {
|
||||||
|
var fullScreenClassName = "leaflet-control-fullscreen",
|
||||||
|
container = L.DomUtil.create("div", fullScreenClassName + " leaflet-bar"),
|
||||||
|
options = this.options
|
||||||
|
|
||||||
|
this._fullScreenButton = this._createButton(
|
||||||
|
options.fullScreenContent,
|
||||||
|
options.fullScreenTitle,
|
||||||
|
"map-fullscreen",
|
||||||
|
container,
|
||||||
|
this._fullScreen
|
||||||
|
)
|
||||||
|
|
||||||
|
return container
|
||||||
|
},
|
||||||
|
_fullScreen: function () {
|
||||||
|
var map = this._map
|
||||||
|
if (screenfull.isEnabled) {
|
||||||
|
screenfull.toggle(map.getContainer())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_createButton: createButton,
|
||||||
|
})
|
||||||
|
|
||||||
|
const initFullScreenControl = () => {
|
||||||
|
L.Map.mergeOptions({
|
||||||
|
fullScreen: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
L.Map.addInitHook(function () {
|
||||||
|
if (this.options.fullScreen) {
|
||||||
|
this.fullScreenControl = new FullScreenControl()
|
||||||
|
this.addControl(this.fullScreenControl)
|
||||||
|
} else {
|
||||||
|
this.fullScreenControl = null
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Location Control
|
||||||
|
|
||||||
|
const LocationControl = L.Control.extend({
|
||||||
|
options: {
|
||||||
|
position: "topright",
|
||||||
|
locationContent:
|
||||||
|
'<span class="embedded-map-control embedded-map-location-icon">' +
|
||||||
|
'<svg width="16px" height="16px" class="spectrum-Icon" focusable="false">' +
|
||||||
|
'<use xlink:href="#spectrum-icon-18-Campaign" /></svg><span>',
|
||||||
|
locationTitle: "Show Your Location",
|
||||||
|
},
|
||||||
|
onAdd: function () {
|
||||||
|
var locationClassName = "leaflet-control-location",
|
||||||
|
container = L.DomUtil.create("div", locationClassName + " leaflet-bar"),
|
||||||
|
options = this.options
|
||||||
|
|
||||||
|
this._locationButton = this._createButton(
|
||||||
|
options.locationContent,
|
||||||
|
options.locationTitle,
|
||||||
|
"map-location",
|
||||||
|
container,
|
||||||
|
this._location
|
||||||
|
)
|
||||||
|
|
||||||
|
this._updateDisabled()
|
||||||
|
|
||||||
|
return container
|
||||||
|
},
|
||||||
|
disable: function () {
|
||||||
|
this._disabled = true
|
||||||
|
this._updateDisabled()
|
||||||
|
return this
|
||||||
|
},
|
||||||
|
enable: function () {
|
||||||
|
this._disabled = false
|
||||||
|
this._updateDisabled()
|
||||||
|
return this
|
||||||
|
},
|
||||||
|
_location: function () {
|
||||||
|
if (this._disabled == true) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.disable()
|
||||||
|
|
||||||
|
const success = pos => {
|
||||||
|
this._map.closePopup()
|
||||||
|
if (typeof this.options.onLocationSuccess === "function") {
|
||||||
|
this.options.onLocationSuccess({
|
||||||
|
lat: pos.coords.latitude,
|
||||||
|
lng: pos.coords.longitude,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const error = err => {
|
||||||
|
if (typeof this.options.onLocationFail === "function") {
|
||||||
|
this.options.onLocationFail(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this._getPosition()
|
||||||
|
.then(success)
|
||||||
|
.catch(error)
|
||||||
|
.finally(() => {
|
||||||
|
this.enable()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
_getPosition: function () {
|
||||||
|
var options = {
|
||||||
|
enableHighAccuracy: false,
|
||||||
|
timeout: 5000,
|
||||||
|
maximumAge: 30000,
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
navigator.geolocation.getCurrentPosition(resolve, reject, options)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
_createButton: createButton,
|
||||||
|
|
||||||
|
_updateDisabled: function () {
|
||||||
|
let disabledClassName = "leaflet-disabled"
|
||||||
|
L.DomUtil.removeClass(this._locationButton, disabledClassName)
|
||||||
|
this._locationButton.setAttribute("aria-disabled", "false")
|
||||||
|
|
||||||
|
if (this._disabled) {
|
||||||
|
L.DomUtil.addClass(this._locationButton, disabledClassName)
|
||||||
|
this._locationButton.setAttribute("aria-disabled", "true")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const initLocationControl = () => {
|
||||||
|
L.Map.mergeOptions({
|
||||||
|
location: false,
|
||||||
|
onLocationFail: null,
|
||||||
|
onLocationSuccess: null,
|
||||||
|
})
|
||||||
|
|
||||||
|
L.Map.addInitHook(function () {
|
||||||
|
if (this.options.location) {
|
||||||
|
this.localControl = new LocationControl()
|
||||||
|
this.addControl(this.LocationControl)
|
||||||
|
} else {
|
||||||
|
this.localControl = null
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const initMapControls = () => {
|
||||||
|
initFullScreenControl()
|
||||||
|
initLocationControl()
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
initFullScreenControl,
|
||||||
|
initLocationControl,
|
||||||
|
initMapControls,
|
||||||
|
FullScreenControl,
|
||||||
|
LocationControl,
|
||||||
|
}
|
|
@ -31,6 +31,7 @@ export { default as cardstat } from "./CardStat.svelte"
|
||||||
export { default as spectrumcard } from "./SpectrumCard.svelte"
|
export { default as spectrumcard } from "./SpectrumCard.svelte"
|
||||||
export { default as tag } from "./Tag.svelte"
|
export { default as tag } from "./Tag.svelte"
|
||||||
export { default as markdownviewer } from "./MarkdownViewer.svelte"
|
export { default as markdownviewer } from "./MarkdownViewer.svelte"
|
||||||
|
export { default as embeddedmap } from "./embedded-map/EmbeddedMap.svelte"
|
||||||
export * from "./charts"
|
export * from "./charts"
|
||||||
export * from "./forms"
|
export * from "./forms"
|
||||||
export * from "./table"
|
export * from "./table"
|
||||||
|
|
|
@ -448,6 +448,13 @@ domelementtype@^2.0.1, domelementtype@^2.2.0:
|
||||||
resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.2.0.tgz#9a0b6c2782ed6a1c7323d42267183df9bd8b1d57"
|
resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.2.0.tgz#9a0b6c2782ed6a1c7323d42267183df9bd8b1d57"
|
||||||
integrity sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==
|
integrity sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==
|
||||||
|
|
||||||
|
domhandler@^4.0.0:
|
||||||
|
version "4.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.3.0.tgz#16c658c626cf966967e306f966b431f77d4a5626"
|
||||||
|
integrity sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g==
|
||||||
|
dependencies:
|
||||||
|
domelementtype "^2.2.0"
|
||||||
|
|
||||||
domhandler@^4.2.0:
|
domhandler@^4.2.0:
|
||||||
version "4.2.2"
|
version "4.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.2.2.tgz#e825d721d19a86b8c201a35264e226c678ee755f"
|
resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.2.2.tgz#e825d721d19a86b8c201a35264e226c678ee755f"
|
||||||
|
@ -455,7 +462,12 @@ domhandler@^4.2.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
domelementtype "^2.2.0"
|
domelementtype "^2.2.0"
|
||||||
|
|
||||||
domutils@^2.6.0:
|
domino@^2.1.6:
|
||||||
|
version "2.1.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/domino/-/domino-2.1.6.tgz#fe4ace4310526e5e7b9d12c7de01b7f485a57ffe"
|
||||||
|
integrity sha512-3VdM/SXBZX2omc9JF9nOPCtDaYQ67BGp5CoLpIQlO2KCAPETs8TcDHacF26jXadGbvUteZzRTeos2fhID5+ucQ==
|
||||||
|
|
||||||
|
domutils@^2.5.2, domutils@^2.6.0:
|
||||||
version "2.8.0"
|
version "2.8.0"
|
||||||
resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135"
|
resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135"
|
||||||
integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==
|
integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==
|
||||||
|
@ -489,6 +501,11 @@ entities@^2.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55"
|
resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55"
|
||||||
integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==
|
integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==
|
||||||
|
|
||||||
|
entities@~2.1.0:
|
||||||
|
version "2.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/entities/-/entities-2.1.0.tgz#992d3129cf7df6870b96c57858c249a120f8b8b5"
|
||||||
|
integrity sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==
|
||||||
|
|
||||||
escalade@^3.1.1:
|
escalade@^3.1.1:
|
||||||
version "3.1.1"
|
version "3.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
|
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
|
||||||
|
@ -499,6 +516,11 @@ escape-string-regexp@^1.0.5:
|
||||||
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
|
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
|
||||||
integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
|
integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
|
||||||
|
|
||||||
|
escape-string-regexp@^4.0.0:
|
||||||
|
version "4.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
|
||||||
|
integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
|
||||||
|
|
||||||
estree-walker@^0.2.1:
|
estree-walker@^0.2.1:
|
||||||
version "0.2.1"
|
version "0.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.2.1.tgz#bdafe8095383d8414d5dc2ecf4c9173b6db9412e"
|
resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.2.1.tgz#bdafe8095383d8414d5dc2ecf4c9173b6db9412e"
|
||||||
|
@ -519,6 +541,11 @@ estree-walker@^2.0.1:
|
||||||
resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac"
|
resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac"
|
||||||
integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==
|
integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==
|
||||||
|
|
||||||
|
eventemitter3@^2.0.3:
|
||||||
|
version "2.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-2.0.3.tgz#b5e1079b59fb5e1ba2771c0a993be060a58c99ba"
|
||||||
|
integrity sha1-teEHm1n7XhuidxwKmTvgYKWMmbo=
|
||||||
|
|
||||||
eventemitter3@^4.0.4:
|
eventemitter3@^4.0.4:
|
||||||
version "4.0.7"
|
version "4.0.7"
|
||||||
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f"
|
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f"
|
||||||
|
@ -656,6 +683,14 @@ is-reference@^1.2.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/estree" "*"
|
"@types/estree" "*"
|
||||||
|
|
||||||
|
is-regex@^1.0.4:
|
||||||
|
version "1.1.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958"
|
||||||
|
integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==
|
||||||
|
dependencies:
|
||||||
|
call-bind "^1.0.2"
|
||||||
|
has-tostringtag "^1.0.0"
|
||||||
|
|
||||||
is-resolvable@^1.1.0:
|
is-resolvable@^1.1.0:
|
||||||
version "1.1.0"
|
version "1.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88"
|
resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88"
|
||||||
|
@ -694,6 +729,13 @@ lilconfig@^2.0.3:
|
||||||
resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.4.tgz#f4507d043d7058b380b6a8f5cb7bcd4b34cee082"
|
resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.4.tgz#f4507d043d7058b380b6a8f5cb7bcd4b34cee082"
|
||||||
integrity sha512-bfTIN7lEsiooCocSISTWXkiWJkRqtL9wYtYy+8EK3Y41qh3mpwPU0ycTOgjdY9ErwXCc8QyrQp82bdL0Xkm9yA==
|
integrity sha512-bfTIN7lEsiooCocSISTWXkiWJkRqtL9wYtYy+8EK3Y41qh3mpwPU0ycTOgjdY9ErwXCc8QyrQp82bdL0Xkm9yA==
|
||||||
|
|
||||||
|
linkify-it@^3.0.1:
|
||||||
|
version "3.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-3.0.3.tgz#a98baf44ce45a550efb4d49c769d07524cc2fa2e"
|
||||||
|
integrity sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==
|
||||||
|
dependencies:
|
||||||
|
uc.micro "^1.0.1"
|
||||||
|
|
||||||
loader-utils@^1.1.0:
|
loader-utils@^1.1.0:
|
||||||
version "1.4.0"
|
version "1.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz#c579b5e34cb34b1a74edc6c1fb36bfa371d5a613"
|
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz#c579b5e34cb34b1a74edc6c1fb36bfa371d5a613"
|
||||||
|
@ -730,6 +772,11 @@ mdn-data@2.0.14:
|
||||||
resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50"
|
resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50"
|
||||||
integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==
|
integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==
|
||||||
|
|
||||||
|
mdurl@^1.0.1:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e"
|
||||||
|
integrity sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=
|
||||||
|
|
||||||
merge-stream@^2.0.0:
|
merge-stream@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
|
resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
|
||||||
|
@ -1117,6 +1164,27 @@ promise.series@^0.2.0:
|
||||||
resolved "https://registry.yarnpkg.com/promise.series/-/promise.series-0.2.0.tgz#2cc7ebe959fc3a6619c04ab4dbdc9e452d864bbd"
|
resolved "https://registry.yarnpkg.com/promise.series/-/promise.series-0.2.0.tgz#2cc7ebe959fc3a6619c04ab4dbdc9e452d864bbd"
|
||||||
integrity sha1-LMfr6Vn8OmYZwEq029yeRS2GS70=
|
integrity sha1-LMfr6Vn8OmYZwEq029yeRS2GS70=
|
||||||
|
|
||||||
|
quill-delta@^3.6.2:
|
||||||
|
version "3.6.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/quill-delta/-/quill-delta-3.6.3.tgz#b19fd2b89412301c60e1ff213d8d860eac0f1032"
|
||||||
|
integrity sha512-wdIGBlcX13tCHOXGMVnnTVFtGRLoP0imqxM696fIPwIf5ODIYUHIvHbZcyvGlZFiFhK5XzDC2lpjbxRhnM05Tg==
|
||||||
|
dependencies:
|
||||||
|
deep-equal "^1.0.1"
|
||||||
|
extend "^3.0.2"
|
||||||
|
fast-diff "1.1.2"
|
||||||
|
|
||||||
|
quill@^1.3.7:
|
||||||
|
version "1.3.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/quill/-/quill-1.3.7.tgz#da5b2f3a2c470e932340cdbf3668c9f21f9286e8"
|
||||||
|
integrity sha512-hG/DVzh/TiknWtE6QmWAF/pxoZKYxfe3J/d/+ShUWkDvvkZQVTPeVmUJVu1uE6DDooC4fWTiCLh84ul89oNz5g==
|
||||||
|
dependencies:
|
||||||
|
clone "^2.1.1"
|
||||||
|
deep-equal "^1.0.1"
|
||||||
|
eventemitter3 "^2.0.3"
|
||||||
|
extend "^3.0.2"
|
||||||
|
parchment "^1.1.4"
|
||||||
|
quill-delta "^3.6.2"
|
||||||
|
|
||||||
randombytes@^2.1.0:
|
randombytes@^2.1.0:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a"
|
resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a"
|
||||||
|
@ -1271,6 +1339,27 @@ shortid@^2.2.15:
|
||||||
dependencies:
|
dependencies:
|
||||||
nanoid "^2.1.0"
|
nanoid "^2.1.0"
|
||||||
|
|
||||||
|
sirv-cli@^0.4.6:
|
||||||
|
version "0.4.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/sirv-cli/-/sirv-cli-0.4.6.tgz#c28ab20deb3b34637f5a60863dc350f055abca04"
|
||||||
|
integrity sha512-/Vj85/kBvPL+n9ibgX6FicLE8VjidC1BhlX67PYPBfbBAphzR6i0k0HtU5c2arejfU3uzq8l3SYPCwl1x7z6Ww==
|
||||||
|
dependencies:
|
||||||
|
console-clear "^1.1.0"
|
||||||
|
get-port "^3.2.0"
|
||||||
|
kleur "^3.0.0"
|
||||||
|
local-access "^1.0.1"
|
||||||
|
sade "^1.4.0"
|
||||||
|
sirv "^0.4.6"
|
||||||
|
tinydate "^1.0.0"
|
||||||
|
|
||||||
|
sirv@^0.4.6:
|
||||||
|
version "0.4.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/sirv/-/sirv-0.4.6.tgz#185e44eb93d24009dd183b7494285c5180b81f22"
|
||||||
|
integrity sha512-rYpOXlNbpHiY4nVXxuDf4mXPvKz1reZGap/LkWp9TvcZ84qD/nPBjjH/6GZsgIjVMbOslnY8YYULAyP8jMn1GQ==
|
||||||
|
dependencies:
|
||||||
|
"@polka/url" "^0.5.0"
|
||||||
|
mime "^2.3.1"
|
||||||
|
|
||||||
slash@^3.0.0:
|
slash@^3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
|
resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
|
||||||
|
|
Loading…
Reference in New Issue