@canopy-iiif/app 1.12.1 → 1.12.3

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/lib/build/iiif.js CHANGED
@@ -1783,8 +1783,10 @@ async function buildIiifCollectionPages(CONFIG) {
1783
1783
  trimmed.toLowerCase().replace(/[^a-z0-9]+/g, "-") ||
1784
1784
  trimmed;
1785
1785
  if (!valueSlug) continue;
1786
- if (!valueMap.has(valueSlug)) {
1787
- valueMap.set(valueSlug, {value: trimmed, slug: valueSlug});
1786
+ if (valueMap.has(valueSlug)) {
1787
+ valueMap.get(valueSlug).count += 1;
1788
+ } else {
1789
+ valueMap.set(valueSlug, {value: trimmed, slug: valueSlug, count: 1});
1788
1790
  }
1789
1791
  }
1790
1792
  }
@@ -1,4 +1,10 @@
1
- const {fs, path, CONTENT_DIR, rootRelativeHref} = require("../common");
1
+ const {
2
+ fs,
3
+ path,
4
+ CONTENT_DIR,
5
+ rootRelativeHref,
6
+ canonicalizeLocaleCode,
7
+ } = require("../common");
2
8
  const mdx = require("../build/mdx.js");
3
9
  const {getPageContext} = require("../page-context");
4
10
 
@@ -38,6 +44,33 @@ function slugFromRelative(relativePath) {
38
44
  return {slug, segments, isIndex};
39
45
  }
40
46
 
