@zappie/foundations 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/branch-map/geometry.ts"],
4
- "sourcesContent": ["import type { Branch } from \"@zappie/sdk\";\n\nexport type Coordinates = { lat: number; lng: number };\nexport function validCoordinates(value?: Coordinates): value is Coordinates {\n return (\n !!value &&\n Number.isFinite(value.lat) &&\n Number.isFinite(value.lng) &&\n Math.abs(value.lat) <= 90 &&\n Math.abs(value.lng) <= 180\n );\n}\nexport function visibleBranches(branches: Branch[]) {\n return branches.filter(\n (branch): branch is Branch & { coordinates: Coordinates } =>\n validCoordinates(branch.coordinates),\n );\n}\nexport function branchBounds(branches: Branch[]) {\n const points = visibleBranches(branches).map((branch) => branch.coordinates);\n if (!points.length) {\n return undefined;\n }\n // The largest longitude gap lies outside the bounds, including across the date line.\n const longitudes = points\n .map((point) => (point.lng + 360) % 360)\n .sort((a, b) => a - b);\n let gap = -1;\n let start = 0;\n for (let index = 0; index < longitudes.length; index++) {\n const next =\n longitudes[(index + 1) % longitudes.length]! +\n (index === longitudes.length - 1 ? 360 : 0);\n if (next - longitudes[index]! > gap) {\n gap = next - longitudes[index]!;\n start = (index + 1) % longitudes.length;\n }\n }\n const normalize = (lng: number) => (lng > 180 ? lng - 360 : lng);\n return {\n north: Math.max(...points.map((point) => point.lat)),\n south: Math.min(...points.map((point) => point.lat)),\n west: normalize(longitudes[start]!),\n east: normalize(\n longitudes[(start + longitudes.length - 1) % longitudes.length]!,\n ),\n };\n}\n"],
5
- "mappings": ";AAGO,SAAS,iBAAiB,OAA2C;AAC1E,SACE,CAAC,CAAC,SACF,OAAO,SAAS,MAAM,GAAG,KACzB,OAAO,SAAS,MAAM,GAAG,KACzB,KAAK,IAAI,MAAM,GAAG,KAAK,MACvB,KAAK,IAAI,MAAM,GAAG,KAAK;AAE3B;AACO,SAAS,gBAAgB,UAAoB;AAClD,SAAO,SAAS;AAAA,IACd,CAAC,WACC,iBAAiB,OAAO,WAAW;AAAA,EACvC;AACF;AACO,SAAS,aAAa,UAAoB;AAC/C,QAAM,SAAS,gBAAgB,QAAQ,EAAE,IAAI,CAAC,WAAW,OAAO,WAAW;AAC3E,MAAI,CAAC,OAAO,QAAQ;AAClB,WAAO;AAAA,EACT;AAEA,QAAM,aAAa,OAChB,IAAI,CAAC,WAAW,MAAM,MAAM,OAAO,GAAG,EACtC,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC;AACvB,MAAI,MAAM;AACV,MAAI,QAAQ;AACZ,WAAS,QAAQ,GAAG,QAAQ,WAAW,QAAQ,SAAS;AACtD,UAAM,OACJ,YAAY,QAAQ,KAAK,WAAW,MAAM,KACzC,UAAU,WAAW,SAAS,IAAI,MAAM;AAC3C,QAAI,OAAO,WAAW,KAAK,IAAK,KAAK;AACnC,YAAM,OAAO,WAAW,KAAK;AAC7B,eAAS,QAAQ,KAAK,WAAW;AAAA,IACnC;AAAA,EACF;AACA,QAAM,YAAY,CAAC,QAAiB,MAAM,MAAM,MAAM,MAAM;AAC5D,SAAO;AAAA,IACL,OAAO,KAAK,IAAI,GAAG,OAAO,IAAI,CAAC,UAAU,MAAM,GAAG,CAAC;AAAA,IACnD,OAAO,KAAK,IAAI,GAAG,OAAO,IAAI,CAAC,UAAU,MAAM,GAAG,CAAC;AAAA,IACnD,MAAM,UAAU,WAAW,KAAK,CAAE;AAAA,IAClC,MAAM;AAAA,MACJ,YAAY,QAAQ,WAAW,SAAS,KAAK,WAAW,MAAM;AAAA,IAChE;AAAA,EACF;AACF;",
4
+ "sourcesContent": ["import type { Branch } from \"@zappie/sdk\";\n\nexport type Coordinates = { lat: number; lng: number };\nexport type Bounds = {\n north: number;\n south: number;\n east: number;\n west: number;\n};\n\nexport function validCoordinates(value?: Coordinates): value is Coordinates {\n return (\n !!value &&\n Number.isFinite(value.lat) &&\n Number.isFinite(value.lng) &&\n Math.abs(value.lat) <= 90 &&\n Math.abs(value.lng) <= 180\n );\n}\n\n/** Branches with a real location. (0, 0) is the placeholder for \"no location on file\". */\nexport function visibleBranches(branches: Branch[]) {\n return branches.filter(\n (branch): branch is Branch & { coordinates: Coordinates } =>\n validCoordinates(branch.coordinates) &&\n (branch.coordinates.lat !== 0 || branch.coordinates.lng !== 0),\n );\n}\n"],
5
+ "mappings": ";AAUO,SAAS,iBAAiB,OAA2C;AAC1E,SACE,CAAC,CAAC,SACF,OAAO,SAAS,MAAM,GAAG,KACzB,OAAO,SAAS,MAAM,GAAG,KACzB,KAAK,IAAI,MAAM,GAAG,KAAK,MACvB,KAAK,IAAI,MAAM,GAAG,KAAK;AAE3B;AAGO,SAAS,gBAAgB,UAAoB;AAClD,SAAO,SAAS;AAAA,IACd,CAAC,WACC,iBAAiB,OAAO,WAAW,MAClC,OAAO,YAAY,QAAQ,KAAK,OAAO,YAAY,QAAQ;AAAA,EAChE;AACF;",
6
6
  "names": []
