@canopy-iiif/app 0.8.4 → 0.8.6
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 +361 -84
- package/lib/build/mdx.js +16 -8
- package/lib/build/pages.js +2 -1
- package/lib/build/styles.js +53 -1
- package/lib/common.js +28 -6
- package/lib/search/search-app.jsx +177 -25
- package/lib/search/search-form-runtime.js +126 -19
- package/lib/search/search.js +130 -18
- package/package.json +4 -1
- package/ui/dist/index.mjs +239 -97
- package/ui/dist/index.mjs.map +4 -4
- package/ui/dist/server.mjs +129 -72
- package/ui/dist/server.mjs.map +4 -4
- package/ui/styles/_variables.scss +1 -0
- package/ui/styles/base/_common.scss +27 -5
- package/ui/styles/base/_heading.scss +2 -4
- package/ui/styles/base/index.scss +1 -0
- package/ui/styles/components/_card.scss +47 -4
- package/ui/styles/components/_sub-navigation.scss +14 -14
- package/ui/styles/components/header/_header.scss +1 -4
- package/ui/styles/components/header/_logo.scss +33 -10
- package/ui/styles/components/search/_filters.scss +5 -7
- package/ui/styles/components/search/_form.scss +55 -17
- package/ui/styles/components/search/_results.scss +13 -15
- package/ui/styles/index.css +250 -72
- package/ui/styles/index.scss +2 -4
- package/ui/tailwind-canopy-iiif-plugin.js +10 -2
- package/ui/tailwind-canopy-iiif-preset.js +21 -19
- package/ui/theme.js +303 -0
- package/ui/styles/variables.emit.scss +0 -72
- package/ui/styles/variables.scss +0 -76
package/ui/dist/index.mjs
CHANGED
|
@@ -104,11 +104,76 @@ function Card({
|
|
|
104
104
|
);
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
+
// ui/src/layout/AnnotationCard.jsx
|
|
108
|
+
import React3, { useMemo } from "react";
|
|
109
|
+
function escapeRegExp(str = "") {
|
|
110
|
+
return String(str).replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
|
|
111
|
+
}
|
|
112
|
+
function buildSnippet({ text = "", query = "", maxLength = 240 }) {
|
|
113
|
+
const clean = String(text || "").replace(/\s+/g, " ").trim();
|
|
114
|
+
if (!clean) return "";
|
|
115
|
+
const term = String(query || "").trim();
|
|
116
|
+
if (!term)
|
|
117
|
+
return clean.length > maxLength ? clean.slice(0, maxLength) + "\u2026" : clean;
|
|
118
|
+
const lower = clean.toLowerCase();
|
|
119
|
+
const termLower = term.toLowerCase();
|
|
120
|
+
const idx = lower.indexOf(termLower);
|
|
121
|
+
if (idx === -1) {
|
|
122
|
+
return clean.length > maxLength ? clean.slice(0, maxLength) + "\u2026" : clean;
|
|
123
|
+
}
|
|
124
|
+
const context = Math.max(0, maxLength / 2);
|
|
125
|
+
const start = Math.max(0, idx - context);
|
|
126
|
+
const end = Math.min(clean.length, idx + term.length + context);
|
|
127
|
+
let snippet = clean.slice(start, end);
|
|
128
|
+
if (start > 0) snippet = "\u2026" + snippet;
|
|
129
|
+
if (end < clean.length) snippet = snippet + "\u2026";
|
|
130
|
+
return snippet;
|
|
131
|
+
}
|
|
132
|
+
function highlightSnippet(snippet, query) {
|
|
133
|
+
if (!query) return snippet;
|
|
134
|
+
const term = String(query).trim();
|
|
135
|
+
if (!term) return snippet;
|
|
136
|
+
const parts = String(snippet).split(
|
|
137
|
+
new RegExp(`(${escapeRegExp(term)})`, "gi")
|
|
138
|
+
);
|
|
139
|
+
const termLower = term.toLowerCase();
|
|
140
|
+
return parts.map(
|
|
141
|
+
(part, idx) => part.toLowerCase() === termLower ? /* @__PURE__ */ React3.createElement("mark", { key: idx }, part) : /* @__PURE__ */ React3.createElement(React3.Fragment, { key: idx }, part)
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
function AnnotationCard({
|
|
145
|
+
href = "#",
|
|
146
|
+
title = "Untitled",
|
|
147
|
+
annotation = "",
|
|
148
|
+
summary = "",
|
|
149
|
+
metadata = [],
|
|
150
|
+
query = ""
|
|
151
|
+
}) {
|
|
152
|
+
const snippetSource = annotation || summary;
|
|
153
|
+
const snippet = useMemo(
|
|
154
|
+
() => buildSnippet({ text: snippetSource, query }),
|
|
155
|
+
[snippetSource, query]
|
|
156
|
+
);
|
|
157
|
+
const highlighted = useMemo(
|
|
158
|
+
() => highlightSnippet(snippet, query),
|
|
159
|
+
[snippet, query]
|
|
160
|
+
);
|
|
161
|
+
const metaList = Array.isArray(metadata) ? metadata.map((m) => String(m || "")).filter(Boolean) : [];
|
|
162
|
+
return /* @__PURE__ */ React3.createElement("a", { href }, /* @__PURE__ */ React3.createElement("article", { className: "canopy-annotation-card" }, /* @__PURE__ */ React3.createElement("h3", null, title), snippet ? /* @__PURE__ */ React3.createElement("p", { className: "mt-2 text-sm leading-relaxed text-slate-700" }, highlighted) : null, metaList.length ? /* @__PURE__ */ React3.createElement("ul", { className: "mt-3 flex flex-wrap gap-2 text-xs text-slate-500" }, metaList.slice(0, 4).map((item, idx) => /* @__PURE__ */ React3.createElement(
|
|
163
|
+
"li",
|
|
164
|
+
{
|
|
165
|
+
key: `${item}-${idx}`,
|
|
166
|
+
className: "rounded-full border border-slate-200 bg-slate-50 px-2 py-1"
|
|
167
|
+
},
|
|
168
|
+
item
|
|
169
|
+
))) : null));
|
|
170
|
+
}
|
|
171
|
+
|
|
107
172
|
// ui/src/layout/Grid.jsx
|
|
108
173
|
import Masonry from "react-masonry-css";
|
|
109
|
-
import
|
|
174
|
+
import React4 from "react";
|
|
110
175
|
function GridItem({ children, className = "", style = {}, ...rest }) {
|
|
111
|
-
return /* @__PURE__ */
|
|
176
|
+
return /* @__PURE__ */ React4.createElement(
|
|
112
177
|
"div",
|
|
113
178
|
{
|
|
114
179
|
className: `canopy-grid-item ${className}`.trim(),
|
|
@@ -136,7 +201,7 @@ function Grid({
|
|
|
136
201
|
640: 2
|
|
137
202
|
};
|
|
138
203
|
const vars = { "--grid-gap": gap, "--grid-padding-y": paddingY };
|
|
139
|
-
return /* @__PURE__ */
|
|
204
|
+
return /* @__PURE__ */ React4.createElement("div", { className: "canopy-grid-wrap" }, /* @__PURE__ */ React4.createElement(
|
|
140
205
|
"style",
|
|
141
206
|
{
|
|
142
207
|
dangerouslySetInnerHTML: {
|
|
@@ -148,7 +213,7 @@ function Grid({
|
|
|
148
213
|
`
|
|
149
214
|
}
|
|
150
215
|
}
|
|
151
|
-
), /* @__PURE__ */
|
|
216
|
+
), /* @__PURE__ */ React4.createElement(
|
|
152
217
|
Masonry,
|
|
153
218
|
{
|
|
154
219
|
breakpointCols: cols,
|
|
@@ -162,7 +227,7 @@ function Grid({
|
|
|
162
227
|
}
|
|
163
228
|
|
|
164
229
|
// ui/src/iiif/Viewer.jsx
|
|
165
|
-
import
|
|
230
|
+
import React5, { useEffect as useEffect2, useState as useState2 } from "react";
|
|
166
231
|
var DEFAULT_VIEWER_OPTIONS = {
|
|
167
232
|
showDownload: false,
|
|
168
233
|
showIIIFBadge: false,
|
|
@@ -218,7 +283,7 @@ var Viewer = (props) => {
|
|
|
218
283
|
} catch (_) {
|
|
219
284
|
json = "{}";
|
|
220
285
|
}
|
|
221
|
-
return /* @__PURE__ */
|
|
286
|
+
return /* @__PURE__ */ React5.createElement("div", { "data-canopy-viewer": "1", className: "not-prose" }, /* @__PURE__ */ React5.createElement(
|
|
222
287
|
"script",
|
|
223
288
|
{
|
|
224
289
|
type: "application/json",
|
|
@@ -226,11 +291,11 @@ var Viewer = (props) => {
|
|
|
226
291
|
}
|
|
227
292
|
));
|
|
228
293
|
}
|
|
229
|
-
return /* @__PURE__ */
|
|
294
|
+
return /* @__PURE__ */ React5.createElement(CloverViewer, { ...props, options: mergedOptions });
|
|
230
295
|
};
|
|
231
296
|
|
|
232
297
|
// ui/src/iiif/Slider.jsx
|
|
233
|
-
import
|
|
298
|
+
import React6, { useEffect as useEffect3, useState as useState3 } from "react";
|
|
234
299
|
var Slider = (props) => {
|
|
235
300
|
const [CloverSlider, setCloverSlider] = useState3(null);
|
|
236
301
|
useEffect3(() => {
|
|
@@ -256,7 +321,7 @@ var Slider = (props) => {
|
|
|
256
321
|
} catch (_) {
|
|
257
322
|
json = "{}";
|
|
258
323
|
}
|
|
259
|
-
return /* @__PURE__ */
|
|
324
|
+
return /* @__PURE__ */ React6.createElement("div", { "data-canopy-slider": "1", className: "not-prose" }, /* @__PURE__ */ React6.createElement(
|
|
260
325
|
"script",
|
|
261
326
|
{
|
|
262
327
|
type: "application/json",
|
|
@@ -264,11 +329,48 @@ var Slider = (props) => {
|
|
|
264
329
|
}
|
|
265
330
|
));
|
|
266
331
|
}
|
|
267
|
-
return /* @__PURE__ */
|
|
332
|
+
return /* @__PURE__ */ React6.createElement(CloverSlider, { ...props });
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
// ui/src/iiif/Scroll.jsx
|
|
336
|
+
import React7, { useEffect as useEffect4, useState as useState4 } from "react";
|
|
337
|
+
var Scroll = (props) => {
|
|
338
|
+
const [CloverScroll, setCloverScroll] = useState4(null);
|
|
339
|
+
useEffect4(() => {
|
|
340
|
+
let mounted = true;
|
|
341
|
+
const canUseDom = typeof window !== "undefined" && typeof document !== "undefined";
|
|
342
|
+
if (canUseDom) {
|
|
343
|
+
import("@samvera/clover-iiif/scroll").then((mod) => {
|
|
344
|
+
if (!mounted) return;
|
|
345
|
+
const Comp = mod && (mod.default || mod.Scroll || mod);
|
|
346
|
+
setCloverScroll(() => Comp);
|
|
347
|
+
}).catch(() => {
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
return () => {
|
|
351
|
+
mounted = false;
|
|
352
|
+
};
|
|
353
|
+
}, []);
|
|
354
|
+
if (!CloverScroll) {
|
|
355
|
+
let json = "{}";
|
|
356
|
+
try {
|
|
357
|
+
json = JSON.stringify(props || {});
|
|
358
|
+
} catch (_) {
|
|
359
|
+
json = "{}";
|
|
360
|
+
}
|
|
361
|
+
return /* @__PURE__ */ React7.createElement("div", { "data-canopy-scroll": "1", className: "not-prose" }, /* @__PURE__ */ React7.createElement(
|
|
362
|
+
"script",
|
|
363
|
+
{
|
|
364
|
+
type: "application/json",
|
|
365
|
+
dangerouslySetInnerHTML: { __html: json }
|
|
366
|
+
}
|
|
367
|
+
));
|
|
368
|
+
}
|
|
369
|
+
return /* @__PURE__ */ React7.createElement(CloverScroll, { ...props });
|
|
268
370
|
};
|
|
269
371
|
|
|
270
372
|
// ui/src/iiif/MdxRelatedItems.jsx
|
|
271
|
-
import
|
|
373
|
+
import React8 from "react";
|
|
272
374
|
function MdxRelatedItems(props) {
|
|
273
375
|
let json = "{}";
|
|
274
376
|
try {
|
|
@@ -276,11 +378,11 @@ function MdxRelatedItems(props) {
|
|
|
276
378
|
} catch (_) {
|
|
277
379
|
json = "{}";
|
|
278
380
|
}
|
|
279
|
-
return /* @__PURE__ */
|
|
381
|
+
return /* @__PURE__ */ React8.createElement("div", { "data-canopy-related-items": "1", className: "not-prose" }, /* @__PURE__ */ React8.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: json } }));
|
|
280
382
|
}
|
|
281
383
|
|
|
282
384
|
// ui/src/search/MdxSearchResults.jsx
|
|
283
|
-
import
|
|
385
|
+
import React9 from "react";
|
|
284
386
|
function MdxSearchResults(props) {
|
|
285
387
|
let json = "{}";
|
|
286
388
|
try {
|
|
@@ -288,11 +390,11 @@ function MdxSearchResults(props) {
|
|
|
288
390
|
} catch (_) {
|
|
289
391
|
json = "{}";
|
|
290
392
|
}
|
|
291
|
-
return /* @__PURE__ */
|
|
393
|
+
return /* @__PURE__ */ React9.createElement("div", { "data-canopy-search-results": "1" }, /* @__PURE__ */ React9.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: json } }));
|
|
292
394
|
}
|
|
293
395
|
|
|
294
396
|
// ui/src/search/SearchSummary.jsx
|
|
295
|
-
import
|
|
397
|
+
import React10 from "react";
|
|
296
398
|
function SearchSummary(props) {
|
|
297
399
|
let json = "{}";
|
|
298
400
|
try {
|
|
@@ -300,11 +402,11 @@ function SearchSummary(props) {
|
|
|
300
402
|
} catch (_) {
|
|
301
403
|
json = "{}";
|
|
302
404
|
}
|
|
303
|
-
return /* @__PURE__ */
|
|
405
|
+
return /* @__PURE__ */ React10.createElement("div", { "data-canopy-search-summary": "1" }, /* @__PURE__ */ React10.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: json } }));
|
|
304
406
|
}
|
|
305
407
|
|
|
306
408
|
// ui/src/search/MdxSearchTabs.jsx
|
|
307
|
-
import
|
|
409
|
+
import React11 from "react";
|
|
308
410
|
function MdxSearchTabs(props) {
|
|
309
411
|
let json = "{}";
|
|
310
412
|
try {
|
|
@@ -312,31 +414,50 @@ function MdxSearchTabs(props) {
|
|
|
312
414
|
} catch (_) {
|
|
313
415
|
json = "{}";
|
|
314
416
|
}
|
|
315
|
-
return /* @__PURE__ */
|
|
417
|
+
return /* @__PURE__ */ React11.createElement("div", { "data-canopy-search-tabs": "1" }, /* @__PURE__ */ React11.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: json } }));
|
|
316
418
|
}
|
|
317
419
|
|
|
318
420
|
// ui/src/search/SearchResults.jsx
|
|
319
|
-
import
|
|
421
|
+
import React12 from "react";
|
|
320
422
|
function SearchResults({
|
|
321
423
|
results = [],
|
|
322
424
|
type = "all",
|
|
323
|
-
layout = "grid"
|
|
425
|
+
layout = "grid",
|
|
426
|
+
query = ""
|
|
324
427
|
}) {
|
|
325
428
|
if (!results.length) {
|
|
326
|
-
return /* @__PURE__ */
|
|
429
|
+
return /* @__PURE__ */ React12.createElement("div", { className: "text-slate-600" }, /* @__PURE__ */ React12.createElement("em", null, "No results"));
|
|
430
|
+
}
|
|
431
|
+
const isAnnotationView = String(type).toLowerCase() === "annotation";
|
|
432
|
+
if (isAnnotationView) {
|
|
433
|
+
return /* @__PURE__ */ React12.createElement("div", { id: "search-results", className: "space-y-4" }, results.map((r, i) => {
|
|
434
|
+
if (!r || !r.annotation) return null;
|
|
435
|
+
return /* @__PURE__ */ React12.createElement(
|
|
436
|
+
AnnotationCard,
|
|
437
|
+
{
|
|
438
|
+
key: r.id || i,
|
|
439
|
+
href: r.href,
|
|
440
|
+
title: r.title || r.href || "Untitled",
|
|
441
|
+
annotation: r.annotation,
|
|
442
|
+
summary: r.summary,
|
|
443
|
+
metadata: Array.isArray(r.metadata) ? r.metadata : [],
|
|
444
|
+
query
|
|
445
|
+
}
|
|
446
|
+
);
|
|
447
|
+
}));
|
|
327
448
|
}
|
|
328
449
|
if (layout === "list") {
|
|
329
|
-
return /* @__PURE__ */
|
|
450
|
+
return /* @__PURE__ */ React12.createElement("ul", { id: "search-results", className: "space-y-3" }, results.map((r, i) => {
|
|
330
451
|
const hasDims = Number.isFinite(Number(r.thumbnailWidth)) && Number(r.thumbnailWidth) > 0 && Number.isFinite(Number(r.thumbnailHeight)) && Number(r.thumbnailHeight) > 0;
|
|
331
452
|
const aspect = hasDims ? Number(r.thumbnailWidth) / Number(r.thumbnailHeight) : void 0;
|
|
332
|
-
return /* @__PURE__ */
|
|
453
|
+
return /* @__PURE__ */ React12.createElement(
|
|
333
454
|
"li",
|
|
334
455
|
{
|
|
335
456
|
key: i,
|
|
336
457
|
className: `search-result ${r.type}`,
|
|
337
458
|
"data-thumbnail-aspect-ratio": aspect
|
|
338
459
|
},
|
|
339
|
-
/* @__PURE__ */
|
|
460
|
+
/* @__PURE__ */ React12.createElement(
|
|
340
461
|
Card,
|
|
341
462
|
{
|
|
342
463
|
href: r.href,
|
|
@@ -350,17 +471,17 @@ function SearchResults({
|
|
|
350
471
|
);
|
|
351
472
|
}));
|
|
352
473
|
}
|
|
353
|
-
return /* @__PURE__ */
|
|
474
|
+
return /* @__PURE__ */ React12.createElement("div", { id: "search-results" }, /* @__PURE__ */ React12.createElement(Grid, null, results.map((r, i) => {
|
|
354
475
|
const hasDims = Number.isFinite(Number(r.thumbnailWidth)) && Number(r.thumbnailWidth) > 0 && Number.isFinite(Number(r.thumbnailHeight)) && Number(r.thumbnailHeight) > 0;
|
|
355
476
|
const aspect = hasDims ? Number(r.thumbnailWidth) / Number(r.thumbnailHeight) : void 0;
|
|
356
|
-
return /* @__PURE__ */
|
|
477
|
+
return /* @__PURE__ */ React12.createElement(
|
|
357
478
|
GridItem,
|
|
358
479
|
{
|
|
359
480
|
key: i,
|
|
360
481
|
className: `search-result ${r.type}`,
|
|
361
482
|
"data-thumbnail-aspect-ratio": aspect
|
|
362
483
|
},
|
|
363
|
-
/* @__PURE__ */
|
|
484
|
+
/* @__PURE__ */ React12.createElement(
|
|
364
485
|
Card,
|
|
365
486
|
{
|
|
366
487
|
href: r.href,
|
|
@@ -376,7 +497,7 @@ function SearchResults({
|
|
|
376
497
|
}
|
|
377
498
|
|
|
378
499
|
// ui/src/search/SearchTabs.jsx
|
|
379
|
-
import
|
|
500
|
+
import React13 from "react";
|
|
380
501
|
function SearchTabs({
|
|
381
502
|
type = "all",
|
|
382
503
|
onTypeChange,
|
|
@@ -391,7 +512,7 @@ function SearchTabs({
|
|
|
391
512
|
const toLabel = (t) => t && t.length ? t.charAt(0).toUpperCase() + t.slice(1) : "";
|
|
392
513
|
const hasFilters = typeof onOpenFilters === "function";
|
|
393
514
|
const filterBadge = activeFilterCount > 0 ? ` (${activeFilterCount})` : "";
|
|
394
|
-
return /* @__PURE__ */
|
|
515
|
+
return /* @__PURE__ */ React13.createElement("div", { className: "canopy-search-tabs-wrapper" }, /* @__PURE__ */ React13.createElement(
|
|
395
516
|
"div",
|
|
396
517
|
{
|
|
397
518
|
role: "tablist",
|
|
@@ -402,7 +523,7 @@ function SearchTabs({
|
|
|
402
523
|
const active = String(type).toLowerCase() === String(t).toLowerCase();
|
|
403
524
|
const cRaw = counts && Object.prototype.hasOwnProperty.call(counts, t) ? counts[t] : void 0;
|
|
404
525
|
const c = Number.isFinite(Number(cRaw)) ? Number(cRaw) : 0;
|
|
405
|
-
return /* @__PURE__ */
|
|
526
|
+
return /* @__PURE__ */ React13.createElement(
|
|
406
527
|
"button",
|
|
407
528
|
{
|
|
408
529
|
key: t,
|
|
@@ -417,7 +538,7 @@ function SearchTabs({
|
|
|
417
538
|
")"
|
|
418
539
|
);
|
|
419
540
|
})
|
|
420
|
-
), hasFilters ? /* @__PURE__ */
|
|
541
|
+
), hasFilters ? /* @__PURE__ */ React13.createElement(
|
|
421
542
|
"button",
|
|
422
543
|
{
|
|
423
544
|
type: "button",
|
|
@@ -425,12 +546,12 @@ function SearchTabs({
|
|
|
425
546
|
"aria-expanded": filtersOpen ? "true" : "false",
|
|
426
547
|
className: "inline-flex items-center gap-2 rounded-md border border-slate-200 bg-white px-3 py-1.5 text-sm font-medium text-slate-700 shadow-sm transition hover:border-brand-200 hover:bg-brand-50 hover:text-brand-700"
|
|
427
548
|
},
|
|
428
|
-
/* @__PURE__ */
|
|
549
|
+
/* @__PURE__ */ React13.createElement("span", null, filtersLabel, filterBadge)
|
|
429
550
|
) : null);
|
|
430
551
|
}
|
|
431
552
|
|
|
432
553
|
// ui/src/search/SearchFiltersDialog.jsx
|
|
433
|
-
import
|
|
554
|
+
import React14 from "react";
|
|
434
555
|
function toArray(input) {
|
|
435
556
|
if (!input) return [];
|
|
436
557
|
if (Array.isArray(input)) return input;
|
|
@@ -469,20 +590,20 @@ function FacetSection({ facet, selected, onToggle }) {
|
|
|
469
590
|
const selectedValues = selected.get(String(slug)) || /* @__PURE__ */ new Set();
|
|
470
591
|
const checkboxId = (valueSlug) => `filter-${slug}-${valueSlug}`;
|
|
471
592
|
const hasSelection = selectedValues.size > 0;
|
|
472
|
-
const [quickQuery, setQuickQuery] =
|
|
593
|
+
const [quickQuery, setQuickQuery] = React14.useState("");
|
|
473
594
|
const hasQuery = quickQuery.trim().length > 0;
|
|
474
|
-
const filteredValues =
|
|
595
|
+
const filteredValues = React14.useMemo(
|
|
475
596
|
() => facetMatches(values, quickQuery),
|
|
476
597
|
[values, quickQuery]
|
|
477
598
|
);
|
|
478
|
-
return /* @__PURE__ */
|
|
599
|
+
return /* @__PURE__ */ React14.createElement(
|
|
479
600
|
"details",
|
|
480
601
|
{
|
|
481
602
|
className: "canopy-search-filters__facet",
|
|
482
603
|
open: hasSelection
|
|
483
604
|
},
|
|
484
|
-
/* @__PURE__ */
|
|
485
|
-
/* @__PURE__ */
|
|
605
|
+
/* @__PURE__ */ React14.createElement("summary", { className: "canopy-search-filters__facet-summary" }, /* @__PURE__ */ React14.createElement("span", null, label), /* @__PURE__ */ React14.createElement("span", { className: "canopy-search-filters__facet-count" }, values.length)),
|
|
606
|
+
/* @__PURE__ */ React14.createElement("div", { className: "canopy-search-filters__facet-content" }, /* @__PURE__ */ React14.createElement("div", { className: "canopy-search-filters__quick" }, /* @__PURE__ */ React14.createElement(
|
|
486
607
|
"input",
|
|
487
608
|
{
|
|
488
609
|
type: "search",
|
|
@@ -492,7 +613,7 @@ function FacetSection({ facet, selected, onToggle }) {
|
|
|
492
613
|
className: "canopy-search-filters__quick-input",
|
|
493
614
|
"aria-label": `Filter ${label} values`
|
|
494
615
|
}
|
|
495
|
-
), quickQuery ? /* @__PURE__ */
|
|
616
|
+
), quickQuery ? /* @__PURE__ */ React14.createElement(
|
|
496
617
|
"button",
|
|
497
618
|
{
|
|
498
619
|
type: "button",
|
|
@@ -500,11 +621,11 @@ function FacetSection({ facet, selected, onToggle }) {
|
|
|
500
621
|
className: "canopy-search-filters__quick-clear"
|
|
501
622
|
},
|
|
502
623
|
"Clear"
|
|
503
|
-
) : null), hasQuery && !filteredValues.length ? /* @__PURE__ */
|
|
624
|
+
) : null), hasQuery && !filteredValues.length ? /* @__PURE__ */ React14.createElement("p", { className: "canopy-search-filters__facet-notice" }, "No matches found.") : null, /* @__PURE__ */ React14.createElement("ul", { className: "canopy-search-filters__facet-list" }, filteredValues.map((entry) => {
|
|
504
625
|
const valueSlug = String(entry.slug || entry.value || "");
|
|
505
626
|
const isChecked = selectedValues.has(valueSlug);
|
|
506
627
|
const inputId = checkboxId(valueSlug);
|
|
507
|
-
return /* @__PURE__ */
|
|
628
|
+
return /* @__PURE__ */ React14.createElement("li", { key: valueSlug, className: "canopy-search-filters__facet-item" }, /* @__PURE__ */ React14.createElement(
|
|
508
629
|
"input",
|
|
509
630
|
{
|
|
510
631
|
id: inputId,
|
|
@@ -516,15 +637,15 @@ function FacetSection({ facet, selected, onToggle }) {
|
|
|
516
637
|
if (onToggle) onToggle(slug, valueSlug, nextChecked);
|
|
517
638
|
}
|
|
518
639
|
}
|
|
519
|
-
), /* @__PURE__ */
|
|
640
|
+
), /* @__PURE__ */ React14.createElement(
|
|
520
641
|
"label",
|
|
521
642
|
{
|
|
522
643
|
htmlFor: inputId,
|
|
523
644
|
className: "canopy-search-filters__facet-label"
|
|
524
645
|
},
|
|
525
|
-
/* @__PURE__ */
|
|
646
|
+
/* @__PURE__ */ React14.createElement("span", null, entry.value, " ", Number.isFinite(entry.doc_count) ? /* @__PURE__ */ React14.createElement("span", { className: "canopy-search-filters__facet-count" }, "(", entry.doc_count, ")") : null)
|
|
526
647
|
));
|
|
527
|
-
}), !filteredValues.length && !hasQuery ? /* @__PURE__ */
|
|
648
|
+
}), !filteredValues.length && !hasQuery ? /* @__PURE__ */ React14.createElement("li", { className: "canopy-search-filters__facet-empty" }, "No values available.") : null))
|
|
528
649
|
);
|
|
529
650
|
}
|
|
530
651
|
function SearchFiltersDialog(props = {}) {
|
|
@@ -544,7 +665,7 @@ function SearchFiltersDialog(props = {}) {
|
|
|
544
665
|
0
|
|
545
666
|
);
|
|
546
667
|
if (!open) return null;
|
|
547
|
-
return /* @__PURE__ */
|
|
668
|
+
return /* @__PURE__ */ React14.createElement(
|
|
548
669
|
"div",
|
|
549
670
|
{
|
|
550
671
|
role: "dialog",
|
|
@@ -555,7 +676,7 @@ function SearchFiltersDialog(props = {}) {
|
|
|
555
676
|
onOpenChange(false);
|
|
556
677
|
}
|
|
557
678
|
},
|
|
558
|
-
/* @__PURE__ */
|
|
679
|
+
/* @__PURE__ */ React14.createElement("div", { className: "canopy-search-filters" }, /* @__PURE__ */ React14.createElement("header", { className: "canopy-search-filters__header" }, /* @__PURE__ */ React14.createElement("div", null, /* @__PURE__ */ React14.createElement("h2", { className: "canopy-search-filters__title" }, title), /* @__PURE__ */ React14.createElement("p", { className: "canopy-search-filters__subtitle" }, subtitle)), /* @__PURE__ */ React14.createElement(
|
|
559
680
|
"button",
|
|
560
681
|
{
|
|
561
682
|
type: "button",
|
|
@@ -563,7 +684,7 @@ function SearchFiltersDialog(props = {}) {
|
|
|
563
684
|
className: "canopy-search-filters__close"
|
|
564
685
|
},
|
|
565
686
|
"Close"
|
|
566
|
-
)), /* @__PURE__ */
|
|
687
|
+
)), /* @__PURE__ */ React14.createElement("div", { className: "canopy-search-filters__body" }, Array.isArray(facets) && facets.length ? /* @__PURE__ */ React14.createElement("div", { className: "canopy-search-filters__facets" }, facets.map((facet) => /* @__PURE__ */ React14.createElement(
|
|
567
688
|
FacetSection,
|
|
568
689
|
{
|
|
569
690
|
key: facet.slug || facet.label,
|
|
@@ -571,7 +692,7 @@ function SearchFiltersDialog(props = {}) {
|
|
|
571
692
|
selected: selectedMap,
|
|
572
693
|
onToggle
|
|
573
694
|
}
|
|
574
|
-
))) : /* @__PURE__ */
|
|
695
|
+
))) : /* @__PURE__ */ React14.createElement("p", { className: "canopy-search-filters__empty" }, "No filters are available for this collection.")), /* @__PURE__ */ React14.createElement("footer", { className: "canopy-search-filters__footer" }, /* @__PURE__ */ React14.createElement("div", null, activeCount ? `${activeCount} filter${activeCount === 1 ? "" : "s"} applied` : "No filters applied"), /* @__PURE__ */ React14.createElement("div", { className: "canopy-search-filters__footer-actions" }, /* @__PURE__ */ React14.createElement(
|
|
575
696
|
"button",
|
|
576
697
|
{
|
|
577
698
|
type: "button",
|
|
@@ -582,7 +703,7 @@ function SearchFiltersDialog(props = {}) {
|
|
|
582
703
|
className: "canopy-search-filters__button canopy-search-filters__button--secondary"
|
|
583
704
|
},
|
|
584
705
|
"Clear all"
|
|
585
|
-
), /* @__PURE__ */
|
|
706
|
+
), /* @__PURE__ */ React14.createElement(
|
|
586
707
|
"button",
|
|
587
708
|
{
|
|
588
709
|
type: "button",
|
|
@@ -595,14 +716,14 @@ function SearchFiltersDialog(props = {}) {
|
|
|
595
716
|
}
|
|
596
717
|
|
|
597
718
|
// ui/src/search-form/MdxSearchFormModal.jsx
|
|
598
|
-
import
|
|
719
|
+
import React18 from "react";
|
|
599
720
|
|
|
600
721
|
// ui/src/Icons.jsx
|
|
601
|
-
import
|
|
602
|
-
var MagnifyingGlassIcon = (props) => /* @__PURE__ */
|
|
722
|
+
import React15 from "react";
|
|
723
|
+
var MagnifyingGlassIcon = (props) => /* @__PURE__ */ React15.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 512 512", ...props }, /* @__PURE__ */ React15.createElement("path", { d: "M456.69 421.39L362.6 327.3a173.81 173.81 0 0034.84-104.58C397.44 126.38 319.06 48 222.72 48S48 126.38 48 222.72s78.38 174.72 174.72 174.72A173.81 173.81 0 00327.3 362.6l94.09 94.09a25 25 0 0035.3-35.3zM97.92 222.72a124.8 124.8 0 11124.8 124.8 124.95 124.95 0 01-124.8-124.8z" }));
|
|
603
724
|
|
|
604
725
|
// ui/src/search/SearchPanelForm.jsx
|
|
605
|
-
import
|
|
726
|
+
import React16 from "react";
|
|
606
727
|
function readBasePath() {
|
|
607
728
|
const normalize = (val) => {
|
|
608
729
|
const raw = typeof val === "string" ? val.trim() : "";
|
|
@@ -661,21 +782,22 @@ function SearchPanelForm(props = {}) {
|
|
|
661
782
|
buttonLabel = "Search",
|
|
662
783
|
label,
|
|
663
784
|
searchPath = "/search",
|
|
664
|
-
inputId: inputIdProp
|
|
785
|
+
inputId: inputIdProp,
|
|
786
|
+
clearLabel = "Clear search"
|
|
665
787
|
} = props || {};
|
|
666
788
|
const text = typeof label === "string" && label.trim() ? label.trim() : buttonLabel;
|
|
667
|
-
const action =
|
|
789
|
+
const action = React16.useMemo(
|
|
668
790
|
() => resolveSearchPath(searchPath),
|
|
669
791
|
[searchPath]
|
|
670
792
|
);
|
|
671
|
-
const autoId = typeof
|
|
672
|
-
const [fallbackId] =
|
|
793
|
+
const autoId = typeof React16.useId === "function" ? React16.useId() : void 0;
|
|
794
|
+
const [fallbackId] = React16.useState(
|
|
673
795
|
() => `canopy-search-form-${Math.random().toString(36).slice(2, 10)}`
|
|
674
796
|
);
|
|
675
797
|
const inputId = inputIdProp || autoId || fallbackId;
|
|
676
|
-
const inputRef =
|
|
677
|
-
const [hasValue, setHasValue] =
|
|
678
|
-
const focusInput =
|
|
798
|
+
const inputRef = React16.useRef(null);
|
|
799
|
+
const [hasValue, setHasValue] = React16.useState(false);
|
|
800
|
+
const focusInput = React16.useCallback(() => {
|
|
679
801
|
const el = inputRef.current;
|
|
680
802
|
if (!el) return;
|
|
681
803
|
if (document.activeElement === el) return;
|
|
@@ -688,30 +810,44 @@ function SearchPanelForm(props = {}) {
|
|
|
688
810
|
}
|
|
689
811
|
}
|
|
690
812
|
}, []);
|
|
691
|
-
const handlePointerDown =
|
|
813
|
+
const handlePointerDown = React16.useCallback(
|
|
692
814
|
(event) => {
|
|
693
815
|
const target = event.target;
|
|
694
816
|
if (target && typeof target.closest === "function") {
|
|
695
817
|
if (target.closest("[data-canopy-search-form-trigger]")) return;
|
|
818
|
+
if (target.closest("[data-canopy-search-form-clear]")) return;
|
|
696
819
|
}
|
|
697
820
|
event.preventDefault();
|
|
698
821
|
focusInput();
|
|
699
822
|
},
|
|
700
823
|
[focusInput]
|
|
701
824
|
);
|
|
702
|
-
|
|
825
|
+
React16.useEffect(() => {
|
|
703
826
|
const el = inputRef.current;
|
|
704
827
|
if (!el) return;
|
|
705
828
|
if (el.value && el.value.trim()) {
|
|
706
829
|
setHasValue(true);
|
|
707
830
|
}
|
|
708
831
|
}, []);
|
|
709
|
-
const handleInputChange =
|
|
832
|
+
const handleInputChange = React16.useCallback((event) => {
|
|
710
833
|
var _a;
|
|
711
|
-
const nextHasValue = Boolean(
|
|
834
|
+
const nextHasValue = Boolean(
|
|
835
|
+
((_a = event == null ? void 0 : event.target) == null ? void 0 : _a.value) && event.target.value.trim()
|
|
836
|
+
);
|
|
712
837
|
setHasValue(nextHasValue);
|
|
713
838
|
}, []);
|
|
714
|
-
|
|
839
|
+
const handleClear = React16.useCallback((event) => {
|
|
840
|
+
}, []);
|
|
841
|
+
const handleClearKey = React16.useCallback(
|
|
842
|
+
(event) => {
|
|
843
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
844
|
+
event.preventDefault();
|
|
845
|
+
handleClear(event);
|
|
846
|
+
}
|
|
847
|
+
},
|
|
848
|
+
[handleClear]
|
|
849
|
+
);
|
|
850
|
+
return /* @__PURE__ */ React16.createElement(
|
|
715
851
|
"form",
|
|
716
852
|
{
|
|
717
853
|
action,
|
|
@@ -721,59 +857,63 @@ function SearchPanelForm(props = {}) {
|
|
|
721
857
|
spellCheck: "false",
|
|
722
858
|
className: "canopy-search-form canopy-search-form-shell",
|
|
723
859
|
onPointerDown: handlePointerDown,
|
|
724
|
-
"data-placeholder": placeholder || "",
|
|
725
860
|
"data-has-value": hasValue ? "1" : "0"
|
|
726
861
|
},
|
|
727
|
-
/* @__PURE__ */
|
|
728
|
-
"
|
|
862
|
+
/* @__PURE__ */ React16.createElement("label", { htmlFor: inputId, className: "canopy-search-form__label" }, /* @__PURE__ */ React16.createElement(MagnifyingGlassIcon, { className: "canopy-search-form__icon" }), /* @__PURE__ */ React16.createElement(
|
|
863
|
+
"input",
|
|
729
864
|
{
|
|
730
|
-
|
|
731
|
-
|
|
865
|
+
id: inputId,
|
|
866
|
+
type: "search",
|
|
867
|
+
name: "q",
|
|
868
|
+
inputMode: "search",
|
|
869
|
+
"data-canopy-search-form-input": true,
|
|
870
|
+
placeholder,
|
|
871
|
+
className: "canopy-search-form__input",
|
|
872
|
+
"aria-label": "Search",
|
|
873
|
+
ref: inputRef,
|
|
874
|
+
onChange: handleInputChange,
|
|
875
|
+
onInput: handleInputChange
|
|
876
|
+
}
|
|
877
|
+
)),
|
|
878
|
+
hasValue ? /* @__PURE__ */ React16.createElement(
|
|
879
|
+
"button",
|
|
880
|
+
{
|
|
881
|
+
type: "button",
|
|
882
|
+
className: "canopy-search-form__clear",
|
|
883
|
+
onClick: handleClear,
|
|
884
|
+
onPointerDown: (event) => event.stopPropagation(),
|
|
885
|
+
onKeyDown: handleClearKey,
|
|
886
|
+
"aria-label": clearLabel,
|
|
887
|
+
"data-canopy-search-form-clear": true
|
|
732
888
|
},
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
{
|
|
737
|
-
id: inputId,
|
|
738
|
-
type: "search",
|
|
739
|
-
name: "q",
|
|
740
|
-
inputMode: "search",
|
|
741
|
-
"data-canopy-search-form-input": true,
|
|
742
|
-
placeholder,
|
|
743
|
-
className: "canopy-search-form__input",
|
|
744
|
-
"aria-label": "Search",
|
|
745
|
-
ref: inputRef,
|
|
746
|
-
onChange: handleInputChange,
|
|
747
|
-
onInput: handleInputChange
|
|
748
|
-
}
|
|
749
|
-
)
|
|
750
|
-
),
|
|
751
|
-
/* @__PURE__ */ React14.createElement(
|
|
889
|
+
"\xD7"
|
|
890
|
+
) : null,
|
|
891
|
+
/* @__PURE__ */ React16.createElement(
|
|
752
892
|
"button",
|
|
753
893
|
{
|
|
754
894
|
type: "submit",
|
|
755
895
|
"data-canopy-search-form-trigger": "submit",
|
|
756
896
|
className: "canopy-search-form__submit"
|
|
757
897
|
},
|
|
758
|
-
/* @__PURE__ */
|
|
759
|
-
/* @__PURE__ */
|
|
898
|
+
/* @__PURE__ */ React16.createElement("span", null, text),
|
|
899
|
+
/* @__PURE__ */ React16.createElement("span", { "aria-hidden": true, className: "canopy-search-form__shortcut" }, /* @__PURE__ */ React16.createElement("span", null, "\u2318"), /* @__PURE__ */ React16.createElement("span", null, "K"))
|
|
760
900
|
)
|
|
761
901
|
);
|
|
762
902
|
}
|
|
763
903
|
|
|
764
904
|
// ui/src/search/SearchPanelTeaserResults.jsx
|
|
765
|
-
import
|
|
905
|
+
import React17 from "react";
|
|
766
906
|
function SearchPanelTeaserResults(props = {}) {
|
|
767
907
|
const { style, className } = props || {};
|
|
768
908
|
const classes = ["canopy-search-teaser", className].filter(Boolean).join(" ");
|
|
769
|
-
return /* @__PURE__ */
|
|
909
|
+
return /* @__PURE__ */ React17.createElement(
|
|
770
910
|
"div",
|
|
771
911
|
{
|
|
772
912
|
"data-canopy-search-form-panel": true,
|
|
773
913
|
className: classes || void 0,
|
|
774
914
|
style
|
|
775
915
|
},
|
|
776
|
-
/* @__PURE__ */
|
|
916
|
+
/* @__PURE__ */ React17.createElement("div", { id: "cplist" })
|
|
777
917
|
);
|
|
778
918
|
}
|
|
779
919
|
|
|
@@ -793,11 +933,11 @@ function MdxSearchFormModal(props = {}) {
|
|
|
793
933
|
const text = typeof label === "string" && label.trim() ? label.trim() : buttonLabel;
|
|
794
934
|
const resolvedSearchPath = resolveSearchPath(searchPath);
|
|
795
935
|
const data = { placeholder, hotkey, maxResults, groupOrder, label: text, searchPath: resolvedSearchPath };
|
|
796
|
-
return /* @__PURE__ */
|
|
936
|
+
return /* @__PURE__ */ React18.createElement("div", { "data-canopy-search-form": true, className: "flex-1 min-w-0" }, /* @__PURE__ */ React18.createElement("div", { className: "relative w-full" }, /* @__PURE__ */ React18.createElement(SearchPanelForm, { placeholder, buttonLabel, label, searchPath: resolvedSearchPath }), /* @__PURE__ */ React18.createElement(SearchPanelTeaserResults, null)), /* @__PURE__ */ React18.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: JSON.stringify(data) } }));
|
|
797
937
|
}
|
|
798
938
|
|
|
799
939
|
// ui/src/search/SearchPanel.jsx
|
|
800
|
-
import
|
|
940
|
+
import React19 from "react";
|
|
801
941
|
function SearchPanel(props = {}) {
|
|
802
942
|
const {
|
|
803
943
|
placeholder = "Search\u2026",
|
|
@@ -814,14 +954,16 @@ function SearchPanel(props = {}) {
|
|
|
814
954
|
const text = typeof label === "string" && label.trim() ? label.trim() : buttonLabel;
|
|
815
955
|
const resolvedSearchPath = resolveSearchPath(searchPath);
|
|
816
956
|
const data = { placeholder, hotkey, maxResults, groupOrder, label: text, searchPath: resolvedSearchPath };
|
|
817
|
-
return /* @__PURE__ */
|
|
957
|
+
return /* @__PURE__ */ React19.createElement("div", { "data-canopy-search-form": true, className: "flex-1 min-w-0" }, /* @__PURE__ */ React19.createElement("div", { className: "relative w-full" }, /* @__PURE__ */ React19.createElement(SearchPanelForm, { placeholder, buttonLabel, label, searchPath: resolvedSearchPath }), /* @__PURE__ */ React19.createElement(SearchPanelTeaserResults, null)), /* @__PURE__ */ React19.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: JSON.stringify(data) } }));
|
|
818
958
|
}
|
|
819
959
|
export {
|
|
960
|
+
AnnotationCard,
|
|
820
961
|
Card,
|
|
821
962
|
Grid,
|
|
822
963
|
GridItem,
|
|
823
964
|
HelloWorld,
|
|
824
965
|
MdxRelatedItems as RelatedItems,
|
|
966
|
+
Scroll,
|
|
825
967
|
SearchFiltersDialog,
|
|
826
968
|
MdxSearchFormModal as SearchFormModal,
|
|
827
969
|
SearchPanel,
|