47
+ // Returns true when the segment is a configured (non-default) locale code that
48
+ // prefixes content routes, e.g. "fr" for content/fr/...
49
+ function isLocaleSegment(segment) {
50
+ if (!segment) return false;
51
+ try {
52
+ return !!canonicalizeLocaleCode(segment);
53
+ } catch (_) {
54
+ return false;
55
+ }
56
+ }
57
+
58
+ // Depth (number of leading segments) that identifies a navigation section root
59
+ // for a page. Locale-prefixed routes root at <locale>/<section>; everything else
60
+ // roots at <section>.
61
+ function rootDepthForSegments(segments) {
62
+ if (!Array.isArray(segments) || !segments.length) return 0;
63
+ if (segments.length > 1 && isLocaleSegment(segments[0])) return 2;
64
+ return 1;
65
+ }
66
+
67
+ // Slug of the section root for a page's segments (locale-aware).
68
+ function rootSegmentForSegments(segments) {
69
+ const depth = rootDepthForSegments(segments);
70
+ if (!depth) return "";
71
+ return segments.slice(0, depth).join("/");
72
+ }
73
+
41
74
  function pageSortKey(relativePath) {
42
75
  const normalized = normalizeRelativePath(relativePath).toLowerCase();
43
76
  if (!normalized) return "";
@@ -210,7 +243,11 @@ function getNavigationCache() {
210
243
 
211
244
  const roots = new Map();
212
245
  for (const node of nodes.values()) {
213
- if (node.segments.length === 1) {
246
+ // A section root is either a top-level node (<section>) or, for
247
+ // locale-prefixed content, a <locale>/<section> node. Rooting a
248
+ // locale-prefixed sidebar at the locale segment alone would list every
249
+ // section under that locale instead of just the current one.
250
+ if (rootDepthForSegments(node.segments) === node.segments.length) {
214
251
  roots.set(node.slug, node);
215
252
  }
216
253
  }
@@ -266,7 +303,7 @@ function getPageInfo(relativePath) {
266
303
  slug: page.slug,
267
304
  segments: page.segments.slice(),
268
305
  relativePath: page.relativePath,
269
- rootSegment: page.segments[0] || "",
306
+ rootSegment: rootSegmentForSegments(page.segments),
270
307
  isIndex: page.isIndex,
271
308
  };
272
309
  }
@@ -278,7 +315,7 @@ function getPageInfo(relativePath) {
278
315
  slug,
279
316
  segments,
280
317
  relativePath: normalized,
281
- rootSegment: segments[0] || "",
318
+ rootSegment: rootSegmentForSegments(segments),
282
319
  isIndex: false,
283
320
  };
284
321
  }
@@ -289,10 +326,12 @@ function buildNavigationForFile(relativePath) {
289
326
  const page = cache.pagesByRelative.get(normalized);
290
327
  const fallback = slugFromRelative(normalized);
291
328
  const slug = page ? page.slug : fallback.slug;
292
- const rootSegment = page
293
- ? page.segments[0] || ""
294
- : fallback.segments[0] || "";
295
- if (!slug || !rootSegment || EXCLUDED_ROOTS.has(rootSegment)) {
329
+ const segments = page ? page.segments : fallback.segments;
330
+ const rootSegment = rootSegmentForSegments(segments);
331
+ // The section name is the last segment of the root (the segment after an
332
+ // optional locale prefix), e.g. "works" for both "works" and "fr/works".
333
+ const sectionName = rootSegment.split("/").pop() || "";
334
+ if (!slug || !rootSegment || EXCLUDED_ROOTS.has(sectionName)) {
296
335
  return null;
297
336
  }
298
337
  const rootNode = cache.roots.get(rootSegment);
@@ -312,7 +351,8 @@ function buildNavigationRoots(currentSlug) {
312
351
  const result = {};
313
352
  const normalizedSlug = typeof currentSlug === "string" ? currentSlug : "";
314
353
  for (const [segment, rootNode] of cache.roots.entries()) {
315
- if (!segment || EXCLUDED_ROOTS.has(segment)) continue;
354
+ const sectionName = segment.split("/").pop() || "";
355
+ if (!segment || EXCLUDED_ROOTS.has(sectionName)) continue;
316
356
  const shouldExpand =
317
357
  normalizedSlug &&
318
358
  (normalizedSlug === segment || normalizedSlug.startsWith(segment + "/"));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canopy-iiif/app",
3
- "version": "1.12.1",
3
+ "version": "1.12.3",
4
4
  "private": false,
5
5
  "license": "MIT",
6
6
  "author": "Mat Jordan <mat@northwestern.edu>",
@@ -4260,7 +4260,8 @@ function normalizeEntries(entries) {
4260
4260
  const dedupeKey = `${labelSlug}__${slug}`;
4261
4261
  if (seen.has(dedupeKey)) return;
4262
4262
  seen.add(dedupeKey);
4263
- values.push({ value: trimmed, slug });
4263
+ const count = valueEntry && typeof valueEntry === "object" && typeof valueEntry.count === "number" ? valueEntry.count : void 0;
4264
+ values.push({ value: trimmed, slug, ...count !== void 0 && { count } });
4264
4265
  });
4265
4266
  if (!values.length) return;
4266
4267
  normalized.push({ label, slug: labelSlug, values });
@@ -4374,6 +4375,8 @@ function Index({
4374
4375
  limit = 15,
4375
4376
  expandLabel = "Show more",
4376
4377
  collapseLabel = "Show less",
4378
+ sortOrder = "alphabetically",
4379
+ showCount = false,
4377
4380
  className = "",
4378
4381
  ...rest
4379
4382
  }) {
@@ -4390,7 +4393,9 @@ function Index({
4390
4393
  values: entry.values,
4391
4394
  limit,
4392
4395
  expandLabel,
4393
- collapseLabel
4396
+ collapseLabel,
4397
+ sortOrder,
4398
+ showCount
4394
4399
  }
4395
4400
  )), /* @__PURE__ */ React34.createElement(
4396
4401
  "script",
@@ -4400,18 +4405,34 @@ function Index({
4400
4405
  }
4401
4406
  ));
4402
4407
  }
4403
- function IndexGroup({ label, labelSlug, values, limit, expandLabel, collapseLabel }) {
4408
+ function sortValues(values, sortOrder) {
4409
+ const copy = [...values];
4410
+ if (sortOrder === "count") {
4411
+ copy.sort((a, b) => {
4412
+ var _a, _b;
4413
+ return ((_a = b.count) != null ? _a : 0) - ((_b = a.count) != null ? _b : 0);
4414
+ });
4415
+ } else {
4416
+ copy.sort(
4417
+ (a, b) => String(a.value).localeCompare(String(b.value), void 0, { sensitivity: "base" })
4418
+ );
4419
+ }
4420
+ return copy;
4421
+ }
4422
+ function IndexGroup({ label, labelSlug, values, limit, expandLabel, collapseLabel, sortOrder, showCount }) {
4404
4423
  const parsedLimit = Number(limit);
4424
+ const showAll = Number.isFinite(parsedLimit) && Math.floor(parsedLimit) === 0;
4405
4425
  const clampedLimit = Number.isFinite(parsedLimit) ? Math.max(1, Math.floor(parsedLimit)) : 15;
4406
- const maxVisible = clampedLimit;
4407
- const hasOverflow = values.length > maxVisible;
4408
- const visibleValues = hasOverflow ? values.slice(0, maxVisible) : values;
4409
- const hiddenValues = hasOverflow ? values.slice(maxVisible) : [];
4426
+ const sorted = sortValues(values, sortOrder);
4427
+ const hasOverflow = !showAll && sorted.length > clampedLimit;
4428
+ const visibleValues = hasOverflow ? sorted.slice(0, clampedLimit) : sorted;
4429
+ const hiddenValues = hasOverflow ? sorted.slice(clampedLimit) : [];
4410
4430
  const labelCollapsed = typeof expandLabel === "string" && expandLabel.trim() ? expandLabel.trim() : "Show more";
4411
4431
  const labelExpanded = typeof collapseLabel === "string" && collapseLabel.trim() ? collapseLabel.trim() : "Show less";
4412
4432
  return /* @__PURE__ */ React34.createElement("dl", { className: "canopy-index__group", "data-canopy-index-group": "", "data-expanded": "0" }, /* @__PURE__ */ React34.createElement("dt", null, label), /* @__PURE__ */ React34.createElement("div", { className: "canopy-index__values" }, visibleValues.map((value) => {
4413
4433
  const href = buildSearchHref(labelSlug, value.slug);
4414
4434
  const key = `${labelSlug || label}-${value.slug || value.value}`;
4435
+ const displayText = showCount && value.count != null ? `${value.value} (${value.count})` : value.value;
4415
4436
  return /* @__PURE__ */ React34.createElement("dd", { key }, href ? /* @__PURE__ */ React34.createElement(
4416
4437
  "a",
4417
4438
  {
@@ -4420,11 +4441,12 @@ function IndexGroup({ label, labelSlug, values, limit, expandLabel, collapseLabe
4420
4441
  "data-index-label": labelSlug,
4421
4442
  "data-index-value": value.slug
4422
4443
  },
4423
- value.value
4424
- ) : value.value);
4444
+ displayText
4445
+ ) : displayText);
4425
4446
  }), hiddenValues.map((value) => {
4426
4447
  const href = buildSearchHref(labelSlug, value.slug);
4427
4448
  const key = `${labelSlug || label}-hidden-${value.slug || value.value}`;
4449
+ const displayText = showCount && value.count != null ? `${value.value} (${value.count})` : value.value;
4428
4450
  return /* @__PURE__ */ React34.createElement("dd", { key, "data-canopy-index-hidden": "", hidden: true }, href ? /* @__PURE__ */ React34.createElement(
4429
4451
  "a",
4430
4452
  {
@@ -4433,8 +4455,8 @@ function IndexGroup({ label, labelSlug, values, limit, expandLabel, collapseLabe
4433
4455
  "data-index-label": labelSlug,
4434
4456
  "data-index-value": value.slug
4435
4457
  },
4436
- value.value
4437
- ) : value.value);
4458
+ displayText
4459
+ ) : displayText);
4438
4460
  })), hasOverflow && /* @__PURE__ */ React34.createElement("div", { className: "canopy-index__more-wrapper" }, /* @__PURE__ */ React34.createElement(
4439
4461
  "button",
4440
4462
  {