Loading src/Playground.vue +23 −1 Original line number Diff line number Diff line <template> <div style="height: 50vh; width: 50vw;"> <div style="height: 75vh; width: 50vw;"> <l-map v-model="zoom" v-model:zoom="zoom" Loading @@ -15,11 +15,16 @@ lol </l-tooltip> </l-marker> <l-marker :lat-lng="[47.41322, -1.219482]"> <l-icon :icon-url="iconUrl" :icon-size="iconSize" /> </l-marker> </l-map> <button @click="changeIcon">New kitten icon</button> </div> </template> <script> import LMap from "./components/LMap.vue"; import LIcon from "./components/LIcon.vue"; import LTileLayer from "./components/LTileLayer.vue"; import LMarker from "./components/LMarker.vue"; import LControlLayers from "./components/LControlLayers.vue"; Loading @@ -28,6 +33,7 @@ import LTooltip from "./components/LTooltip.vue"; export default { components: { LMap, LIcon, LTileLayer, LMarker, LControlLayers, Loading @@ -36,12 +42,28 @@ export default { data() { return { zoom: 2, iconWidth: 25, iconHeight: 40, }; }, computed: { iconUrl() { return `https://placekitten.com/${this.iconWidth}/${this.iconHeight}`; }, iconSize() { return [this.iconWidth, this.iconHeight]; }, }, methods: { log(a) { console.log(a); }, changeIcon() { this.iconWidth += 2; if (this.iconWidth > this.iconHeight) { this.iconWidth = Math.floor(this.iconHeight / 2); } }, }, }; </script> src/components/LIcon.vue 0 → 100644 +105 −0 Original line number Diff line number Diff line <script> import { onMounted, ref, inject, nextTick, h } from "vue"; import { propsBinder, remapEvents } from "../utils"; import { props } from "../functions/icon"; /** * Icon component, lets you add and custom icons to the map */ export default { name: "LIcon", props, setup(props, context) { const root = ref(null); const canSetParentHtml = inject("canSetParentHtml"); const setParentHtml = inject("setParentHtml"); const setIcon = inject("setIcon"); let onDomEvent; let offDomEvent; let divIcon; let icon; let iconObject = undefined; const createIcon = (el, recreationNeeded, htmlSwapNeeded) => { const elHtml = el && el.innerHTML; if (!recreationNeeded) { if (htmlSwapNeeded && iconObject && canSetParentHtml()) { setParentHtml(elHtml); } return; } const listeners = remapEvents(context.attrs); if (iconObject) { offDomEvent(iconObject, listeners); } const options = { ...props, }; if (elHtml) { options.html = elHtml; } iconObject = options.html ? divIcon(options) : icon(options); onDomEvent(iconObject, listeners); setIcon(iconObject); }; const scheduleCreateIcon = () => { nextTick(() => createIcon(root.value, true, false)); }; const scheduleHtmlSwap = () => { nextTick(() => createIcon(root.value, false, true)); }; const methods = { setIconUrl: scheduleCreateIcon, setIconRetinaUrl: scheduleCreateIcon, setIconSize: scheduleCreateIcon, setIconAnchor: scheduleCreateIcon, setPopupAnchor: scheduleCreateIcon, setTooltipAnchor: scheduleCreateIcon, setShadowUrl: scheduleCreateIcon, setShadowRetinaUrl: scheduleCreateIcon, setShadowAnchor: scheduleCreateIcon, setBgPos: scheduleCreateIcon, setClassName: scheduleCreateIcon, setHtml: scheduleCreateIcon, }; onMounted(async () => { const { DomEvent, divIcon: lDivIcon, icon: lIcon } = await import( "leaflet/dist/leaflet-src.esm" ); onDomEvent = DomEvent.on; offDomEvent = DomEvent.off; divIcon = lDivIcon; icon = lIcon; propsBinder(methods, {}, props); const observer = new MutationObserver(scheduleHtmlSwap); observer.observe(root.value, { attributes: true, childList: true, characterData: true, subtree: true, }); scheduleCreateIcon(); }); return { root }; }, render() { const content = this.$slots.default ? this.$slots.default() : undefined; return h("div", { ref: "root" }, content); }, }; </script> src/components/LMarker.vue +10 −1 Original line number Diff line number Diff line <script> import { onMounted, ref, h, inject } from "vue"; import { onMounted, ref, h, provide, inject } from "vue"; import { remapEvents, propsBinder, Loading @@ -22,6 +22,15 @@ export default { const addLayer = inject("addLayer"); const latLng = provideLeafletWrapper("latLng"); provide("canSetParentHtml", () => !!leafletRef.value.getElement()); provide( "setParentHtml", (html) => (leafletRef.value.getElement().innerHTML = html) ); provide( "setIcon", (newIcon) => leafletRef.value.setIcon && leafletRef.value.setIcon(newIcon) ); const { options, methods } = markerSetup(props, leafletRef, context); onMounted(async () => { Loading src/functions/icon.js 0 → 100644 +67 −0 Original line number Diff line number Diff line export const props = { iconUrl: { type: String, custom: true, default: null, }, iconRetinaUrl: { type: String, custom: true, default: null, }, iconSize: { type: [Object, Array], custom: true, default: null, }, iconAnchor: { type: [Object, Array], custom: true, default: null, }, popupAnchor: { type: [Object, Array], custom: true, default: () => [0, 0], }, tooltipAnchor: { type: [Object, Array], custom: true, default: () => [0, 0], }, shadowUrl: { type: String, custom: true, default: null, }, shadowRetinaUrl: { type: String, custom: true, default: null, }, shadowSize: { type: [Object, Array], custom: true, default: null, }, shadowAnchor: { type: [Object, Array], custom: true, default: null, }, bgPos: { type: [Object, Array], custom: true, default: () => [0, 0], }, className: { type: String, custom: true, default: "", }, options: { type: Object, custom: true, default: () => ({}), }, }; Loading
src/Playground.vue +23 −1 Original line number Diff line number Diff line <template> <div style="height: 50vh; width: 50vw;"> <div style="height: 75vh; width: 50vw;"> <l-map v-model="zoom" v-model:zoom="zoom" Loading @@ -15,11 +15,16 @@ lol </l-tooltip> </l-marker> <l-marker :lat-lng="[47.41322, -1.219482]"> <l-icon :icon-url="iconUrl" :icon-size="iconSize" /> </l-marker> </l-map> <button @click="changeIcon">New kitten icon</button> </div> </template> <script> import LMap from "./components/LMap.vue"; import LIcon from "./components/LIcon.vue"; import LTileLayer from "./components/LTileLayer.vue"; import LMarker from "./components/LMarker.vue"; import LControlLayers from "./components/LControlLayers.vue"; Loading @@ -28,6 +33,7 @@ import LTooltip from "./components/LTooltip.vue"; export default { components: { LMap, LIcon, LTileLayer, LMarker, LControlLayers, Loading @@ -36,12 +42,28 @@ export default { data() { return { zoom: 2, iconWidth: 25, iconHeight: 40, }; }, computed: { iconUrl() { return `https://placekitten.com/${this.iconWidth}/${this.iconHeight}`; }, iconSize() { return [this.iconWidth, this.iconHeight]; }, }, methods: { log(a) { console.log(a); }, changeIcon() { this.iconWidth += 2; if (this.iconWidth > this.iconHeight) { this.iconWidth = Math.floor(this.iconHeight / 2); } }, }, }; </script>
src/components/LIcon.vue 0 → 100644 +105 −0 Original line number Diff line number Diff line <script> import { onMounted, ref, inject, nextTick, h } from "vue"; import { propsBinder, remapEvents } from "../utils"; import { props } from "../functions/icon"; /** * Icon component, lets you add and custom icons to the map */ export default { name: "LIcon", props, setup(props, context) { const root = ref(null); const canSetParentHtml = inject("canSetParentHtml"); const setParentHtml = inject("setParentHtml"); const setIcon = inject("setIcon"); let onDomEvent; let offDomEvent; let divIcon; let icon; let iconObject = undefined; const createIcon = (el, recreationNeeded, htmlSwapNeeded) => { const elHtml = el && el.innerHTML; if (!recreationNeeded) { if (htmlSwapNeeded && iconObject && canSetParentHtml()) { setParentHtml(elHtml); } return; } const listeners = remapEvents(context.attrs); if (iconObject) { offDomEvent(iconObject, listeners); } const options = { ...props, }; if (elHtml) { options.html = elHtml; } iconObject = options.html ? divIcon(options) : icon(options); onDomEvent(iconObject, listeners); setIcon(iconObject); }; const scheduleCreateIcon = () => { nextTick(() => createIcon(root.value, true, false)); }; const scheduleHtmlSwap = () => { nextTick(() => createIcon(root.value, false, true)); }; const methods = { setIconUrl: scheduleCreateIcon, setIconRetinaUrl: scheduleCreateIcon, setIconSize: scheduleCreateIcon, setIconAnchor: scheduleCreateIcon, setPopupAnchor: scheduleCreateIcon, setTooltipAnchor: scheduleCreateIcon, setShadowUrl: scheduleCreateIcon, setShadowRetinaUrl: scheduleCreateIcon, setShadowAnchor: scheduleCreateIcon, setBgPos: scheduleCreateIcon, setClassName: scheduleCreateIcon, setHtml: scheduleCreateIcon, }; onMounted(async () => { const { DomEvent, divIcon: lDivIcon, icon: lIcon } = await import( "leaflet/dist/leaflet-src.esm" ); onDomEvent = DomEvent.on; offDomEvent = DomEvent.off; divIcon = lDivIcon; icon = lIcon; propsBinder(methods, {}, props); const observer = new MutationObserver(scheduleHtmlSwap); observer.observe(root.value, { attributes: true, childList: true, characterData: true, subtree: true, }); scheduleCreateIcon(); }); return { root }; }, render() { const content = this.$slots.default ? this.$slots.default() : undefined; return h("div", { ref: "root" }, content); }, }; </script>
src/components/LMarker.vue +10 −1 Original line number Diff line number Diff line <script> import { onMounted, ref, h, inject } from "vue"; import { onMounted, ref, h, provide, inject } from "vue"; import { remapEvents, propsBinder, Loading @@ -22,6 +22,15 @@ export default { const addLayer = inject("addLayer"); const latLng = provideLeafletWrapper("latLng"); provide("canSetParentHtml", () => !!leafletRef.value.getElement()); provide( "setParentHtml", (html) => (leafletRef.value.getElement().innerHTML = html) ); provide( "setIcon", (newIcon) => leafletRef.value.setIcon && leafletRef.value.setIcon(newIcon) ); const { options, methods } = markerSetup(props, leafletRef, context); onMounted(async () => { Loading
src/functions/icon.js 0 → 100644 +67 −0 Original line number Diff line number Diff line export const props = { iconUrl: { type: String, custom: true, default: null, }, iconRetinaUrl: { type: String, custom: true, default: null, }, iconSize: { type: [Object, Array], custom: true, default: null, }, iconAnchor: { type: [Object, Array], custom: true, default: null, }, popupAnchor: { type: [Object, Array], custom: true, default: () => [0, 0], }, tooltipAnchor: { type: [Object, Array], custom: true, default: () => [0, 0], }, shadowUrl: { type: String, custom: true, default: null, }, shadowRetinaUrl: { type: String, custom: true, default: null, }, shadowSize: { type: [Object, Array], custom: true, default: null, }, shadowAnchor: { type: [Object, Array], custom: true, default: null, }, bgPos: { type: [Object, Array], custom: true, default: () => [0, 0], }, className: { type: String, custom: true, default: "", }, options: { type: Object, custom: true, default: () => ({}), }, };