ardo 3.5.0 → 3.6.0

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.
Files changed (70) hide show
  1. package/README.md +1 -1
  2. package/dist/{DocPage-EIVMae_6.js → DocPage-Yt8MMpBg.js} +351 -67
  3. package/dist/DocPage-Yt8MMpBg.js.map +1 -0
  4. package/dist/assets/src/ui/components/Accordion.css.ts.vanilla-BPEoNgJt.css +103 -0
  5. package/dist/assets/src/ui/components/Badge.css.ts.vanilla-D9jsj0rg.css +44 -0
  6. package/dist/assets/src/ui/components/Card.css.ts.vanilla-CO6uiEzC.css +97 -0
  7. package/dist/assets/src/ui/theme/{dark.css.ts.vanilla-CSJkJvIz.css → dark.css.ts.vanilla-yFCWJiX4.css} +3 -0
  8. package/dist/assets/src/ui/theme/{light.css.ts.vanilla-CFz9jeJK.css → light.css.ts.vanilla-DDKnXFOi.css} +3 -0
  9. package/dist/{brand-icons-Di8w0Nu9.js → brand-icons-DBTSSnty.js} +1 -1
  10. package/dist/{brand-icons-Di8w0Nu9.js.map → brand-icons-DBTSSnty.js.map} +1 -1
  11. package/dist/config/index.d.ts +2 -2
  12. package/dist/config/index.d.ts.map +1 -1
  13. package/dist/config/index.js +5 -0
  14. package/dist/config/index.js.map +1 -1
  15. package/dist/{contract.css-eFQbUr4z.d.ts → contract.css-BwTjjNXQ.d.ts} +4 -1
  16. package/dist/contract.css-BwTjjNXQ.d.ts.map +1 -0
  17. package/dist/{favicon-Cx-inut3.js → favicon-DN3ymUVm.js} +1 -1
  18. package/dist/{favicon-Cx-inut3.js.map → favicon-DN3ymUVm.js.map} +1 -1
  19. package/dist/{generator-CYSyo4Vz.js → generator-Cc4WGRdf.js} +1 -1
  20. package/dist/{generator-CYSyo4Vz.js.map → generator-Cc4WGRdf.js.map} +1 -1
  21. package/dist/icons/index.d.ts +5 -6
  22. package/dist/icons/index.d.ts.map +1 -1
  23. package/dist/icons/index.js +1 -1
  24. package/dist/{index-CuMTHUxX.d.ts → index-BSWG2TdH.d.ts} +6 -8
  25. package/dist/index-BSWG2TdH.d.ts.map +1 -0
  26. package/dist/{index-BcekgOfA.d.ts → index-Dg1xEJ_Y.d.ts} +132 -45
  27. package/dist/index-Dg1xEJ_Y.d.ts.map +1 -0
  28. package/dist/index.d.ts +3 -3
  29. package/dist/index.js +3 -3
  30. package/dist/mdx/provider.d.ts +1 -1
  31. package/dist/mdx/provider.d.ts.map +1 -1
  32. package/dist/mdx/provider.js +6 -1
  33. package/dist/mdx/provider.js.map +1 -1
  34. package/dist/runtime/index.d.ts +2 -2
  35. package/dist/runtime/index.js +1 -1
  36. package/dist/{sidebar-utils-C06DJsx4.js → sidebar-utils-tTBVAPLE.js} +1 -1
  37. package/dist/{sidebar-utils-C06DJsx4.js.map → sidebar-utils-tTBVAPLE.js.map} +1 -1
  38. package/dist/theme/index.d.ts +7 -1
  39. package/dist/theme/index.d.ts.map +1 -1
  40. package/dist/theme/index.js +9 -0
  41. package/dist/theme/index.js.map +1 -1
  42. package/dist/typedoc/components/index.d.ts +8 -8
  43. package/dist/typedoc/components/index.d.ts.map +1 -1
  44. package/dist/typedoc/components/index.js.map +1 -1
  45. package/dist/typedoc/index.d.ts +1 -1
  46. package/dist/typedoc/index.d.ts.map +1 -1
  47. package/dist/typedoc/index.js +3 -3
  48. package/dist/typedoc/index.js.map +1 -1
  49. package/dist/{types-Ck2Vm7NB.d.ts → types-DZKj8kWR.d.ts} +1 -1
  50. package/dist/types-DZKj8kWR.d.ts.map +1 -0
  51. package/dist/{types-B75OhnGa.d.ts → types-iGO1oGpR.d.ts} +49 -4
  52. package/dist/types-iGO1oGpR.d.ts.map +1 -0
  53. package/dist/ui/index.d.ts +2 -2
  54. package/dist/ui/index.js +3 -3
  55. package/dist/ui/styles.css +206 -0
  56. package/dist/ui/styles.js +4 -2
  57. package/dist/{ui-B6X8gAvz.js → ui-Cs2nhBpA.js} +288 -131
  58. package/dist/ui-Cs2nhBpA.js.map +1 -0
  59. package/dist/vite/index.d.ts +1 -1
  60. package/dist/vite/index.d.ts.map +1 -1
  61. package/dist/vite/index.js +543 -33
  62. package/dist/vite/index.js.map +1 -1
  63. package/package.json +15 -15
  64. package/dist/DocPage-EIVMae_6.js.map +0 -1
  65. package/dist/contract.css-eFQbUr4z.d.ts.map +0 -1
  66. package/dist/index-BcekgOfA.d.ts.map +0 -1
  67. package/dist/index-CuMTHUxX.d.ts.map +0 -1
  68. package/dist/types-B75OhnGa.d.ts.map +0 -1
  69. package/dist/types-Ck2Vm7NB.d.ts.map +0 -1
  70. package/dist/ui-B6X8gAvz.js.map +0 -1