7
7
  }
@@ -1,62 +1,67 @@
1
1
  "use client";
2
2
 
3
3
  // src/branch-map/index.tsx
4
- import { useEffect, useRef, useState } from "react";
4
+ import { useEffect, useRef } from "react";
5
+ import { COLOR_PROBE_STYLE, readColors } from "./colors.js";
5
6
  import {
6
7
  validCoordinates,
7
8
  visibleBranches
8
9
  } from "./geometry.js";
10
+ import { useMapRuntime } from "./use-map-runtime.js";
11
+ import { DeliveryMap } from "./delivery-map.js";
9
12
  import { jsx, jsxs } from "react/jsx-runtime";
13
+ var loadBranchMap = (mapTypeControl) => (target, key, signal) => import("./runtime.js").then(
14
+ ({ createMap }) => createMap(target, key, signal, { mapTypeControl })
15
+ );
10
16
  function BranchMap({
11
17
  branches,
12
18
  selectedBranchId,
13
19
  onSelect,
14
20
  shopperLocation,
15
21
  browserKey,
22
+ maxZoom,
23
+ pinImageUrl,
24
+ mapTypeControl = false,
25
+ pinLabel,
26
+ compact = false,
27
+ onLoadError,
28
+ renderLoadError,
16
29
  ...props
17
30
  }) {
18
- const element = useRef(null);
19
- const [runtime, setRuntime] = useState();
20
- const [failed, setFailed] = useState(false);
31
+ const probe = useRef(null);
21
32
  const hasCoordinates = visibleBranches(branches).length > 0;
33
+ const { element, runtime, failure, retry } = useMapRuntime(browserKey, hasCoordinates, loadBranchMap(mapTypeControl), onLoadError);
34
+ const failed = failure !== void 0;
35
+ const effectiveMaxZoom = maxZoom ?? (validCoordinates(shopperLocation) ? 15 : 14);
22
36
  useEffect(() => {
23
- if (!browserKey || !element.current || !hasCoordinates) {
24
- return;
25
- }
26
- let disposed = false;
27
- const controller = new AbortController();
28
- let instance;
29
- const target = element.current;
30
- setFailed(false);
31
- void import("./runtime.js").then(({ createMap }) => createMap(target, browserKey, controller.signal)).then((map) => {
32
- instance = map;
33
- if (disposed) {
34
- map.destroy();
35
- } else {
36
- setRuntime(map);
37
- }
38
- }).catch(() => {
39
- if (!disposed) {
40
- setFailed(true);
41
- }
37
+ runtime?.update(branches, selectedBranchId, onSelect, shopperLocation, {
38
+ maxZoom: effectiveMaxZoom,
39
+ pinImageUrl,
40
+ mapTypeControl,
41
+ pinLabel,
42
+ compact,
43
+ colors: readColors(probe.current)
42
44
  });
43
- return () => {
44
- disposed = true;
45
- controller.abort();
46
- instance?.destroy();
47
- setRuntime(void 0);
48
- };
49
- }, [browserKey, hasCoordinates]);
50
- useEffect(() => {
51
- runtime?.update(branches, selectedBranchId, onSelect, shopperLocation);
52
- }, [runtime, branches, selectedBranchId, onSelect, shopperLocation]);
45
+ }, [
46
+ runtime,
47
+ branches,
48
+ selectedBranchId,
49
+ onSelect,
50
+ shopperLocation,
51
+ effectiveMaxZoom,
52
+ pinImageUrl,
53
+ mapTypeControl,
54
+ pinLabel,
55
+ compact
56
+ ]);
53
57
  if (!browserKey) {
54
58
  return null;
55
59
  }
56
60
  const unavailable = branches.filter(
57
- (branch) => !validCoordinates(branch.coordinates)
61
+ (branch) => !visibleBranches([branch]).length
58
62
  );
59
63
  return /* @__PURE__ */ jsxs("div", { ...props, "data-branch-map": "", children: [
64
+ /* @__PURE__ */ jsx("span", { ref: probe, hidden: true, style: COLOR_PROBE_STYLE }),
60
65
  /* @__PURE__ */ jsx(
61
66
  "div",
62
67
  {
@@ -66,7 +71,7 @@ function BranchMap({
66
71
  style: { height: "100%" }
67
72
  }
68
73
  ),
69
- failed || !hasCoordinates ? /* @__PURE__ */ jsx("p", { role: "status", children: "Map location unavailable" }) : null,
74
+ failure && renderLoadError ? renderLoadError({ error: failure.error, retry }) : failed || !hasCoordinates ? /* @__PURE__ */ jsx("p", { role: "status", children: "Map location unavailable" }) : null,
70
75
  unavailable.map((branch) => /* @__PURE__ */ jsxs(
71
76
  "button",
72
77
  {
@@ -84,6 +89,7 @@ function BranchMap({
84
89
  ] });
85
90
  }
86
91
  export {
87
- BranchMap
92
+ BranchMap,
93
+ DeliveryMap
88
94
  };
89
95
  //# sourceMappingURL=index.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/branch-map/index.tsx"],
4
- "sourcesContent": ["\"use client\";\nimport type { Branch } from \"@zappie/sdk\";\nimport { useEffect, useRef, useState, type HTMLAttributes } from \"react\";\nimport {\n validCoordinates,\n visibleBranches,\n type Coordinates,\n} from \"./geometry\";\nimport type { createMap } from \"./runtime\";\n\n/** Branches, selection and optional shopper coordinates for BranchMap. */\nexport type BranchMapProps = Omit<\n HTMLAttributes<HTMLDivElement>,\n \"onSelect\"\n> & {\n branches: Branch[];\n selectedBranchId?: string;\n onSelect: (branch: Branch) => void;\n shopperLocation?: Coordinates;\n browserKey?: string;\n};\n\n/**\n * Show branch locations. Maps loads after mount when you supply a browser key.\n * Set the container height with CSS. Style pins with --branch-map-pin,\n * --branch-map-pin-selected, --branch-map-pin-border and --branch-map-pin-size.\n */\nexport function BranchMap({\n branches,\n selectedBranchId,\n onSelect,\n shopperLocation,\n browserKey,\n ...props\n}: BranchMapProps) {\n const element = useRef<HTMLDivElement>(null);\n const [runtime, setRuntime] =\n useState<Awaited<ReturnType<typeof createMap>>>();\n const [failed, setFailed] = useState(false);\n const hasCoordinates = visibleBranches(branches).length > 0;\n useEffect(() => {\n if (!browserKey || !element.current || !hasCoordinates) {\n return;\n }\n let disposed = false;\n const controller = new AbortController();\n let instance: Awaited<ReturnType<typeof createMap>> | undefined;\n const target = element.current;\n setFailed(false);\n void import(\"./runtime\")\n .then(({ createMap }) => createMap(target, browserKey, controller.signal))\n .then((map) => {\n instance = map;\n if (disposed) {\n map.destroy();\n } else {\n setRuntime(map);\n }\n })\n .catch(() => {\n if (!disposed) {\n setFailed(true);\n }\n });\n return () => {\n disposed = true;\n controller.abort();\n instance?.destroy();\n setRuntime(undefined);\n };\n }, [browserKey, hasCoordinates]);\n useEffect(() => {\n runtime?.update(branches, selectedBranchId, onSelect, shopperLocation);\n }, [runtime, branches, selectedBranchId, onSelect, shopperLocation]);\n if (!browserKey) {\n return null;\n }\n const unavailable = branches.filter(\n (branch) => !validCoordinates(branch.coordinates),\n );\n return (\n <div {...props} data-branch-map=\"\">\n <div\n hidden={failed || !hasCoordinates}\n ref={element}\n data-branch-map-canvas=\"\"\n style={{ height: \"100%\" }}\n />\n {failed || !hasCoordinates ? (\n <p role=\"status\">Map location unavailable</p>\n ) : null}\n {unavailable.map((branch) => (\n <button\n key={branch.id}\n type=\"button\"\n aria-pressed={branch.id === selectedBranchId}\n onClick={() => onSelect(branch)}\n >\n {branch.name}: Map location unavailable\n </button>\n ))}\n {runtime && validCoordinates(shopperLocation) ? (\n <button type=\"button\" onClick={() => runtime.locate(shopperLocation)}>\n Use my current location\n </button>\n ) : null}\n </div>\n );\n}\n"],
5
- "mappings": ";;;AAEA,SAAS,WAAW,QAAQ,gBAAqC;AACjE;AAAA,EACE;AAAA,EACA;AAAA,OAEK;AA2ED,cAUE,YAVF;AAvDC,SAAS,UAAU;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAmB;AACjB,QAAM,UAAU,OAAuB,IAAI;AAC3C,QAAM,CAAC,SAAS,UAAU,IACxB,SAAgD;AAClD,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAS,KAAK;AAC1C,QAAM,iBAAiB,gBAAgB,QAAQ,EAAE,SAAS;AAC1D,YAAU,MAAM;AACd,QAAI,CAAC,cAAc,CAAC,QAAQ,WAAW,CAAC,gBAAgB;AACtD;AAAA,IACF;AACA,QAAI,WAAW;AACf,UAAM,aAAa,IAAI,gBAAgB;AACvC,QAAI;AACJ,UAAM,SAAS,QAAQ;AACvB,cAAU,KAAK;AACf,SAAK,OAAO,cAAW,EACpB,KAAK,CAAC,EAAE,UAAU,MAAM,UAAU,QAAQ,YAAY,WAAW,MAAM,CAAC,EACxE,KAAK,CAAC,QAAQ;AACb,iBAAW;AACX,UAAI,UAAU;AACZ,YAAI,QAAQ;AAAA,MACd,OAAO;AACL,mBAAW,GAAG;AAAA,MAChB;AAAA,IACF,CAAC,EACA,MAAM,MAAM;AACX,UAAI,CAAC,UAAU;AACb,kBAAU,IAAI;AAAA,MAChB;AAAA,IACF,CAAC;AACH,WAAO,MAAM;AACX,iBAAW;AACX,iBAAW,MAAM;AACjB,gBAAU,QAAQ;AAClB,iBAAW,MAAS;AAAA,IACtB;AAAA,EACF,GAAG,CAAC,YAAY,cAAc,CAAC;AAC/B,YAAU,MAAM;AACd,aAAS,OAAO,UAAU,kBAAkB,UAAU,eAAe;AAAA,EACvE,GAAG,CAAC,SAAS,UAAU,kBAAkB,UAAU,eAAe,CAAC;AACnE,MAAI,CAAC,YAAY;AACf,WAAO;AAAA,EACT;AACA,QAAM,cAAc,SAAS;AAAA,IAC3B,CAAC,WAAW,CAAC,iBAAiB,OAAO,WAAW;AAAA,EAClD;AACA,SACE,qBAAC,SAAK,GAAG,OAAO,mBAAgB,IAC9B;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,QAAQ,UAAU,CAAC;AAAA,QACnB,KAAK;AAAA,QACL,0BAAuB;AAAA,QACvB,OAAO,EAAE,QAAQ,OAAO;AAAA;AAAA,IAC1B;AAAA,IACC,UAAU,CAAC,iBACV,oBAAC,OAAE,MAAK,UAAS,sCAAwB,IACvC;AAAA,IACH,YAAY,IAAI,CAAC,WAChB;AAAA,MAAC;AAAA;AAAA,QAEC,MAAK;AAAA,QACL,gBAAc,OAAO,OAAO;AAAA,QAC5B,SAAS,MAAM,SAAS,MAAM;AAAA,QAE7B;AAAA,iBAAO;AAAA,UAAK;AAAA;AAAA;AAAA,MALR,OAAO;AAAA,IAMd,CACD;AAAA,IACA,WAAW,iBAAiB,eAAe,IAC1C,oBAAC,YAAO,MAAK,UAAS,SAAS,MAAM,QAAQ,OAAO,eAAe,GAAG,qCAEtE,IACE;AAAA,KACN;AAEJ;",
4
+ "sourcesContent": ["\"use client\";\nimport type { Branch } from \"@zappie/sdk\";\nimport { useEffect, useRef, type HTMLAttributes, type ReactNode } from \"react\";\nimport { COLOR_PROBE_STYLE, readColors } from \"./colors\";\nimport {\n validCoordinates,\n visibleBranches,\n type Coordinates,\n} from \"./geometry\";\nimport type { createMap } from \"./runtime\";\nimport { useMapRuntime, type MapLoadErrorState } from \"./use-map-runtime\";\n\nexport { DeliveryMap } from \"./delivery-map\";\nexport type { DeliveryMapProps } from \"./delivery-map\";\nexport type { MapLoadErrorState } from \"./use-map-runtime\";\n\n/** What a template gets to render when the map fails to load. */\nexport type BranchMapLoadErrorState = MapLoadErrorState;\n\n/** Branches, selection and optional shopper coordinates for BranchMap. */\nexport type BranchMapProps = Omit<\n HTMLAttributes<HTMLDivElement>,\n \"onSelect\"\n> & {\n branches: Branch[];\n selectedBranchId?: string;\n onSelect: (branch: Branch) => void;\n /**\n * The shopper's coordinates. The map shows them with a house marker and\n * draws a line to the selected branch, or to the nearest one.\n */\n shopperLocation?: Coordinates;\n browserKey?: string;\n /**\n * Highest zoom applied after fitting. Defaults to 15 when a shopper location\n * is shown and 14 otherwise.\n */\n maxZoom?: number;\n /** Image shown inside each branch pin, such as the business logo. */\n pinImageUrl?: string;\n /** Show the map and satellite switch. Defaults to false. */\n mapTypeControl?: boolean;\n /**\n * Show the branch name above the selected pin, or above every pin. Off by\n * default. With a shopper location and no selection, \"selected\" labels the\n * nearest branch.\n */\n pinLabel?: \"selected\" | \"all\";\n /** Frame a small map, with tighter padding and a wider single-branch view. */\n compact?: boolean;\n /** Called each time the map fails to load. */\n onLoadError?: (error: unknown) => void;\n /**\n * Render your own message when the map fails to load, such as a retry\n * button that calls `retry`. Without it the map shows \"Map location unavailable\".\n */\n renderLoadError?: (state: BranchMapLoadErrorState) => ReactNode;\n};\n\nconst loadBranchMap =\n (mapTypeControl: boolean) =>\n (target: HTMLElement, key: string, signal: AbortSignal) =>\n import(\"./runtime\").then(({ createMap }) =>\n createMap(target, key, signal, { mapTypeControl }),\n );\n\n/**\n * Show branch locations. Maps loads after mount when you supply a browser key.\n * Nearby branches group into numbered clusters. Set the container height with\n * CSS. Colour clusters, the shopper marker, labels and the route with\n * --map-accent and --map-accent-foreground. Style dot pins with\n * --branch-map-pin, --branch-map-pin-selected, --branch-map-pin-border and\n * --branch-map-pin-size.\n */\nexport function BranchMap({\n branches,\n selectedBranchId,\n onSelect,\n shopperLocation,\n browserKey,\n maxZoom,\n pinImageUrl,\n mapTypeControl = false,\n pinLabel,\n compact = false,\n onLoadError,\n renderLoadError,\n ...props\n}: BranchMapProps) {\n const probe = useRef<HTMLSpanElement>(null);\n const hasCoordinates = visibleBranches(branches).length > 0;\n const { element, runtime, failure, retry } = useMapRuntime<\n Awaited<ReturnType<typeof createMap>>\n >(browserKey, hasCoordinates, loadBranchMap(mapTypeControl), onLoadError);\n const failed = failure !== undefined;\n const effectiveMaxZoom =\n maxZoom ?? (validCoordinates(shopperLocation) ? 15 : 14);\n useEffect(() => {\n runtime?.update(branches, selectedBranchId, onSelect, shopperLocation, {\n maxZoom: effectiveMaxZoom,\n pinImageUrl,\n mapTypeControl,\n pinLabel,\n compact,\n colors: readColors(probe.current),\n });\n }, [\n runtime,\n branches,\n selectedBranchId,\n onSelect,\n shopperLocation,\n effectiveMaxZoom,\n pinImageUrl,\n mapTypeControl,\n pinLabel,\n compact,\n ]);\n if (!browserKey) {\n return null;\n }\n const unavailable = branches.filter(\n (branch) => !visibleBranches([branch]).length,\n );\n return (\n <div {...props} data-branch-map=\"\">\n <span ref={probe} hidden style={COLOR_PROBE_STYLE} />\n <div\n hidden={failed || !hasCoordinates}\n ref={element}\n data-branch-map-canvas=\"\"\n style={{ height: \"100%\" }}\n />\n {failure && renderLoadError ? (\n renderLoadError({ error: failure.error, retry })\n ) : failed || !hasCoordinates ? (\n <p role=\"status\">Map location unavailable</p>\n ) : null}\n {unavailable.map((branch) => (\n <button\n key={branch.id}\n type=\"button\"\n aria-pressed={branch.id === selectedBranchId}\n onClick={() => onSelect(branch)}\n >\n {branch.name}: Map location unavailable\n </button>\n ))}\n {runtime && validCoordinates(shopperLocation) ? (\n <button type=\"button\" onClick={() => runtime.locate(shopperLocation)}>\n Use my current location\n </button>\n ) : null}\n </div>\n );\n}\n"],
5
+ "mappings": ";;;AAEA,SAAS,WAAW,cAAmD;AACvE,SAAS,mBAAmB,kBAAkB;AAC9C;AAAA,EACE;AAAA,EACA;AAAA,OAEK;AAEP,SAAS,qBAA6C;AAEtD,SAAS,mBAAmB;AAkHtB,cAaE,YAbF;AAnEN,IAAM,gBACJ,CAAC,mBACD,CAAC,QAAqB,KAAa,WACjC,OAAO,cAAW,EAAE;AAAA,EAAK,CAAC,EAAE,UAAU,MACpC,UAAU,QAAQ,KAAK,QAAQ,EAAE,eAAe,CAAC;AACnD;AAUG,SAAS,UAAU;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,iBAAiB;AAAA,EACjB;AAAA,EACA,UAAU;AAAA,EACV;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAmB;AACjB,QAAM,QAAQ,OAAwB,IAAI;AAC1C,QAAM,iBAAiB,gBAAgB,QAAQ,EAAE,SAAS;AAC1D,QAAM,EAAE,SAAS,SAAS,SAAS,MAAM,IAAI,cAE3C,YAAY,gBAAgB,cAAc,cAAc,GAAG,WAAW;AACxE,QAAM,SAAS,YAAY;AAC3B,QAAM,mBACJ,YAAY,iBAAiB,eAAe,IAAI,KAAK;AACvD,YAAU,MAAM;AACd,aAAS,OAAO,UAAU,kBAAkB,UAAU,iBAAiB;AAAA,MACrE,SAAS;AAAA,MACT;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,QAAQ,WAAW,MAAM,OAAO;AAAA,IAClC,CAAC;AAAA,EACH,GAAG;AAAA,IACD;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AACD,MAAI,CAAC,YAAY;AACf,WAAO;AAAA,EACT;AACA,QAAM,cAAc,SAAS;AAAA,IAC3B,CAAC,WAAW,CAAC,gBAAgB,CAAC,MAAM,CAAC,EAAE;AAAA,EACzC;AACA,SACE,qBAAC,SAAK,GAAG,OAAO,mBAAgB,IAC9B;AAAA,wBAAC,UAAK,KAAK,OAAO,QAAM,MAAC,OAAO,mBAAmB;AAAA,IACnD;AAAA,MAAC;AAAA;AAAA,QACC,QAAQ,UAAU,CAAC;AAAA,QACnB,KAAK;AAAA,QACL,0BAAuB;AAAA,QACvB,OAAO,EAAE,QAAQ,OAAO;AAAA;AAAA,IAC1B;AAAA,IACC,WAAW,kBACV,gBAAgB,EAAE,OAAO,QAAQ,OAAO,MAAM,CAAC,IAC7C,UAAU,CAAC,iBACb,oBAAC,OAAE,MAAK,UAAS,sCAAwB,IACvC;AAAA,IACH,YAAY,IAAI,CAAC,WAChB;AAAA,MAAC;AAAA;AAAA,QAEC,MAAK;AAAA,QACL,gBAAc,OAAO,OAAO;AAAA,QAC5B,SAAS,MAAM,SAAS,MAAM;AAAA,QAE7B;AAAA,iBAAO;AAAA,UAAK;AAAA;AAAA;AAAA,MALR,OAAO;AAAA,IAMd,CACD;AAAA,IACA,WAAW,iBAAiB,eAAe,IAC1C,oBAAC,YAAO,MAAK,UAAS,SAAS,MAAM,QAAQ,OAAO,eAAe,GAAG,qCAEtE,IACE;AAAA,KACN;AAEJ;",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,143 @@
1
+ var __typeError = (msg) => {
2
+ throw TypeError(msg);
3
+ };
4
+ var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
5
+ var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
6
+ var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
7
+ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
8
+
9
+ // src/branch-map/markers.ts
10
+ import { DEFAULT_COLORS } from "./colors.js";
11
+ var SHADOW_SM = "0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)";
12
+ var SHADOW_MD = "0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)";
13
+ var MARKER_BOX = 40;
14
+ var LABEL_GAP = 4;
15
+ function branchMarker(imageUrl, focused) {
16
+ return imageUrl ? {
17
+ kind: "logo",
18
+ size: focused ? 40 : 32,
19
+ imageUrl,
20
+ shadow: focused ? "md" : "sm"
21
+ } : { kind: "dot", selected: focused };
22
+ }
23
+ var HOUSE_ICON = '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" style="display:block"><path d="M15 22a1 1 0 0 1-1-1v-4a1 1 0 0 1 .445-.832l3-2a1 1 0 0 1 1.11 0l3 2A1 1 0 0 1 22 17v4a1 1 0 0 1-1 1z"/><path d="M18 10a8 8 0 0 0-16 0c0 4.993 5.539 10.193 7.399 11.799a1 1 0 0 0 .601.2"/><path d="M18 22v-3"/><circle cx="10" cy="10" r="3"/></svg>';
24
+ function paintMarker(element, spec, colors) {
25
+ const { left, top } = element.style;
26
+ try {
27
+ paint(element, spec, colors);
28
+ } finally {
29
+ element.style.left = left;
30
+ element.style.top = top;
31
+ }
32
+ }
33
+ function paint(element, spec, colors) {
34
+ element.dataset.marker = spec.kind;
35
+ if (spec.kind === "logo") {
36
+ const lift = spec.lift ?? MARKER_BOX / 2;
37
+ element.style.cssText = `position:absolute;box-sizing:border-box;transform:translate(-50%,calc(-50% - ${lift}px));width:${spec.size}px;height:${spec.size}px;padding:0;border-radius:9999px;overflow:hidden;border:2px solid white;background:white;box-shadow:${spec.shadow === "md" ? SHADOW_MD : SHADOW_SM};cursor:pointer;transition:width 150ms cubic-bezier(0.4,0,0.2,1),height 150ms cubic-bezier(0.4,0,0.2,1)`;
38
+ let image = element.querySelector("img");
39
+ if (!image) {
40
+ element.replaceChildren();
41
+ image = document.createElement("img");
42
+ image.alt = "";
43
+ image.decoding = "async";
44
+ image.draggable = false;
45
+ image.onerror = () => {
46
+ image.style.visibility = "hidden";
47
+ };
48
+ element.appendChild(image);
49
+ }
50
+ image.style.cssText = "display:block;width:100%;height:100%;border-radius:9999px;object-fit:cover";
51
+ if (image.getAttribute("src") !== spec.imageUrl) {
52
+ image.src = spec.imageUrl;
53
+ }
54
+ return;
55
+ }
56
+ if (spec.kind === "dot") {
57
+ element.replaceChildren();
58
+ element.style.cssText = `position:absolute;transform:translate(-50%,-100%);width:var(--branch-map-pin-size,24px);height:var(--branch-map-pin-size,24px);border-radius:50%;border:2px solid var(--branch-map-pin-border,currentColor);background:var(${spec.selected ? "--branch-map-pin-selected" : "--branch-map-pin"},currentColor);cursor:pointer`;
59
+ return;
60
+ }
61
+ element.style.cssText = `position:absolute;box-sizing:border-box;transform:translate(-50%,calc(-50% - ${spec.lift ?? MARKER_BOX / 2}px));width:32px;height:32px;padding:4px;border-radius:4px;border:2px solid white;background:${colors.accent};color:${spec.color};box-shadow:${SHADOW_MD}`;
62
+ if (!element.firstChild) {
63
+ element.innerHTML = HOUSE_ICON;
64
+ }
65
+ }
66
+ function paintLabel(element, options) {
67
+ const { left, top } = element.style;
68
+ element.style.cssText = `position:absolute;box-sizing:border-box;transform:translate(-50%,calc(-100% - ${options.offset}));white-space:nowrap;pointer-events:none;padding:4px 8px;border-radius:8px;font-size:12px;line-height:16px;font-weight:500;${options.capitalize ? "text-transform:capitalize;" : ""}background:var(--branch-map-label-background,white);color:var(--branch-map-label-color,#111);border:2px solid var(--branch-map-label-border,${options.colors.accent});box-shadow:${SHADOW_MD}`;
69
+ element.style.left = left;
70
+ element.style.top = top;
71
+ }
72
+ function labelOffset(spec) {
73
+ return spec.kind === "dot" ? `var(--branch-map-pin-size,24px) + ${LABEL_GAP}px` : `${MARKER_BOX + LABEL_GAP}px`;
74
+ }
75
+ function markerOverlayClass(maps) {
76
+ var _position, _pane, _a;
77
+ return _a = class extends maps.OverlayView {
78
+ constructor(key, position, element) {
79
+ super();
80
+ __privateAdd(this, _position);
81
+ __privateAdd(this, _pane);
82
+ this.key = key;
83
+ this.element = element;
84
+ __privateSet(this, _position, position);
85
+ this.label = document.createElement("span");
86
+ this.label.setAttribute("aria-hidden", "true");
87
+ }
88
+ getPosition() {
89
+ return new maps.LatLng(__privateGet(this, _position));
90
+ }
91
+ getVisible() {
92
+ return true;
93
+ }
94
+ setPosition(position) {
95
+ __privateSet(this, _position, position);
96
+ if (this.getMap()) {
97
+ this.draw();
98
+ }
99
+ }
100
+ // An unlabelled marker has no label node in the map.
101
+ setLabel(text) {
102
+ this.label.textContent = text ?? "";
103
+ if (!text) {
104
+ this.label.remove();
105
+ } else if (__privateGet(this, _pane) && !this.label.isConnected) {
106
+ __privateGet(this, _pane).appendChild(this.label);
107
+ }
108
+ }
109
+ onAdd() {
110
+ __privateSet(this, _pane, this.getPanes()?.overlayMouseTarget);
111
+ __privateGet(this, _pane)?.appendChild(this.element);
112
+ if (this.label.textContent) {
113
+ __privateGet(this, _pane)?.appendChild(this.label);
114
+ }
115
+ maps.OverlayView.preventMapHitsAndGesturesFrom(this.element);
116
+ }
117
+ draw() {
118
+ const point = this.getProjection()?.fromLatLngToDivPixel(
119
+ new maps.LatLng(__privateGet(this, _position))
120
+ );
121
+ if (point) {
122
+ for (const node of [this.element, this.label]) {
123
+ node.style.left = `${point.x}px`;
124
+ node.style.top = `${point.y}px`;
125
+ }
126
+ }
127
+ }
128
+ onRemove() {
129
+ __privateSet(this, _pane, void 0);
130
+ this.element.remove();
131
+ this.label.remove();
132
+ }
133
+ }, _position = new WeakMap(), _pane = new WeakMap(), _a;
134
+ }
135
+ export {
136
+ DEFAULT_COLORS,
137
+ branchMarker,
138
+ labelOffset,
139
+ markerOverlayClass,
140
+ paintLabel,
141
+ paintMarker
142
+ };
143
+ //# sourceMappingURL=markers.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/branch-map/markers.ts"],
4
+ "sourcesContent": ["/// <reference types=\"google.maps\" />\nimport type { MapColors } from \"./colors\";\nimport type { Coordinates } from \"./geometry\";\n\nexport { DEFAULT_COLORS, type MapColors } from \"./colors\";\n\nconst SHADOW_SM =\n \"0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)\";\nconst SHADOW_MD =\n \"0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)\";\n/** Every marker sits in a 40px box whose bottom centre is the location. */\nconst MARKER_BOX = 40;\nconst LABEL_GAP = 4;\n\n/** What a marker shows. */\nexport type MarkerSpec =\n | {\n kind: \"logo\";\n /** Outer diameter, including the white border. */\n size: number;\n imageUrl: string;\n shadow: \"sm\" | \"md\";\n /** Offset from the location to the logo's centre. Defaults to the 40px box. */\n lift?: number;\n }\n | { kind: \"dot\"; selected: boolean }\n | {\n kind: \"house\";\n /** Icon colour. The fill is the accent. */\n color: string;\n /** Offset from the location to the icon's centre. Defaults to the 40px box. */\n lift?: number;\n };\n\n/** Choose the branch marker: the logo when there is an image, otherwise the themed dot. */\nexport function branchMarker(\n imageUrl: string | undefined,\n focused: boolean,\n): MarkerSpec {\n return imageUrl\n ? {\n kind: \"logo\",\n size: focused ? 40 : 32,\n imageUrl,\n shadow: focused ? \"md\" : \"sm\",\n }\n : { kind: \"dot\", selected: focused };\n}\n\nconst HOUSE_ICON =\n '<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" style=\"display:block\"><path d=\"M15 22a1 1 0 0 1-1-1v-4a1 1 0 0 1 .445-.832l3-2a1 1 0 0 1 1.11 0l3 2A1 1 0 0 1 22 17v4a1 1 0 0 1-1 1z\"/><path d=\"M18 10a8 8 0 0 0-16 0c0 4.993 5.539 10.193 7.399 11.799a1 1 0 0 0 .601.2\"/><path d=\"M18 22v-3\"/><circle cx=\"10\" cy=\"10\" r=\"3\"/></svg>';\n\n/** Style a marker element in place, so selection changes keep the same node. */\nexport function paintMarker(\n element: HTMLElement,\n spec: MarkerSpec,\n colors: MapColors,\n) {\n const { left, top } = element.style;\n try {\n paint(element, spec, colors);\n } finally {\n element.style.left = left;\n element.style.top = top;\n }\n}\n\nfunction paint(element: HTMLElement, spec: MarkerSpec, colors: MapColors) {\n element.dataset.marker = spec.kind;\n if (spec.kind === \"logo\") {\n const lift = spec.lift ?? MARKER_BOX / 2;\n element.style.cssText = `position:absolute;box-sizing:border-box;transform:translate(-50%,calc(-50% - ${lift}px));width:${spec.size}px;height:${spec.size}px;padding:0;border-radius:9999px;overflow:hidden;border:2px solid white;background:white;box-shadow:${spec.shadow === \"md\" ? SHADOW_MD : SHADOW_SM};cursor:pointer;transition:width 150ms cubic-bezier(0.4,0,0.2,1),height 150ms cubic-bezier(0.4,0,0.2,1)`;\n let image = element.querySelector(\"img\");\n if (!image) {\n element.replaceChildren();\n image = document.createElement(\"img\");\n image.alt = \"\";\n image.decoding = \"async\";\n image.draggable = false;\n image.onerror = () => {\n image!.style.visibility = \"hidden\";\n };\n element.appendChild(image);\n }\n image.style.cssText =\n \"display:block;width:100%;height:100%;border-radius:9999px;object-fit:cover\";\n if (image.getAttribute(\"src\") !== spec.imageUrl) {\n image.src = spec.imageUrl;\n }\n return;\n }\n if (spec.kind === \"dot\") {\n element.replaceChildren();\n element.style.cssText = `position:absolute;transform:translate(-50%,-100%);width:var(--branch-map-pin-size,24px);height:var(--branch-map-pin-size,24px);border-radius:50%;border:2px solid var(--branch-map-pin-border,currentColor);background:var(${spec.selected ? \"--branch-map-pin-selected\" : \"--branch-map-pin\"},currentColor);cursor:pointer`;\n return;\n }\n element.style.cssText = `position:absolute;box-sizing:border-box;transform:translate(-50%,calc(-50% - ${spec.lift ?? MARKER_BOX / 2}px));width:32px;height:32px;padding:4px;border-radius:4px;border:2px solid white;background:${colors.accent};color:${spec.color};box-shadow:${SHADOW_MD}`;\n if (!element.firstChild) {\n element.innerHTML = HOUSE_ICON;\n }\n}\n\n/** Style a label above a marker. Branch labels are capitalised like the default storefront's. */\nexport function paintLabel(\n element: HTMLElement,\n options: { offset: string; capitalize: boolean; colors: MapColors },\n) {\n const { left, top } = element.style;\n element.style.cssText = `position:absolute;box-sizing:border-box;transform:translate(-50%,calc(-100% - ${options.offset}));white-space:nowrap;pointer-events:none;padding:4px 8px;border-radius:8px;font-size:12px;line-height:16px;font-weight:500;${options.capitalize ? \"text-transform:capitalize;\" : \"\"}background:var(--branch-map-label-background,white);color:var(--branch-map-label-color,#111);border:2px solid var(--branch-map-label-border,${options.colors.accent});box-shadow:${SHADOW_MD}`;\n element.style.left = left;\n element.style.top = top;\n}\n\n/** Offset from the location to the bottom of a label above a marker. */\nexport function labelOffset(spec: MarkerSpec) {\n return spec.kind === \"dot\"\n ? `var(--branch-map-pin-size,24px) + ${LABEL_GAP}px`\n : `${MARKER_BOX + LABEL_GAP}px`;\n}\n\nexport type MarkerOverlay = google.maps.OverlayView & {\n readonly element: HTMLElement;\n readonly label: HTMLElement;\n /** The clusterer reads markers through these two methods. */\n getPosition(): google.maps.LatLng;\n getVisible(): boolean;\n setLabel(text: string | undefined): void;\n setPosition(position: Coordinates): void;\n};\n\n/**\n * An overlay that places one element (a button or a div) and an optional\n * label at a location. The DOM nodes persist across restyles.\n */\nexport function markerOverlayClass(\n maps: typeof google.maps,\n): new (\n key: string,\n position: Coordinates,\n element: HTMLElement,\n) => MarkerOverlay {\n return class Marker extends maps.OverlayView implements MarkerOverlay {\n // Declared first so two markers compare unequal at once (the clusterer deep-compares them).\n readonly key: string;\n readonly element: HTMLElement;\n readonly label: HTMLElement;\n #position: Coordinates;\n #pane: Element | undefined;\n constructor(key: string, position: Coordinates, element: HTMLElement) {\n super();\n this.key = key;\n this.element = element;\n this.#position = position;\n this.label = document.createElement(\"span\");\n this.label.setAttribute(\"aria-hidden\", \"true\");\n }\n getPosition() {\n return new maps.LatLng(this.#position);\n }\n getVisible() {\n return true;\n }\n setPosition(position: Coordinates) {\n this.#position = position;\n if (this.getMap()) {\n this.draw();\n }\n }\n // An unlabelled marker has no label node in the map.\n setLabel(text: string | undefined) {\n this.label.textContent = text ?? \"\";\n if (!text) {\n this.label.remove();\n } else if (this.#pane && !this.label.isConnected) {\n this.#pane.appendChild(this.label);\n }\n }\n onAdd() {\n this.#pane = this.getPanes()?.overlayMouseTarget;\n this.#pane?.appendChild(this.element);\n if (this.label.textContent) {\n this.#pane?.appendChild(this.label);\n }\n maps.OverlayView.preventMapHitsAndGesturesFrom(this.element);\n }\n draw() {\n const point = this.getProjection()?.fromLatLngToDivPixel(\n new maps.LatLng(this.#position),\n );\n if (point) {\n for (const node of [this.element, this.label]) {\n node.style.left = `${point.x}px`;\n node.style.top = `${point.y}px`;\n }\n }\n }\n onRemove() {\n this.#pane = undefined;\n this.element.remove();\n this.label.remove();\n }\n };\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }