mirror of
https://github.com/KevinMidboe/nova-map-fields.git
synced 2026-04-25 00:13:40 +00:00
Compare commits
44 Commits
024010b867
...
09eb797534
| Author | SHA1 | Date | |
|---|---|---|---|
| 09eb797534 | |||
| dcfa0f9918 | |||
| d82f779b23 | |||
| 567e6f948b | |||
| ffe41aeca3 | |||
| f5f8ab9333 | |||
| c50b1846fe | |||
| ac52186b38 | |||
| e416e97f1d | |||
| c15cc02a09 | |||
| 9078300037 | |||
| 24eabaec3a | |||
| 4c0f2750fd | |||
| 0c1abff773 | |||
| 2e7602a928 | |||
| 0debcb02a1 | |||
| 8c8c21b432 | |||
| 06fc0a8291 | |||
| a8756bee8b | |||
| a327c7a40f | |||
| ea32ebbdc0 | |||
| ef3149d0f1 | |||
| 9fc1f312c5 | |||
| 4726aedd1f | |||
| 8415f8a940 | |||
| 7b94c0d795 | |||
| 28aef8d328 | |||
| 7545c4ffc9 | |||
| c205e8bf23 | |||
| cd2c3484bf | |||
| 82b5f9f32f | |||
| 3bf7d511e5 | |||
| 1a2987154e | |||
| 6532d68f54 | |||
| ff4aeb273e | |||
| 48b476a238 | |||
| ad52007642 | |||
| 5b2bcb110e | |||
| 0c22a4127f | |||
| 218bea2aeb | |||
| d1080bc6b1 | |||
| f346aa72e0 | |||
| c48e06c80e | |||
| 64e592ba3c |
25554
dist/js/field.js
vendored
25554
dist/js/field.js
vendored
File diff suppressed because one or more lines are too long
@@ -2,7 +2,11 @@
|
|||||||
<div>
|
<div>
|
||||||
<panel-item :field="field">
|
<panel-item :field="field">
|
||||||
<template slot="value">
|
<template slot="value">
|
||||||
<component :is="'field-' + field.map.type" :field="field" v-model="field.value"></component>
|
<component
|
||||||
|
:is="'field-' + field.map.type"
|
||||||
|
:field="field"
|
||||||
|
v-model="field.value"
|
||||||
|
></component>
|
||||||
</template>
|
</template>
|
||||||
</panel-item>
|
</panel-item>
|
||||||
</div>
|
</div>
|
||||||
@@ -10,6 +14,6 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
props: ['resource', 'resourceName', 'resourceId', 'field'],
|
props: ["resource", "resourceName", "resourceId", "field"],
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,22 +1,20 @@
|
|||||||
<template>
|
<template>
|
||||||
<div :style="{'height': field.map.height}">
|
<div :style="{ height: field.map.height }">
|
||||||
<l-map
|
<l-map
|
||||||
style="z-index:0"
|
style="z-index: 0"
|
||||||
:zoom="field.map.zoom"
|
:zoom="field.map.zoom"
|
||||||
:center="center"
|
:center="center"
|
||||||
:bounds="bounds"
|
:bounds="bounds"
|
||||||
@click="onMapClick"
|
@click="onMapClick"
|
||||||
>
|
>
|
||||||
<l-tile-layer
|
<l-tile-layer :url="url" :attribution="attribution" />
|
||||||
:url="url"
|
|
||||||
:attribution="attribution"/>
|
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</l-map>
|
</l-map>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {LMap, LTileLayer} from 'vue2-leaflet'
|
import { LMap, LTileLayer } from "vue2-leaflet";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
@@ -36,24 +34,25 @@ export default {
|
|||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
attribution: '© <a target="_blank" href="http://osm.org/copyright">OpenStreetMap</a> contributors',
|
attribution:
|
||||||
url: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'
|
'© <a target="_blank" href="http://osm.org/copyright">OpenStreetMap</a> contributors',
|
||||||
}
|
url: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
|
||||||
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
onMapClick(evt) {
|
onMapClick(evt) {
|
||||||
this.$emit('click', evt);
|
this.$emit("click", evt);
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
LMap,
|
LMap,
|
||||||
LTileLayer,
|
LTileLayer,
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
@import "../../../node_modules/leaflet/dist/leaflet.css"
|
@import "../../../node_modules/leaflet/dist/leaflet.css";
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,25 +1,31 @@
|
|||||||
<template>
|
<template>
|
||||||
<default-field :field="field" :errors="errors">
|
<default-field :field="field" :errors="errors" :full-width-content="true">
|
||||||
<template slot="field">
|
<template slot="field">
|
||||||
<component v-if="value" :is="'field-' + field.map.type" :field="field" :edit="true" v-model="value"></component>
|
<component
|
||||||
|
v-if="value"
|
||||||
|
:is="'field-' + field.map.type"
|
||||||
|
:field="field"
|
||||||
|
:edit="true"
|
||||||
|
v-model="value"
|
||||||
|
></component>
|
||||||
</template>
|
</template>
|
||||||
</default-field>
|
</default-field>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { FormField, HandlesValidationErrors } from 'laravel-nova'
|
import { FormField, HandlesValidationErrors } from "laravel-nova";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
mixins: [FormField, HandlesValidationErrors],
|
mixins: [FormField, HandlesValidationErrors],
|
||||||
|
|
||||||
props: ['resourceName', 'resourceId', 'field'],
|
props: ["resourceName", "resourceId", "field"],
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
/*
|
/*
|
||||||
* Set the initial, internal value for the field.
|
* Set the initial, internal value for the field.
|
||||||
*/
|
*/
|
||||||
setInitialValue() {
|
setInitialValue() {
|
||||||
this.value = this.field.value || {}
|
this.value = this.field.value || {};
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -33,8 +39,8 @@ export default {
|
|||||||
* Update the field's internal value.
|
* Update the field's internal value.
|
||||||
*/
|
*/
|
||||||
handleChange(value) {
|
handleChange(value) {
|
||||||
this.value = value
|
this.value = value;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -4,6 +4,6 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
props: ['resourceName', 'field'],
|
props: ["resourceName", "field"],
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<field-map :center="position" :field="field">
|
<field-map :center="position" :field="field">
|
||||||
<l-marker :lat-lng="position" :draggable="edit" @dragend="onDragEnd"/>
|
<l-marker :lat-lng="position" :draggable="edit" @dragend="onDragEnd" />
|
||||||
</field-map>
|
</field-map>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {LMarker} from 'vue2-leaflet'
|
import { LMarker } from "vue2-leaflet";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
@@ -20,13 +20,16 @@ export default {
|
|||||||
edit: {
|
edit: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
position() {
|
position() {
|
||||||
return L.latLng(this.value.lat || this.field.map.center.latitude, this.value.lng || this.field.map.center.longitude);
|
return L.latLng(
|
||||||
}
|
this.value.lat || this.field.map.center.latitude,
|
||||||
|
this.value.lng || this.field.map.center.longitude
|
||||||
|
);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
@@ -37,36 +40,46 @@ export default {
|
|||||||
onDragEnd(evt) {
|
onDragEnd(evt) {
|
||||||
let latLng = evt.target.getLatLng();
|
let latLng = evt.target.getLatLng();
|
||||||
|
|
||||||
this.$emit('input', {
|
this.$emit("input", {
|
||||||
lat: latLng.lat,
|
lat: latLng.lat,
|
||||||
lng: latLng.lng,
|
lng: latLng.lng,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
registerWatchers() {
|
registerWatchers() {
|
||||||
this.getGlobalFieldComponents().forEach(component => {
|
this.getGlobalFieldComponents().forEach((component) => {
|
||||||
if ([this.field.latitudeField, this.field.longitudeField].includes(component.field.attribute)) {
|
if (
|
||||||
component.$watch('value', (value) => {
|
[
|
||||||
this.value[component.field.attribute] = value;
|
this.field.latitudeField,
|
||||||
}, {immediate: true})
|
this.field.longitudeField,
|
||||||
|
].includes(component.field.attribute)
|
||||||
|
) {
|
||||||
|
component.$watch(
|
||||||
|
"value",
|
||||||
|
(value) => {
|
||||||
|
this.value[component.field.attribute] = value;
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
getGlobalFieldComponents(components = null) {
|
getGlobalFieldComponents(components = null) {
|
||||||
if (! components) {
|
if (!components) {
|
||||||
components = this.$root.$children;
|
components = this.$root.$children;
|
||||||
}
|
}
|
||||||
|
|
||||||
let returnArray = [];
|
let returnArray = [];
|
||||||
|
|
||||||
components.forEach(component => {
|
components.forEach((component) => {
|
||||||
if (component.field) {
|
if (component.field) {
|
||||||
return returnArray.push(component);
|
return returnArray.push(component);
|
||||||
}
|
}
|
||||||
|
|
||||||
returnArray = returnArray.concat(this.getGlobalFieldComponents(component.$children));
|
returnArray = returnArray.concat(
|
||||||
|
this.getGlobalFieldComponents(component.$children)
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
return returnArray;
|
return returnArray;
|
||||||
@@ -75,6 +88,6 @@ export default {
|
|||||||
|
|
||||||
components: {
|
components: {
|
||||||
LMarker,
|
LMarker,
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
111
resources/js/components/fields/NavigationRoute.vue
Normal file
111
resources/js/components/fields/NavigationRoute.vue
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<field-polyline
|
||||||
|
:edit="edit"
|
||||||
|
:field="field"
|
||||||
|
:value="localValue"
|
||||||
|
@input="triggerChange"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<p v-if="edit"><b>Velg rutetype:</b></p>
|
||||||
|
<div v-if="edit" class="flex block my-2">
|
||||||
|
<div v-for="option in routeTypes" class="ml-2">
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
:id="option.value"
|
||||||
|
:checked="option.value === selectedRouteType"
|
||||||
|
:value="option.value"
|
||||||
|
@change="updateRouteType">
|
||||||
|
<label :for="option.value">{{ option.name }}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
value: {
|
||||||
|
type: Object,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
field: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
edit: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
localValue: {},
|
||||||
|
routeTypes: [{
|
||||||
|
value: 'driving',
|
||||||
|
name: 'Bil'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'walking',
|
||||||
|
name: 'Gange'
|
||||||
|
}],
|
||||||
|
selectedRouteType: null
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
created() {
|
||||||
|
this.selectedRouteType = this.routeTypes[0].value;
|
||||||
|
|
||||||
|
if (this.value !== null) {
|
||||||
|
this.localValue = this.value;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
queryNavigation(markers) {
|
||||||
|
const cords = markers
|
||||||
|
.map((marker) => `${marker.lng},${marker.lat}`)
|
||||||
|
.join(";");
|
||||||
|
|
||||||
|
const API_URL = 'https://api.mapbox.com/directions/v5'
|
||||||
|
let ACCESS_TOKEN = 'pk.eyJ1Ijoia2V2aW5taWRib2UiLCJhIjoiY2pydWhlamQyMHJ2NTRhdGN1em5ndXVyMyJ9.Ejdo_3iuuGOD662Bh6es4w';
|
||||||
|
|
||||||
|
if (window.fjordmap && window.fjordmap.mapbox_access_token) {
|
||||||
|
ACCESS_TOKEN = window.fjordmap.mapbox_access_token;
|
||||||
|
}
|
||||||
|
const URL = `${API_URL}/mapbox/${this.selectedRouteType}/${cords}?geometries=geojson&access_token=${ACCESS_TOKEN}`;
|
||||||
|
return fetch(URL)
|
||||||
|
.then((resp) => resp.json())
|
||||||
|
.then((response) => response.routes[0].geometry.coordinates)
|
||||||
|
.then(this.mapNavigationRoute);
|
||||||
|
},
|
||||||
|
|
||||||
|
mapNavigationRoute(coords) {
|
||||||
|
return coords.map(([lng, lat]) => {
|
||||||
|
return { lat, lng };
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
updateRouteType(event) {
|
||||||
|
this.selectedRouteType = event.target.value;
|
||||||
|
this.triggerChange(this.localValue);
|
||||||
|
},
|
||||||
|
|
||||||
|
async triggerChange(markers) {
|
||||||
|
if (markers.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.localValue = markers;
|
||||||
|
|
||||||
|
console.log("forwarding markers:", markers);
|
||||||
|
const navigationRouteCoords = await this.queryNavigation(markers);
|
||||||
|
console.log("got navigationRouteCoords:", navigationRouteCoords);
|
||||||
|
|
||||||
|
this.$emit("input", navigationRouteCoords);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
150
resources/js/components/fields/Polygon.vue
Normal file
150
resources/js/components/fields/Polygon.vue
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
<template>
|
||||||
|
<div @keydown.backspace="removeLastMarker">
|
||||||
|
<field-map
|
||||||
|
:bounds="bounds"
|
||||||
|
:center="center"
|
||||||
|
:field="field"
|
||||||
|
@click="createMarker"
|
||||||
|
>
|
||||||
|
<l-marker
|
||||||
|
v-for="(marker, index) in markers"
|
||||||
|
:lat-lng="marker"
|
||||||
|
:draggable="edit"
|
||||||
|
@dragstart="saveDraggingMarker"
|
||||||
|
@dragend="updateMarkerPosition"
|
||||||
|
>
|
||||||
|
<l-icon
|
||||||
|
:icon-size="[24, 24]"
|
||||||
|
:icon-anchor="[12, 12]"
|
||||||
|
:icon-url="
|
||||||
|
index === markers.length - 1
|
||||||
|
? '/images/highlighted-point_marker.svg'
|
||||||
|
: '/images/point_marker.svg'
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</l-marker>
|
||||||
|
<l-polygon :lat-lngs="value" :visible="true" />
|
||||||
|
</field-map>
|
||||||
|
|
||||||
|
<l-control position="bottomleft" v-if="edit" class="block my-2">
|
||||||
|
<button
|
||||||
|
@click="removeLastMarker"
|
||||||
|
class="btn btn-default btn-primary"
|
||||||
|
>
|
||||||
|
Slett forrige
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
@click="removeAllMarkers"
|
||||||
|
class="btn btn-default btn-primary"
|
||||||
|
>
|
||||||
|
Slett alle
|
||||||
|
</button>
|
||||||
|
</l-control>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { LMarker, LIcon, LPolygon, LControl } from "vue2-leaflet";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
value: {
|
||||||
|
type: Object,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
field: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
edit: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
startBounds: undefined,
|
||||||
|
markerBeingDragged: undefined,
|
||||||
|
lastMarkerAddedTime: 0,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
markers() {
|
||||||
|
if (!this.value[0]) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.value;
|
||||||
|
},
|
||||||
|
|
||||||
|
center() {
|
||||||
|
if (!this.markers.length) {
|
||||||
|
return {
|
||||||
|
lat: this.field.map.center.latitude,
|
||||||
|
lng: this.field.map.center.longitude,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.bounds.getCenter();
|
||||||
|
},
|
||||||
|
|
||||||
|
bounds() {
|
||||||
|
if (this.startBounds !== undefined) {
|
||||||
|
return this.startBounds;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.markers.length) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (this.startBounds = new L.LatLngBounds(this.markers));
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
triggerChange(evt) {
|
||||||
|
this.$emit("input", this.markers);
|
||||||
|
},
|
||||||
|
saveDraggingMarker(evt) {
|
||||||
|
this.markerBeingDragged = evt.target._latlng;
|
||||||
|
},
|
||||||
|
removeLastMarker(event) {
|
||||||
|
this.markers.splice(-1, 1);
|
||||||
|
this.triggerChange();
|
||||||
|
event.preventDefault();
|
||||||
|
},
|
||||||
|
removeAllMarkers(event) {
|
||||||
|
this.value = [];
|
||||||
|
this.triggerChange();
|
||||||
|
event.preventDefault();
|
||||||
|
},
|
||||||
|
createMarker(evt) {
|
||||||
|
if (new Date().getTime() - this.lastMarkerAddedTime < 20) {
|
||||||
|
// Don't want to double register clicks from dragend, if within 20 ms return
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.markers.push(evt.latlng);
|
||||||
|
this.triggerChange();
|
||||||
|
},
|
||||||
|
updateMarkerPosition(evt) {
|
||||||
|
const newMarker = evt.target._latlng;
|
||||||
|
const { lat, lng } = this.markerBeingDragged;
|
||||||
|
|
||||||
|
const markerIndex = this.value.findIndex(
|
||||||
|
(marker) => marker.lat === lat && marker.lng === lng
|
||||||
|
);
|
||||||
|
this.value.splice(markerIndex, 1, newMarker);
|
||||||
|
this.lastMarkerAddedTime = new Date().getTime();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
components: {
|
||||||
|
LMarker,
|
||||||
|
LIcon,
|
||||||
|
LPolygon,
|
||||||
|
LControl,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
@@ -1,14 +1,40 @@
|
|||||||
<template>
|
<template>
|
||||||
<div @keydown.backspace="removeLastMarker">
|
<div @keydown.backspace="removeLastMarker">
|
||||||
<field-map :bounds="bounds" :center="center" :field="field" @click="createMarker">
|
<field-map
|
||||||
<l-marker v-for="marker in markers" :lat-lng="marker" :draggable="edit" @dragend="triggerChange" />
|
:bounds="bounds"
|
||||||
<l-polyline :lat-lngs="value" :visible="true" />
|
:center="center"
|
||||||
|
:field="field"
|
||||||
|
@click="createMarker"
|
||||||
|
>
|
||||||
|
<l-marker
|
||||||
|
v-for="(marker, index) in markers"
|
||||||
|
:lat-lng="marker"
|
||||||
|
:draggable="edit"
|
||||||
|
@dragstart="saveDraggingMarker"
|
||||||
|
@dragend="updateMarkerPosition"
|
||||||
|
/>
|
||||||
|
<l-polyline :lat-lngs="value" :visible="true" :fill="false" />
|
||||||
</field-map>
|
</field-map>
|
||||||
|
|
||||||
|
<l-control position="bottomleft" v-if="edit" class="block my-2">
|
||||||
|
<button
|
||||||
|
@click="removeLastMarker"
|
||||||
|
class="btn btn-default btn-primary"
|
||||||
|
>
|
||||||
|
Slett forrige
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
@click="removeAllMarkers"
|
||||||
|
class="btn btn-default btn-primary"
|
||||||
|
>
|
||||||
|
Slett alle
|
||||||
|
</button>
|
||||||
|
</l-control>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {LMarker, LPolyline} from 'vue2-leaflet'
|
import { LMarker, LIcon, LPolyline } from "vue2-leaflet";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
@@ -23,13 +49,15 @@ export default {
|
|||||||
edit: {
|
edit: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
startBounds: undefined,
|
startBounds: undefined,
|
||||||
}
|
markerBeingDragged: undefined,
|
||||||
|
lastMarkerAddedTime: 0,
|
||||||
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
@@ -61,27 +89,51 @@ export default {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.startBounds = new L.LatLngBounds(this.markers);
|
return (this.startBounds = new L.LatLngBounds(this.markers));
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
triggerChange() {
|
triggerChange(evt) {
|
||||||
this.$emit('input', this.markers);
|
this.$emit("input", this.markers);
|
||||||
},
|
},
|
||||||
removeLastMarker() {
|
saveDraggingMarker(evt) {
|
||||||
this.markers.splice(-1,1);
|
this.markerBeingDragged = evt.target._latlng;
|
||||||
|
},
|
||||||
|
removeLastMarker(event) {
|
||||||
|
this.markers.splice(-1, 1);
|
||||||
this.triggerChange();
|
this.triggerChange();
|
||||||
|
event.preventDefault();
|
||||||
|
},
|
||||||
|
removeAllMarkers(event) {
|
||||||
|
this.value = [];
|
||||||
|
this.triggerChange();
|
||||||
|
event.preventDefault();
|
||||||
},
|
},
|
||||||
createMarker(evt) {
|
createMarker(evt) {
|
||||||
|
if (new Date().getTime() - this.lastMarkerAddedTime < 20) {
|
||||||
|
// Don't want to double register clicks from dragend, if within 20 ms return
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.markers.push(evt.latlng);
|
this.markers.push(evt.latlng);
|
||||||
this.triggerChange();
|
this.triggerChange();
|
||||||
}
|
},
|
||||||
|
updateMarkerPosition(evt) {
|
||||||
|
const newMarker = evt.target._latlng;
|
||||||
|
const { lat, lng } = this.markerBeingDragged;
|
||||||
|
|
||||||
|
const markerIndex = this.value.findIndex(
|
||||||
|
(marker) => marker.lat === lat && marker.lng === lng
|
||||||
|
);
|
||||||
|
this.value.splice(markerIndex, 1, newMarker);
|
||||||
|
this.lastMarkerAddedTime = new Date().getTime();
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
LMarker,
|
LMarker,
|
||||||
|
LIcon,
|
||||||
LPolyline,
|
LPolyline,
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,12 +1,21 @@
|
|||||||
Nova.booting((Vue, router) => {
|
Nova.booting((Vue, router) => {
|
||||||
Vue.component('index-nova-map-fields', require('./components/IndexField'));
|
Vue.component("index-nova-map-fields", require("./components/IndexField"));
|
||||||
Vue.component('detail-nova-map-fields', require('./components/DetailField'));
|
Vue.component(
|
||||||
Vue.component('form-nova-map-fields', require('./components/FormField'));
|
"detail-nova-map-fields",
|
||||||
|
require("./components/DetailField")
|
||||||
|
);
|
||||||
|
Vue.component("form-nova-map-fields", require("./components/FormField"));
|
||||||
|
|
||||||
Vue.component('field-map', require('./components/FieldMap'));
|
Vue.component("field-map", require("./components/FieldMap"));
|
||||||
Vue.component('field-marker', require('./components/fields/Marker'));
|
Vue.component("field-marker", require("./components/fields/Marker"));
|
||||||
Vue.component('field-polyline', require('./components/fields/Polyline'));
|
Vue.component("field-polyline", require("./components/fields/Polyline"));
|
||||||
|
Vue.component("field-polygon", require("./components/fields/Polygon"));
|
||||||
|
Vue.component(
|
||||||
|
"field-navigation-route",
|
||||||
|
require("./components/fields/NavigationRoute")
|
||||||
|
);
|
||||||
|
|
||||||
// Config leaflet images to load images from CDN
|
// Config leaflet images to load images from CDN
|
||||||
L.Icon.Default.imagePath = 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.3.4/images/';
|
L.Icon.Default.imagePath =
|
||||||
})
|
"https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.3.4/images/";
|
||||||
|
});
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ abstract class MapField extends Field
|
|||||||
*/
|
*/
|
||||||
public function __construct($name, $attribute = null, $resolveCallback = null)
|
public function __construct($name, $attribute = null, $resolveCallback = null)
|
||||||
{
|
{
|
||||||
$this->setHeight('250px');
|
$this->setHeight('400px');
|
||||||
$this->setCenter(0, 0);
|
$this->setCenter(0, 0);
|
||||||
$this->setZoom(13);
|
$this->setZoom(13);
|
||||||
|
|
||||||
|
|||||||
40
src/NavigationRoute.php
Normal file
40
src/NavigationRoute.php
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Lassehaslev\NovaMapFields;
|
||||||
|
|
||||||
|
class NavigationRoute extends MapField
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Create a new field.
|
||||||
|
* And set default properties.
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
* @param string|null $attribute
|
||||||
|
* @param mixed|null $resolveCallback
|
||||||
|
*/
|
||||||
|
public function __construct($name, $attribute = null, $resolveCallback = null)
|
||||||
|
{
|
||||||
|
$this->help('Click on the map to create a new point. Drag a marker to change the point. When the map is selected, you can press [backspace] to remove markers.');
|
||||||
|
|
||||||
|
parent::__construct($name, $attribute = null, $resolveCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve the attribute before sending to frontend.
|
||||||
|
*
|
||||||
|
* @param mixed $resource
|
||||||
|
* @param mixed|null $attribute
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function resolveAttribute($resource, $attribute = null)
|
||||||
|
{
|
||||||
|
$value = $resource->{$attribute};
|
||||||
|
|
||||||
|
if (is_array($value)) {
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return json_decode($value);
|
||||||
|
}
|
||||||
|
}
|
||||||
40
src/Polygon.php
Normal file
40
src/Polygon.php
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Lassehaslev\NovaMapFields;
|
||||||
|
|
||||||
|
class Polygon extends MapField
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Create a new field.
|
||||||
|
* And set default properties.
|
||||||
|
*
|
||||||
|
* @param string $name
|
||||||
|
* @param string|null $attribute
|
||||||
|
* @param mixed|null $resolveCallback
|
||||||
|
*/
|
||||||
|
public function __construct($name, $attribute = null, $resolveCallback = null)
|
||||||
|
{
|
||||||
|
$this->help('Click on the map to create a new point. Drag a marker to change the point. When the map is selected, you can press [backspace] to remove markers.');
|
||||||
|
|
||||||
|
parent::__construct($name, $attribute = null, $resolveCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve the attribute before sending to frontend.
|
||||||
|
*
|
||||||
|
* @param mixed $resource
|
||||||
|
* @param mixed|null $attribute
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function resolveAttribute($resource, $attribute = null)
|
||||||
|
{
|
||||||
|
$value = $resource->{$attribute};
|
||||||
|
|
||||||
|
if (is_array($value)) {
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return json_decode($value);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user