@canopy-iiif/app 1.8.10 → 1.8.12

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canopy-iiif/app",
3
- "version": "1.8.10",
3
+ "version": "1.8.12",
4
4
  "private": false,
5
5
  "license": "MIT",
6
6
  "author": "Mat Jordan <mat@northwestern.edu>",
package/ui/dist/index.mjs CHANGED
@@ -46126,6 +46126,8 @@ var DEFAULT_TILE_LAYERS = [
46126
46126
  maxZoom: 19
46127
46127
  }
46128
46128
  ];
46129
+ var TRANSPARENT_TILE_LAYER_NAME = "Transparent";
46130
+ var TRANSPARENT_TILE_URL = "data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=";
46129
46131
  var CUSTOM_MARKER_SIZE = 40;
46130
46132
  var CUSTOM_MARKER_RADIUS = CUSTOM_MARKER_SIZE / 2;
46131
46133
  var CUSTOM_MARKER_POPUP_OFFSET = -CUSTOM_MARKER_RADIUS + 6;
@@ -46437,6 +46439,24 @@ function extractManifestKeysFromIiif(resource, fallback) {
46437
46439
  }
46438
46440
  return fallbackKey ? [fallbackKey] : [];
46439
46441
  }
46442
+ function createTransparentTileLayers(leaflet) {
46443
+ if (!leaflet) return [];
46444
+ try {
46445
+ return [
46446
+ {
46447
+ name: TRANSPARENT_TILE_LAYER_NAME,
46448
+ layer: leaflet.tileLayer(TRANSPARENT_TILE_URL, {
46449
+ attribution: "",
46450
+ maxZoom: 25,
46451
+ minZoom: 0,
46452
+ opacity: 0
46453
+ })
46454
+ }
46455
+ ];
46456
+ } catch (_) {
46457
+ return [];
46458
+ }
46459
+ }
46440
46460
  function buildTileLayers(inputLayers, leaflet) {
46441
46461
  if (!leaflet) return [];
46442
46462
  const layers = Array.isArray(inputLayers) && inputLayers.length ? inputLayers : DEFAULT_TILE_LAYERS;
@@ -46453,8 +46473,25 @@ function buildTileLayers(inputLayers, leaflet) {
46453
46473
  };
46454
46474
  }).filter(Boolean);
46455
46475
  }
