@canopy-iiif/app 0.8.3 → 0.8.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.
- package/lib/build/build.js +2 -0
- package/lib/build/dev.js +38 -22
- package/lib/build/iiif.js +359 -83
- package/lib/build/mdx.js +12 -2
- package/lib/build/pages.js +15 -1
- package/lib/build/styles.js +53 -1
- package/lib/common.js +28 -6
- package/lib/components/navigation.js +308 -0
- package/lib/page-context.js +14 -0
- 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 +204 -101
- package/ui/dist/index.mjs.map +4 -4
- package/ui/dist/server.mjs +167 -59
- 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 +76 -0
- package/ui/styles/components/header/_header.scss +1 -4
- package/ui/styles/components/header/_logo.scss +33 -10
- package/ui/styles/components/index.scss +1 -0
- 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 +49 -14
- package/ui/styles/index.css +344 -56
- 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(),
|
|
@@ -120,7 +185,7 @@ function GridItem({ children, className = "", style = {}, ...rest }) {
|
|
|
120
185
|
}
|
|
121
186
|
function Grid({
|
|
122
187
|
breakpointCols,
|
|
123
|
-
gap = "
|
|
188
|
+
gap = "1.618rem",
|
|
124
189
|
paddingY = "0",
|
|
125
190
|
className = "",
|
|
126
191
|
style = {},
|
|
@@ -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,11 @@ var Slider = (props) => {
|
|
|
264
329
|
}
|
|
265
330
|
));
|
|
266
331
|
}
|
|
267
|
-
return /* @__PURE__ */
|
|
332
|
+
return /* @__PURE__ */ React6.createElement(CloverSlider, { ...props });
|
|
268
333
|
};
|
|
269
334
|
|
|
270
335
|
// ui/src/iiif/MdxRelatedItems.jsx
|
|
271
|
-
import
|
|
336
|
+
import React7 from "react";
|
|
272
337
|
function MdxRelatedItems(props) {
|
|
273
338
|
let json = "{}";
|
|
274
339
|
try {
|
|
@@ -276,11 +341,11 @@ function MdxRelatedItems(props) {
|
|
|
276
341
|
} catch (_) {
|
|
277
342
|
json = "{}";
|
|
278
343
|
}
|
|
279
|
-
return /* @__PURE__ */
|
|
344
|
+
return /* @__PURE__ */ React7.createElement("div", { "data-canopy-related-items": "1", className: "not-prose" }, /* @__PURE__ */ React7.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: json } }));
|
|
280
345
|
}
|
|
281
346
|
|
|
282
347
|
// ui/src/search/MdxSearchResults.jsx
|
|
283
|
-
import
|
|
348
|
+
import React8 from "react";
|
|
284
349
|
function MdxSearchResults(props) {
|
|
285
350
|
let json = "{}";
|
|
286
351
|
try {
|
|
@@ -288,11 +353,11 @@ function MdxSearchResults(props) {
|
|
|
288
353
|
} catch (_) {
|
|
289
354
|
json = "{}";
|
|
290
355
|
}
|
|
291
|
-
return /* @__PURE__ */
|
|
356
|
+
return /* @__PURE__ */ React8.createElement("div", { "data-canopy-search-results": "1" }, /* @__PURE__ */ React8.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: json } }));
|
|
292
357
|
}
|
|
293
358
|
|
|
294
359
|
// ui/src/search/SearchSummary.jsx
|
|
295
|
-
import
|
|
360
|
+
import React9 from "react";
|
|
296
361
|
function SearchSummary(props) {
|
|
297
362
|
let json = "{}";
|
|
298
363
|
try {
|
|
@@ -300,11 +365,11 @@ function SearchSummary(props) {
|
|
|
300
365
|
} catch (_) {
|
|
301
366
|
json = "{}";
|
|
302
367
|
}
|
|
303
|
-
return /* @__PURE__ */
|
|
368
|
+
return /* @__PURE__ */ React9.createElement("div", { "data-canopy-search-summary": "1" }, /* @__PURE__ */ React9.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: json } }));
|
|
304
369
|
}
|
|
305
370
|
|
|
306
371
|
// ui/src/search/MdxSearchTabs.jsx
|
|
307
|
-
import
|
|
372
|
+
import React10 from "react";
|
|
308
373
|
function MdxSearchTabs(props) {
|
|
309
374
|
let json = "{}";
|
|
310
375
|
try {
|
|
@@ -312,31 +377,50 @@ function MdxSearchTabs(props) {
|
|
|
312
377
|
} catch (_) {
|
|
313
378
|
json = "{}";
|
|
314
379
|
}
|
|
315
|
-
return /* @__PURE__ */
|
|
380
|
+
return /* @__PURE__ */ React10.createElement("div", { "data-canopy-search-tabs": "1" }, /* @__PURE__ */ React10.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: json } }));
|
|
316
381
|
}
|
|
317
382
|
|
|
318
383
|
// ui/src/search/SearchResults.jsx
|
|
319
|
-
import
|
|
384
|
+
import React11 from "react";
|
|
320
385
|
function SearchResults({
|
|
321
386
|
results = [],
|
|
322
387
|
type = "all",
|
|
323
|
-
layout = "grid"
|
|
388
|
+
layout = "grid",
|
|
389
|
+
query = ""
|
|
324
390
|
}) {
|
|
325
391
|
if (!results.length) {
|
|
326
|
-
return /* @__PURE__ */
|
|
392
|
+
return /* @__PURE__ */ React11.createElement("div", { className: "text-slate-600" }, /* @__PURE__ */ React11.createElement("em", null, "No results"));
|
|
393
|
+
}
|
|
394
|
+
const isAnnotationView = String(type).toLowerCase() === "annotation";
|
|
395
|
+
if (isAnnotationView) {
|
|
396
|
+
return /* @__PURE__ */ React11.createElement("div", { id: "search-results", className: "space-y-4" }, results.map((r, i) => {
|
|
397
|
+
if (!r || !r.annotation) return null;
|
|
398
|
+
return /* @__PURE__ */ React11.createElement(
|
|
399
|
+
AnnotationCard,
|
|
400
|
+
{
|
|
401
|
+
key: r.id || i,
|
|
402
|
+
href: r.href,
|
|
403
|
+
title: r.title || r.href || "Untitled",
|
|
404
|
+
annotation: r.annotation,
|
|
405
|
+
summary: r.summary,
|
|
406
|
+
metadata: Array.isArray(r.metadata) ? r.metadata : [],
|
|
407
|
+
query
|
|
408
|
+
}
|
|
409
|
+
);
|
|
410
|
+
}));
|
|
327
411
|
}
|
|
328
412
|
if (layout === "list") {
|
|
329
|
-
return /* @__PURE__ */
|
|
413
|
+
return /* @__PURE__ */ React11.createElement("ul", { id: "search-results", className: "space-y-3" }, results.map((r, i) => {
|
|
330
414
|
const hasDims = Number.isFinite(Number(r.thumbnailWidth)) && Number(r.thumbnailWidth) > 0 && Number.isFinite(Number(r.thumbnailHeight)) && Number(r.thumbnailHeight) > 0;
|
|
331
415
|
const aspect = hasDims ? Number(r.thumbnailWidth) / Number(r.thumbnailHeight) : void 0;
|
|
332
|
-
return /* @__PURE__ */
|
|
416
|
+
return /* @__PURE__ */ React11.createElement(
|
|
333
417
|
"li",
|
|
334
418
|
{
|
|
335
419
|
key: i,
|
|
336
420
|
className: `search-result ${r.type}`,
|
|
337
421
|
"data-thumbnail-aspect-ratio": aspect
|
|
338
422
|
},
|
|
339
|
-
/* @__PURE__ */
|
|
423
|
+
/* @__PURE__ */ React11.createElement(
|
|
340
424
|
Card,
|
|
341
425
|
{
|
|
342
426
|
href: r.href,
|
|
@@ -350,17 +434,17 @@ function SearchResults({
|
|
|
350
434
|
);
|
|
351
435
|
}));
|
|
352
436
|
}
|
|
353
|
-
return /* @__PURE__ */
|
|
437
|
+
return /* @__PURE__ */ React11.createElement("div", { id: "search-results" }, /* @__PURE__ */ React11.createElement(Grid, null, results.map((r, i) => {
|
|
354
438
|
const hasDims = Number.isFinite(Number(r.thumbnailWidth)) && Number(r.thumbnailWidth) > 0 && Number.isFinite(Number(r.thumbnailHeight)) && Number(r.thumbnailHeight) > 0;
|
|
355
439
|
const aspect = hasDims ? Number(r.thumbnailWidth) / Number(r.thumbnailHeight) : void 0;
|
|
356
|
-
return /* @__PURE__ */
|
|
440
|
+
return /* @__PURE__ */ React11.createElement(
|
|
357
441
|
GridItem,
|
|
358
442
|
{
|
|
359
443
|
key: i,
|
|
360
444
|
className: `search-result ${r.type}`,
|
|
361
445
|
"data-thumbnail-aspect-ratio": aspect
|
|
362
446
|
},
|
|
363
|
-
/* @__PURE__ */
|
|
447
|
+
/* @__PURE__ */ React11.createElement(
|
|
364
448
|
Card,
|
|
365
449
|
{
|
|
366
450
|
href: r.href,
|
|
@@ -376,7 +460,7 @@ function SearchResults({
|
|
|
376
460
|
}
|
|
377
461
|
|
|
378
462
|
// ui/src/search/SearchTabs.jsx
|
|
379
|
-
import
|
|
463
|
+
import React12 from "react";
|
|
380
464
|
function SearchTabs({
|
|
381
465
|
type = "all",
|
|
382
466
|
onTypeChange,
|
|
@@ -391,26 +475,25 @@ function SearchTabs({
|
|
|
391
475
|
const toLabel = (t) => t && t.length ? t.charAt(0).toUpperCase() + t.slice(1) : "";
|
|
392
476
|
const hasFilters = typeof onOpenFilters === "function";
|
|
393
477
|
const filterBadge = activeFilterCount > 0 ? ` (${activeFilterCount})` : "";
|
|
394
|
-
return /* @__PURE__ */
|
|
478
|
+
return /* @__PURE__ */ React12.createElement("div", { className: "canopy-search-tabs-wrapper" }, /* @__PURE__ */ React12.createElement(
|
|
395
479
|
"div",
|
|
396
480
|
{
|
|
397
481
|
role: "tablist",
|
|
398
482
|
"aria-label": "Search types",
|
|
399
|
-
className: "
|
|
483
|
+
className: "canopy-search-tabs"
|
|
400
484
|
},
|
|
401
485
|
orderedTypes.map((t) => {
|
|
402
486
|
const active = String(type).toLowerCase() === String(t).toLowerCase();
|
|
403
487
|
const cRaw = counts && Object.prototype.hasOwnProperty.call(counts, t) ? counts[t] : void 0;
|
|
404
488
|
const c = Number.isFinite(Number(cRaw)) ? Number(cRaw) : 0;
|
|
405
|
-
return /* @__PURE__ */
|
|
489
|
+
return /* @__PURE__ */ React12.createElement(
|
|
406
490
|
"button",
|
|
407
491
|
{
|
|
408
492
|
key: t,
|
|
409
493
|
role: "tab",
|
|
410
494
|
"aria-selected": active,
|
|
411
495
|
type: "button",
|
|
412
|
-
onClick: () => onTypeChange && onTypeChange(t)
|
|
413
|
-
className: "px-3 py-2 text-sm rounded-t-md border-b-2 -mb-px transition-colors " + (active ? "border-brand-600 text-brand-700" : "border-transparent text-slate-600 hover:text-slate-900 hover:border-slate-300")
|
|
496
|
+
onClick: () => onTypeChange && onTypeChange(t)
|
|
414
497
|
},
|
|
415
498
|
toLabel(t),
|
|
416
499
|
" (",
|
|
@@ -418,7 +501,7 @@ function SearchTabs({
|
|
|
418
501
|
")"
|
|
419
502
|
);
|
|
420
503
|
})
|
|
421
|
-
), hasFilters ? /* @__PURE__ */
|
|
504
|
+
), hasFilters ? /* @__PURE__ */ React12.createElement(
|
|
422
505
|
"button",
|
|
423
506
|
{
|
|
424
507
|
type: "button",
|
|
@@ -426,12 +509,12 @@ function SearchTabs({
|
|
|
426
509
|
"aria-expanded": filtersOpen ? "true" : "false",
|
|
427
510
|
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"
|
|
428
511
|
},
|
|
429
|
-
/* @__PURE__ */
|
|
512
|
+
/* @__PURE__ */ React12.createElement("span", null, filtersLabel, filterBadge)
|
|
430
513
|
) : null);
|
|
431
514
|
}
|
|
432
515
|
|
|
433
516
|
// ui/src/search/SearchFiltersDialog.jsx
|
|
434
|
-
import
|
|
517
|
+
import React13 from "react";
|
|
435
518
|
function toArray(input) {
|
|
436
519
|
if (!input) return [];
|
|
437
520
|
if (Array.isArray(input)) return input;
|
|
@@ -470,20 +553,20 @@ function FacetSection({ facet, selected, onToggle }) {
|
|
|
470
553
|
const selectedValues = selected.get(String(slug)) || /* @__PURE__ */ new Set();
|
|
471
554
|
const checkboxId = (valueSlug) => `filter-${slug}-${valueSlug}`;
|
|
472
555
|
const hasSelection = selectedValues.size > 0;
|
|
473
|
-
const [quickQuery, setQuickQuery] =
|
|
556
|
+
const [quickQuery, setQuickQuery] = React13.useState("");
|
|
474
557
|
const hasQuery = quickQuery.trim().length > 0;
|
|
475
|
-
const filteredValues =
|
|
558
|
+
const filteredValues = React13.useMemo(
|
|
476
559
|
() => facetMatches(values, quickQuery),
|
|
477
560
|
[values, quickQuery]
|
|
478
561
|
);
|
|
479
|
-
return /* @__PURE__ */
|
|
562
|
+
return /* @__PURE__ */ React13.createElement(
|
|
480
563
|
"details",
|
|
481
564
|
{
|
|
482
565
|
className: "canopy-search-filters__facet",
|
|
483
566
|
open: hasSelection
|
|
484
567
|
},
|
|
485
|
-
/* @__PURE__ */
|
|
486
|
-
/* @__PURE__ */
|
|
568
|
+
/* @__PURE__ */ React13.createElement("summary", { className: "canopy-search-filters__facet-summary" }, /* @__PURE__ */ React13.createElement("span", null, label), /* @__PURE__ */ React13.createElement("span", { className: "canopy-search-filters__facet-count" }, values.length)),
|
|
569
|
+
/* @__PURE__ */ React13.createElement("div", { className: "canopy-search-filters__facet-content" }, /* @__PURE__ */ React13.createElement("div", { className: "canopy-search-filters__quick" }, /* @__PURE__ */ React13.createElement(
|
|
487
570
|
"input",
|
|
488
571
|
{
|
|
489
572
|
type: "search",
|
|
@@ -493,7 +576,7 @@ function FacetSection({ facet, selected, onToggle }) {
|
|
|
493
576
|
className: "canopy-search-filters__quick-input",
|
|
494
577
|
"aria-label": `Filter ${label} values`
|
|
495
578
|
}
|
|
496
|
-
), quickQuery ? /* @__PURE__ */
|
|
579
|
+
), quickQuery ? /* @__PURE__ */ React13.createElement(
|
|
497
580
|
"button",
|
|
498
581
|
{
|
|
499
582
|
type: "button",
|
|
@@ -501,11 +584,11 @@ function FacetSection({ facet, selected, onToggle }) {
|
|
|
501
584
|
className: "canopy-search-filters__quick-clear"
|
|
502
585
|
},
|
|
503
586
|
"Clear"
|
|
504
|
-
) : null), hasQuery && !filteredValues.length ? /* @__PURE__ */
|
|
587
|
+
) : null), hasQuery && !filteredValues.length ? /* @__PURE__ */ React13.createElement("p", { className: "canopy-search-filters__facet-notice" }, "No matches found.") : null, /* @__PURE__ */ React13.createElement("ul", { className: "canopy-search-filters__facet-list" }, filteredValues.map((entry) => {
|
|
505
588
|
const valueSlug = String(entry.slug || entry.value || "");
|
|
506
589
|
const isChecked = selectedValues.has(valueSlug);
|
|
507
590
|
const inputId = checkboxId(valueSlug);
|
|
508
|
-
return /* @__PURE__ */
|
|
591
|
+
return /* @__PURE__ */ React13.createElement("li", { key: valueSlug, className: "canopy-search-filters__facet-item" }, /* @__PURE__ */ React13.createElement(
|
|
509
592
|
"input",
|
|
510
593
|
{
|
|
511
594
|
id: inputId,
|
|
@@ -517,15 +600,15 @@ function FacetSection({ facet, selected, onToggle }) {
|
|
|
517
600
|
if (onToggle) onToggle(slug, valueSlug, nextChecked);
|
|
518
601
|
}
|
|
519
602
|
}
|
|
520
|
-
), /* @__PURE__ */
|
|
603
|
+
), /* @__PURE__ */ React13.createElement(
|
|
521
604
|
"label",
|
|
522
605
|
{
|
|
523
606
|
htmlFor: inputId,
|
|
524
607
|
className: "canopy-search-filters__facet-label"
|
|
525
608
|
},
|
|
526
|
-
/* @__PURE__ */
|
|
609
|
+
/* @__PURE__ */ React13.createElement("span", null, entry.value, " ", Number.isFinite(entry.doc_count) ? /* @__PURE__ */ React13.createElement("span", { className: "canopy-search-filters__facet-count" }, "(", entry.doc_count, ")") : null)
|
|
527
610
|
));
|
|
528
|
-
}), !filteredValues.length && !hasQuery ? /* @__PURE__ */
|
|
611
|
+
}), !filteredValues.length && !hasQuery ? /* @__PURE__ */ React13.createElement("li", { className: "canopy-search-filters__facet-empty" }, "No values available.") : null))
|
|
529
612
|
);
|
|
530
613
|
}
|
|
531
614
|
function SearchFiltersDialog(props = {}) {
|
|
@@ -545,7 +628,7 @@ function SearchFiltersDialog(props = {}) {
|
|
|
545
628
|
0
|
|
546
629
|
);
|
|
547
630
|
if (!open) return null;
|
|
548
|
-
return /* @__PURE__ */
|
|
631
|
+
return /* @__PURE__ */ React13.createElement(
|
|
549
632
|
"div",
|
|
550
633
|
{
|
|
551
634
|
role: "dialog",
|
|
@@ -556,7 +639,7 @@ function SearchFiltersDialog(props = {}) {
|
|
|
556
639
|
onOpenChange(false);
|
|
557
640
|
}
|
|
558
641
|
},
|
|
559
|
-
/* @__PURE__ */
|
|
642
|
+
/* @__PURE__ */ React13.createElement("div", { className: "canopy-search-filters" }, /* @__PURE__ */ React13.createElement("header", { className: "canopy-search-filters__header" }, /* @__PURE__ */ React13.createElement("div", null, /* @__PURE__ */ React13.createElement("h2", { className: "canopy-search-filters__title" }, title), /* @__PURE__ */ React13.createElement("p", { className: "canopy-search-filters__subtitle" }, subtitle)), /* @__PURE__ */ React13.createElement(
|
|
560
643
|
"button",
|
|
561
644
|
{
|
|
562
645
|
type: "button",
|
|
@@ -564,7 +647,7 @@ function SearchFiltersDialog(props = {}) {
|
|
|
564
647
|
className: "canopy-search-filters__close"
|
|
565
648
|
},
|
|
566
649
|
"Close"
|
|
567
|
-
)), /* @__PURE__ */
|
|
650
|
+
)), /* @__PURE__ */ React13.createElement("div", { className: "canopy-search-filters__body" }, Array.isArray(facets) && facets.length ? /* @__PURE__ */ React13.createElement("div", { className: "canopy-search-filters__facets" }, facets.map((facet) => /* @__PURE__ */ React13.createElement(
|
|
568
651
|
FacetSection,
|
|
569
652
|
{
|
|
570
653
|
key: facet.slug || facet.label,
|
|
@@ -572,7 +655,7 @@ function SearchFiltersDialog(props = {}) {
|
|
|
572
655
|
selected: selectedMap,
|
|
573
656
|
onToggle
|
|
574
657
|
}
|
|
575
|
-
))) : /* @__PURE__ */
|
|
658
|
+
))) : /* @__PURE__ */ React13.createElement("p", { className: "canopy-search-filters__empty" }, "No filters are available for this collection.")), /* @__PURE__ */ React13.createElement("footer", { className: "canopy-search-filters__footer" }, /* @__PURE__ */ React13.createElement("div", null, activeCount ? `${activeCount} filter${activeCount === 1 ? "" : "s"} applied` : "No filters applied"), /* @__PURE__ */ React13.createElement("div", { className: "canopy-search-filters__footer-actions" }, /* @__PURE__ */ React13.createElement(
|
|
576
659
|
"button",
|
|
577
660
|
{
|
|
578
661
|
type: "button",
|
|
@@ -583,7 +666,7 @@ function SearchFiltersDialog(props = {}) {
|
|
|
583
666
|
className: "canopy-search-filters__button canopy-search-filters__button--secondary"
|
|
584
667
|
},
|
|
585
668
|
"Clear all"
|
|
586
|
-
), /* @__PURE__ */
|
|
669
|
+
), /* @__PURE__ */ React13.createElement(
|
|
587
670
|
"button",
|
|
588
671
|
{
|
|
589
672
|
type: "button",
|
|
@@ -596,14 +679,14 @@ function SearchFiltersDialog(props = {}) {
|
|
|
596
679
|
}
|
|
597
680
|
|
|
598
681
|
// ui/src/search-form/MdxSearchFormModal.jsx
|
|
599
|
-
import
|
|
682
|
+
import React17 from "react";
|
|
600
683
|
|
|
601
684
|
// ui/src/Icons.jsx
|
|
602
|
-
import
|
|
603
|
-
var MagnifyingGlassIcon = (props) => /* @__PURE__ */
|
|
685
|
+
import React14 from "react";
|
|
686
|
+
var MagnifyingGlassIcon = (props) => /* @__PURE__ */ React14.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 512 512", ...props }, /* @__PURE__ */ React14.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" }));
|
|
604
687
|
|
|
605
688
|
// ui/src/search/SearchPanelForm.jsx
|
|
606
|
-
import
|
|
689
|
+
import React15 from "react";
|
|
607
690
|
function readBasePath() {
|
|
608
691
|
const normalize = (val) => {
|
|
609
692
|
const raw = typeof val === "string" ? val.trim() : "";
|
|
@@ -662,21 +745,22 @@ function SearchPanelForm(props = {}) {
|
|
|
662
745
|
buttonLabel = "Search",
|
|
663
746
|
label,
|
|
664
747
|
searchPath = "/search",
|
|
665
|
-
inputId: inputIdProp
|
|
748
|
+
inputId: inputIdProp,
|
|
749
|
+
clearLabel = "Clear search"
|
|
666
750
|
} = props || {};
|
|
667
751
|
const text = typeof label === "string" && label.trim() ? label.trim() : buttonLabel;
|
|
668
|
-
const action =
|
|
752
|
+
const action = React15.useMemo(
|
|
669
753
|
() => resolveSearchPath(searchPath),
|
|
670
754
|
[searchPath]
|
|
671
755
|
);
|
|
672
|
-
const autoId = typeof
|
|
673
|
-
const [fallbackId] =
|
|
756
|
+
const autoId = typeof React15.useId === "function" ? React15.useId() : void 0;
|
|
757
|
+
const [fallbackId] = React15.useState(
|
|
674
758
|
() => `canopy-search-form-${Math.random().toString(36).slice(2, 10)}`
|
|
675
759
|
);
|
|
676
760
|
const inputId = inputIdProp || autoId || fallbackId;
|
|
677
|
-
const inputRef =
|
|
678
|
-
const [hasValue, setHasValue] =
|
|
679
|
-
const focusInput =
|
|
761
|
+
const inputRef = React15.useRef(null);
|
|
762
|
+
const [hasValue, setHasValue] = React15.useState(false);
|
|
763
|
+
const focusInput = React15.useCallback(() => {
|
|
680
764
|
const el = inputRef.current;
|
|
681
765
|
if (!el) return;
|
|
682
766
|
if (document.activeElement === el) return;
|
|
@@ -689,30 +773,44 @@ function SearchPanelForm(props = {}) {
|
|
|
689
773
|
}
|
|
690
774
|
}
|
|
691
775
|
}, []);
|
|
692
|
-
const handlePointerDown =
|
|
776
|
+
const handlePointerDown = React15.useCallback(
|
|
693
777
|
(event) => {
|
|
694
778
|
const target = event.target;
|
|
695
779
|
if (target && typeof target.closest === "function") {
|
|
696
780
|
if (target.closest("[data-canopy-search-form-trigger]")) return;
|
|
781
|
+
if (target.closest("[data-canopy-search-form-clear]")) return;
|
|
697
782
|
}
|
|
698
783
|
event.preventDefault();
|
|
699
784
|
focusInput();
|
|
700
785
|
},
|
|
701
786
|
[focusInput]
|
|
702
787
|
);
|
|
703
|
-
|
|
788
|
+
React15.useEffect(() => {
|
|
704
789
|
const el = inputRef.current;
|
|
705
790
|
if (!el) return;
|
|
706
791
|
if (el.value && el.value.trim()) {
|
|
707
792
|
setHasValue(true);
|
|
708
793
|
}
|
|
709
794
|
}, []);
|
|
710
|
-
const handleInputChange =
|
|
795
|
+
const handleInputChange = React15.useCallback((event) => {
|
|
711
796
|
var _a;
|
|
712
|
-
const nextHasValue = Boolean(
|
|
797
|
+
const nextHasValue = Boolean(
|
|
798
|
+
((_a = event == null ? void 0 : event.target) == null ? void 0 : _a.value) && event.target.value.trim()
|
|
799
|
+
);
|
|
713
800
|
setHasValue(nextHasValue);
|
|
714
801
|
}, []);
|
|
715
|
-
|
|
802
|
+
const handleClear = React15.useCallback((event) => {
|
|
803
|
+
}, []);
|
|
804
|
+
const handleClearKey = React15.useCallback(
|
|
805
|
+
(event) => {
|
|
806
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
807
|
+
event.preventDefault();
|
|
808
|
+
handleClear(event);
|
|
809
|
+
}
|
|
810
|
+
},
|
|
811
|
+
[handleClear]
|
|
812
|
+
);
|
|
813
|
+
return /* @__PURE__ */ React15.createElement(
|
|
716
814
|
"form",
|
|
717
815
|
{
|
|
718
816
|
action,
|
|
@@ -722,59 +820,63 @@ function SearchPanelForm(props = {}) {
|
|
|
722
820
|
spellCheck: "false",
|
|
723
821
|
className: "canopy-search-form canopy-search-form-shell",
|
|
724
822
|
onPointerDown: handlePointerDown,
|
|
725
|
-
"data-placeholder": placeholder || "",
|
|
726
823
|
"data-has-value": hasValue ? "1" : "0"
|
|
727
824
|
},
|
|
728
|
-
/* @__PURE__ */
|
|
729
|
-
"
|
|
825
|
+
/* @__PURE__ */ React15.createElement("label", { htmlFor: inputId, className: "canopy-search-form__label" }, /* @__PURE__ */ React15.createElement(MagnifyingGlassIcon, { className: "canopy-search-form__icon" }), /* @__PURE__ */ React15.createElement(
|
|
826
|
+
"input",
|
|
730
827
|
{
|
|
731
|
-
|
|
732
|
-
|
|
828
|
+
id: inputId,
|
|
829
|
+
type: "search",
|
|
830
|
+
name: "q",
|
|
831
|
+
inputMode: "search",
|
|
832
|
+
"data-canopy-search-form-input": true,
|
|
833
|
+
placeholder,
|
|
834
|
+
className: "canopy-search-form__input",
|
|
835
|
+
"aria-label": "Search",
|
|
836
|
+
ref: inputRef,
|
|
837
|
+
onChange: handleInputChange,
|
|
838
|
+
onInput: handleInputChange
|
|
839
|
+
}
|
|
840
|
+
)),
|
|
841
|
+
hasValue ? /* @__PURE__ */ React15.createElement(
|
|
842
|
+
"button",
|
|
843
|
+
{
|
|
844
|
+
type: "button",
|
|
845
|
+
className: "canopy-search-form__clear",
|
|
846
|
+
onClick: handleClear,
|
|
847
|
+
onPointerDown: (event) => event.stopPropagation(),
|
|
848
|
+
onKeyDown: handleClearKey,
|
|
849
|
+
"aria-label": clearLabel,
|
|
850
|
+
"data-canopy-search-form-clear": true
|
|
733
851
|
},
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
{
|
|
738
|
-
id: inputId,
|
|
739
|
-
type: "search",
|
|
740
|
-
name: "q",
|
|
741
|
-
inputMode: "search",
|
|
742
|
-
"data-canopy-search-form-input": true,
|
|
743
|
-
placeholder,
|
|
744
|
-
className: "canopy-search-form__input",
|
|
745
|
-
"aria-label": "Search",
|
|
746
|
-
ref: inputRef,
|
|
747
|
-
onChange: handleInputChange,
|
|
748
|
-
onInput: handleInputChange
|
|
749
|
-
}
|
|
750
|
-
)
|
|
751
|
-
),
|
|
752
|
-
/* @__PURE__ */ React14.createElement(
|
|
852
|
+
"\xD7"
|
|
853
|
+
) : null,
|
|
854
|
+
/* @__PURE__ */ React15.createElement(
|
|
753
855
|
"button",
|
|
754
856
|
{
|
|
755
857
|
type: "submit",
|
|
756
858
|
"data-canopy-search-form-trigger": "submit",
|
|
757
859
|
className: "canopy-search-form__submit"
|
|
758
860
|
},
|
|
759
|
-
/* @__PURE__ */
|
|
760
|
-
/* @__PURE__ */
|
|
861
|
+
/* @__PURE__ */ React15.createElement("span", null, text),
|
|
862
|
+
/* @__PURE__ */ React15.createElement("span", { "aria-hidden": true, className: "canopy-search-form__shortcut" }, /* @__PURE__ */ React15.createElement("span", null, "\u2318"), /* @__PURE__ */ React15.createElement("span", null, "K"))
|
|
761
863
|
)
|
|
762
864
|
);
|
|
763
865
|
}
|
|
764
866
|
|
|
765
867
|
// ui/src/search/SearchPanelTeaserResults.jsx
|
|
766
|
-
import
|
|
868
|
+
import React16 from "react";
|
|
767
869
|
function SearchPanelTeaserResults(props = {}) {
|
|
768
870
|
const { style, className } = props || {};
|
|
769
871
|
const classes = ["canopy-search-teaser", className].filter(Boolean).join(" ");
|
|
770
|
-
return /* @__PURE__ */
|
|
872
|
+
return /* @__PURE__ */ React16.createElement(
|
|
771
873
|
"div",
|
|
772
874
|
{
|
|
773
875
|
"data-canopy-search-form-panel": true,
|
|
774
876
|
className: classes || void 0,
|
|
775
877
|
style
|
|
776
878
|
},
|
|
777
|
-
/* @__PURE__ */
|
|
879
|
+
/* @__PURE__ */ React16.createElement("div", { id: "cplist" })
|
|
778
880
|
);
|
|
779
881
|
}
|
|
780
882
|
|
|
@@ -794,11 +896,11 @@ function MdxSearchFormModal(props = {}) {
|
|
|
794
896
|
const text = typeof label === "string" && label.trim() ? label.trim() : buttonLabel;
|
|
795
897
|
const resolvedSearchPath = resolveSearchPath(searchPath);
|
|
796
898
|
const data = { placeholder, hotkey, maxResults, groupOrder, label: text, searchPath: resolvedSearchPath };
|
|
797
|
-
return /* @__PURE__ */
|
|
899
|
+
return /* @__PURE__ */ React17.createElement("div", { "data-canopy-search-form": true, className: "flex-1 min-w-0" }, /* @__PURE__ */ React17.createElement("div", { className: "relative w-full" }, /* @__PURE__ */ React17.createElement(SearchPanelForm, { placeholder, buttonLabel, label, searchPath: resolvedSearchPath }), /* @__PURE__ */ React17.createElement(SearchPanelTeaserResults, null)), /* @__PURE__ */ React17.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: JSON.stringify(data) } }));
|
|
798
900
|
}
|
|
799
901
|
|
|
800
902
|
// ui/src/search/SearchPanel.jsx
|
|
801
|
-
import
|
|
903
|
+
import React18 from "react";
|
|
802
904
|
function SearchPanel(props = {}) {
|
|
803
905
|
const {
|
|
804
906
|
placeholder = "Search\u2026",
|
|
@@ -815,9 +917,10 @@ function SearchPanel(props = {}) {
|
|
|
815
917
|
const text = typeof label === "string" && label.trim() ? label.trim() : buttonLabel;
|
|
816
918
|
const resolvedSearchPath = resolveSearchPath(searchPath);
|
|
817
919
|
const data = { placeholder, hotkey, maxResults, groupOrder, label: text, searchPath: resolvedSearchPath };
|
|
818
|
-
return /* @__PURE__ */
|
|
920
|
+
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) } }));
|
|
819
921
|
}
|
|
820
922
|
export {
|
|
923
|
+
AnnotationCard,
|
|
821
924
|
Card,
|
|
822
925
|
Grid,
|
|
823
926
|
GridItem,
|