Unverified Commit 40e22c85 authored by Nicolò Maria Mezzopera's avatar Nicolò Maria Mezzopera
Browse files

feat: continue work on base components

parent 933ffeb6
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -20,7 +20,9 @@
    "eslint": "^6.7.2",
    "eslint-config-prettier": "^6.11.0",
    "eslint-plugin-prettier": "^3.1.4",
    "eslint-plugin-vue": "^7.0.0-0"
    "eslint-plugin-vue": "^7.0.0-0",
    "leaflet": "^1.6.0",
    "prettier": "^2.0.5"
  },
  "eslintConfig": {
    "root": true,
+9 −2
Original line number Diff line number Diff line
<template>
  <div style="height: 50vh; width: 50vw;">
    <l-map :zoom="3" :center="[47.41322, -1.219482]">
    <l-map :zoom="3" :center="[47.41322, -1.219482]" @move="log('move')">
      <l-tile-layer url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png">
      </l-tile-layer>
      <l-marker :lat-lng="[0, 0]" draggable></l-marker>
    </l-map>
  </div>
</template>
<script>
import "leaflet/dist/leaflet.css";
import LMap from "./components/LMap.vue";
import LTileLayer from "./components/LTileLayer.vue";
import LMarker from "./components/LMarker.vue";

export default {
  components: {
    LMap,
    LTileLayer,
    LMarker,
  },
  methods: {
    log(a) {
      console.log(a);
    },
  },
};
</script>
+189 −34
Original line number Diff line number Diff line
<template>
  <div style="width:100%; height: 100%" ref="root">
  <div style="width: 100%; height: 100%;" ref="root">
    <slot v-if="ready"></slot>
  </div>
</template>

<script>
import { onMounted, ref, computed, provide } from 'vue';
import { onMounted, onBeforeUnmount, ref, computed, provide } from "vue";
import { remapEvents, propsBinder, debounce } from "../utils.js";

export default {
  props: {
@@ -15,7 +16,7 @@ export default {
    center: {
      type: [Object, Array],
      custom: true,
      default: () => [0, 0]
      default: () => [0, 0],
    },
    /**
     * The bounds of the map, supports .sync modifier
@@ -23,14 +24,14 @@ export default {
    bounds: {
      type: [Array, Object],
      custom: true,
      default: null
      default: null,
    },
    /**
     * The max bounds of the map
     */
    maxBounds: {
      type: [Array, Object],
      default: null
      default: null,
    },
    /**
     * The zoom of the map, supports .sync modifier
@@ -38,21 +39,21 @@ export default {
    zoom: {
      type: Number,
      custom: true,
      default: 0
      default: 0,
    },
    /**
     * The minZoom of the map
     */
    minZoom: {
      type: Number,
      default: null
      default: null,
    },
    /**
     * The maxZoom of the map
     */
    maxZoom: {
      type: Number,
      default: null
      default: null,
    },
    /**
     * The paddingBottomRight of the map
@@ -60,7 +61,7 @@ export default {
    paddingBottomRight: {
      type: Array,
      custom: true,
      default: null
      default: null,
    },
    /**
     * The paddingTopLeft of the map
@@ -68,7 +69,7 @@ export default {
    paddingTopLeft: {
      type: Array,
      custom: true,
      default: null
      default: null,
    },
    /**
     * The padding of the map
@@ -76,14 +77,14 @@ export default {
    padding: {
      type: Array,
      custom: true,
      default: null
      default: null,
    },
    /**
     * The worldCopyJump option for the map
     */
    worldCopyJump: {
      type: Boolean,
      default: false
      default: false,
    },
    /**
     * The crs option for the map
@@ -91,53 +92,61 @@ export default {
     */
    crs: {
      type: Object,
      custom: true
      custom: true,
    },
    maxBoundsViscosity: {
      type: Number,
      default: null
      default: null,
    },
    inertia: {
      type: Boolean,
      default: null
      default: null,
    },
    inertiaDeceleration: {
      type: Number,
      default: null
      default: null,
    },
    inertiaMaxSpeed: {
      type: Number,
      default: null
      default: null,
    },
    easeLinearity: {
      type: Number,
      default: null
      default: null,
    },
    zoomAnimation: {
      type: Boolean,
      default: null
      default: null,
    },
    zoomAnimationThreshold: {
      type: Number,
      default: null
      default: null,
    },
    fadeAnimation: {
      type: Boolean,
      default: null
      default: null,
    },
    markerZoomAnimation: {
      type: Boolean,
      default: null
      default: null,
    },
    noBlockingAnimations: {
      type: Boolean,
      default: false
    }
      default: false,
    },
  setup(props) {
  },
  setup(props, context) {
    const root = ref(null);
    const ready = ref(false);
    const mapRef = ref(null);
    const layerControl = ref(null);
    const layersToAdd = ref([]);
    const layersInControl = ref([]);
    const lastSetCenter = ref(null);
    const lastSetBounds = ref(null);
    const paddingBottomRight = ref(null);
    const paddingTopLeft = ref(null);
    const padding = ref(null);

    const options = {
      minZoom: props.minZoom,
@@ -155,27 +164,173 @@ export default {
      zoomAnimation: props.zoomAnimation,
      zoomAnimationThreshold: props.zoomAnimationThreshold,
      fadeAnimation: props.fadeAnimation,
      markerZoomAnimation: props.markerZoomAnimation
      markerZoomAnimation: props.markerZoomAnimation,
    };

    const addLayer = layer => {
    const methods = {
      addLayer(layer) {
        mapRef.value.addLayer(layer);
      },
      removeLayer(layer, alreadyRemoved) {
        if (layer.layerType !== undefined) {
          if (layerControl.value === undefined) {
            layersToAdd.value = layersToAdd.value.filter(
              (l) => l.name !== layer.name
            );
          } else {
            layerControl.value.removeLayer(layer);
            layersInControl.value = layersInControl.value.filter(
              (l) => l.mapObject._leaflet_id !== layer.mapObject._leaflet_id
            );
          }
        }
        if (!alreadyRemoved) {
          mapRef.value.removeLayer(layer.mapObject);
        }
      },

      registerLayerControl(lControlLayers) {
        layerControl.value = lControlLayers;
        mapRef.value.addControl(lControlLayers.mapObject);
        layersToAdd.value.forEach((layer) => {
          layerControl.value.addLayer(layer);
        });
        layersToAdd.value = [];
      },

      setZoom(newVal) {
        mapRef.value.setZoom(newVal, {
          animate: props.noBlockingAnimations ? false : null,
        });
      },

      setPaddingBottomRight(newVal) {
        paddingBottomRight.value = newVal;
      },
      setPaddingTopLeft(newVal) {
        paddingTopLeft.value = newVal;
      },
      setPadding(newVal) {
        padding.value = newVal;
      },
      setCrs(newVal) {
        const prevBounds = mapRef.value.getBounds();
        mapRef.value.options.crs = newVal;
        mapRef.value.fitBounds(prevBounds, { animate: false, padding: [0, 0] });
      },
      fitBounds(bounds) {
        mapRef.value.fitBounds(bounds, {
          animate: this.noBlockingAnimations ? false : null,
        });
      },
    };
    const removeLayer = layer => {
      mapRef.value.removeLayer(layer);

    provide("addLayer", methods.addLayer);
    provide("removeLayer", methods.removeLayer);

    const eventHandlers = {
      moveEndHandler() {
        /**
         * Triggers when zoom is updated
         * @type {number,string}
         */
        context.emit("update:zoom", mapRef.value.getZoom());
        const center = mapRef.value.getCenter();
        /**
         * Triggers when center is updated
         * @type {object,array}
         */
        context.emit("update:center", center);
        const bounds = mapRef.value.getBounds();
        /**
         * Triggers when bounds are updated
         * @type {object}
         */
        context.emit("update:bounds", bounds);
      },
      overlayAddHandler(e) {
        const layer = layersInControl.value.find((l) => l.name === e.name);
        if (layer) {
          layer.updateVisibleProp(true);
        }
      },
      overlayRemoveHandler(e) {
        const layer = layersInControl.value.find((l) => l.name === e.name);
        if (layer) {
          layer.updateVisibleProp(false);
        }
      },
    };
    provide('addLayer', addLayer);
    provide('removeLayer', removeLayer);

    onMounted(async () => {
      const { map, CRS } = await import('leaflet/dist/leaflet-src.esm');
      const { map, CRS, Icon, latLngBounds, latLng, DomEvent } = await import(
        "leaflet/dist/leaflet-src.esm"
      );
      delete Icon.Default.prototype._getIconUrl;

      Icon.Default.mergeOptions({
        iconRetinaUrl: require("leaflet/dist/images/marker-icon-2x.png"),
        iconUrl: require("leaflet/dist/images/marker-icon.png"),
        shadowUrl: require("leaflet/dist/images/marker-shadow.png"),
      });

      options.crs = options.crs || CRS.EPSG3857;

      const methodsWithDependencies = {
        setBounds(newVal) {
          if (!newVal) {
            return;
          }
          const newBounds = latLngBounds(newVal);
          if (!newBounds.isValid()) {
            return;
          }
          const oldBounds = lastSetBounds.value || mapRef.value.getBounds();
          const boundsChanged = !oldBounds.equals(newBounds, 0); // set maxMargin to 0 - check exact equals
          if (boundsChanged) {
            lastSetBounds.value = newBounds;
            mapRef.value.fitBounds(newBounds, this.fitBoundsOptions);
          }
        },

        setCenter(newVal) {
          if (newVal == null) {
            return;
          }
          const newCenter = latLng(newVal);
          const oldCenter = lastSetCenter.value || mapRef.value.getCenter();
          if (
            oldCenter.lat !== newCenter.lat ||
            oldCenter.lng !== newCenter.lng
          ) {
            lastSetCenter.value = newCenter;
            mapRef.value.panTo(newCenter, {
              animate: this.noBlockingAnimations ? false : null,
            });
          }
        },
      };

      mapRef.value = map(root.value, options);

      propsBinder({ ...methods, methodsWithDependencies }, mapRef.value, props);
      const listeners = remapEvents(context.attrs);

      mapRef.value.on("moveend", debounce(eventHandlers.moveEndHandler, 100));
      mapRef.value.on("overlayadd", eventHandlers.overlayAddHandler);
      mapRef.value.on("overlayremove", eventHandlers.overlayRemoveHandler);
      DomEvent.on(mapRef.value, listeners);
      ready.value = true;
    });

    onBeforeUnmount(() => {
      if (mapRef.value) {
        mapRef.value.remove();
      }
    });

    const mapObject = computed(() => mapRef.value);
    return { root, ready, mapObject };
  }
  },
};
</script>
+104 −0
Original line number Diff line number Diff line
<script>
/* eslint-disable */
import { remapEvents, propsBinder, debounce } from "../utils.js";

import { onMounted, ref, computed, inject, watch } from "vue";
import { props as layerProps, setup as layerSetup } from "../functions/layer";

/**
 * Marker component, lets you add and personalize markers on the map
 */
export default {
  name: "LMarker",
  props: {
    ...layerProps,
    pane: {
      type: String,
      default: "markerPane",
    },
    draggable: {
      type: Boolean,
      custom: true,
      default: false,
    },
    latLng: {
      type: [Object, Array],
      custom: true,
      default: null,
    },
    icon: {
      type: [Object],
      custom: false,
    },
    zIndexOffset: {
      type: Number,
      custom: false,
      default: null,
    },
  },
  setup(props, context) {
    const mapRef = ref({});
    const ready = ref(false);
    const addLayer = inject("addLayer");
    const options = {
      ...layerSetup(props, mapRef),
      ...props,
    };

    onMounted(async () => {
      const { marker, DomEvent, latLng } = await import(
        "leaflet/dist/leaflet-src.esm"
      );

      mapRef.value = marker(props.latLng, options);

      const listeners = remapEvents(context.attrs);
      DomEvent.on(mapRef.value, listeners);

      const methods = {
        setDraggable(value) {
          if (mapRef.value.dragging) {
            newVal
              ? mapRef.value.dragging.enable()
              : mapRef.value.dragging.disable();
          }
        },
        latLngSync(event) {
          context.emit("update:latLng", event.latlng);
          context.emit("update:lat-lng", event.latlng);
        },
        setLatLng(newVal) {
          if (newVal == null) {
            return;
          }

          if (mapRef.value) {
            const oldLatLng = mapRef.value.getLatLng();
            const newLatLng = latlng(newVal);
            if (
              newLatLng.lat !== oldLatLng.lat ||
              newLatLng.lng !== oldLatLng.lng
            ) {
              mapRef.value.setLatLng(newLatLng);
            }
          }
        },
      };

      mapRef.value.on("move", debounce(methods.latLngSync, 100));
      propsBinder(methods, mapRef.value, props);
      addLayer(mapRef.value);
      ready.value = true;
    });

    const mapObject = computed(() => mapRef.value);
    return { mapObject, ready };
  },
  render: function (h) {
    if (this.ready && this.$slots.default) {
      return h("div", { style: { display: "none" } }, this.$slots.default);
    }
    return null;
  },
};
</script>
+49 −15
Original line number Diff line number Diff line
<script>
import { onMounted, ref, computed, inject } from 'vue';
import { props as layerProps, setup as layerSetup } from '../functions/layer';
/* eslint-disable */
import { onMounted, ref, computed, inject } from "vue";
import { props as layerProps, setup as layerSetup } from "../functions/layer";
import { remapEvents, propsBinder } from "../utils.js";
import {
  props as gridLayerProps,
  setup as gridLayerSetup
} from '../functions/gridLayer';
  setup as gridLayerSetup,
} from "../functions/gridLayer";
import {
  props as tileLayerProps,
  setup as tileLayerSetup
} from '../functions/tileLayer';
  setup as tileLayerSetup,
} from "../functions/tileLayer";

export default {
  props: {
@@ -17,27 +19,59 @@ export default {
    ...tileLayerProps,
    url: {
      type: String,
      default: null
    }
      default: null,
    },
  setup(props) {
  },
  setup(props, context) {
    const mapRef = ref({});
    const addLayer = inject('addLayer');
    const addLayer = inject("addLayer");

    const { options: layerOptions, methods: layerMethods } = layerSetup(
      props,
      mapRef
    );

    const {
      options: gridLayerOptions,
      methods: gridLayerMethods,
    } = gridLayerSetup(props);

    const {
      options: tileLayerOptions,
      methods: tileLayerMethods,
    } = tileLayerSetup(props);

    const options = {
      ...layerSetup(props),
      ...gridLayerSetup(props),
      ...tileLayerSetup(props)
      ...layerOptions,
      ...gridLayerOptions,
      ...tileLayerOptions,
    };

    const methods = {
      ...layerMethods,
      ...gridLayerMethods,
      ...tileLayerMethods,
    };

    onMounted(async () => {
      const { tileLayer } = await import('leaflet/dist/leaflet-src.esm');
      const { tileLayer, DomEvent } = await import(
        "leaflet/dist/leaflet-src.esm"
      );
      mapRef.value = tileLayer(props.url, options);

      const listeners = remapEvents(context.attrs);
      DomEvent.on(mapRef.value, listeners);

      propsBinder(methods, mapRef.value, props);

      addLayer(mapRef.value);
    });

    const mapObject = computed(() => mapRef.value);
    return { mapObject };
  },
  render() {
    return null;
  }
  },
};
</script>
Loading