@@ -1,7 +1,7 @@
1
- import { a as ArdoProvider, d as useArdoSidebar, l as useArdoContexts, o as ArdoSiteConfigProvider, s as useArdoConfig, u as useArdoPageData } from "./sidebar-utils-C06DJsx4.js";
2
- import { $ as footerLink, A as home, B as MonitorIcon, F as ChevronDownIcon, G as SunIcon, H as PackageIcon, I as CodeIcon, K as WrenchIcon, L as FileCodeIcon, M as layout, N as BookOpenIcon, O as ArdoLayout, P as BoxIcon, R as FileTextIcon, U as SearchIcon, V as MoonIcon, W as SettingsIcon, Y as footerArdoLink, Z as footerContainer, j as homeMain, nt as footerPrimary, q as XIcon, tt as footerOwl, z as MessageCircleIcon } from "./DocPage-EIVMae_6.js";
3
- import { c as TwitterIcon, i as LinkedinIcon, n as GithubIcon, o as NpmIcon, u as YoutubeIcon } from "./brand-icons-Di8w0Nu9.js";
4
- import React, { Children, cloneElement, createContext, isValidElement, use, useEffect, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
1
+ import { a as ArdoProvider, d as useArdoSidebar, l as useArdoContexts, o as ArdoSiteConfigProvider, s as useArdoConfig, u as useArdoPageData } from "./sidebar-utils-tTBVAPLE.js";
2
+ import { B as ChevronDownIcon, F as home, G as MonitorIcon, H as FileCodeIcon, I as homeMain, J as SearchIcon, K as MoonIcon, L as layout, N as ArdoLayout, Q as XIcon, R as BookOpenIcon, U as FileTextIcon, V as CodeIcon, W as MessageCircleIcon, X as SunIcon, Y as SettingsIcon, Z as WrenchIcon, et as footerArdoLink, it as footerLink, nt as footerContainer, ot as footerOwl, q as PackageIcon, st as footerPrimary, z as BoxIcon } from "./DocPage-Yt8MMpBg.js";
3
+ import { c as TwitterIcon, i as LinkedinIcon, n as GithubIcon, o as NpmIcon, u as YoutubeIcon } from "./brand-icons-DBTSSnty.js";
4
+ import React, { Children, cloneElement, createContext, isValidElement, use, useCallback, useEffect, useId, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
5
5
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
6
6
  import { Link, NavLink, Outlet, isRouteErrorResponse, useLocation, useMatches, useNavigate, useRouteError } from "react-router";
7
7
  import "./assets/src/ui/components/HeaderSearch.css.ts.vanilla-KAo_Mlc-.css";
@@ -224,6 +224,45 @@ function ArdoFooter({ children, className, ardoLink = true, message, copyright,
224
224
  var inline = "xapf7e0";
225
225
  var trigger = "xapf7e1";
226
226
  //#endregion
227
+ //#region src/ui/components/search-a11y.ts
228
+ function getSearchOptionId(listboxId, resultId, index) {
229
+ return `${listboxId}-option-${toDomIdSegment(resultId)}-${index}`;
230
+ }
231
+ function getActiveSearchOptionId({ getOptionId, isOpen, results, selectedIndex }) {
232
+ if (!isOpen || results.length === 0) return;
233
+ const selectedResult = getResultAtIndex(results, selectedIndex);
234
+ if (selectedResult == null) return;
235
+ return getOptionId(selectedResult.id, selectedIndex);
236
+ }
237
+ function getSearchKeyboardAction({ key, results, selectedIndex }) {
238
+ switch (key) {
239
+ case "ArrowDown": return results.length > 0 ? {
240
+ type: "select-index",
241
+ index: Math.min(selectedIndex + 1, results.length - 1)
242
+ } : { type: "none" };
243
+ case "ArrowUp": return results.length > 0 ? {
244
+ type: "select-index",
245
+ index: Math.max(selectedIndex - 1, 0)
246
+ } : { type: "none" };
247
+ case "Enter": {
248
+ const selectedResult = getResultAtIndex(results, selectedIndex);
249
+ return selectedResult == null ? { type: "none" } : {
250
+ type: "navigate",
251
+ path: selectedResult.path
252
+ };
253
+ }
254
+ case "Escape": return { type: "close" };
255
+ default: return { type: "none" };
256
+ }
257
+ }
258
+ function toDomIdSegment(value) {
259
+ return Array.from(value, (char) => char.charCodeAt(0).toString(36)).join("-");
260
+ }
261
+ function getResultAtIndex(results, selectedIndex) {
262
+ if (selectedIndex < 0 || selectedIndex >= results.length) return;
263
+ return results[selectedIndex];
264
+ }
265
+ //#endregion
227
266
  //#region src/ui/components/search-hooks.ts
228
267
  function useGlobalSearchShortcut(inputRef, setIsOpen) {
229
268
  useEffect(() => {
@@ -311,6 +350,64 @@ function SearchPopover({ anchorRef, children }) {
311
350
  }), document.body);
312
351
  }
313
352
  //#endregion
353
+ //#region src/ui/components/SearchResults.tsx
354
+ function SearchResults({ getOptionId, listboxId, results, selectedIndex, query, onClose }) {
355
+ return /* @__PURE__ */ jsxs(Fragment, { children: [results.length > 0 ? /* @__PURE__ */ jsx("ul", {
356
+ id: listboxId,
357
+ className: searchResults,
358
+ role: "listbox",
359
+ "aria-label": "Search results",
360
+ children: results.map((result, index) => /* @__PURE__ */ jsx("li", {
361
+ role: "presentation",
362
+ children: /* @__PURE__ */ jsxs(Link, {
363
+ id: getOptionId(result.id, index),
364
+ to: result.path,
365
+ role: "option",
366
+ "aria-selected": index === selectedIndex,
367
+ className: [searchResult, index === selectedIndex && "selected"].filter(Boolean).join(" "),
368
+ onClick: onClose,
369
+ children: [/* @__PURE__ */ jsx("span", {
370
+ className: searchResultTitle,
371
+ children: result.title
372
+ }), result.section !== void 0 && /* @__PURE__ */ jsx("span", {
373
+ className: "wxcdiv7",
374
+ children: result.section
375
+ })]
376
+ })
377
+ }, result.id))
378
+ }) : /* @__PURE__ */ jsx("div", {
379
+ id: listboxId,
380
+ role: "listbox",
381
+ "aria-label": "Search results",
382
+ children: /* @__PURE__ */ jsxs("div", {
383
+ className: searchNoResults,
384
+ role: "status",
385
+ "aria-live": "polite",
386
+ children: [/* @__PURE__ */ jsx(ArdoOwlMark, {
387
+ size: 36,
388
+ className: searchNoResultsOwl,
389
+ title: ""
390
+ }), /* @__PURE__ */ jsxs("span", { children: [
391
+ "No results found for \"",
392
+ query,
393
+ "\""
394
+ ] })]
395
+ })
396
+ }), /* @__PURE__ */ jsxs("div", {
397
+ className: searchFooter,
398
+ children: [
399
+ /* @__PURE__ */ jsxs("span", { children: [
400
+ /* @__PURE__ */ jsx("kbd", { children: "↑" }),
401
+ " ",
402
+ /* @__PURE__ */ jsx("kbd", { children: "↓" }),
403
+ " to navigate"
404
+ ] }),
405
+ /* @__PURE__ */ jsxs("span", { children: [/* @__PURE__ */ jsx("kbd", { children: "↵" }), " to select"] }),
406
+ /* @__PURE__ */ jsxs("span", { children: [/* @__PURE__ */ jsx("kbd", { children: "esc" }), " to close"] })
407
+ ]
408
+ })] });
409
+ }
410
+ //#endregion
314
411
  //#region src/ui/components/Search.tsx
315
412
  function useSearchIndex() {
316
413
  return useMemo(() => {
@@ -348,46 +445,6 @@ function normalizeResults(rawResults) {
348
445
  }];
349
446
  });
350
447
  }
351
- function SearchResults({ results, selectedIndex, query, onClose }) {
352
- return /* @__PURE__ */ jsxs(Fragment, { children: [results.length > 0 ? /* @__PURE__ */ jsx("ul", {
353
- className: searchResults,
354
- children: results.map((result, index) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(Link, {
355
- to: result.path,
356
- className: [searchResult, index === selectedIndex && "selected"].filter(Boolean).join(" "),
357
- onClick: onClose,
358
- children: [/* @__PURE__ */ jsx("span", {
359
- className: searchResultTitle,
360
- children: result.title
361
- }), result.section !== void 0 && /* @__PURE__ */ jsx("span", {
362
- className: "wxcdiv7",
363
- children: result.section
364
- })]
365
- }) }, result.id))
366
- }) : /* @__PURE__ */ jsxs("div", {
367
- className: searchNoResults,
368
- children: [/* @__PURE__ */ jsx(ArdoOwlMark, {
369
- size: 36,
370
- className: searchNoResultsOwl,
371
- title: ""
372
- }), /* @__PURE__ */ jsxs("span", { children: [
373
- "No results found for \"",
374
- query,
375
- "\""
376
- ] })]
377
- }), /* @__PURE__ */ jsxs("div", {
378
- className: searchFooter,
379
- children: [
380
- /* @__PURE__ */ jsxs("span", { children: [
381
- /* @__PURE__ */ jsx("kbd", { children: "↑" }),
382
- " ",
383
- /* @__PURE__ */ jsx("kbd", { children: "↓" }),
384
- " to navigate"
385
- ] }),
386
- /* @__PURE__ */ jsxs("span", { children: [/* @__PURE__ */ jsx("kbd", { children: "↵" }), " to select"] }),
387
- /* @__PURE__ */ jsxs("span", { children: [/* @__PURE__ */ jsx("kbd", { children: "esc" }), " to close"] })
388
- ]
389
- })] });
390
- }
391
448
  function useSearch(searchIndex) {
392
449
  const [isOpen, setIsOpen] = useState(false);
393
450
  const [query, setQuery] = useState("");
@@ -415,7 +472,7 @@ function useSearch(searchIndex) {
415
472
  search
416
473
  };
417
474
  }
418
- function SearchInput({ inputRef, placeholder, query, hasQuery, onSearch, onKeyDown, onFocus }) {
475
+ function SearchInput({ inputRef, placeholder, query, hasQuery, onSearch, onKeyDown, onFocus, activeOptionId, expanded, listboxId }) {
419
476
  return /* @__PURE__ */ jsxs("div", {
420
477
  className: searchField,
421
478
  children: [
@@ -431,7 +488,13 @@ function SearchInput({ inputRef, placeholder, query, hasQuery, onSearch, onKeyDo
431
488
  },
432
489
  onKeyDown,
433
490
  onFocus,
434
- "aria-label": "Search"
491
+ "aria-label": "Search",
492
+ role: "combobox",
493
+ "aria-autocomplete": "list",
494
+ "aria-expanded": expanded,
495
+ "aria-controls": listboxId,
496
+ "aria-activedescendant": activeOptionId,
497
+ "aria-haspopup": "listbox"
435
498
  }),
436
499
  hasQuery && /* @__PURE__ */ jsx("button", {
437
500
  type: "button",
@@ -451,12 +514,32 @@ function SearchInput({ inputRef, placeholder, query, hasQuery, onSearch, onKeyDo
451
514
  ]
452
515
  });
453
516
  }
517
+ function useSearchA11y({ expanded, results, selectedIndex }) {
518
+ const listboxId = `${useId()}-results`;
519
+ const getOptionId = (resultId, index) => getSearchOptionId(listboxId, resultId, index);
520
+ return {
521
+ activeOptionId: getActiveSearchOptionId({
522
+ getOptionId,
523
+ isOpen: expanded,
524
+ results,
525
+ selectedIndex
526
+ }),
527
+ getOptionId,
528
+ listboxId
529
+ };
530
+ }
454
531
  function ArdoSearch({ placeholder = "Search...", autoFocus = false }) {
455
532
  const navigate = useNavigate();
456
533
  const containerRef = useRef(null);
457
534
  const inputRef = useRef(null);
458
535
  const state = useSearch(useSearchIndex());
459
536
  const hasQuery = state.query.trim().length > 0;
537
+ const expanded = state.isOpen && hasQuery;
538
+ const searchA11y = useSearchA11y({
539
+ expanded,
540
+ results: state.results,
541
+ selectedIndex: state.selectedIndex
542
+ });
460
543
  useEffect(() => {
461
544
  if (autoFocus) inputRef.current?.focus();
462
545
  }, [autoFocus]);
@@ -468,39 +551,34 @@ function ArdoSearch({ placeholder = "Search...", autoFocus = false }) {
468
551
  setIsOpen: state.setIsOpen
469
552
  });
470
553
  const handleKeyDown = (e) => {
471
- switch (e.key) {
472
- case "ArrowDown":
473
- if (state.results.length > 0) {
474
- e.preventDefault();
475
- state.setSelectedIndex((p) => Math.min(p + 1, state.results.length - 1));
476
- }
477
- break;
478
- case "ArrowUp":
479
- if (state.results.length > 0) {
554
+ const action = getSearchKeyboardAction({
555
+ key: e.key,
556
+ results: state.results,
557
+ selectedIndex: state.selectedIndex
558
+ });
559
+ switch (action.type) {
560
+ case "select-index":
561
+ if (state.isOpen) {
480
562
  e.preventDefault();
481
- state.setSelectedIndex((p) => Math.max(p - 1, 0));
563
+ state.setSelectedIndex(action.index);
482
564
  }
483
565
  break;
484
- case "Enter": {
485
- const p = state.results[state.selectedIndex]?.path;
486
- if (typeof p === "string") {
487
- e.preventDefault();
488
- navigate(p);
489
- state.setIsOpen(false);
490
- }
566
+ case "navigate":
567
+ e.preventDefault();
568
+ navigate(action.path);
569
+ state.setIsOpen(false);
491
570
  break;
492
- }
493
- case "Escape":
571
+ case "close":
494
572
  state.setIsOpen(false);
495
573
  inputRef.current?.blur();
496
574
  break;
497
- default: break;
575
+ case "none": break;
498
576
  }
499
577
  };
500
578
  return /* @__PURE__ */ jsxs("div", {
501
579
  className: search,
502
580
  ref: containerRef,
503
- "data-expanded": state.isOpen || hasQuery ? "true" : "false",
581
+ "data-expanded": expanded ? "true" : "false",
504
582
  onMouseDown: () => inputRef.current?.focus(),
505
583
  children: [/* @__PURE__ */ jsx(SearchInput, {
506
584
  inputRef,
@@ -511,10 +589,15 @@ function ArdoSearch({ placeholder = "Search...", autoFocus = false }) {
511
589
  onKeyDown: handleKeyDown,
512
590
  onFocus: () => {
513
591
  if (hasQuery) state.setIsOpen(true);
514
- }
515
- }), state.isOpen && hasQuery && /* @__PURE__ */ jsx(SearchPopover, {
592
+ },
593
+ activeOptionId: searchA11y.activeOptionId,
594
+ expanded,
595
+ listboxId: searchA11y.listboxId
596
+ }), expanded && /* @__PURE__ */ jsx(SearchPopover, {
516
597
  anchorRef: containerRef,
517
598
  children: /* @__PURE__ */ jsx(SearchResults, {
599
+ getOptionId: searchA11y.getOptionId,
600
+ listboxId: searchA11y.listboxId,
518
601
  results: state.results,
519
602
  selectedIndex: state.selectedIndex,
520
603
  query: state.query,
@@ -667,6 +750,131 @@ var mobilePanelClose = "qjc2r5e";
667
750
  var mobilePanelHeader = "qjc2r5d";
668
751
  var mobilePanelSidebar = "qjc2r5g";
669
752
  //#endregion
753
+ //#region src/ui/mobile-drawer-a11y.ts
754
+ function getTrappedFocusTarget({ activeElement, focusableElements, shiftKey }) {
755
+ const firstElement = focusableElements[0];
756
+ const lastElement = focusableElements.at(-1);
757
+ if (firstElement == null || lastElement == null) return;
758
+ if (shiftKey && activeElement === firstElement) return lastElement;
759
+ if (!shiftKey && activeElement === lastElement) return firstElement;
760
+ }
761
+ //#endregion
762
+ //#region src/ui/MobileSlidePanel.tsx
763
+ const FOCUSABLE_SELECTOR = [
764
+ "a[href]",
765
+ "button:not([disabled])",
766
+ "input:not([disabled])",
767
+ "select:not([disabled])",
768
+ "textarea:not([disabled])",
769
+ "[tabindex]:not([tabindex=\"-1\"])"
770
+ ].join(",");
771
+ function MobileSlidePanel({ logo: logo$2, title, nav, themeToggle, triggerRef, children, onClose }) {
772
+ const panelRef = useRef(null);
773
+ useMobilePanelFocus({
774
+ onClose,
775
+ panelRef,
776
+ triggerRef
777
+ });
778
+ return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("div", {
779
+ className: mobileBackdrop,
780
+ "data-open": "true",
781
+ onClick: onClose
782
+ }), /* @__PURE__ */ jsxs("div", {
783
+ ref: panelRef,
784
+ className: mobilePanel,
785
+ "data-open": "true",
786
+ role: "dialog",
787
+ "aria-modal": "true",
788
+ "aria-label": title === "" ? "Navigation menu" : `${title} navigation menu`,
789
+ tabIndex: -1,
790
+ children: [
791
+ /* @__PURE__ */ jsxs("div", {
792
+ className: mobilePanelHeader,
793
+ children: [/* @__PURE__ */ jsx(Link, {
794
+ to: "/",
795
+ className: logoLink,
796
+ onClick: onClose,
797
+ children: logo$2 != null && /* @__PURE__ */ jsx("img", {
798
+ src: typeof logo$2 === "string" ? logo$2 : logo$2.light,
799
+ alt: title,
800
+ className: "qjc2r56"
801
+ })
802
+ }), /* @__PURE__ */ jsxs("div", {
803
+ style: {
804
+ display: "flex",
805
+ alignItems: "center",
806
+ gap: "0.5rem"
807
+ },
808
+ children: [themeToggle && /* @__PURE__ */ jsx(ArdoThemeToggle, {}), /* @__PURE__ */ jsx("button", {
809
+ type: "button",
810
+ className: mobilePanelClose,
811
+ onClick: onClose,
812
+ "aria-label": "Close menu",
813
+ children: /* @__PURE__ */ jsx(XIcon, { size: 20 })
814
+ })]
815
+ })]
816
+ }),
817
+ nav != null && /* @__PURE__ */ jsx("div", {
818
+ className: "qjc2r5f",
819
+ onClickCapture: handleLinkClick(onClose),
820
+ children: nav
821
+ }),
822
+ /* @__PURE__ */ jsx("div", {
823
+ className: mobilePanelSidebar,
824
+ onClickCapture: handleLinkClick(onClose),
825
+ children
826
+ })
827
+ ]
828
+ })] });
829
+ }
830
+ function useMobilePanelFocus({ onClose, panelRef, triggerRef }) {
831
+ useEffect(() => {
832
+ const panel = panelRef.current;
833
+ if (panel == null) return;
834
+ const triggerElement = triggerRef.current;
835
+ focusInitialPanelElement(panel);
836
+ const handleKeyDown = (event) => {
837
+ if (event.key === "Escape") {
838
+ event.preventDefault();
839
+ onClose();
840
+ return;
841
+ }
842
+ if (event.key === "Tab") trapPanelFocus(event, panel);
843
+ };
844
+ document.addEventListener("keydown", handleKeyDown);
845
+ return () => {
846
+ document.removeEventListener("keydown", handleKeyDown);
847
+ triggerElement?.focus();
848
+ };
849
+ }, [
850
+ onClose,
851
+ panelRef,
852
+ triggerRef
853
+ ]);
854
+ }
855
+ function focusInitialPanelElement(panel) {
856
+ (getFocusablePanelElements(panel)[0] ?? panel).focus();
857
+ }
858
+ function trapPanelFocus(event, panel) {
859
+ const focusableElements = getFocusablePanelElements(panel);
860
+ const target = getTrappedFocusTarget({
861
+ activeElement: document.activeElement instanceof HTMLElement ? document.activeElement : null,
862
+ focusableElements,
863
+ shiftKey: event.shiftKey
864
+ });
865
+ if (target == null) return;
866
+ event.preventDefault();
867
+ target.focus();
868
+ }
869
+ function getFocusablePanelElements(panel) {
870
+ return [...panel.querySelectorAll(FOCUSABLE_SELECTOR)].filter((element) => !element.hasAttribute("disabled") && element.getAttribute("aria-hidden") !== "true");
871
+ }
872
+ function handleLinkClick(onClose) {
873
+ return (event) => {
874
+ if (event.target instanceof HTMLElement && event.target.closest("a") !== null) onClose();
875
+ };
876
+ }
877
+ //#endregion
670
878
  //#region src/ui/Header.tsx
671
879
  /**
672
880
  * Mobile menu open state — resets on navigation and locks body scroll
@@ -686,14 +894,18 @@ function useMobileMenu() {
686
894
  }, [open]);
687
895
  return [open, setOpen];
688
896
  }
689
- function ArdoHeader({ logo: logo$2, title, nav, actions, search = true, searchPlaceholder, themeToggle = true, mobileMenuContent, className }) {
897
+ function ArdoHeader({ logo: logo$1, title, nav, actions, search = true, searchPlaceholder, themeToggle = true, mobileMenuContent, className }) {
690
898
  const config = useArdoConfig();
691
899
  const [mobileMenuOpen, setMobileMenuOpen] = useMobileMenu();
692
- const resolvedLogo = logo$2;
900
+ const mobileMenuButtonRef = useRef(null);
901
+ const resolvedLogo = logo$1;
693
902
  const resolvedTitle = title ?? config.title;
694
903
  const hasLogo = resolvedLogo !== void 0;
695
904
  const hasTitle = resolvedTitle !== "";
696
905
  const hasMobileMenu = mobileMenuContent != null;
906
+ const closeMobileMenu = useCallback(() => {
907
+ setMobileMenuOpen(false);
908
+ }, [setMobileMenuOpen]);
697
909
  return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("header", {
698
910
  className: className ?? "qjc2r50",
699
911
  children: /* @__PURE__ */ jsxs("div", {
@@ -702,6 +914,7 @@ function ArdoHeader({ logo: logo$2, title, nav, actions, search = true, searchPl
702
914
  /* @__PURE__ */ jsxs("div", {
703
915
  className: headerLeft,
704
916
  children: [hasMobileMenu && /* @__PURE__ */ jsx("button", {
917
+ ref: mobileMenuButtonRef,
705
918
  type: "button",
706
919
  className: "qjc2r58",
707
920
  onClick: () => {
@@ -747,72 +960,16 @@ function ArdoHeader({ logo: logo$2, title, nav, actions, search = true, searchPl
747
960
  })
748
961
  ]
749
962
  })
750
- }), hasMobileMenu && /* @__PURE__ */ jsx(MobileSlidePanel, {
751
- isOpen: mobileMenuOpen,
963
+ }), hasMobileMenu && mobileMenuOpen && /* @__PURE__ */ jsx(MobileSlidePanel, {
752
964
  logo: resolvedLogo,
753
965
  title: resolvedTitle,
754
966
  nav,
755
967
  themeToggle,
756
- onClose: () => {
757
- setMobileMenuOpen(false);
758
- },
968
+ triggerRef: mobileMenuButtonRef,
969
+ onClose: closeMobileMenu,
759
970
  children: mobileMenuContent
760
971
  })] });
761
972
  }
762
- function MobileSlidePanel({ isOpen, logo: logo$1, title, nav, themeToggle, children, onClose }) {
763
- return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("div", {
764
- className: mobileBackdrop,
765
- "data-open": isOpen,
766
- onClick: onClose
767
- }), /* @__PURE__ */ jsxs("div", {
768
- className: mobilePanel,
769
- "data-open": isOpen,
770
- "aria-hidden": !isOpen,
771
- children: [
772
- /* @__PURE__ */ jsxs("div", {
773
- className: mobilePanelHeader,
774
- children: [/* @__PURE__ */ jsx(Link, {
775
- to: "/",
776
- className: logoLink,
777
- onClick: onClose,
778
- children: logo$1 != null && /* @__PURE__ */ jsx("img", {
779
- src: typeof logo$1 === "string" ? logo$1 : logo$1.light,
780
- alt: title,
781
- className: "qjc2r56"
782
- })
783
- }), /* @__PURE__ */ jsxs("div", {
784
- style: {
785
- display: "flex",
786
- alignItems: "center",
787
- gap: "0.5rem"
788
- },
789
- children: [themeToggle && /* @__PURE__ */ jsx(ArdoThemeToggle, {}), /* @__PURE__ */ jsx("button", {
790
- type: "button",
791
- className: mobilePanelClose,
792
- onClick: onClose,
793
- "aria-label": "Close menu",
794
- children: /* @__PURE__ */ jsx(XIcon, { size: 20 })
795
- })]
796
- })]
797
- }),
798
- nav != null && /* @__PURE__ */ jsx("div", {
799
- className: "qjc2r5f",
800
- onClickCapture: handleLinkClick(onClose),
801
- children: nav
802
- }),
803
- /* @__PURE__ */ jsx("div", {
804
- className: mobilePanelSidebar,
805
- onClickCapture: handleLinkClick(onClose),
806
- children
807
- })
808
- ]
809
- })] });
810
- }
811
- function handleLinkClick(onClose) {
812
- return (event) => {
813
- if (event.target instanceof HTMLElement && event.target.closest("a") !== null) onClose();
814
- };
815
- }
816
973
  function ArdoSocialLink({ href, icon, ariaLabel, className }) {
817
974
  return /* @__PURE__ */ jsx("a", {
818
975
  href,
@@ -1760,4 +1917,4 @@ function ArdoNavLink({ to, href, children, className, activeMatch: _activeMatch
1760
1917
  //#endregion
1761
1918
  export { ArdoOwlMark as _, ArdoHero as a, ArdoRoot as c, ArdoSidebarLink as d, ArdoHeader as f, ArdoFooter as g, ArdoSearch as h, ArdoErrorBoundary as i, ArdoSidebar as l, ArdoThemeToggle as m, ArdoNavLink as n, ArdoFeatureCard as o, ArdoSocialLink as p, ArdoHomePage as r, ArdoFeatures as s, ArdoNav as t, ArdoSidebarGroup as u };
1762
1919
 
1763
- //# sourceMappingURL=ui-B6X8gAvz.js.map
1920
+ //# sourceMappingURL=ui-Cs2nhBpA.js.map