46456
- function buildMarkerIcon(marker, leaflet, colorOverride) {
46476
+ function buildMarkerIcon(marker, leaflet, colorOverride, markerStyle) {
46457
46477
  if (!leaflet) return null;
46478
+ if (markerStyle === "small") {
46479
+ const label = marker.keyLabel || marker.title || marker.summary || "";
46480
+ const safeLabel = label ? escapeHtml(label) : "";
46481
+ const color2 = colorOverride ? escapeHtml(colorOverride) : DEFAULT_ACCENT_HEX;
46482
+ const dotStyle = color2 ? ` style="background-color:${color2}"` : "";
46483
+ const html2 = `<div class="canopy-map__marker-label"><span class="canopy-map__marker-label-dot"${dotStyle}></span>` + (safeLabel ? `<span class="canopy-map__marker-label-text">${safeLabel}</span>` : "") + `</div>`;
46484
+ try {
46485
+ return leaflet.divIcon({
46486
+ className: "canopy-map__marker canopy-map__marker--label",
46487
+ html: html2,
46488
+ iconAnchor: [6, 6],
46489
+ popupAnchor: [0, 0]
46490
+ });
46491
+ } catch (_) {
46492
+ return null;
46493
+ }
46494
+ }
46458
46495
  const hasThumbnail = Boolean(marker && marker.thumbnail);
46459
46496
  const size = CUSTOM_MARKER_SIZE;
46460
46497
  const anchor = CUSTOM_MARKER_RADIUS;
@@ -46476,12 +46513,13 @@ function buildMarkerIcon(marker, leaflet, colorOverride) {
46476
46513
  return null;
46477
46514
  }
46478
46515
  }
46479
- function buildClusterOptions(leaflet) {
46516
+ function buildClusterOptions(leaflet, maxClusterRadius) {
46480
46517
  if (!leaflet) return null;
46481
46518
  const size = CUSTOM_MARKER_SIZE;
46482
46519
  const anchor = CUSTOM_MARKER_RADIUS;
46483
46520
  return {
46484
46521
  chunkedLoading: true,
46522
+ maxClusterRadius: typeof maxClusterRadius === "number" ? maxClusterRadius : void 0,
46485
46523
  iconCreateFunction: (cluster) => {
46486
46524
  const count = cluster && typeof cluster.getChildCount === "function" ? cluster.getChildCount() : 0;
46487
46525
  return leaflet.divIcon({
@@ -46635,6 +46673,17 @@ function normalizeGeoReferences(value) {
46635
46673
  const list = Array.isArray(value) ? value : value ? [value] : [];
46636
46674
  return list.map((entry, index) => normalizeGeoReferenceEntry(entry, index)).filter(Boolean);
46637
46675
  }
46676
+ function normalizeMarkerVariant(value) {
46677
+ if (!value) return null;
46678
+ try {
46679
+ const normalized = String(value).trim().toLowerCase();
46680
+ if (!normalized) return null;
46681
+ if (["small", "label", "inline"].includes(normalized)) return "small";
46682
+ return null;
46683
+ } catch (_) {
46684
+ return null;
46685
+ }
46686
+ }
46638
46687
  function extractNavMarkers(data, allowedKeys) {
46639
46688
  if (!data || !Array.isArray(data.manifests)) return [];
46640
46689
  const keys2 = allowedKeys instanceof Set ? allowedKeys : /* @__PURE__ */ new Set();
@@ -46704,8 +46753,10 @@ function Map3({
46704
46753
  legend = [],
46705
46754
  geoReferences = [],
46706
46755
  tileLayers = [],
46756
+ disableTileLayers = false,
46707
46757
  scrollWheelZoom = false,
46708
46758
  cluster = true,
46759
+ maxClusterRadius = null,
46709
46760
  customPoints = [],
46710
46761
  navDataset = null,
46711
46762
  iiifContent = null,
@@ -46871,28 +46922,38 @@ function Map3({
46871
46922
  if (!value || !label) return null;
46872
46923
  return {
46873
46924
  keyValue: String(value).trim(),
46874
- label: String(label).trim()
46925
+ label: String(label).trim(),
46926
+ markerType: normalizeMarkerVariant(
46927
+ entry.markerType || entry.type || entry.variant
46928
+ )
46875
46929
  };
46876
46930
  }).filter(Boolean);
46877
46931
  }, [resolvedKeyInput]);
46878
46932
  const markerKeyData = React38.useMemo(() => {
46879
- if (!normalizedLegendConfig.length) return { groups: [], colorMap: null };
46880
- const colorMap = createMarkerMap();
46933
+ if (!normalizedLegendConfig.length) return { groups: [], metaMap: null };
46934
+ const metaMap = createMarkerMap();
46881
46935
  const palette = generateLegendColors(normalizedLegendConfig.length);
46882
46936
  const groups = normalizedLegendConfig.map((entry, index) => {
46883
46937
  const color = palette[index] || palette[0] || DEFAULT_ACCENT_HEX;
46884
- colorMap.set(entry.keyValue, color);
46938
+ metaMap.set(entry.keyValue, {
46939
+ color,
46940
+ markerType: entry.markerType || null
46941
+ });
46885
46942
  return {
46886
46943
  keyValue: entry.keyValue,
46887
46944
  label: entry.label,
46888
- color
46945
+ color,
46946
+ markerType: entry.markerType || null
46889
46947
  };
46890
46948
  });
46891
- return { groups, colorMap };
46949
+ return { groups, metaMap };
46892
46950
  }, [normalizedLegendConfig]);
46893
46951
  const markerKeyGroups = markerKeyData.groups;
46894
- const markerKeyColorMap = markerKeyData.colorMap;
46895
- const clusterOptions = React38.useMemo(() => buildClusterOptions(leafletLib), [leafletLib]);
46952
+ const markerKeyMetaMap = markerKeyData.metaMap;
46953
+ const clusterOptions = React38.useMemo(
46954
+ () => buildClusterOptions(leafletLib, typeof maxClusterRadius === "number" ? maxClusterRadius : null),
46955
+ [leafletLib, maxClusterRadius]
46956
+ );
46896
46957
  React38.useEffect(() => {
46897
46958
  if (!containerRef.current || mapRef.current || !leafletLib) return void 0;
46898
46959
  const map = leafletLib.map(containerRef.current, {
@@ -46900,7 +46961,7 @@ function Map3({
46900
46961
  scrollWheelZoom: scrollWheelZoom === true
46901
46962
  });
46902
46963
  mapRef.current = map;
46903
- const layers = buildTileLayers(tileLayers, leafletLib);
46964
+ const layers = disableTileLayers === true ? createTransparentTileLayers(leafletLib) : buildTileLayers(tileLayers, leafletLib);
46904
46965
  const layerControlEntries = {};
46905
46966
  layers.forEach((entry, index) => {
46906
46967
  try {
@@ -46930,7 +46991,7 @@ function Map3({
46930
46991
  mapRef.current = null;
46931
46992
  layerRef.current = null;
46932
46993
  };
46933
- }, [tileLayers, scrollWheelZoom, cluster, clusterOptions, leafletLib]);
46994
+ }, [tileLayers, disableTileLayers, scrollWheelZoom, cluster, clusterOptions, leafletLib]);
46934
46995
  React38.useEffect(() => {
46935
46996
  const map = mapRef.current;
46936
46997
  if (!map || !leafletLib) return void 0;
@@ -46980,14 +47041,39 @@ function Map3({
46980
47041
  }
46981
47042
  const bounds = [];
46982
47043
  const popupCleanups = [];
47044
+ const hoverCleanups = [];
46983
47045
  allMarkers.forEach((marker) => {
46984
47046
  if (!marker || !Number.isFinite(marker.lat) || !Number.isFinite(marker.lng)) return;
46985
47047
  const latlng = leafletLib.latLng(marker.lat, marker.lng);
46986
47048
  bounds.push(latlng);
46987
- const colorOverride = marker.type === "custom" && markerKeyColorMap && marker.keyValue ? markerKeyColorMap.get(String(marker.keyValue).trim()) : null;
46988
- const icon = buildMarkerIcon(marker, leafletLib, colorOverride);
47049
+ const markerMeta = marker.type === "custom" && markerKeyMetaMap && marker.keyValue ? markerKeyMetaMap.get(String(marker.keyValue).trim()) : null;
47050
+ const colorOverride = markerMeta && markerMeta.color ? markerMeta.color : null;
47051
+ const markerStyle = markerMeta && markerMeta.markerType ? markerMeta.markerType : null;
47052
+ const icon = buildMarkerIcon(marker, leafletLib, colorOverride, markerStyle);
46989
47053
  const leafletMarker = leafletLib.marker(latlng, icon ? { icon } : void 0);
46990
- const popup = renderPopup(marker);
47054
+ const shouldShowPopup = markerStyle !== "small";
47055
+ const popup = shouldShowPopup ? renderPopup(marker) : null;
47056
+ const handleMouseOver = () => {
47057
+ const el = leafletMarker && leafletMarker.getElement && leafletMarker.getElement();
47058
+ if (el) el.classList.add("is-hovered");
47059
+ };
47060
+ const handleMouseOut = () => {
47061
+ const el = leafletMarker && leafletMarker.getElement && leafletMarker.getElement();
47062
+ if (el) el.classList.remove("is-hovered");
47063
+ };
47064
+ try {
47065
+ leafletMarker.on("mouseover", handleMouseOver);
47066
+ leafletMarker.on("mouseout", handleMouseOut);
47067
+ hoverCleanups.push(() => {
47068
+ try {
47069
+ leafletMarker.off("mouseover", handleMouseOver);
47070
+ leafletMarker.off("mouseout", handleMouseOut);
47071
+ } catch (_) {
47072
+ }
47073
+ handleMouseOut();
47074
+ });
47075
+ } catch (_) {
47076
+ }
46991
47077
  if (popup && popup.element) {
46992
47078
  try {
46993
47079
  leafletMarker.bindPopup(popup.element);
@@ -47061,6 +47147,12 @@ function Map3({
47061
47147
  } catch (_) {
47062
47148
  }
47063
47149
  });
47150
+ hoverCleanups.forEach((cleanup) => {
47151
+ try {
47152
+ cleanup();
47153
+ } catch (_) {
47154
+ }
47155
+ });
47064
47156
  };
47065
47157
  }, [allMarkers, defaultCenter, defaultZoom, leafletLib]);
47066
47158
  const isLoadingMarkers = iiifTargets.loading || navState.loading;
@@ -47089,7 +47181,10 @@ function Map3({
47089
47181
  return /* @__PURE__ */ React38.createElement(React38.Fragment, null, mapElement, /* @__PURE__ */ React38.createElement("div", { className: "canopy-map__key", "aria-label": "Map key" }, /* @__PURE__ */ React38.createElement("ul", { className: "canopy-map__key-list" }, markerKeyGroups.map((group) => /* @__PURE__ */ React38.createElement("li", { key: group.label, className: "canopy-map__key-item" }, /* @__PURE__ */ React38.createElement(
47090
47182
  "span",
47091
47183
  {
47092
- className: "canopy-map__key-dot",
47184
+ className: [
47185
+ "canopy-map__key-dot",
47186
+ group.markerType === "small" ? "canopy-map__key-dot--small" : null
47187
+ ].filter(Boolean).join(" "),
47093
47188
  "aria-hidden": "true",
47094
47189
  style: { backgroundColor: group.color || void 0 }
47095
47190
  }