mirror of
https://github.com/KevinMidboe/nova-map-fields.git
synced 2025-10-29 09:40:27 +00:00
First commit.
This commit is contained in:
10
.gitignore
vendored
Executable file
10
.gitignore
vendored
Executable file
@@ -0,0 +1,10 @@
|
|||||||
|
/.idea
|
||||||
|
/vendor
|
||||||
|
/node_modules
|
||||||
|
package-lock.json
|
||||||
|
composer.phar
|
||||||
|
composer.lock
|
||||||
|
phpunit.xml
|
||||||
|
.phpunit.result.cache
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
29
composer.json
Executable file
29
composer.json
Executable file
@@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"name": "lassehaslev/nova-map-fields",
|
||||||
|
"description": "A Laravel Nova field.",
|
||||||
|
"keywords": [
|
||||||
|
"laravel",
|
||||||
|
"nova"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"require": {
|
||||||
|
"php": ">=7.1.0"
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Lassehaslev\\NovaMapFields\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"extra": {
|
||||||
|
"laravel": {
|
||||||
|
"providers": [
|
||||||
|
"Lassehaslev\\NovaMapFields\\FieldServiceProvider"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"sort-packages": true
|
||||||
|
},
|
||||||
|
"minimum-stability": "dev",
|
||||||
|
"prefer-stable": true
|
||||||
|
}
|
||||||
0
dist/css/field.css
vendored
Executable file
0
dist/css/field.css
vendored
Executable file
BIN
dist/images/vendor/leaflet/dist/layers-2x.png
vendored
Executable file
BIN
dist/images/vendor/leaflet/dist/layers-2x.png
vendored
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
BIN
dist/images/vendor/leaflet/dist/layers.png
vendored
Executable file
BIN
dist/images/vendor/leaflet/dist/layers.png
vendored
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 696 B |
BIN
dist/images/vendor/leaflet/dist/marker-icon.png
vendored
Executable file
BIN
dist/images/vendor/leaflet/dist/marker-icon.png
vendored
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
25553
dist/js/field.js
vendored
Executable file
25553
dist/js/field.js
vendored
Executable file
File diff suppressed because one or more lines are too long
4
dist/mix-manifest.json
vendored
Executable file
4
dist/mix-manifest.json
vendored
Executable file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"/js/field.js": "/js/field.js",
|
||||||
|
"/css/field.css": "/css/field.css"
|
||||||
|
}
|
||||||
21
package.json
Executable file
21
package.json
Executable file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"dev": "npm run development",
|
||||||
|
"development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
|
||||||
|
"watch": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
|
||||||
|
"watch-poll": "npm run watch -- --watch-poll",
|
||||||
|
"hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js",
|
||||||
|
"prod": "npm run production",
|
||||||
|
"production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"cross-env": "^5.0.0",
|
||||||
|
"laravel-mix": "^1.0",
|
||||||
|
"laravel-nova": "^1.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"vue": "^2.5.0",
|
||||||
|
"vue2-leaflet": "^1.0.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
15
resources/js/components/DetailField.vue
Executable file
15
resources/js/components/DetailField.vue
Executable file
@@ -0,0 +1,15 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<panel-item :field="field">
|
||||||
|
<template slot="value">
|
||||||
|
<component :is="'field-' + field.map.type" :field="field" v-model="field.value"></component>
|
||||||
|
</template>
|
||||||
|
</panel-item>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: ['resource', 'resourceName', 'resourceId', 'field'],
|
||||||
|
}
|
||||||
|
</script>
|
||||||
59
resources/js/components/FieldMap.vue
Executable file
59
resources/js/components/FieldMap.vue
Executable file
@@ -0,0 +1,59 @@
|
|||||||
|
<template>
|
||||||
|
<div :style="{'height': field.map.height}">
|
||||||
|
<l-map
|
||||||
|
style="z-index:0"
|
||||||
|
:zoom="field.map.zoom"
|
||||||
|
:center="center"
|
||||||
|
:bounds="bounds"
|
||||||
|
@click="onMapClick"
|
||||||
|
>
|
||||||
|
<l-tile-layer
|
||||||
|
:url="url"
|
||||||
|
:attribution="attribution"/>
|
||||||
|
<slot></slot>
|
||||||
|
</l-map>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {LMap, LTileLayer} from 'vue2-leaflet'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
field: {
|
||||||
|
type: Object,
|
||||||
|
requred: true,
|
||||||
|
},
|
||||||
|
center: {
|
||||||
|
type: Object,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
bounds: {
|
||||||
|
type: Object,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
attribution: '© <a target="_blank" href="http://osm.org/copyright">OpenStreetMap</a> contributors',
|
||||||
|
url: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
onMapClick(evt) {
|
||||||
|
this.$emit('click', evt);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
components: {
|
||||||
|
LMap,
|
||||||
|
LTileLayer,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
@import "../../../node_modules/leaflet/dist/leaflet.css"
|
||||||
|
</style>
|
||||||
40
resources/js/components/FormField.vue
Executable file
40
resources/js/components/FormField.vue
Executable file
@@ -0,0 +1,40 @@
|
|||||||
|
<template>
|
||||||
|
<default-field :field="field" :errors="errors">
|
||||||
|
<template slot="field">
|
||||||
|
<component v-if="value" :is="'field-' + field.map.type" :field="field" :edit="true" v-model="value"></component>
|
||||||
|
</template>
|
||||||
|
</default-field>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { FormField, HandlesValidationErrors } from 'laravel-nova'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
mixins: [FormField, HandlesValidationErrors],
|
||||||
|
|
||||||
|
props: ['resourceName', 'resourceId', 'field'],
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
/*
|
||||||
|
* Set the initial, internal value for the field.
|
||||||
|
*/
|
||||||
|
setInitialValue() {
|
||||||
|
this.value = this.field.value || {}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fill the given FormData object with the field's internal value.
|
||||||
|
*/
|
||||||
|
fill(formData) {
|
||||||
|
formData.append(this.field.attribute, JSON.stringify(this.value));
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the field's internal value.
|
||||||
|
*/
|
||||||
|
handleChange(value) {
|
||||||
|
this.value = value
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
9
resources/js/components/IndexField.vue
Executable file
9
resources/js/components/IndexField.vue
Executable file
@@ -0,0 +1,9 @@
|
|||||||
|
<template>
|
||||||
|
<span>{{ field.value }}</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: ['resourceName', 'field'],
|
||||||
|
}
|
||||||
|
</script>
|
||||||
80
resources/js/components/fields/Marker.vue
Executable file
80
resources/js/components/fields/Marker.vue
Executable file
@@ -0,0 +1,80 @@
|
|||||||
|
<template>
|
||||||
|
<field-map :center="position" :field="field">
|
||||||
|
<l-marker :lat-lng="position" :draggable="edit" @dragend="onDragEnd"/>
|
||||||
|
</field-map>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {LMarker} from 'vue2-leaflet'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
value: {
|
||||||
|
type: Object,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
field: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
edit: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
position() {
|
||||||
|
return L.latLng(this.value.lat || this.field.map.center.latitude, this.value.lng || this.field.map.center.longitude);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
this.registerWatchers();
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
onDragEnd(evt) {
|
||||||
|
let latLng = evt.target.getLatLng();
|
||||||
|
|
||||||
|
this.$emit('input', {
|
||||||
|
lat: latLng.lat,
|
||||||
|
lng: latLng.lng,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
registerWatchers() {
|
||||||
|
this.getGlobalFieldComponents().forEach(component => {
|
||||||
|
if ([this.field.latitudeField, this.field.longitudeField].includes(component.field.attribute)) {
|
||||||
|
component.$watch('value', (value) => {
|
||||||
|
this.value[component.field.attribute] = value;
|
||||||
|
}, {immediate: true})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
getGlobalFieldComponents(components = null) {
|
||||||
|
if (! components) {
|
||||||
|
components = this.$root.$children;
|
||||||
|
}
|
||||||
|
|
||||||
|
let returnArray = [];
|
||||||
|
|
||||||
|
components.forEach(component => {
|
||||||
|
if (component.field) {
|
||||||
|
return returnArray.push(component);
|
||||||
|
}
|
||||||
|
|
||||||
|
returnArray = returnArray.concat(this.getGlobalFieldComponents(component.$children));
|
||||||
|
});
|
||||||
|
|
||||||
|
return returnArray;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
components: {
|
||||||
|
LMarker,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
87
resources/js/components/fields/Polyline.vue
Executable file
87
resources/js/components/fields/Polyline.vue
Executable file
@@ -0,0 +1,87 @@
|
|||||||
|
<template>
|
||||||
|
<div @keydown.backspace="removeLastMarker">
|
||||||
|
<field-map :bounds="bounds" :center="center" :field="field" @click="createMarker">
|
||||||
|
<l-marker v-for="marker in markers" :lat-lng="marker" :draggable="edit" @dragend="triggerChange" />
|
||||||
|
<l-polyline :lat-lngs="value" :visible="true" />
|
||||||
|
</field-map>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {LMarker, LPolyline} 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,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
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() {
|
||||||
|
this.$emit('input', this.markers);
|
||||||
|
},
|
||||||
|
removeLastMarker() {
|
||||||
|
this.markers.splice(-1,1);
|
||||||
|
this.triggerChange();
|
||||||
|
},
|
||||||
|
createMarker(evt) {
|
||||||
|
this.markers.push(evt.latlng);
|
||||||
|
this.triggerChange();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
components: {
|
||||||
|
LMarker,
|
||||||
|
LPolyline,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
12
resources/js/field.js
Executable file
12
resources/js/field.js
Executable file
@@ -0,0 +1,12 @@
|
|||||||
|
Nova.booting((Vue, router) => {
|
||||||
|
Vue.component('index-nova-map-fields', require('./components/IndexField'));
|
||||||
|
Vue.component('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-marker', require('./components/fields/Marker'));
|
||||||
|
Vue.component('field-polyline', require('./components/fields/Polyline'));
|
||||||
|
|
||||||
|
// Config leaflet images to load images from CDN
|
||||||
|
L.Icon.Default.imagePath = 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.3.4/images/';
|
||||||
|
})
|
||||||
1
resources/sass/field.scss
Executable file
1
resources/sass/field.scss
Executable file
@@ -0,0 +1 @@
|
|||||||
|
// Nova Tool CSS
|
||||||
28
src/FieldServiceProvider.php
Executable file
28
src/FieldServiceProvider.php
Executable file
@@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Lassehaslev\NovaMapFields;
|
||||||
|
|
||||||
|
use Laravel\Nova\Nova;
|
||||||
|
use Laravel\Nova\Events\ServingNova;
|
||||||
|
use Illuminate\Support\ServiceProvider;
|
||||||
|
|
||||||
|
class FieldServiceProvider extends ServiceProvider
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Bootstrap any application services.
|
||||||
|
*/
|
||||||
|
public function boot()
|
||||||
|
{
|
||||||
|
Nova::serving(function (ServingNova $event) {
|
||||||
|
Nova::script('nova-map-fields', __DIR__.'/../dist/js/field.js');
|
||||||
|
Nova::style('nova-map-fields', __DIR__.'/../dist/css/field.css');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register any application services.
|
||||||
|
*/
|
||||||
|
public function register()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
109
src/MapField.php
Executable file
109
src/MapField.php
Executable file
@@ -0,0 +1,109 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Lassehaslev\NovaMapFields;
|
||||||
|
|
||||||
|
use Laravel\Nova\Fields\Field;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
|
abstract class MapField extends Field
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The field's component.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public $component = 'nova-map-fields';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hide from index field.
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
public $showOnIndex = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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->setHeight('250px');
|
||||||
|
$this->setCenter(0, 0);
|
||||||
|
$this->setZoom(13);
|
||||||
|
|
||||||
|
$this->withMapMeta('type', $this->getMapFieldType());
|
||||||
|
|
||||||
|
parent::__construct($name, $attribute = null, $resolveCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the height for the map.
|
||||||
|
*
|
||||||
|
* @param mixed $height
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setHeight($height)
|
||||||
|
{
|
||||||
|
return $this->withMapMeta('height', $height);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the zoom for the map.
|
||||||
|
*
|
||||||
|
* @param mixed $level
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setZoom($level)
|
||||||
|
{
|
||||||
|
return $this->withMapMeta('zoom', $level);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the position for the map.
|
||||||
|
*
|
||||||
|
* @param mixed $latitude
|
||||||
|
* @param mixed $longitude
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setCenter($latitude, $longitude)
|
||||||
|
{
|
||||||
|
return $this->withMapMeta('center', [
|
||||||
|
'latitude' => $latitude,
|
||||||
|
'longitude' => $longitude,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add meta data for the map.
|
||||||
|
*
|
||||||
|
* @param mixed $key
|
||||||
|
* @param mixed $value
|
||||||
|
*/
|
||||||
|
protected function withMapMeta($key, $value)
|
||||||
|
{
|
||||||
|
$existingMapMeta = $this->meta['map'] ?? [];
|
||||||
|
|
||||||
|
return $this->withMeta([
|
||||||
|
'map' => array_merge($existingMapMeta, [
|
||||||
|
$key => $value,
|
||||||
|
]),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get component name.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getMapFieldType()
|
||||||
|
{
|
||||||
|
return Str::kebab(class_basename($this));
|
||||||
|
}
|
||||||
|
}
|
||||||
85
src/Marker.php
Executable file
85
src/Marker.php
Executable file
@@ -0,0 +1,85 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Lassehaslev\NovaMapFields;
|
||||||
|
|
||||||
|
use Laravel\Nova\Http\Requests\NovaRequest;
|
||||||
|
|
||||||
|
class Marker 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('Drag the marker to change the point.');
|
||||||
|
|
||||||
|
parent::__construct($name, $attribute = null, $resolveCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the latutude field.
|
||||||
|
*
|
||||||
|
* @param mixed $fieldName
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setLatitude($fieldName)
|
||||||
|
{
|
||||||
|
return $this->withMeta([
|
||||||
|
'latitudeField' => $fieldName,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the longitude field.
|
||||||
|
*
|
||||||
|
* @param mixed $fieldName
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setLongitude($fieldName)
|
||||||
|
{
|
||||||
|
return $this->withMeta([
|
||||||
|
'longitudeField' => $fieldName,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve the attribute before sending to frontend.
|
||||||
|
*
|
||||||
|
* @param mixed $resource
|
||||||
|
* @param mixed|null $attribute
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function resolveAttribute($resource, $attribute = null)
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'lat' => $resource->{$this->meta['latitudeField']},
|
||||||
|
'lng' => $resource->{$this->meta['longitudeField']},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hydrate the given attribute on the model based on the incoming request.
|
||||||
|
*
|
||||||
|
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
|
||||||
|
* @param string $requestAttribute
|
||||||
|
* @param object $model
|
||||||
|
* @param string $attribute
|
||||||
|
*/
|
||||||
|
protected function fillAttributeFromRequest(NovaRequest $request, $requestAttribute, $model, $attribute)
|
||||||
|
{
|
||||||
|
if ($request->exists($requestAttribute)) {
|
||||||
|
$latLng = json_decode($request[$requestAttribute]);
|
||||||
|
|
||||||
|
$model->{$this->meta['latitudeField']} = $latLng->lat;
|
||||||
|
$model->{$this->meta['longitudeField']} = $latLng->lng;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
39
src/Polyline.php
Executable file
39
src/Polyline.php
Executable file
@@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Lassehaslev\NovaMapFields;
|
||||||
|
|
||||||
|
class Polyline 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)
|
||||||
|
{
|
||||||
|
return json_decode($resource->{$attribute});
|
||||||
|
|
||||||
|
return [
|
||||||
|
'lat' => $resource->{$this->meta['latitudeField']},
|
||||||
|
'lng' => $resource->{$this->meta['longitudeField']},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
5
webpack.mix.js
Executable file
5
webpack.mix.js
Executable file
@@ -0,0 +1,5 @@
|
|||||||
|
let mix = require('laravel-mix')
|
||||||
|
|
||||||
|
mix.setPublicPath('dist')
|
||||||
|
.js('resources/js/field.js', 'js')
|
||||||
|
.sass('resources/sass/field.scss', 'css')
|
||||||
Reference in New Issue
Block a user