From 68391ef52297b9bf4ba9c464f0fb52846dc2e0c7 Mon Sep 17 00:00:00 2001
From: Anita Lipsky <7695311+purplebugs@users.noreply.github.com>
Date: Tue, 16 Jul 2024 19:00:07 +0200
Subject: [PATCH] feat: Toggle map marker details when clicked (#23)
---
src/alpaca-map.js | 190 +++++++++++++++++++++++++++++++++++++++++-----
src/svg-icons.js | 81 ++++++++++++++++----
2 files changed, 234 insertions(+), 37 deletions(-)
diff --git a/src/alpaca-map.js b/src/alpaca-map.js
index c27c489..9278ae4 100644
--- a/src/alpaca-map.js
+++ b/src/alpaca-map.js
@@ -57,8 +57,11 @@ export default class AlpacaMap extends LitElement {
/* From me */
--almost-black: #333333;
--grey: #666666;
+ --brown: #83580b;
+ --green: #7a9a01; /* Pantone 377 C - from my chair */
- color: var(--almost-black);
+ --private-farm: var(--brown);
+ --public-farm: var(--green);
}
.web-component-container {
@@ -182,6 +185,98 @@ export default class AlpacaMap extends LitElement {
width: auto;
background-color: var(--pale-blue);
}
+
+ /********* Farm styles in unhighlighted state *********/
+ /* Ref: https://developers.google.com/maps/documentation/javascript/advanced-markers/html-markers#maps_advanced_markers_html-css */
+
+ .farm {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
+ background-color: white;
+ border-radius: 1rem;
+ box-shadow: 10px 10px 5px #0003;
+ color: var(--almost-black);
+
+ /* Override google map font to avoid flicker when load */
+ font:
+ 400 1.5em Poppins,
+ Arial,
+ sans-serif;
+ padding: 0.75rem;
+
+ width: auto;
+ max-width: 15rem;
+ }
+
+ .farm::after {
+ border-left: 9px solid transparent;
+ border-right: 9px solid transparent;
+ content: "";
+ height: 0;
+ left: 50%;
+ position: absolute;
+ top: 100%;
+ transform: translate(-50%);
+ width: 0;
+ z-index: 1;
+ }
+
+ .farm .summary {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
+ font-size: 1.5rem;
+ gap: 0.5rem;
+ }
+
+ .farm .details {
+ display: none;
+ flex-direction: column;
+ flex: 1;
+ }
+
+ /********* Farm styles in highlighted state *********/
+
+ /* .farm.highlight {
+ background-color: #ffffff;
+ border-radius: 8px;
+ box-shadow: 10px 10px 5px rgba(0, 0, 0, 0.2);
+ height: 80px;
+ padding: 8px 15px;
+ width: auto;
+ } */
+
+ .farm.highlight .details {
+ display: flex;
+ }
+
+ /********* Farm category colours *********/
+ .farm.private {
+ border: 0.2em solid var(--private-farm);
+ }
+
+ .farm.public {
+ border: 0.2em solid var(--public-farm);
+ }
+
+ .farm.private::after {
+ border-top: 9px solid var(--private-farm);
+ }
+
+ .farm.public::after {
+ border-top: 9px solid var(--public-farm);
+ }
+
+ .farm.private .icon svg {
+ fill: var(--private-farm);
+ }
+
+ .farm.public .icon svg {
+ fill: var(--public-farm);
+ }
`,
];
@@ -290,25 +385,53 @@ export default class AlpacaMap extends LitElement {
this.map.mapTypes.set("styled_map", styledMapType);
this.map.setMapTypeId("styled_map");
- const infoWindow = new google.maps.InfoWindow({
- content: "",
- disableAutoPan: true,
- });
-
// Add markers to the map
+ function toggleHighlight(markerView, farm) {
+ if (markerView.content.classList.contains("highlight")) {
+ markerView.content.classList.remove("highlight");
+ markerView.zIndex = null;
+ } else {
+ markerView.content.classList.add("highlight");
+ markerView.zIndex = 1;
+ }
+ }
+
+ function buildContent(farm) {
+ const content = document.createElement("div");
+ content.classList.add("farm");
+ content.classList.add(farm?.category?.private ? "private" : "public");
+
+ content.innerHTML = `
+
+
${iconHouseFlag().svgString}
+
${farm?.count?.alpacas?.status?.active} 🦙
+
+
+
+
${farm?.name}
+ ${farm?.city}
+
${farm?.location?.google?.formatted_address}
+
+ Directions
+
+
+ `;
+
+ return content;
+ }
const markers = this.farms.map((farm) => {
- const marker = new google.maps.marker.AdvancedMarkerElement({
+ const marker = new AdvancedMarkerElement({
+ content: buildContent(farm),
position: farm.location.lat_lng,
+ title: farm?.name,
});
// markers can only be keyboard focusable when they have click listeners
- // open info window when marker is clicked
+
+ // toggle marker summary/details when marker is clicked
marker.addListener("click", () => {
- infoWindow.setContent(
- farm.location.lat_lng.lat + ", " + farm.location.lat_lng.lng
- );
- infoWindow.open(this.map, marker);
+ toggleHighlight(marker, farm);
});
farm._marker = marker;
@@ -317,7 +440,20 @@ export default class AlpacaMap extends LitElement {
});
// Add a marker clusterer to manage the markers.
- this.cluster = new MarkerClusterer({ map: this.map });
+ this.cluster = new MarkerClusterer({
+ map: this.map,
+ algorithmOptions: {
+ radius: 100,
+ /*
+ Ref: https://www.npmjs.com/package/supercluster
+ minPoints: 10,
+ minZoom: 4,
+ maxZoom: 16,
+ radius: 100,
+ maxZoom: 16,
+ */
+ },
+ });
this.cluster.addMarkers(markers);
}
@@ -372,17 +508,23 @@ export default class AlpacaMap extends LitElement {
-
+
-
+
-
+
@@ -392,18 +534,20 @@ export default class AlpacaMap extends LitElement {
name="alpacaWalking"
/>
${iconPersonHiking().htmlObject}Alpaca walking
-
+
-
+
@@ -412,12 +556,16 @@ export default class AlpacaMap extends LitElement {
id="overnightStay"
name="overnightStay"
/>
-
+
-
+
diff --git a/src/svg-icons.js b/src/svg-icons.js
index cbf6edb..4d930ba 100644
--- a/src/svg-icons.js
+++ b/src/svg-icons.js
@@ -1,4 +1,5 @@
import { css, svg, html } from "lit";
+import { unsafeSVG } from "lit/directives/unsafe-svg.js"; /* This is safe since this is duplicating internal known markup, not markup from user */
export const iconStyles = [
css`
@@ -11,49 +12,97 @@ export const iconStyles = [
];
export const iconBed = () => {
- const svgIcon = svg``;
+ const path = ``;
- return html``;
+ const svgFragment = unsafeSVG(path);
+
+ return {
+ svgString: toSVGString(path),
+ htmlObject: toHTMLObject(svgFragment),
+ };
};
export const iconCalendarCheck = () => {
- const svgIcon = svg``;
+ const path = ``;
+
+ const svgFragment = unsafeSVG(path);
- return html``;
+ return {
+ svgString: toSVGString(path),
+ htmlObject: toHTMLObject(svgFragment),
+ };
};
export const iconHandshake = () => {
- const svgIcon = svg``;
+ const path = ``;
+
+ const svgFragment = unsafeSVG(path);
- return html``;
+ return {
+ svgString: toSVGString(path),
+ htmlObject: toHTMLObject(svgFragment),
+ };
};
export const iconHouseFlag = () => {
- const svgIcon = svg``;
+ const path = ``;
- return html``;
+ const svgFragment = unsafeSVG(path);
+
+ return {
+ svgString: toSVGString(path),
+ htmlObject: toHTMLObject(svgFragment),
+ };
};
export const iconKey = () => {
- const svgIcon = svg``;
+ const path = ``;
+
+ const svgFragment = unsafeSVG(path);
- return html``;
+ return {
+ svgString: toSVGString(path),
+ htmlObject: toHTMLObject(svgFragment),
+ };
};
export const iconMars = () => {
- const svgIcon = svg``;
+ const path = ``;
+
+ const svgFragment = unsafeSVG(path);
- return html``;
+ return {
+ svgString: toSVGString(path),
+ htmlObject: toHTMLObject(svgFragment),
+ };
};
export const iconPersonHiking = () => {
- const svgIcon = svg``;
+ const path = ``;
- return html``;
+ const svgFragment = unsafeSVG(path);
+
+ return {
+ svgString: toSVGString(path),
+ htmlObject: toHTMLObject(svgFragment),
+ };
};
export const iconStore = () => {
- const svgIcon = svg``;
+ const path = ``;
+
+ const svgFragment = unsafeSVG(path);
+
+ return {
+ svgString: toSVGString(path),
+ htmlObject: toHTMLObject(svgFragment),
+ };
+};
+
+export const toSVGString = (path) => {
+ return ``;
+};
- return html``;
+export const toHTMLObject = (svgFragment) => {
+ return html``;
};