@canopy-iiif/app 0.9.1 → 0.9.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/mdx.js +57 -23
- package/lib/build/search-index.js +10 -5
- package/lib/build/search.js +9 -1
- package/lib/search/search.js +1 -2
- package/package.json +4 -2
- package/ui/dist/index.mjs +731 -307
- package/ui/dist/index.mjs.map +4 -4
- package/ui/dist/server.mjs +640 -76
- package/ui/dist/server.mjs.map +4 -4
- package/ui/styles/components/header/_header.scss +153 -3
- package/ui/styles/components/header/_logo.scss +3 -2
- package/ui/styles/components/header/_navbar.scss +52 -10
- package/ui/styles/components/index.scss +1 -0
- package/ui/styles/components/modal/_modal.scss +122 -0
- package/ui/styles/components/modal/index.scss +1 -0
- package/ui/styles/components/search/_filters.scss +197 -225
- package/ui/styles/index.css +532 -313
- package/ui/theme.js +8 -8
package/ui/dist/index.mjs
CHANGED
|
@@ -104,7 +104,7 @@ function Card({
|
|
|
104
104
|
);
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
-
// ui/src/layout/
|
|
107
|
+
// ui/src/layout/TextCard.jsx
|
|
108
108
|
import React3, { useMemo } from "react";
|
|
109
109
|
function escapeRegExp(str = "") {
|
|
110
110
|
return String(str).replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
|
|
@@ -141,7 +141,7 @@ function highlightSnippet(snippet, query) {
|
|
|
141
141
|
(part, idx) => part.toLowerCase() === termLower ? /* @__PURE__ */ React3.createElement("mark", { key: idx }, part) : /* @__PURE__ */ React3.createElement(React3.Fragment, { key: idx }, part)
|
|
142
142
|
);
|
|
143
143
|
}
|
|
144
|
-
function
|
|
144
|
+
function TextCard({
|
|
145
145
|
href = "#",
|
|
146
146
|
title = "Untitled",
|
|
147
147
|
annotation = "",
|
|
@@ -226,8 +226,621 @@ function Grid({
|
|
|
226
226
|
));
|
|
227
227
|
}
|
|
228
228
|
|
|
229
|
+
// ui/src/layout/CanopyHeader.jsx
|
|
230
|
+
import React11 from "react";
|
|
231
|
+
|
|
232
|
+
// ui/src/search/SearchPanel.jsx
|
|
233
|
+
import React8 from "react";
|
|
234
|
+
|
|
235
|
+
// ui/src/Icons.jsx
|
|
236
|
+
import React5 from "react";
|
|
237
|
+
var MagnifyingGlassIcon = (props) => /* @__PURE__ */ React5.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 512 512", ...props }, /* @__PURE__ */ React5.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" }));
|
|
238
|
+
|
|
239
|
+
// ui/src/search/SearchPanelForm.jsx
|
|
240
|
+
import React6 from "react";
|
|
241
|
+
function readBasePath() {
|
|
242
|
+
const normalize = (val) => {
|
|
243
|
+
const raw = typeof val === "string" ? val.trim() : "";
|
|
244
|
+
if (!raw) return "";
|
|
245
|
+
return raw.replace(/\/+$/, "");
|
|
246
|
+
};
|
|
247
|
+
try {
|
|
248
|
+
if (typeof window !== "undefined" && window.CANOPY_BASE_PATH != null) {
|
|
249
|
+
const fromWindow = normalize(window.CANOPY_BASE_PATH);
|
|
250
|
+
if (fromWindow) return fromWindow;
|
|
251
|
+
}
|
|
252
|
+
} catch (_) {
|
|
253
|
+
}
|
|
254
|
+
try {
|
|
255
|
+
if (typeof globalThis !== "undefined" && globalThis.CANOPY_BASE_PATH != null) {
|
|
256
|
+
const fromGlobal = normalize(globalThis.CANOPY_BASE_PATH);
|
|
257
|
+
if (fromGlobal) return fromGlobal;
|
|
258
|
+
}
|
|
259
|
+
} catch (_) {
|
|
260
|
+
}
|
|
261
|
+
try {
|
|
262
|
+
if (typeof process !== "undefined" && process.env && process.env.CANOPY_BASE_PATH) {
|
|
263
|
+
const fromEnv = normalize(process.env.CANOPY_BASE_PATH);
|
|
264
|
+
if (fromEnv) return fromEnv;
|
|
265
|
+
}
|
|
266
|
+
} catch (_) {
|
|
267
|
+
}
|
|
268
|
+
return "";
|
|
269
|
+
}
|
|
270
|
+
function isAbsoluteUrl(href) {
|
|
271
|
+
try {
|
|
272
|
+
return /^https?:/i.test(String(href || ""));
|
|
273
|
+
} catch (_) {
|
|
274
|
+
return false;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
function resolveSearchPath(pathValue) {
|
|
278
|
+
let raw = typeof pathValue === "string" ? pathValue.trim() : "";
|
|
279
|
+
if (!raw) raw = "/search";
|
|
280
|
+
if (isAbsoluteUrl(raw)) return raw;
|
|
281
|
+
const normalizedPath = raw.startsWith("/") ? raw : `/${raw}`;
|
|
282
|
+
const base = readBasePath();
|
|
283
|
+
if (!base) return normalizedPath;
|
|
284
|
+
const baseWithLead = base.startsWith("/") ? base : `/${base}`;
|
|
285
|
+
const baseTrimmed = baseWithLead.replace(/\/+$/, "");
|
|
286
|
+
if (!baseTrimmed) return normalizedPath;
|
|
287
|
+
if (normalizedPath === baseTrimmed || normalizedPath.startsWith(`${baseTrimmed}/`)) {
|
|
288
|
+
return normalizedPath;
|
|
289
|
+
}
|
|
290
|
+
const pathTrimmed = normalizedPath.replace(/^\/+/, "");
|
|
291
|
+
return `${baseTrimmed}/${pathTrimmed}`;
|
|
292
|
+
}
|
|
293
|
+
function SearchPanelForm(props = {}) {
|
|
294
|
+
const {
|
|
295
|
+
placeholder = "Search\u2026",
|
|
296
|
+
buttonLabel = "Search",
|
|
297
|
+
label,
|
|
298
|
+
searchPath = "/search",
|
|
299
|
+
inputId: inputIdProp,
|
|
300
|
+
clearLabel = "Clear search"
|
|
301
|
+
} = props || {};
|
|
302
|
+
const text = typeof label === "string" && label.trim() ? label.trim() : buttonLabel;
|
|
303
|
+
const action = React6.useMemo(
|
|
304
|
+
() => resolveSearchPath(searchPath),
|
|
305
|
+
[searchPath]
|
|
306
|
+
);
|
|
307
|
+
const autoId = typeof React6.useId === "function" ? React6.useId() : void 0;
|
|
308
|
+
const [fallbackId] = React6.useState(
|
|
309
|
+
() => `canopy-search-form-${Math.random().toString(36).slice(2, 10)}`
|
|
310
|
+
);
|
|
311
|
+
const inputId = inputIdProp || autoId || fallbackId;
|
|
312
|
+
const inputRef = React6.useRef(null);
|
|
313
|
+
const [hasValue, setHasValue] = React6.useState(false);
|
|
314
|
+
const focusInput = React6.useCallback(() => {
|
|
315
|
+
const el = inputRef.current;
|
|
316
|
+
if (!el) return;
|
|
317
|
+
if (document.activeElement === el) return;
|
|
318
|
+
try {
|
|
319
|
+
el.focus({ preventScroll: true });
|
|
320
|
+
} catch (_) {
|
|
321
|
+
try {
|
|
322
|
+
el.focus();
|
|
323
|
+
} catch (_2) {
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}, []);
|
|
327
|
+
const handlePointerDown = React6.useCallback(
|
|
328
|
+
(event) => {
|
|
329
|
+
const target = event.target;
|
|
330
|
+
if (target && typeof target.closest === "function") {
|
|
331
|
+
if (target.closest("[data-canopy-search-form-trigger]")) return;
|
|
332
|
+
if (target.closest("[data-canopy-search-form-clear]")) return;
|
|
333
|
+
}
|
|
334
|
+
event.preventDefault();
|
|
335
|
+
focusInput();
|
|
336
|
+
},
|
|
337
|
+
[focusInput]
|
|
338
|
+
);
|
|
339
|
+
React6.useEffect(() => {
|
|
340
|
+
const el = inputRef.current;
|
|
341
|
+
if (!el) return;
|
|
342
|
+
if (el.value && el.value.trim()) {
|
|
343
|
+
setHasValue(true);
|
|
344
|
+
}
|
|
345
|
+
}, []);
|
|
346
|
+
const handleInputChange = React6.useCallback((event) => {
|
|
347
|
+
var _a;
|
|
348
|
+
const nextHasValue = Boolean(
|
|
349
|
+
((_a = event == null ? void 0 : event.target) == null ? void 0 : _a.value) && event.target.value.trim()
|
|
350
|
+
);
|
|
351
|
+
setHasValue(nextHasValue);
|
|
352
|
+
}, []);
|
|
353
|
+
const handleClear = React6.useCallback((event) => {
|
|
354
|
+
}, []);
|
|
355
|
+
const handleClearKey = React6.useCallback(
|
|
356
|
+
(event) => {
|
|
357
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
358
|
+
event.preventDefault();
|
|
359
|
+
handleClear(event);
|
|
360
|
+
}
|
|
361
|
+
},
|
|
362
|
+
[handleClear]
|
|
363
|
+
);
|
|
364
|
+
return /* @__PURE__ */ React6.createElement(
|
|
365
|
+
"form",
|
|
366
|
+
{
|
|
367
|
+
action,
|
|
368
|
+
method: "get",
|
|
369
|
+
role: "search",
|
|
370
|
+
autoComplete: "off",
|
|
371
|
+
spellCheck: "false",
|
|
372
|
+
className: "canopy-search-form canopy-search-form-shell",
|
|
373
|
+
onPointerDown: handlePointerDown,
|
|
374
|
+
"data-has-value": hasValue ? "1" : "0"
|
|
375
|
+
},
|
|
376
|
+
/* @__PURE__ */ React6.createElement("label", { htmlFor: inputId, className: "canopy-search-form__label" }, /* @__PURE__ */ React6.createElement(MagnifyingGlassIcon, { className: "canopy-search-form__icon" }), /* @__PURE__ */ React6.createElement(
|
|
377
|
+
"input",
|
|
378
|
+
{
|
|
379
|
+
id: inputId,
|
|
380
|
+
type: "search",
|
|
381
|
+
name: "q",
|
|
382
|
+
inputMode: "search",
|
|
383
|
+
"data-canopy-search-form-input": true,
|
|
384
|
+
placeholder,
|
|
385
|
+
className: "canopy-search-form__input",
|
|
386
|
+
"aria-label": "Search",
|
|
387
|
+
ref: inputRef,
|
|
388
|
+
onChange: handleInputChange,
|
|
389
|
+
onInput: handleInputChange
|
|
390
|
+
}
|
|
391
|
+
)),
|
|
392
|
+
hasValue ? /* @__PURE__ */ React6.createElement(
|
|
393
|
+
"button",
|
|
394
|
+
{
|
|
395
|
+
type: "button",
|
|
396
|
+
className: "canopy-search-form__clear",
|
|
397
|
+
onClick: handleClear,
|
|
398
|
+
onPointerDown: (event) => event.stopPropagation(),
|
|
399
|
+
onKeyDown: handleClearKey,
|
|
400
|
+
"aria-label": clearLabel,
|
|
401
|
+
"data-canopy-search-form-clear": true
|
|
402
|
+
},
|
|
403
|
+
"\xD7"
|
|
404
|
+
) : null,
|
|
405
|
+
/* @__PURE__ */ React6.createElement(
|
|
406
|
+
"button",
|
|
407
|
+
{
|
|
408
|
+
type: "submit",
|
|
409
|
+
"data-canopy-search-form-trigger": "submit",
|
|
410
|
+
className: "canopy-search-form__submit"
|
|
411
|
+
},
|
|
412
|
+
/* @__PURE__ */ React6.createElement("span", null, text),
|
|
413
|
+
/* @__PURE__ */ React6.createElement("span", { "aria-hidden": true, className: "canopy-search-form__shortcut" }, /* @__PURE__ */ React6.createElement("span", null, "\u2318"), /* @__PURE__ */ React6.createElement("span", null, "K"))
|
|
414
|
+
)
|
|
415
|
+
);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// ui/src/search/SearchPanelTeaserResults.jsx
|
|
419
|
+
import React7 from "react";
|
|
420
|
+
function SearchPanelTeaserResults(props = {}) {
|
|
421
|
+
const { style, className } = props || {};
|
|
422
|
+
const classes = ["canopy-search-teaser", className].filter(Boolean).join(" ");
|
|
423
|
+
return /* @__PURE__ */ React7.createElement(
|
|
424
|
+
"div",
|
|
425
|
+
{
|
|
426
|
+
"data-canopy-search-form-panel": true,
|
|
427
|
+
className: classes || void 0,
|
|
428
|
+
style
|
|
429
|
+
},
|
|
430
|
+
/* @__PURE__ */ React7.createElement("div", { id: "cplist" })
|
|
431
|
+
);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
// ui/src/search/SearchPanel.jsx
|
|
435
|
+
function SearchPanel(props = {}) {
|
|
436
|
+
const {
|
|
437
|
+
placeholder = "Search\u2026",
|
|
438
|
+
hotkey = "mod+k",
|
|
439
|
+
maxResults = 8,
|
|
440
|
+
groupOrder = ["work", "docs", "page"],
|
|
441
|
+
// Kept for backward compat; form always renders submit
|
|
442
|
+
button = true,
|
|
443
|
+
// eslint-disable-line no-unused-vars
|
|
444
|
+
buttonLabel = "Search",
|
|
445
|
+
label,
|
|
446
|
+
searchPath = "/search"
|
|
447
|
+
} = props || {};
|
|
448
|
+
const text = typeof label === "string" && label.trim() ? label.trim() : buttonLabel;
|
|
449
|
+
const resolvedSearchPath = resolveSearchPath(searchPath);
|
|
450
|
+
const data = { placeholder, hotkey, maxResults, groupOrder, label: text, searchPath: resolvedSearchPath };
|
|
451
|
+
return /* @__PURE__ */ React8.createElement("div", { "data-canopy-search-form": true, className: "flex-1 min-w-0" }, /* @__PURE__ */ React8.createElement("div", { className: "relative w-full" }, /* @__PURE__ */ React8.createElement(SearchPanelForm, { placeholder, buttonLabel, label, searchPath: resolvedSearchPath }), /* @__PURE__ */ React8.createElement(SearchPanelTeaserResults, null)), /* @__PURE__ */ React8.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: JSON.stringify(data) } }));
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// ui/src/layout/CanopyBrand.jsx
|
|
455
|
+
import React9 from "react";
|
|
456
|
+
function CanopyBrand(props = {}) {
|
|
457
|
+
const {
|
|
458
|
+
labelId,
|
|
459
|
+
label = "Canopy IIIF",
|
|
460
|
+
href = "/",
|
|
461
|
+
className,
|
|
462
|
+
Logo
|
|
463
|
+
} = props || {};
|
|
464
|
+
const spanProps = labelId ? { id: labelId } : {};
|
|
465
|
+
const classes = ["canopy-logo", className].filter(Boolean).join(" ");
|
|
466
|
+
return /* @__PURE__ */ React9.createElement("a", { href, className: classes }, typeof Logo === "function" ? /* @__PURE__ */ React9.createElement(Logo, null) : null, /* @__PURE__ */ React9.createElement("span", { ...spanProps }, label));
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
// ui/src/layout/CanopyModal.jsx
|
|
470
|
+
import React10 from "react";
|
|
471
|
+
function CanopyModal(props = {}) {
|
|
472
|
+
const {
|
|
473
|
+
id,
|
|
474
|
+
variant,
|
|
475
|
+
open = false,
|
|
476
|
+
labelledBy,
|
|
477
|
+
label,
|
|
478
|
+
logo: Logo,
|
|
479
|
+
href = "/",
|
|
480
|
+
closeLabel = "Close",
|
|
481
|
+
closeDataAttr,
|
|
482
|
+
onClose,
|
|
483
|
+
onBackgroundClick,
|
|
484
|
+
bodyClassName,
|
|
485
|
+
padded = true,
|
|
486
|
+
className,
|
|
487
|
+
children
|
|
488
|
+
} = props;
|
|
489
|
+
const rootClassName = ["canopy-modal", variant ? `canopy-modal--${variant}` : null, className].filter(Boolean).join(" ");
|
|
490
|
+
const modalProps = {
|
|
491
|
+
id,
|
|
492
|
+
className: rootClassName,
|
|
493
|
+
role: "dialog",
|
|
494
|
+
"aria-modal": "true",
|
|
495
|
+
"aria-hidden": open ? "false" : "true",
|
|
496
|
+
"data-open": open ? "true" : "false"
|
|
497
|
+
};
|
|
498
|
+
if (variant) modalProps["data-canopy-modal"] = variant;
|
|
499
|
+
const resolvedLabelId = labelledBy || (label ? `${variant || "modal"}-label` : void 0);
|
|
500
|
+
if (resolvedLabelId) modalProps["aria-labelledby"] = resolvedLabelId;
|
|
501
|
+
if (typeof onBackgroundClick === "function") {
|
|
502
|
+
modalProps.onClick = (event) => {
|
|
503
|
+
if (event.target === event.currentTarget) onBackgroundClick(event);
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
const closeButtonProps = {
|
|
507
|
+
type: "button",
|
|
508
|
+
className: "canopy-modal__close",
|
|
509
|
+
"aria-label": closeLabel
|
|
510
|
+
};
|
|
511
|
+
if (typeof closeDataAttr === "string" && closeDataAttr) {
|
|
512
|
+
closeButtonProps["data-canopy-header-close"] = closeDataAttr;
|
|
513
|
+
}
|
|
514
|
+
if (typeof onClose === "function") {
|
|
515
|
+
closeButtonProps.onClick = onClose;
|
|
516
|
+
}
|
|
517
|
+
const bodyClasses = ["canopy-modal__body"];
|
|
518
|
+
if (padded) bodyClasses.push("canopy-modal__body--padded");
|
|
519
|
+
if (bodyClassName) bodyClasses.push(bodyClassName);
|
|
520
|
+
const bodyClassNameValue = bodyClasses.join(" ");
|
|
521
|
+
return /* @__PURE__ */ React10.createElement("div", { ...modalProps }, /* @__PURE__ */ React10.createElement("div", { className: "canopy-modal__panel" }, /* @__PURE__ */ React10.createElement("button", { ...closeButtonProps }, /* @__PURE__ */ React10.createElement(
|
|
522
|
+
"svg",
|
|
523
|
+
{
|
|
524
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
525
|
+
viewBox: "0 0 24 24",
|
|
526
|
+
fill: "none",
|
|
527
|
+
stroke: "currentColor",
|
|
528
|
+
strokeWidth: "1.5",
|
|
529
|
+
className: "canopy-modal__close-icon"
|
|
530
|
+
},
|
|
531
|
+
/* @__PURE__ */ React10.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M6 6l12 12M6 18L18 6" })
|
|
532
|
+
), /* @__PURE__ */ React10.createElement("span", { className: "sr-only" }, closeLabel)), /* @__PURE__ */ React10.createElement("div", { className: bodyClassNameValue }, label ? /* @__PURE__ */ React10.createElement("div", { className: "canopy-modal__brand" }, /* @__PURE__ */ React10.createElement(
|
|
533
|
+
CanopyBrand,
|
|
534
|
+
{
|
|
535
|
+
labelId: resolvedLabelId,
|
|
536
|
+
label,
|
|
537
|
+
href,
|
|
538
|
+
Logo,
|
|
539
|
+
className: "canopy-modal__brand-link"
|
|
540
|
+
}
|
|
541
|
+
)) : null, children)));
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
// ui/src/layout/CanopyHeader.jsx
|
|
545
|
+
function HeaderScript() {
|
|
546
|
+
const code = `
|
|
547
|
+
(function () {
|
|
548
|
+
if (typeof window === 'undefined') return;
|
|
549
|
+
|
|
550
|
+
var doc = document;
|
|
551
|
+
var body = doc.body;
|
|
552
|
+
var root = doc.documentElement;
|
|
553
|
+
|
|
554
|
+
function ready(fn) {
|
|
555
|
+
if (doc.readyState === 'loading') {
|
|
556
|
+
doc.addEventListener('DOMContentLoaded', fn, { once: true });
|
|
557
|
+
} else {
|
|
558
|
+
fn();
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
ready(function () {
|
|
563
|
+
var header = doc.querySelector('.canopy-header');
|
|
564
|
+
if (!header) return;
|
|
565
|
+
|
|
566
|
+
var NAV_ATTR = 'data-mobile-nav';
|
|
567
|
+
var SEARCH_ATTR = 'data-mobile-search';
|
|
568
|
+
|
|
569
|
+
function modalFor(type) {
|
|
570
|
+
return doc.querySelector('[data-canopy-modal="' + type + '"]');
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
function each(list, fn) {
|
|
574
|
+
if (!list || typeof fn !== 'function') return;
|
|
575
|
+
Array.prototype.forEach.call(list, fn);
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
function setExpanded(type, expanded) {
|
|
579
|
+
var toggles = header.querySelectorAll('[data-canopy-header-toggle="' + type + '"]');
|
|
580
|
+
each(toggles, function (btn) {
|
|
581
|
+
btn.setAttribute('aria-expanded', expanded ? 'true' : 'false');
|
|
582
|
+
});
|
|
583
|
+
var modal = modalFor(type);
|
|
584
|
+
if (modal) {
|
|
585
|
+
modal.setAttribute('data-open', expanded ? 'true' : 'false');
|
|
586
|
+
modal.setAttribute('aria-hidden', expanded ? 'false' : 'true');
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
function lockScroll(shouldLock) {
|
|
591
|
+
if (!body) return;
|
|
592
|
+
if (shouldLock) {
|
|
593
|
+
if (!body.dataset.canopyScrollLock) {
|
|
594
|
+
body.dataset.canopyScrollPrevOverflow = body.style.overflow || '';
|
|
595
|
+
if (root && root.dataset) {
|
|
596
|
+
root.dataset.canopyScrollPrevOverflow = root.style.overflow || '';
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
body.dataset.canopyScrollLock = '1';
|
|
600
|
+
body.style.overflow = 'hidden';
|
|
601
|
+
if (root) root.style.overflow = 'hidden';
|
|
602
|
+
} else {
|
|
603
|
+
if (body.dataset.canopyScrollLock) {
|
|
604
|
+
delete body.dataset.canopyScrollLock;
|
|
605
|
+
body.style.overflow = body.dataset.canopyScrollPrevOverflow || '';
|
|
606
|
+
delete body.dataset.canopyScrollPrevOverflow;
|
|
607
|
+
}
|
|
608
|
+
if (root && root.dataset) {
|
|
609
|
+
root.style.overflow = root.dataset.canopyScrollPrevOverflow || '';
|
|
610
|
+
delete root.dataset.canopyScrollPrevOverflow;
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
function stateFor(type) {
|
|
616
|
+
if (type === 'nav') return header.getAttribute(NAV_ATTR);
|
|
617
|
+
if (type === 'search') return header.getAttribute(SEARCH_ATTR);
|
|
618
|
+
return 'closed';
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
function focusSearchForm() {
|
|
622
|
+
var input = header.querySelector('[data-canopy-search-form-input]');
|
|
623
|
+
if (!input) return;
|
|
624
|
+
var raf = typeof window !== 'undefined' && window.requestAnimationFrame;
|
|
625
|
+
(raf || function (fn) { return setTimeout(fn, 16); })(function () {
|
|
626
|
+
try {
|
|
627
|
+
input.focus({ preventScroll: true });
|
|
628
|
+
} catch (_) {
|
|
629
|
+
try { input.focus(); } catch (_) {}
|
|
630
|
+
}
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
function focusNavMenu() {
|
|
635
|
+
var modal = modalFor('nav');
|
|
636
|
+
if (!modal) return;
|
|
637
|
+
var target = modal.querySelector('button, a, input, [tabindex]:not([tabindex="-1"])');
|
|
638
|
+
if (!target) return;
|
|
639
|
+
var raf = typeof window !== 'undefined' && window.requestAnimationFrame;
|
|
640
|
+
(raf || function (fn) { return setTimeout(fn, 16); })(function () {
|
|
641
|
+
try {
|
|
642
|
+
target.focus({ preventScroll: true });
|
|
643
|
+
} catch (_) {
|
|
644
|
+
try { target.focus(); } catch (_) {}
|
|
645
|
+
}
|
|
646
|
+
});
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
function setState(type, next) {
|
|
650
|
+
if (type === 'nav') header.setAttribute(NAV_ATTR, next);
|
|
651
|
+
if (type === 'search') header.setAttribute(SEARCH_ATTR, next);
|
|
652
|
+
setExpanded(type, next === 'open');
|
|
653
|
+
var navOpen = header.getAttribute(NAV_ATTR) === 'open';
|
|
654
|
+
var searchOpen = header.getAttribute(SEARCH_ATTR) === 'open';
|
|
655
|
+
lockScroll(navOpen || searchOpen);
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
function toggle(type, force) {
|
|
659
|
+
var current = stateFor(type) === 'open';
|
|
660
|
+
var shouldOpen = typeof force === 'boolean' ? force : !current;
|
|
661
|
+
if (shouldOpen && type === 'nav') setState('search', 'closed');
|
|
662
|
+
if (shouldOpen && type === 'search') setState('nav', 'closed');
|
|
663
|
+
setState(type, shouldOpen ? 'open' : 'closed');
|
|
664
|
+
if (type === 'search' && shouldOpen) focusSearchForm();
|
|
665
|
+
if (type === 'nav' && shouldOpen) focusNavMenu();
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
each(header.querySelectorAll('[data-canopy-header-toggle]'), function (btn) {
|
|
669
|
+
btn.addEventListener('click', function (event) {
|
|
670
|
+
event.preventDefault();
|
|
671
|
+
var type = btn.getAttribute('data-canopy-header-toggle');
|
|
672
|
+
if (!type) return;
|
|
673
|
+
toggle(type);
|
|
674
|
+
});
|
|
675
|
+
});
|
|
676
|
+
|
|
677
|
+
each(doc.querySelectorAll('[data-canopy-header-close]'), function (btn) {
|
|
678
|
+
btn.addEventListener('click', function () {
|
|
679
|
+
var type = btn.getAttribute('data-canopy-header-close');
|
|
680
|
+
if (!type) return;
|
|
681
|
+
toggle(type, false);
|
|
682
|
+
});
|
|
683
|
+
});
|
|
684
|
+
|
|
685
|
+
var navModal = modalFor('nav');
|
|
686
|
+
if (navModal) {
|
|
687
|
+
navModal.addEventListener('click', function (event) {
|
|
688
|
+
if (event.target === navModal) {
|
|
689
|
+
toggle('nav', false);
|
|
690
|
+
return;
|
|
691
|
+
}
|
|
692
|
+
var target = event.target && event.target.closest && event.target.closest('a');
|
|
693
|
+
if (!target) return;
|
|
694
|
+
toggle('nav', false);
|
|
695
|
+
});
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
var searchModal = modalFor('search');
|
|
699
|
+
if (searchModal) {
|
|
700
|
+
searchModal.addEventListener('click', function (event) {
|
|
701
|
+
if (event.target === searchModal) toggle('search', false);
|
|
702
|
+
});
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
doc.addEventListener('keydown', function (event) {
|
|
706
|
+
if (event.key !== 'Escape') return;
|
|
707
|
+
var navOpen = header.getAttribute(NAV_ATTR) === 'open';
|
|
708
|
+
var searchOpen = header.getAttribute(SEARCH_ATTR) === 'open';
|
|
709
|
+
if (!navOpen && !searchOpen) return;
|
|
710
|
+
event.preventDefault();
|
|
711
|
+
toggle('nav', false);
|
|
712
|
+
toggle('search', false);
|
|
713
|
+
});
|
|
714
|
+
|
|
715
|
+
var mq = window.matchMedia('(min-width: 48rem)');
|
|
716
|
+
function syncDesktopState() {
|
|
717
|
+
if (mq.matches) {
|
|
718
|
+
setState('nav', 'closed');
|
|
719
|
+
setState('search', 'closed');
|
|
720
|
+
setExpanded('nav', false);
|
|
721
|
+
setExpanded('search', false);
|
|
722
|
+
lockScroll(false);
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
try {
|
|
727
|
+
mq.addEventListener('change', syncDesktopState);
|
|
728
|
+
} catch (_) {
|
|
729
|
+
mq.addListener(syncDesktopState);
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
syncDesktopState();
|
|
733
|
+
});
|
|
734
|
+
})();
|
|
735
|
+
`;
|
|
736
|
+
return /* @__PURE__ */ React11.createElement(
|
|
737
|
+
"script",
|
|
738
|
+
{
|
|
739
|
+
dangerouslySetInnerHTML: {
|
|
740
|
+
__html: code
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
);
|
|
744
|
+
}
|
|
745
|
+
function ensureArray(navLinks) {
|
|
746
|
+
if (!Array.isArray(navLinks)) return [];
|
|
747
|
+
return navLinks.filter((link) => link && typeof link === "object" && typeof link.href === "string");
|
|
748
|
+
}
|
|
749
|
+
function CanopyHeader(props = {}) {
|
|
750
|
+
const {
|
|
751
|
+
navigation: navLinksProp,
|
|
752
|
+
searchLabel = "Search",
|
|
753
|
+
searchHotkey = "mod+k",
|
|
754
|
+
searchPlaceholder = "Search\u2026",
|
|
755
|
+
brandHref = "/",
|
|
756
|
+
title = "Canopy IIIF",
|
|
757
|
+
logo: SiteLogo
|
|
758
|
+
} = props;
|
|
759
|
+
const navLinks = ensureArray(navLinksProp);
|
|
760
|
+
return /* @__PURE__ */ React11.createElement(React11.Fragment, null, /* @__PURE__ */ React11.createElement("header", { className: "canopy-header", "data-mobile-nav": "closed", "data-mobile-search": "closed" }, /* @__PURE__ */ React11.createElement("div", { className: "canopy-header__brand" }, /* @__PURE__ */ React11.createElement(
|
|
761
|
+
CanopyBrand,
|
|
762
|
+
{
|
|
763
|
+
label: title,
|
|
764
|
+
href: brandHref,
|
|
765
|
+
className: "canopy-header__brand-link",
|
|
766
|
+
Logo: SiteLogo
|
|
767
|
+
}
|
|
768
|
+
)), /* @__PURE__ */ React11.createElement("div", { className: "canopy-header__desktop-search" }, /* @__PURE__ */ React11.createElement(SearchPanel, { label: searchLabel, hotkey: searchHotkey, placeholder: searchPlaceholder })), /* @__PURE__ */ React11.createElement("nav", { className: "canopy-nav-links canopy-header__desktop-nav", "aria-label": "Primary navigation" }, navLinks.map((link) => /* @__PURE__ */ React11.createElement("a", { key: link.href, href: link.href }, link.label || link.href))), /* @__PURE__ */ React11.createElement("div", { className: "canopy-header__actions" }, /* @__PURE__ */ React11.createElement(
|
|
769
|
+
"button",
|
|
770
|
+
{
|
|
771
|
+
type: "button",
|
|
772
|
+
className: "canopy-header__icon-button canopy-header__search-trigger",
|
|
773
|
+
"aria-label": "Open search",
|
|
774
|
+
"aria-controls": "canopy-modal-search",
|
|
775
|
+
"aria-expanded": "false",
|
|
776
|
+
"data-canopy-header-toggle": "search"
|
|
777
|
+
},
|
|
778
|
+
/* @__PURE__ */ React11.createElement(
|
|
779
|
+
"svg",
|
|
780
|
+
{
|
|
781
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
782
|
+
viewBox: "0 0 24 24",
|
|
783
|
+
fill: "none",
|
|
784
|
+
stroke: "currentColor",
|
|
785
|
+
strokeWidth: "1.5",
|
|
786
|
+
className: "canopy-header__search-icon"
|
|
787
|
+
},
|
|
788
|
+
/* @__PURE__ */ React11.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "m21 21-3.8-3.8M10.5 18a7.5 7.5 0 1 1 0-15 7.5 7.5 0 0 1 0 15Z" })
|
|
789
|
+
)
|
|
790
|
+
), /* @__PURE__ */ React11.createElement(
|
|
791
|
+
"button",
|
|
792
|
+
{
|
|
793
|
+
type: "button",
|
|
794
|
+
className: "canopy-header__icon-button canopy-header__menu",
|
|
795
|
+
"aria-label": "Open navigation",
|
|
796
|
+
"aria-controls": "canopy-modal-nav",
|
|
797
|
+
"aria-expanded": "false",
|
|
798
|
+
"data-canopy-header-toggle": "nav"
|
|
799
|
+
},
|
|
800
|
+
/* @__PURE__ */ React11.createElement(
|
|
801
|
+
"svg",
|
|
802
|
+
{
|
|
803
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
804
|
+
fill: "none",
|
|
805
|
+
viewBox: "0 0 24 24",
|
|
806
|
+
strokeWidth: "1.5",
|
|
807
|
+
stroke: "currentColor",
|
|
808
|
+
className: "canopy-header__menu-icon"
|
|
809
|
+
},
|
|
810
|
+
/* @__PURE__ */ React11.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" })
|
|
811
|
+
)
|
|
812
|
+
))), /* @__PURE__ */ React11.createElement(
|
|
813
|
+
CanopyModal,
|
|
814
|
+
{
|
|
815
|
+
id: "canopy-modal-nav",
|
|
816
|
+
variant: "nav",
|
|
817
|
+
labelledBy: "canopy-modal-nav-label",
|
|
818
|
+
label: title,
|
|
819
|
+
logo: SiteLogo,
|
|
820
|
+
href: brandHref,
|
|
821
|
+
closeLabel: "Close navigation",
|
|
822
|
+
closeDataAttr: "nav"
|
|
823
|
+
},
|
|
824
|
+
/* @__PURE__ */ React11.createElement("nav", { className: "canopy-nav-links canopy-modal__nav", "aria-label": "Primary navigation" }, navLinks.map((link) => /* @__PURE__ */ React11.createElement("a", { key: link.href, href: link.href }, link.label || link.href)))
|
|
825
|
+
), /* @__PURE__ */ React11.createElement(
|
|
826
|
+
CanopyModal,
|
|
827
|
+
{
|
|
828
|
+
id: "canopy-modal-search",
|
|
829
|
+
variant: "search",
|
|
830
|
+
labelledBy: "canopy-modal-search-label",
|
|
831
|
+
label: title,
|
|
832
|
+
logo: SiteLogo,
|
|
833
|
+
href: brandHref,
|
|
834
|
+
closeLabel: "Close search",
|
|
835
|
+
closeDataAttr: "search",
|
|
836
|
+
bodyClassName: "canopy-modal__body--search"
|
|
837
|
+
},
|
|
838
|
+
/* @__PURE__ */ React11.createElement(SearchPanel, { label: searchLabel, hotkey: searchHotkey, placeholder: searchPlaceholder })
|
|
839
|
+
), /* @__PURE__ */ React11.createElement(HeaderScript, null));
|
|
840
|
+
}
|
|
841
|
+
|
|
229
842
|
// ui/src/iiif/Viewer.jsx
|
|
230
|
-
import
|
|
843
|
+
import React12, { useEffect as useEffect2, useState as useState2 } from "react";
|
|
231
844
|
var DEFAULT_VIEWER_OPTIONS = {
|
|
232
845
|
showDownload: false,
|
|
233
846
|
showIIIFBadge: false,
|
|
@@ -283,7 +896,7 @@ var Viewer = (props) => {
|
|
|
283
896
|
} catch (_) {
|
|
284
897
|
json = "{}";
|
|
285
898
|
}
|
|
286
|
-
return /* @__PURE__ */
|
|
899
|
+
return /* @__PURE__ */ React12.createElement("div", { "data-canopy-viewer": "1", className: "not-prose" }, /* @__PURE__ */ React12.createElement(
|
|
287
900
|
"script",
|
|
288
901
|
{
|
|
289
902
|
type: "application/json",
|
|
@@ -291,11 +904,11 @@ var Viewer = (props) => {
|
|
|
291
904
|
}
|
|
292
905
|
));
|
|
293
906
|
}
|
|
294
|
-
return /* @__PURE__ */
|
|
907
|
+
return /* @__PURE__ */ React12.createElement(CloverViewer, { ...props, options: mergedOptions });
|
|
295
908
|
};
|
|
296
909
|
|
|
297
910
|
// ui/src/iiif/Slider.jsx
|
|
298
|
-
import
|
|
911
|
+
import React13, { useEffect as useEffect3, useState as useState3 } from "react";
|
|
299
912
|
var Slider = (props) => {
|
|
300
913
|
const [CloverSlider, setCloverSlider] = useState3(null);
|
|
301
914
|
useEffect3(() => {
|
|
@@ -321,7 +934,7 @@ var Slider = (props) => {
|
|
|
321
934
|
} catch (_) {
|
|
322
935
|
json = "{}";
|
|
323
936
|
}
|
|
324
|
-
return /* @__PURE__ */
|
|
937
|
+
return /* @__PURE__ */ React13.createElement("div", { "data-canopy-slider": "1", className: "not-prose" }, /* @__PURE__ */ React13.createElement(
|
|
325
938
|
"script",
|
|
326
939
|
{
|
|
327
940
|
type: "application/json",
|
|
@@ -329,11 +942,11 @@ var Slider = (props) => {
|
|
|
329
942
|
}
|
|
330
943
|
));
|
|
331
944
|
}
|
|
332
|
-
return /* @__PURE__ */
|
|
945
|
+
return /* @__PURE__ */ React13.createElement(CloverSlider, { ...props });
|
|
333
946
|
};
|
|
334
947
|
|
|
335
948
|
// ui/src/iiif/Scroll.jsx
|
|
336
|
-
import
|
|
949
|
+
import React14, { useEffect as useEffect4, useState as useState4 } from "react";
|
|
337
950
|
var Scroll = (props) => {
|
|
338
951
|
const [CloverScroll, setCloverScroll] = useState4(null);
|
|
339
952
|
useEffect4(() => {
|
|
@@ -358,7 +971,7 @@ var Scroll = (props) => {
|
|
|
358
971
|
} catch (_) {
|
|
359
972
|
json = "{}";
|
|
360
973
|
}
|
|
361
|
-
return /* @__PURE__ */
|
|
974
|
+
return /* @__PURE__ */ React14.createElement("div", { "data-canopy-scroll": "1", className: "not-prose" }, /* @__PURE__ */ React14.createElement(
|
|
362
975
|
"script",
|
|
363
976
|
{
|
|
364
977
|
type: "application/json",
|
|
@@ -366,11 +979,11 @@ var Scroll = (props) => {
|
|
|
366
979
|
}
|
|
367
980
|
));
|
|
368
981
|
}
|
|
369
|
-
return /* @__PURE__ */
|
|
982
|
+
return /* @__PURE__ */ React14.createElement(CloverScroll, { ...props });
|
|
370
983
|
};
|
|
371
984
|
|
|
372
985
|
// ui/src/iiif/MdxRelatedItems.jsx
|
|
373
|
-
import
|
|
986
|
+
import React15 from "react";
|
|
374
987
|
function MdxRelatedItems(props) {
|
|
375
988
|
let json = "{}";
|
|
376
989
|
try {
|
|
@@ -378,11 +991,11 @@ function MdxRelatedItems(props) {
|
|
|
378
991
|
} catch (_) {
|
|
379
992
|
json = "{}";
|
|
380
993
|
}
|
|
381
|
-
return /* @__PURE__ */
|
|
994
|
+
return /* @__PURE__ */ React15.createElement("div", { "data-canopy-related-items": "1", className: "not-prose" }, /* @__PURE__ */ React15.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: json } }));
|
|
382
995
|
}
|
|
383
996
|
|
|
384
997
|
// ui/src/search/MdxSearchResults.jsx
|
|
385
|
-
import
|
|
998
|
+
import React16 from "react";
|
|
386
999
|
function MdxSearchResults(props) {
|
|
387
1000
|
let json = "{}";
|
|
388
1001
|
try {
|
|
@@ -390,11 +1003,11 @@ function MdxSearchResults(props) {
|
|
|
390
1003
|
} catch (_) {
|
|
391
1004
|
json = "{}";
|
|
392
1005
|
}
|
|
393
|
-
return /* @__PURE__ */
|
|
1006
|
+
return /* @__PURE__ */ React16.createElement("div", { "data-canopy-search-results": "1" }, /* @__PURE__ */ React16.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: json } }));
|
|
394
1007
|
}
|
|
395
1008
|
|
|
396
1009
|
// ui/src/search/SearchSummary.jsx
|
|
397
|
-
import
|
|
1010
|
+
import React17 from "react";
|
|
398
1011
|
function SearchSummary(props) {
|
|
399
1012
|
let json = "{}";
|
|
400
1013
|
try {
|
|
@@ -402,11 +1015,11 @@ function SearchSummary(props) {
|
|
|
402
1015
|
} catch (_) {
|
|
403
1016
|
json = "{}";
|
|
404
1017
|
}
|
|
405
|
-
return /* @__PURE__ */
|
|
1018
|
+
return /* @__PURE__ */ React17.createElement("div", { "data-canopy-search-summary": "1" }, /* @__PURE__ */ React17.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: json } }));
|
|
406
1019
|
}
|
|
407
1020
|
|
|
408
1021
|
// ui/src/search/MdxSearchTabs.jsx
|
|
409
|
-
import
|
|
1022
|
+
import React18 from "react";
|
|
410
1023
|
function MdxSearchTabs(props) {
|
|
411
1024
|
let json = "{}";
|
|
412
1025
|
try {
|
|
@@ -414,11 +1027,11 @@ function MdxSearchTabs(props) {
|
|
|
414
1027
|
} catch (_) {
|
|
415
1028
|
json = "{}";
|
|
416
1029
|
}
|
|
417
|
-
return /* @__PURE__ */
|
|
1030
|
+
return /* @__PURE__ */ React18.createElement("div", { "data-canopy-search-tabs": "1" }, /* @__PURE__ */ React18.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: json } }));
|
|
418
1031
|
}
|
|
419
1032
|
|
|
420
1033
|
// ui/src/search/SearchResults.jsx
|
|
421
|
-
import
|
|
1034
|
+
import React19 from "react";
|
|
422
1035
|
function SearchResults({
|
|
423
1036
|
results = [],
|
|
424
1037
|
type = "all",
|
|
@@ -426,38 +1039,48 @@ function SearchResults({
|
|
|
426
1039
|
query = ""
|
|
427
1040
|
}) {
|
|
428
1041
|
if (!results.length) {
|
|
429
|
-
return /* @__PURE__ */
|
|
1042
|
+
return /* @__PURE__ */ React19.createElement("div", { className: "text-slate-600" }, /* @__PURE__ */ React19.createElement("em", null, "No results"));
|
|
430
1043
|
}
|
|
431
|
-
const
|
|
1044
|
+
const normalizedType = String(type || "all").toLowerCase();
|
|
1045
|
+
const isAnnotationView = normalizedType === "annotation";
|
|
432
1046
|
if (isAnnotationView) {
|
|
433
|
-
return /* @__PURE__ */
|
|
434
|
-
if (!r
|
|
435
|
-
return
|
|
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
|
-
);
|
|
1047
|
+
return /* @__PURE__ */ React19.createElement("div", { id: "search-results", className: "space-y-4" }, results.map((r, i) => {
|
|
1048
|
+
if (!r) return null;
|
|
1049
|
+
return renderTextCard(r, r.id || i);
|
|
447
1050
|
}));
|
|
448
1051
|
}
|
|
1052
|
+
const renderTextCard = (record, key) => {
|
|
1053
|
+
if (!record) return null;
|
|
1054
|
+
return /* @__PURE__ */ React19.createElement(
|
|
1055
|
+
TextCard,
|
|
1056
|
+
{
|
|
1057
|
+
key,
|
|
1058
|
+
href: record.href,
|
|
1059
|
+
title: record.title || record.href || "Untitled",
|
|
1060
|
+
annotation: record.annotation,
|
|
1061
|
+
summary: record.summary || record.summaryValue || "",
|
|
1062
|
+
metadata: Array.isArray(record.metadata) ? record.metadata : [],
|
|
1063
|
+
query
|
|
1064
|
+
}
|
|
1065
|
+
);
|
|
1066
|
+
};
|
|
1067
|
+
const isWorkRecord = (record) => String(record && record.type).toLowerCase() === "work";
|
|
1068
|
+
const shouldRenderAsTextCard = (record) => !isWorkRecord(record) || normalizedType !== "work";
|
|
449
1069
|
if (layout === "list") {
|
|
450
|
-
return /* @__PURE__ */
|
|
1070
|
+
return /* @__PURE__ */ React19.createElement("ul", { id: "search-results", className: "space-y-3" }, results.map((r, i) => {
|
|
1071
|
+
if (shouldRenderAsTextCard(r)) {
|
|
1072
|
+
return /* @__PURE__ */ React19.createElement("li", { key: i, className: `search-result ${r && r.type}` }, renderTextCard(r, i));
|
|
1073
|
+
}
|
|
451
1074
|
const hasDims = Number.isFinite(Number(r.thumbnailWidth)) && Number(r.thumbnailWidth) > 0 && Number.isFinite(Number(r.thumbnailHeight)) && Number(r.thumbnailHeight) > 0;
|
|
452
1075
|
const aspect = hasDims ? Number(r.thumbnailWidth) / Number(r.thumbnailHeight) : void 0;
|
|
453
|
-
return /* @__PURE__ */
|
|
1076
|
+
return /* @__PURE__ */ React19.createElement(
|
|
454
1077
|
"li",
|
|
455
1078
|
{
|
|
456
1079
|
key: i,
|
|
457
1080
|
className: `search-result ${r.type}`,
|
|
458
1081
|
"data-thumbnail-aspect-ratio": aspect
|
|
459
1082
|
},
|
|
460
|
-
/* @__PURE__ */
|
|
1083
|
+
/* @__PURE__ */ React19.createElement(
|
|
461
1084
|
Card,
|
|
462
1085
|
{
|
|
463
1086
|
href: r.href,
|
|
@@ -471,17 +1094,20 @@ function SearchResults({
|
|
|
471
1094
|
);
|
|
472
1095
|
}));
|
|
473
1096
|
}
|
|
474
|
-
return /* @__PURE__ */
|
|
1097
|
+
return /* @__PURE__ */ React19.createElement("div", { id: "search-results" }, /* @__PURE__ */ React19.createElement(Grid, null, results.map((r, i) => {
|
|
1098
|
+
if (shouldRenderAsTextCard(r)) {
|
|
1099
|
+
return /* @__PURE__ */ React19.createElement(GridItem, { key: i, className: `search-result ${r && r.type}` }, renderTextCard(r, i));
|
|
1100
|
+
}
|
|
475
1101
|
const hasDims = Number.isFinite(Number(r.thumbnailWidth)) && Number(r.thumbnailWidth) > 0 && Number.isFinite(Number(r.thumbnailHeight)) && Number(r.thumbnailHeight) > 0;
|
|
476
1102
|
const aspect = hasDims ? Number(r.thumbnailWidth) / Number(r.thumbnailHeight) : void 0;
|
|
477
|
-
return /* @__PURE__ */
|
|
1103
|
+
return /* @__PURE__ */ React19.createElement(
|
|
478
1104
|
GridItem,
|
|
479
1105
|
{
|
|
480
1106
|
key: i,
|
|
481
1107
|
className: `search-result ${r.type}`,
|
|
482
1108
|
"data-thumbnail-aspect-ratio": aspect
|
|
483
1109
|
},
|
|
484
|
-
/* @__PURE__ */
|
|
1110
|
+
/* @__PURE__ */ React19.createElement(
|
|
485
1111
|
Card,
|
|
486
1112
|
{
|
|
487
1113
|
href: r.href,
|
|
@@ -497,7 +1123,7 @@ function SearchResults({
|
|
|
497
1123
|
}
|
|
498
1124
|
|
|
499
1125
|
// ui/src/search/SearchTabs.jsx
|
|
500
|
-
import
|
|
1126
|
+
import React20 from "react";
|
|
501
1127
|
function SearchTabs({
|
|
502
1128
|
type = "all",
|
|
503
1129
|
onTypeChange,
|
|
@@ -512,7 +1138,7 @@ function SearchTabs({
|
|
|
512
1138
|
const toLabel = (t) => t && t.length ? t.charAt(0).toUpperCase() + t.slice(1) : "";
|
|
513
1139
|
const hasFilters = typeof onOpenFilters === "function";
|
|
514
1140
|
const filterBadge = activeFilterCount > 0 ? ` (${activeFilterCount})` : "";
|
|
515
|
-
return /* @__PURE__ */
|
|
1141
|
+
return /* @__PURE__ */ React20.createElement("div", { className: "canopy-search-tabs-wrapper" }, /* @__PURE__ */ React20.createElement(
|
|
516
1142
|
"div",
|
|
517
1143
|
{
|
|
518
1144
|
role: "tablist",
|
|
@@ -523,7 +1149,7 @@ function SearchTabs({
|
|
|
523
1149
|
const active = String(type).toLowerCase() === String(t).toLowerCase();
|
|
524
1150
|
const cRaw = counts && Object.prototype.hasOwnProperty.call(counts, t) ? counts[t] : void 0;
|
|
525
1151
|
const c = Number.isFinite(Number(cRaw)) ? Number(cRaw) : 0;
|
|
526
|
-
return /* @__PURE__ */
|
|
1152
|
+
return /* @__PURE__ */ React20.createElement(
|
|
527
1153
|
"button",
|
|
528
1154
|
{
|
|
529
1155
|
key: t,
|
|
@@ -538,7 +1164,7 @@ function SearchTabs({
|
|
|
538
1164
|
")"
|
|
539
1165
|
);
|
|
540
1166
|
})
|
|
541
|
-
), hasFilters ? /* @__PURE__ */
|
|
1167
|
+
), hasFilters ? /* @__PURE__ */ React20.createElement(
|
|
542
1168
|
"button",
|
|
543
1169
|
{
|
|
544
1170
|
type: "button",
|
|
@@ -546,12 +1172,12 @@ function SearchTabs({
|
|
|
546
1172
|
"aria-expanded": filtersOpen ? "true" : "false",
|
|
547
1173
|
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"
|
|
548
1174
|
},
|
|
549
|
-
/* @__PURE__ */
|
|
1175
|
+
/* @__PURE__ */ React20.createElement("span", null, filtersLabel, filterBadge)
|
|
550
1176
|
) : null);
|
|
551
1177
|
}
|
|
552
1178
|
|
|
553
1179
|
// ui/src/search/SearchFiltersDialog.jsx
|
|
554
|
-
import
|
|
1180
|
+
import React21 from "react";
|
|
555
1181
|
function toArray(input) {
|
|
556
1182
|
if (!input) return [];
|
|
557
1183
|
if (Array.isArray(input)) return input;
|
|
@@ -590,20 +1216,20 @@ function FacetSection({ facet, selected, onToggle }) {
|
|
|
590
1216
|
const selectedValues = selected.get(String(slug)) || /* @__PURE__ */ new Set();
|
|
591
1217
|
const checkboxId = (valueSlug) => `filter-${slug}-${valueSlug}`;
|
|
592
1218
|
const hasSelection = selectedValues.size > 0;
|
|
593
|
-
const [quickQuery, setQuickQuery] =
|
|
1219
|
+
const [quickQuery, setQuickQuery] = React21.useState("");
|
|
594
1220
|
const hasQuery = quickQuery.trim().length > 0;
|
|
595
|
-
const filteredValues =
|
|
1221
|
+
const filteredValues = React21.useMemo(
|
|
596
1222
|
() => facetMatches(values, quickQuery),
|
|
597
1223
|
[values, quickQuery]
|
|
598
1224
|
);
|
|
599
|
-
return /* @__PURE__ */
|
|
1225
|
+
return /* @__PURE__ */ React21.createElement(
|
|
600
1226
|
"details",
|
|
601
1227
|
{
|
|
602
1228
|
className: "canopy-search-filters__facet",
|
|
603
1229
|
open: hasSelection
|
|
604
1230
|
},
|
|
605
|
-
/* @__PURE__ */
|
|
606
|
-
/* @__PURE__ */
|
|
1231
|
+
/* @__PURE__ */ React21.createElement("summary", { className: "canopy-search-filters__facet-summary" }, /* @__PURE__ */ React21.createElement("span", null, label), /* @__PURE__ */ React21.createElement("span", { className: "canopy-search-filters__facet-count" }, values.length)),
|
|
1232
|
+
/* @__PURE__ */ React21.createElement("div", { className: "canopy-search-filters__facet-content" }, /* @__PURE__ */ React21.createElement("div", { className: "canopy-search-filters__quick" }, /* @__PURE__ */ React21.createElement(
|
|
607
1233
|
"input",
|
|
608
1234
|
{
|
|
609
1235
|
type: "search",
|
|
@@ -613,7 +1239,7 @@ function FacetSection({ facet, selected, onToggle }) {
|
|
|
613
1239
|
className: "canopy-search-filters__quick-input",
|
|
614
1240
|
"aria-label": `Filter ${label} values`
|
|
615
1241
|
}
|
|
616
|
-
), quickQuery ? /* @__PURE__ */
|
|
1242
|
+
), quickQuery ? /* @__PURE__ */ React21.createElement(
|
|
617
1243
|
"button",
|
|
618
1244
|
{
|
|
619
1245
|
type: "button",
|
|
@@ -621,11 +1247,11 @@ function FacetSection({ facet, selected, onToggle }) {
|
|
|
621
1247
|
className: "canopy-search-filters__quick-clear"
|
|
622
1248
|
},
|
|
623
1249
|
"Clear"
|
|
624
|
-
) : null), hasQuery && !filteredValues.length ? /* @__PURE__ */
|
|
1250
|
+
) : null), hasQuery && !filteredValues.length ? /* @__PURE__ */ React21.createElement("p", { className: "canopy-search-filters__facet-notice" }, "No matches found.") : null, /* @__PURE__ */ React21.createElement("ul", { className: "canopy-search-filters__facet-list" }, filteredValues.map((entry) => {
|
|
625
1251
|
const valueSlug = String(entry.slug || entry.value || "");
|
|
626
1252
|
const isChecked = selectedValues.has(valueSlug);
|
|
627
1253
|
const inputId = checkboxId(valueSlug);
|
|
628
|
-
return /* @__PURE__ */
|
|
1254
|
+
return /* @__PURE__ */ React21.createElement("li", { key: valueSlug, className: "canopy-search-filters__facet-item" }, /* @__PURE__ */ React21.createElement(
|
|
629
1255
|
"input",
|
|
630
1256
|
{
|
|
631
1257
|
id: inputId,
|
|
@@ -637,15 +1263,15 @@ function FacetSection({ facet, selected, onToggle }) {
|
|
|
637
1263
|
if (onToggle) onToggle(slug, valueSlug, nextChecked);
|
|
638
1264
|
}
|
|
639
1265
|
}
|
|
640
|
-
), /* @__PURE__ */
|
|
1266
|
+
), /* @__PURE__ */ React21.createElement(
|
|
641
1267
|
"label",
|
|
642
1268
|
{
|
|
643
1269
|
htmlFor: inputId,
|
|
644
1270
|
className: "canopy-search-filters__facet-label"
|
|
645
1271
|
},
|
|
646
|
-
/* @__PURE__ */
|
|
1272
|
+
/* @__PURE__ */ React21.createElement("span", null, entry.value, " ", Number.isFinite(entry.doc_count) ? /* @__PURE__ */ React21.createElement("span", { className: "canopy-search-filters__facet-count" }, "(", entry.doc_count, ")") : null)
|
|
647
1273
|
));
|
|
648
|
-
}), !filteredValues.length && !hasQuery ? /* @__PURE__ */
|
|
1274
|
+
}), !filteredValues.length && !hasQuery ? /* @__PURE__ */ React21.createElement("li", { className: "canopy-search-filters__facet-empty" }, "No values available.") : null))
|
|
649
1275
|
);
|
|
650
1276
|
}
|
|
651
1277
|
function SearchFiltersDialog(props = {}) {
|
|
@@ -656,35 +1282,51 @@ function SearchFiltersDialog(props = {}) {
|
|
|
656
1282
|
selected = {},
|
|
657
1283
|
onToggle,
|
|
658
1284
|
onClear,
|
|
659
|
-
title
|
|
660
|
-
subtitle = "Refine results by metadata"
|
|
1285
|
+
title,
|
|
1286
|
+
subtitle = "Refine results by metadata",
|
|
1287
|
+
brandLabel = "Canopy IIIF",
|
|
1288
|
+
brandHref = "/",
|
|
1289
|
+
logo: SiteLogo
|
|
661
1290
|
} = props;
|
|
662
1291
|
const selectedMap = normalizeSelected(selected);
|
|
663
1292
|
const activeCount = Array.from(selectedMap.values()).reduce(
|
|
664
1293
|
(total, set) => total + set.size,
|
|
665
1294
|
0
|
|
666
1295
|
);
|
|
1296
|
+
React21.useEffect(() => {
|
|
1297
|
+
if (!open) return void 0;
|
|
1298
|
+
if (typeof document === "undefined") return void 0;
|
|
1299
|
+
const body = document.body;
|
|
1300
|
+
const root = document.documentElement;
|
|
1301
|
+
const prevBody = body ? body.style.overflow : "";
|
|
1302
|
+
const prevRoot = root ? root.style.overflow : "";
|
|
1303
|
+
if (body) body.style.overflow = "hidden";
|
|
1304
|
+
if (root) root.style.overflow = "hidden";
|
|
1305
|
+
return () => {
|
|
1306
|
+
if (body) body.style.overflow = prevBody;
|
|
1307
|
+
if (root) root.style.overflow = prevRoot;
|
|
1308
|
+
};
|
|
1309
|
+
}, [open]);
|
|
667
1310
|
if (!open) return null;
|
|
668
|
-
|
|
669
|
-
|
|
1311
|
+
const brandId = "canopy-modal-filters-label";
|
|
1312
|
+
const subtitleText = subtitle != null ? subtitle : title;
|
|
1313
|
+
return /* @__PURE__ */ React21.createElement(
|
|
1314
|
+
CanopyModal,
|
|
670
1315
|
{
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
1316
|
+
id: "canopy-modal-filters",
|
|
1317
|
+
variant: "filters",
|
|
1318
|
+
open: true,
|
|
1319
|
+
labelledBy: brandId,
|
|
1320
|
+
label: brandLabel,
|
|
1321
|
+
logo: SiteLogo,
|
|
1322
|
+
href: brandHref,
|
|
1323
|
+
closeLabel: "Close filters",
|
|
1324
|
+
onClose: () => onOpenChange && onOpenChange(false),
|
|
1325
|
+
onBackgroundClick: () => onOpenChange && onOpenChange(false),
|
|
1326
|
+
bodyClassName: "canopy-modal__body--filters"
|
|
678
1327
|
},
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
{
|
|
682
|
-
type: "button",
|
|
683
|
-
onClick: () => onOpenChange && onOpenChange(false),
|
|
684
|
-
className: "canopy-search-filters__close"
|
|
685
|
-
},
|
|
686
|
-
"Close"
|
|
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(
|
|
1328
|
+
subtitleText ? /* @__PURE__ */ React21.createElement("p", { className: "canopy-search-filters__subtitle" }, subtitleText) : null,
|
|
1329
|
+
/* @__PURE__ */ React21.createElement("div", { className: "canopy-search-filters__body" }, Array.isArray(facets) && facets.length ? /* @__PURE__ */ React21.createElement("div", { className: "canopy-search-filters__facets" }, facets.map((facet) => /* @__PURE__ */ React21.createElement(
|
|
688
1330
|
FacetSection,
|
|
689
1331
|
{
|
|
690
1332
|
key: facet.slug || facet.label,
|
|
@@ -692,7 +1334,8 @@ function SearchFiltersDialog(props = {}) {
|
|
|
692
1334
|
selected: selectedMap,
|
|
693
1335
|
onToggle
|
|
694
1336
|
}
|
|
695
|
-
))) : /* @__PURE__ */
|
|
1337
|
+
))) : /* @__PURE__ */ React21.createElement("p", { className: "canopy-search-filters__empty" }, "No filters are available for this collection.")),
|
|
1338
|
+
/* @__PURE__ */ React21.createElement("footer", { className: "canopy-search-filters__footer" }, /* @__PURE__ */ React21.createElement("div", null, activeCount ? `${activeCount} filter${activeCount === 1 ? "" : "s"} applied` : "No filters applied"), /* @__PURE__ */ React21.createElement("div", { className: "canopy-search-filters__footer-actions" }, /* @__PURE__ */ React21.createElement(
|
|
696
1339
|
"button",
|
|
697
1340
|
{
|
|
698
1341
|
type: "button",
|
|
@@ -703,7 +1346,7 @@ function SearchFiltersDialog(props = {}) {
|
|
|
703
1346
|
className: "canopy-search-filters__button canopy-search-filters__button--secondary"
|
|
704
1347
|
},
|
|
705
1348
|
"Clear all"
|
|
706
|
-
), /* @__PURE__ */
|
|
1349
|
+
), /* @__PURE__ */ React21.createElement(
|
|
707
1350
|
"button",
|
|
708
1351
|
{
|
|
709
1352
|
type: "button",
|
|
@@ -711,213 +1354,12 @@ function SearchFiltersDialog(props = {}) {
|
|
|
711
1354
|
className: "canopy-search-filters__button canopy-search-filters__button--primary"
|
|
712
1355
|
},
|
|
713
1356
|
"Done"
|
|
714
|
-
)))
|
|
715
|
-
);
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
// ui/src/search-form/MdxSearchFormModal.jsx
|
|
719
|
-
import React18 from "react";
|
|
720
|
-
|
|
721
|
-
// ui/src/Icons.jsx
|
|
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" }));
|
|
724
|
-
|
|
725
|
-
// ui/src/search/SearchPanelForm.jsx
|
|
726
|
-
import React16 from "react";
|
|
727
|
-
function readBasePath() {
|
|
728
|
-
const normalize = (val) => {
|
|
729
|
-
const raw = typeof val === "string" ? val.trim() : "";
|
|
730
|
-
if (!raw) return "";
|
|
731
|
-
return raw.replace(/\/+$/, "");
|
|
732
|
-
};
|
|
733
|
-
try {
|
|
734
|
-
if (typeof window !== "undefined" && window.CANOPY_BASE_PATH != null) {
|
|
735
|
-
const fromWindow = normalize(window.CANOPY_BASE_PATH);
|
|
736
|
-
if (fromWindow) return fromWindow;
|
|
737
|
-
}
|
|
738
|
-
} catch (_) {
|
|
739
|
-
}
|
|
740
|
-
try {
|
|
741
|
-
if (typeof globalThis !== "undefined" && globalThis.CANOPY_BASE_PATH != null) {
|
|
742
|
-
const fromGlobal = normalize(globalThis.CANOPY_BASE_PATH);
|
|
743
|
-
if (fromGlobal) return fromGlobal;
|
|
744
|
-
}
|
|
745
|
-
} catch (_) {
|
|
746
|
-
}
|
|
747
|
-
try {
|
|
748
|
-
if (typeof process !== "undefined" && process.env && process.env.CANOPY_BASE_PATH) {
|
|
749
|
-
const fromEnv = normalize(process.env.CANOPY_BASE_PATH);
|
|
750
|
-
if (fromEnv) return fromEnv;
|
|
751
|
-
}
|
|
752
|
-
} catch (_) {
|
|
753
|
-
}
|
|
754
|
-
return "";
|
|
755
|
-
}
|
|
756
|
-
function isAbsoluteUrl(href) {
|
|
757
|
-
try {
|
|
758
|
-
return /^https?:/i.test(String(href || ""));
|
|
759
|
-
} catch (_) {
|
|
760
|
-
return false;
|
|
761
|
-
}
|
|
762
|
-
}
|
|
763
|
-
function resolveSearchPath(pathValue) {
|
|
764
|
-
let raw = typeof pathValue === "string" ? pathValue.trim() : "";
|
|
765
|
-
if (!raw) raw = "/search";
|
|
766
|
-
if (isAbsoluteUrl(raw)) return raw;
|
|
767
|
-
const normalizedPath = raw.startsWith("/") ? raw : `/${raw}`;
|
|
768
|
-
const base = readBasePath();
|
|
769
|
-
if (!base) return normalizedPath;
|
|
770
|
-
const baseWithLead = base.startsWith("/") ? base : `/${base}`;
|
|
771
|
-
const baseTrimmed = baseWithLead.replace(/\/+$/, "");
|
|
772
|
-
if (!baseTrimmed) return normalizedPath;
|
|
773
|
-
if (normalizedPath === baseTrimmed || normalizedPath.startsWith(`${baseTrimmed}/`)) {
|
|
774
|
-
return normalizedPath;
|
|
775
|
-
}
|
|
776
|
-
const pathTrimmed = normalizedPath.replace(/^\/+/, "");
|
|
777
|
-
return `${baseTrimmed}/${pathTrimmed}`;
|
|
778
|
-
}
|
|
779
|
-
function SearchPanelForm(props = {}) {
|
|
780
|
-
const {
|
|
781
|
-
placeholder = "Search\u2026",
|
|
782
|
-
buttonLabel = "Search",
|
|
783
|
-
label,
|
|
784
|
-
searchPath = "/search",
|
|
785
|
-
inputId: inputIdProp,
|
|
786
|
-
clearLabel = "Clear search"
|
|
787
|
-
} = props || {};
|
|
788
|
-
const text = typeof label === "string" && label.trim() ? label.trim() : buttonLabel;
|
|
789
|
-
const action = React16.useMemo(
|
|
790
|
-
() => resolveSearchPath(searchPath),
|
|
791
|
-
[searchPath]
|
|
792
|
-
);
|
|
793
|
-
const autoId = typeof React16.useId === "function" ? React16.useId() : void 0;
|
|
794
|
-
const [fallbackId] = React16.useState(
|
|
795
|
-
() => `canopy-search-form-${Math.random().toString(36).slice(2, 10)}`
|
|
796
|
-
);
|
|
797
|
-
const inputId = inputIdProp || autoId || fallbackId;
|
|
798
|
-
const inputRef = React16.useRef(null);
|
|
799
|
-
const [hasValue, setHasValue] = React16.useState(false);
|
|
800
|
-
const focusInput = React16.useCallback(() => {
|
|
801
|
-
const el = inputRef.current;
|
|
802
|
-
if (!el) return;
|
|
803
|
-
if (document.activeElement === el) return;
|
|
804
|
-
try {
|
|
805
|
-
el.focus({ preventScroll: true });
|
|
806
|
-
} catch (_) {
|
|
807
|
-
try {
|
|
808
|
-
el.focus();
|
|
809
|
-
} catch (_2) {
|
|
810
|
-
}
|
|
811
|
-
}
|
|
812
|
-
}, []);
|
|
813
|
-
const handlePointerDown = React16.useCallback(
|
|
814
|
-
(event) => {
|
|
815
|
-
const target = event.target;
|
|
816
|
-
if (target && typeof target.closest === "function") {
|
|
817
|
-
if (target.closest("[data-canopy-search-form-trigger]")) return;
|
|
818
|
-
if (target.closest("[data-canopy-search-form-clear]")) return;
|
|
819
|
-
}
|
|
820
|
-
event.preventDefault();
|
|
821
|
-
focusInput();
|
|
822
|
-
},
|
|
823
|
-
[focusInput]
|
|
824
|
-
);
|
|
825
|
-
React16.useEffect(() => {
|
|
826
|
-
const el = inputRef.current;
|
|
827
|
-
if (!el) return;
|
|
828
|
-
if (el.value && el.value.trim()) {
|
|
829
|
-
setHasValue(true);
|
|
830
|
-
}
|
|
831
|
-
}, []);
|
|
832
|
-
const handleInputChange = React16.useCallback((event) => {
|
|
833
|
-
var _a;
|
|
834
|
-
const nextHasValue = Boolean(
|
|
835
|
-
((_a = event == null ? void 0 : event.target) == null ? void 0 : _a.value) && event.target.value.trim()
|
|
836
|
-
);
|
|
837
|
-
setHasValue(nextHasValue);
|
|
838
|
-
}, []);
|
|
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(
|
|
851
|
-
"form",
|
|
852
|
-
{
|
|
853
|
-
action,
|
|
854
|
-
method: "get",
|
|
855
|
-
role: "search",
|
|
856
|
-
autoComplete: "off",
|
|
857
|
-
spellCheck: "false",
|
|
858
|
-
className: "canopy-search-form canopy-search-form-shell",
|
|
859
|
-
onPointerDown: handlePointerDown,
|
|
860
|
-
"data-has-value": hasValue ? "1" : "0"
|
|
861
|
-
},
|
|
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",
|
|
864
|
-
{
|
|
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
|
|
888
|
-
},
|
|
889
|
-
"\xD7"
|
|
890
|
-
) : null,
|
|
891
|
-
/* @__PURE__ */ React16.createElement(
|
|
892
|
-
"button",
|
|
893
|
-
{
|
|
894
|
-
type: "submit",
|
|
895
|
-
"data-canopy-search-form-trigger": "submit",
|
|
896
|
-
className: "canopy-search-form__submit"
|
|
897
|
-
},
|
|
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"))
|
|
900
|
-
)
|
|
901
|
-
);
|
|
902
|
-
}
|
|
903
|
-
|
|
904
|
-
// ui/src/search/SearchPanelTeaserResults.jsx
|
|
905
|
-
import React17 from "react";
|
|
906
|
-
function SearchPanelTeaserResults(props = {}) {
|
|
907
|
-
const { style, className } = props || {};
|
|
908
|
-
const classes = ["canopy-search-teaser", className].filter(Boolean).join(" ");
|
|
909
|
-
return /* @__PURE__ */ React17.createElement(
|
|
910
|
-
"div",
|
|
911
|
-
{
|
|
912
|
-
"data-canopy-search-form-panel": true,
|
|
913
|
-
className: classes || void 0,
|
|
914
|
-
style
|
|
915
|
-
},
|
|
916
|
-
/* @__PURE__ */ React17.createElement("div", { id: "cplist" })
|
|
1357
|
+
)))
|
|
917
1358
|
);
|
|
918
1359
|
}
|
|
919
1360
|
|
|
920
1361
|
// ui/src/search-form/MdxSearchFormModal.jsx
|
|
1362
|
+
import React22 from "react";
|
|
921
1363
|
function MdxSearchFormModal(props = {}) {
|
|
922
1364
|
const {
|
|
923
1365
|
placeholder = "Search\u2026",
|
|
@@ -933,31 +1375,12 @@ function MdxSearchFormModal(props = {}) {
|
|
|
933
1375
|
const text = typeof label === "string" && label.trim() ? label.trim() : buttonLabel;
|
|
934
1376
|
const resolvedSearchPath = resolveSearchPath(searchPath);
|
|
935
1377
|
const data = { placeholder, hotkey, maxResults, groupOrder, label: text, searchPath: resolvedSearchPath };
|
|
936
|
-
return /* @__PURE__ */
|
|
937
|
-
}
|
|
938
|
-
|
|
939
|
-
// ui/src/search/SearchPanel.jsx
|
|
940
|
-
import React19 from "react";
|
|
941
|
-
function SearchPanel(props = {}) {
|
|
942
|
-
const {
|
|
943
|
-
placeholder = "Search\u2026",
|
|
944
|
-
hotkey = "mod+k",
|
|
945
|
-
maxResults = 8,
|
|
946
|
-
groupOrder = ["work", "docs", "page"],
|
|
947
|
-
// Kept for backward compat; form always renders submit
|
|
948
|
-
button = true,
|
|
949
|
-
// eslint-disable-line no-unused-vars
|
|
950
|
-
buttonLabel = "Search",
|
|
951
|
-
label,
|
|
952
|
-
searchPath = "/search"
|
|
953
|
-
} = props || {};
|
|
954
|
-
const text = typeof label === "string" && label.trim() ? label.trim() : buttonLabel;
|
|
955
|
-
const resolvedSearchPath = resolveSearchPath(searchPath);
|
|
956
|
-
const data = { placeholder, hotkey, maxResults, groupOrder, label: text, searchPath: resolvedSearchPath };
|
|
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) } }));
|
|
1378
|
+
return /* @__PURE__ */ React22.createElement("div", { "data-canopy-search-form": true, className: "flex-1 min-w-0" }, /* @__PURE__ */ React22.createElement("div", { className: "relative w-full" }, /* @__PURE__ */ React22.createElement(SearchPanelForm, { placeholder, buttonLabel, label, searchPath: resolvedSearchPath }), /* @__PURE__ */ React22.createElement(SearchPanelTeaserResults, null)), /* @__PURE__ */ React22.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: JSON.stringify(data) } }));
|
|
958
1379
|
}
|
|
959
1380
|
export {
|
|
960
|
-
|
|
1381
|
+
CanopyBrand,
|
|
1382
|
+
CanopyHeader,
|
|
1383
|
+
CanopyModal,
|
|
961
1384
|
Card,
|
|
962
1385
|
Grid,
|
|
963
1386
|
GridItem,
|
|
@@ -975,6 +1398,7 @@ export {
|
|
|
975
1398
|
MdxSearchTabs as SearchTabs,
|
|
976
1399
|
SearchTabs as SearchTabsUI,
|
|
977
1400
|
Slider,
|
|
1401
|
+
TextCard,
|
|
978
1402
|
Viewer
|
|
979
1403
|
};
|
|
980
1404
|
//# sourceMappingURL=index.mjs.map
|