@mond-design-system/react 6.1.0 → 6.1.1

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/dist/index.js CHANGED
@@ -129,7 +129,7 @@ function Icon({ name, size: size2, label, className, ...rest }) {
129
129
  }
130
130
 
131
131
  // mds-cssmod:/home/runner/work/mond-design-system/mond-design-system/packages/react/src/components/Button/Button.module.css?mds-scoped
132
- var Button_module_default = { "button": "mds-Button__button", "size-sm": "mds-Button__size-sm", "size-md": "mds-Button__size-md", "size-lg": "mds-Button__size-lg", "slot": "mds-Button__slot", "variant-primary": "mds-Button__variant-primary", "variant-secondary": "mds-Button__variant-secondary", "variant-ghost": "mds-Button__variant-ghost", "variant-highlight": "mds-Button__variant-highlight", "variant-danger": "mds-Button__variant-danger", "variant-warning": "mds-Button__variant-warning", "fullWidth": "mds-Button__fullWidth", "icon-only": "mds-Button__icon-only", "on-media": "mds-Button__on-media", "shape-rect": "mds-Button__shape-rect", "shape-pill": "mds-Button__shape-pill" };
132
+ var Button_module_default = { "button": "mds-Button__button", "size-sm": "mds-Button__size-sm", "size-md": "mds-Button__size-md", "size-lg": "mds-Button__size-lg", "slot": "mds-Button__slot", "variant-primary": "mds-Button__variant-primary", "variant-secondary": "mds-Button__variant-secondary", "variant-ghost": "mds-Button__variant-ghost", "variant-highlight": "mds-Button__variant-highlight", "variant-danger": "mds-Button__variant-danger", "variant-warning": "mds-Button__variant-warning", "fullWidth": "mds-Button__fullWidth", "icon-only": "mds-Button__icon-only mds-hit-area__hitArea", "on-media": "mds-Button__on-media", "shape-rect": "mds-Button__shape-rect", "shape-pill": "mds-Button__shape-pill" };
133
133
  var ICON_PX = { sm: 16, md: 20, lg: 24 };
134
134
  function Button({
135
135
  variant = "primary",
@@ -147,13 +147,14 @@ function Button({
147
147
  as,
148
148
  className,
149
149
  children,
150
+ onClick,
150
151
  ...rest
151
152
  }) {
152
153
  const glyph = iconOnly && children != null ? /* @__PURE__ */ jsx("span", { className: Button_module_default.slot, children }) : children;
153
154
  const body = loading && iconOnly ? null : glyph;
154
155
  const Element = as ?? (href !== void 0 ? "a" : "button");
155
156
  const isButton = Element === "button";
156
- const own = isButton ? { type, disabled: disabled || loading } : { href };
157
+ const own = isButton ? { type, disabled } : { href };
157
158
  return /* @__PURE__ */ jsxs(
158
159
  Element,
159
160
  {
@@ -168,6 +169,8 @@ function Button({
168
169
  className
169
170
  ),
170
171
  "aria-busy": loading || void 0,
172
+ "aria-disabled": loading || void 0,
173
+ onClick: loading ? (event) => event.preventDefault() : onClick,
171
174
  ...own,
172
175
  ...rest,
173
176
  children: [
@@ -180,19 +183,21 @@ function Button({
180
183
  }
181
184
 
182
185
  // mds-cssmod:/home/runner/work/mond-design-system/mond-design-system/packages/react/src/components/Link/Link.module.css?mds-scoped
183
- var Link_module_default = { "link": "mds-Link__link", "variant-inline": "mds-Link__variant-inline", "variant-standalone": "mds-Link__variant-standalone", "variant-plain": "mds-Link__variant-plain", "size-xs": "mds-Link__size-xs", "size-sm": "mds-Link__size-sm", "size-base": "mds-Link__size-base", "size-lg": "mds-Link__size-lg", "size-xl": "mds-Link__size-xl" };
186
+ var Link_module_default = { "link": "mds-Link__link", "variant-inline": "mds-Link__variant-inline", "variant-standalone": "mds-Link__variant-standalone", "variant-plain": "mds-Link__variant-plain", "size-xs": "mds-Link__size-xs", "size-sm": "mds-Link__size-sm", "size-base": "mds-Link__size-base", "size-lg": "mds-Link__size-lg", "size-xl": "mds-Link__size-xl", "hiddenNote": "mds-Link__hiddenNote" };
184
187
  function Link({
185
188
  variant = "inline",
186
189
  size: size2,
187
190
  external = false,
191
+ externalLabel,
188
192
  as,
193
+ children,
189
194
  ...rest
190
195
  }) {
191
196
  const Element = as ?? "a";
192
197
  const { className, type, ...others } = rest;
193
198
  const externalProps = external ? { target: "_blank", rel: "noopener noreferrer" } : {};
194
199
  const typeProps = Element === "button" ? { type: type ?? "button" } : type !== void 0 ? { type } : {};
195
- return /* @__PURE__ */ jsx(
200
+ return /* @__PURE__ */ jsxs(
196
201
  Element,
197
202
  {
198
203
  className: cx(
@@ -203,7 +208,13 @@ function Link({
203
208
  ),
204
209
  ...externalProps,
205
210
  ...others,
206
- ...typeProps
211
+ ...typeProps,
212
+ children: [
213
+ children,
214
+ external && externalLabel !== void 0 && /* The leading space keeps the name "Docs opens in new tab", not
215
+ "Docsopens…" — inline text concatenates without one. */
216
+ /* @__PURE__ */ jsx("span", { className: Link_module_default.hiddenNote, children: ` ${externalLabel}` })
217
+ ]
207
218
  }
208
219
  );
209
220
  }
@@ -354,7 +365,7 @@ function ChipBar({
354
365
  }
355
366
 
356
367
  // mds-cssmod:/home/runner/work/mond-design-system/mond-design-system/packages/react/src/components/CountButton/CountButton.module.css?mds-scoped
357
- var CountButton_module_default = { "button": "mds-CountButton__button", "tone-accent": "mds-CountButton__tone-accent", "tone-danger": "mds-CountButton__tone-danger", "glyph": "mds-CountButton__glyph" };
368
+ var CountButton_module_default = { "button": "mds-CountButton__button mds-hit-area__hitArea", "tone-accent": "mds-CountButton__tone-accent", "tone-danger": "mds-CountButton__tone-danger", "glyph": "mds-CountButton__glyph" };
358
369
  var SPINNER_PX = 20;
359
370
  function CountButton({
360
371
  icon,
@@ -365,16 +376,20 @@ function CountButton({
365
376
  loading = false,
366
377
  className,
367
378
  children,
379
+ onClick,
368
380
  ...rest
369
381
  }) {
370
382
  const isToggle = active !== void 0;
383
+ const spoken = typeof children === "string" || typeof children === "number" ? `${label} ${children}` : label;
371
384
  return /* @__PURE__ */ jsxs(
372
385
  "button",
373
386
  {
374
387
  type: "button",
375
- disabled: disabled || loading,
376
- "aria-label": label,
388
+ disabled,
389
+ onClick: loading ? (event) => event.preventDefault() : onClick,
390
+ "aria-label": spoken,
377
391
  "aria-pressed": isToggle ? active : void 0,
392
+ "aria-disabled": loading || void 0,
378
393
  "aria-busy": loading || void 0,
379
394
  className: cx(CountButton_module_default.button, active && CountButton_module_default[`tone-${tone}`], className),
380
395
  ...rest,
@@ -500,7 +515,7 @@ function Field({ label, children, hint, error, required = false, className }) {
500
515
  /* @__PURE__ */ jsx(
501
516
  FieldContext.Provider,
502
517
  {
503
- value: { id, describedBy: message ? messageId : void 0, invalid: error !== void 0 },
518
+ value: { id, describedBy: message ? messageId : void 0, invalid: error !== void 0, required },
504
519
  children
505
520
  }
506
521
  ),
@@ -527,6 +542,7 @@ function Select({ size: size2 = "md", className, children, ...rest }) {
527
542
  id: rest.id ?? field?.id,
528
543
  "aria-describedby": rest["aria-describedby"] ?? field?.describedBy,
529
544
  "aria-invalid": rest["aria-invalid"] ?? (field?.invalid || void 0),
545
+ "aria-required": rest["aria-required"] ?? (field?.required || void 0),
530
546
  className: cx(Select_module_default.select, Select_module_default[`size-${size2}`], className),
531
547
  ...rest,
532
548
  children
@@ -538,10 +554,10 @@ function Select({ size: size2 = "md", className, children, ...rest }) {
538
554
  var OverlayHistoryContext = createContext(null);
539
555
 
540
556
  // src/hooks/useOverlay.ts
541
- var FOCUSABLE = 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
557
+ var FOCUSABLE = 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), summary, audio[controls], video[controls], [contenteditable]:not([contenteditable="false"]), [tabindex]:not([tabindex="-1"])';
542
558
  function useOverlay(options) {
543
559
  const ref = useRef(null);
544
- const { open, onClose, lockScroll = true } = options;
560
+ const { open, onClose, lockScroll = true, modal = true } = options;
545
561
  const close = useRef(onClose);
546
562
  useEffect(() => {
547
563
  close.current = onClose;
@@ -556,13 +572,24 @@ function useOverlay(options) {
556
572
  const panel = ref.current;
557
573
  const previous = document.activeElement instanceof HTMLElement ? document.activeElement : null;
558
574
  panel?.focus();
575
+ let marked = [];
576
+ if (modal && panel) {
577
+ let portal = panel;
578
+ while (portal.parentElement && portal.parentElement !== document.body) {
579
+ portal = portal.parentElement;
580
+ }
581
+ marked = [...document.body.children].filter(
582
+ (sibling) => sibling !== portal && !sibling.hasAttribute("inert")
583
+ );
584
+ for (const sibling of marked) sibling.setAttribute("inert", "");
585
+ }
559
586
  const onKeyDown = (event) => {
560
587
  if (event.key === "Escape") {
561
588
  event.stopPropagation();
562
589
  close.current();
563
590
  return;
564
591
  }
565
- if (event.key !== "Tab" || !panel) return;
592
+ if (!modal || event.key !== "Tab" || !panel) return;
566
593
  const focusable = [...panel.querySelectorAll(FOCUSABLE)];
567
594
  if (focusable.length === 0) {
568
595
  event.preventDefault();
@@ -586,9 +613,13 @@ function useOverlay(options) {
586
613
  return () => {
587
614
  document.removeEventListener("keydown", onKeyDown);
588
615
  if (lockScroll) document.body.style.overflow = bodyOverflow;
589
- previous?.focus();
616
+ for (const sibling of marked) sibling.removeAttribute("inert");
617
+ const active = document.activeElement;
618
+ if (modal || panel && active instanceof HTMLElement && panel.contains(active)) {
619
+ previous?.focus();
620
+ }
590
621
  };
591
- }, [open, lockScroll]);
622
+ }, [open, lockScroll, modal]);
592
623
  return ref;
593
624
  }
594
625
  function usePresence(open, durationMs) {
@@ -609,7 +640,8 @@ function usePresence(open, durationMs) {
609
640
  }, [open]);
610
641
  useEffect(() => {
611
642
  if (open) return;
612
- const timer = setTimeout(() => setMounted(false), durationMs);
643
+ const reduced = typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
644
+ const timer = setTimeout(() => setMounted(false), reduced ? 0 : durationMs);
613
645
  return () => clearTimeout(timer);
614
646
  }, [open, durationMs]);
615
647
  return { mounted: open || mounted, visible };
@@ -619,7 +651,7 @@ function usePresence(open, durationMs) {
619
651
  var Overlay_module_default = { "scrim": "mds-Overlay__scrim", "variant-modal": "mds-Overlay__variant-modal", "variant-sheet": "mds-Overlay__variant-sheet", "variant-lightbox": "mds-Overlay__variant-lightbox" };
620
652
  var OVERLAY_EXIT_MS = 200;
621
653
  var SHEET_EXIT_MS = 320;
622
- function Overlay({ open, onClose, label, variant, role = "dialog", closeOnScrimClick, panelClassName, children }) {
654
+ function Overlay({ open, onClose, label, variant, role = "dialog", describedBy, closeOnScrimClick, panelClassName, children }) {
623
655
  const { mounted, visible } = usePresence(open, variant === "sheet" ? SHEET_EXIT_MS : OVERLAY_EXIT_MS);
624
656
  const ref = useOverlay({ open, onClose });
625
657
  if (!mounted) return null;
@@ -638,6 +670,7 @@ function Overlay({ open, onClose, label, variant, role = "dialog", closeOnScrimC
638
670
  role,
639
671
  "aria-modal": "true",
640
672
  "aria-label": label,
673
+ "aria-describedby": describedBy,
641
674
  tabIndex: -1,
642
675
  className: panelClassName,
643
676
  "data-open": visible || void 0,
@@ -681,7 +714,7 @@ function SheetFooter({ children }) {
681
714
  }
682
715
 
683
716
  // mds-cssmod:/home/runner/work/mond-design-system/mond-design-system/packages/react/src/components/DateTimePicker/DateTimePicker.module.css?mds-scoped
684
- var DateTimePicker_module_default = { "trigger": "mds-DateTimePicker__trigger", "hasValue": "mds-DateTimePicker__hasValue", "triggerLabel": "mds-DateTimePicker__triggerLabel", "glyph": "mds-DateTimePicker__glyph", "timeRow": "mds-DateTimePicker__timeRow", "grow": "mds-DateTimePicker__grow", "meridiem": "mds-DateTimePicker__meridiem", "colon": "mds-DateTimePicker__colon", "calendarHeader": "mds-DateTimePicker__calendarHeader", "monthLabel": "mds-DateTimePicker__monthLabel", "weekdays": "mds-DateTimePicker__weekdays", "weekday": "mds-DateTimePicker__weekday", "grid": "mds-DateTimePicker__grid", "day": "mds-DateTimePicker__day", "today": "mds-DateTimePicker__today", "selected": "mds-DateTimePicker__selected", "outside": "mds-DateTimePicker__outside" };
717
+ var DateTimePicker_module_default = { "trigger": "mds-DateTimePicker__trigger", "hasValue": "mds-DateTimePicker__hasValue", "triggerLabel": "mds-DateTimePicker__triggerLabel", "glyph": "mds-DateTimePicker__glyph", "timeRow": "mds-DateTimePicker__timeRow", "grow": "mds-DateTimePicker__grow", "meridiem": "mds-DateTimePicker__meridiem", "colon": "mds-DateTimePicker__colon", "calendarHeader": "mds-DateTimePicker__calendarHeader", "monthLabel": "mds-DateTimePicker__monthLabel", "weekdays": "mds-DateTimePicker__weekdays", "weekday": "mds-DateTimePicker__weekday", "grid": "mds-DateTimePicker__grid", "week": "mds-DateTimePicker__week", "day": "mds-DateTimePicker__day", "today": "mds-DateTimePicker__today", "selected": "mds-DateTimePicker__selected", "outside": "mds-DateTimePicker__outside" };
685
718
  function localeData(locale) {
686
719
  const resolved = new Intl.DateTimeFormat(locale).resolvedOptions().locale;
687
720
  const intlLocale = new Intl.Locale(resolved);
@@ -908,9 +941,11 @@ function DateTimePicker({
908
941
  if (el) dayRefs.current.set(cellStart, el);
909
942
  else dayRefs.current.delete(cellStart);
910
943
  } : void 0,
944
+ role: "gridcell",
911
945
  "aria-hidden": inMonth ? void 0 : true,
912
946
  "aria-label": intl.dayLabel.format(cell),
913
- "aria-pressed": isSelected,
947
+ "aria-selected": isSelected,
948
+ "aria-current": isToday ? "date" : void 0,
914
949
  onClick: inMonth ? () => pickDay(cell.getDate()) : void 0,
915
950
  onKeyDown: inMonth && !isDisabled ? (e) => onDayKeyDown(e, cell) : void 0,
916
951
  className: cx(DateTimePicker_module_default.day, isToday && DateTimePicker_module_default.today, isSelected && DateTimePicker_module_default.selected, !inMonth && DateTimePicker_module_default.outside),
@@ -928,6 +963,7 @@ function DateTimePicker({
928
963
  type: "button",
929
964
  disabled,
930
965
  "aria-describedby": field?.describedBy,
966
+ "aria-invalid": field?.invalid || void 0,
931
967
  "aria-haspopup": "dialog",
932
968
  "aria-expanded": open,
933
969
  "aria-label": ariaLabel === void 0 ? void 0 : [ariaLabel, shown].filter(Boolean).join(": "),
@@ -972,11 +1008,13 @@ function DateTimePicker({
972
1008
  children: /* @__PURE__ */ jsx(ChevronLeftGlyph, { className: DateTimePicker_module_default.glyph })
973
1009
  }
974
1010
  ),
975
- /* @__PURE__ */ jsx("span", { className: DateTimePicker_module_default.monthLabel, children: intl.monthTitle.format(first) }),
1011
+ /* @__PURE__ */ jsx("span", { className: DateTimePicker_module_default.monthLabel, "aria-live": "polite", children: intl.monthTitle.format(first) }),
976
1012
  /* @__PURE__ */ jsx(Button, { iconOnly: true, "aria-label": text.nextMonth, variant: "ghost", size: "sm", onClick: () => shiftMonth(1), children: /* @__PURE__ */ jsx(ChevronRightGlyph, { className: DateTimePicker_module_default.glyph }) })
977
1013
  ] }),
978
- /* @__PURE__ */ jsx("div", { className: DateTimePicker_module_default.weekdays, children: intl.weekdays.map((w, i) => /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: DateTimePicker_module_default.weekday, children: w }, `wd-${i}`)) }),
979
- /* @__PURE__ */ jsx("div", { className: DateTimePicker_module_default.grid, children: dayCells })
1014
+ /* @__PURE__ */ jsxs("div", { role: "grid", "aria-label": intl.monthTitle.format(first), children: [
1015
+ /* @__PURE__ */ jsx("div", { role: "row", className: DateTimePicker_module_default.weekdays, children: intl.weekdays.map((w, i) => /* @__PURE__ */ jsx("span", { role: "columnheader", className: DateTimePicker_module_default.weekday, children: w }, `wd-${i}`)) }),
1016
+ /* @__PURE__ */ jsx("div", { role: "rowgroup", className: DateTimePicker_module_default.grid, children: Array.from({ length: spanCells / 7 }, (_, w) => /* @__PURE__ */ jsx("div", { role: "row", className: DateTimePicker_module_default.week, children: dayCells.slice(w * 7, w * 7 + 7) }, `w-${w}`)) })
1017
+ ] })
980
1018
  ] })
981
1019
  ] }),
982
1020
  /* @__PURE__ */ jsxs(SheetFooter, { children: [
@@ -1137,6 +1175,7 @@ function Input({
1137
1175
  id: rest.id ?? field?.id,
1138
1176
  "aria-describedby": rest["aria-describedby"] ?? field?.describedBy,
1139
1177
  "aria-invalid": rest["aria-invalid"] ?? ((invalid ?? field?.invalid) || void 0),
1178
+ "aria-required": rest["aria-required"] ?? (field?.required || void 0),
1140
1179
  className: cx(
1141
1180
  Input_module_default.input,
1142
1181
  Input_module_default[`size-${size2}`],
@@ -1159,7 +1198,7 @@ function Input({
1159
1198
  }
1160
1199
 
1161
1200
  // mds-cssmod:/home/runner/work/mond-design-system/mond-design-system/packages/react/src/components/PasswordInput/PasswordInput.module.css?mds-scoped
1162
- var PasswordInput_module_default = { "wrap": "mds-PasswordInput__wrap", "input": "mds-PasswordInput__input", "toggle": "mds-PasswordInput__toggle" };
1201
+ var PasswordInput_module_default = { "wrap": "mds-PasswordInput__wrap", "input": "mds-PasswordInput__input", "toggle": "mds-PasswordInput__toggle mds-hit-area__hitArea" };
1163
1202
  function PasswordInput({
1164
1203
  showLabel,
1165
1204
  hideLabel,
@@ -1201,28 +1240,31 @@ function PasswordInput({
1201
1240
  var Textarea_module_default = { "textarea": "mds-Textarea__textarea", "wrap": "mds-Textarea__wrap", "count": "mds-Textarea__count" };
1202
1241
  function Textarea({ rows = 3, showCount = false, className, ...rest }) {
1203
1242
  const field = useFieldContext();
1243
+ const countId = useId();
1204
1244
  const [tracked, setTracked] = useState(() => String(rest.defaultValue ?? ""));
1205
1245
  const current = rest.value !== void 0 ? String(rest.value) : tracked;
1206
1246
  const onChange = (event) => {
1207
1247
  setTracked(event.target.value);
1208
1248
  rest.onChange?.(event);
1209
1249
  };
1250
+ const describedBy = [rest["aria-describedby"] ?? field?.describedBy, showCount ? countId : void 0].filter(Boolean).join(" ") || void 0;
1210
1251
  const textarea = /* @__PURE__ */ jsx(
1211
1252
  "textarea",
1212
1253
  {
1213
1254
  id: rest.id ?? field?.id,
1214
- "aria-describedby": rest["aria-describedby"] ?? field?.describedBy,
1215
1255
  "aria-invalid": rest["aria-invalid"] ?? (field?.invalid || void 0),
1256
+ "aria-required": rest["aria-required"] ?? (field?.required || void 0),
1216
1257
  rows,
1217
1258
  className: cx(Textarea_module_default.textarea, className),
1218
1259
  ...rest,
1260
+ "aria-describedby": describedBy,
1219
1261
  ...showCount ? { onChange } : {}
1220
1262
  }
1221
1263
  );
1222
1264
  if (!showCount) return textarea;
1223
1265
  return /* @__PURE__ */ jsxs("span", { className: Textarea_module_default.wrap, children: [
1224
1266
  textarea,
1225
- /* @__PURE__ */ jsx("span", { className: Textarea_module_default.count, "aria-hidden": "true", children: rest.maxLength !== void 0 ? `${current.length}/${rest.maxLength}` : current.length })
1267
+ /* @__PURE__ */ jsx("span", { id: countId, className: Textarea_module_default.count, children: rest.maxLength !== void 0 ? `${current.length}/${rest.maxLength}` : current.length })
1226
1268
  ] });
1227
1269
  }
1228
1270
 
@@ -1270,6 +1312,8 @@ function Switch({
1270
1312
  loading = false,
1271
1313
  disabled = false,
1272
1314
  className,
1315
+ onClick,
1316
+ onChange,
1273
1317
  ...rest
1274
1318
  }) {
1275
1319
  return /* @__PURE__ */ jsxs("label", { className: cx(Switch_module_default.root, className), children: [
@@ -1280,7 +1324,10 @@ function Switch({
1280
1324
  type: "checkbox",
1281
1325
  role: "switch",
1282
1326
  className: Switch_module_default.track,
1283
- disabled: disabled || loading,
1327
+ disabled,
1328
+ onClick: loading ? (event) => event.preventDefault() : onClick,
1329
+ onChange: loading ? void 0 : onChange,
1330
+ "aria-disabled": loading || void 0,
1284
1331
  "aria-busy": loading || void 0,
1285
1332
  ...rest
1286
1333
  }
@@ -1425,6 +1472,7 @@ function ListGroup({ label, className, ...rest }) {
1425
1472
  const list = /* @__PURE__ */ jsx(ListGroupContext.Provider, { value: true, children: /* @__PURE__ */ jsx(
1426
1473
  "ul",
1427
1474
  {
1475
+ role: "list",
1428
1476
  className: cx(List_module_default.group, className),
1429
1477
  "aria-labelledby": label != null ? headingId : void 0,
1430
1478
  ...rest
@@ -1591,10 +1639,10 @@ function DataTable({
1591
1639
 
1592
1640
  // mds-cssmod:/home/runner/work/mond-design-system/mond-design-system/packages/react/src/components/EmptyState/EmptyState.module.css?mds-scoped
1593
1641
  var EmptyState_module_default = { "root": "mds-EmptyState__root", "icon": "mds-EmptyState__icon", "action": "mds-EmptyState__action" };
1594
- function EmptyState({ title, description, icon, action, className }) {
1642
+ function EmptyState({ title, level = 3, description, icon, action, className }) {
1595
1643
  return /* @__PURE__ */ jsxs("div", { className: cx(EmptyState_module_default.root, className), children: [
1596
1644
  icon != null && /* @__PURE__ */ jsx("span", { className: EmptyState_module_default.icon, "aria-hidden": "true", children: icon }),
1597
- /* @__PURE__ */ jsx(Heading, { level: 3, children: title }),
1645
+ /* @__PURE__ */ jsx(Heading, { level, children: title }),
1598
1646
  description !== void 0 && /* @__PURE__ */ jsx(Text, { tone: "secondary", children: description }),
1599
1647
  action != null && /* @__PURE__ */ jsx("span", { className: EmptyState_module_default.action, children: action })
1600
1648
  ] });
@@ -1682,6 +1730,7 @@ function TabPanel({ value, className, children, ...rest }) {
1682
1730
  role: "tabpanel",
1683
1731
  id: `${tabs.baseId}-panel-${value}`,
1684
1732
  "aria-labelledby": `${tabs.baseId}-tab-${value}`,
1733
+ tabIndex: 0,
1685
1734
  hidden: !selected,
1686
1735
  className: cx(Tabs_module_default.panel, className),
1687
1736
  ...rest,
@@ -1706,7 +1755,22 @@ function ToastProvider({
1706
1755
  const [toasts, setToasts] = useState([]);
1707
1756
  const nextId = useRef(1);
1708
1757
  const leaving = useRef(/* @__PURE__ */ new Map());
1758
+ const clocks = useRef(
1759
+ /* @__PURE__ */ new Map()
1760
+ );
1761
+ const held = useRef(0);
1762
+ const surfaces = useRef(/* @__PURE__ */ new Map());
1709
1763
  const dismiss = useCallback((id) => {
1764
+ const clock2 = clocks.current.get(id);
1765
+ if (clock2 !== void 0) {
1766
+ clearTimeout(clock2.handle);
1767
+ clocks.current.delete(id);
1768
+ }
1769
+ const surface = surfaces.current.get(id);
1770
+ if (surface?.contains(document.activeElement)) {
1771
+ const neighbour = surface.nextElementSibling ?? surface.previousElementSibling;
1772
+ neighbour?.querySelector("button:last-child")?.focus();
1773
+ }
1710
1774
  setToasts((current) => current.filter((entry) => entry.id !== id));
1711
1775
  const said = leaving.current.get(id);
1712
1776
  if (said !== void 0) {
@@ -1714,55 +1778,98 @@ function ToastProvider({
1714
1778
  said();
1715
1779
  }
1716
1780
  }, []);
1781
+ const start = useCallback(
1782
+ (id, remaining) => {
1783
+ clocks.current.set(id, {
1784
+ handle: setTimeout(() => dismiss(id), remaining),
1785
+ startedAt: Date.now(),
1786
+ remaining
1787
+ });
1788
+ },
1789
+ [dismiss]
1790
+ );
1791
+ const hold = useCallback(() => {
1792
+ if (held.current++ > 0) return;
1793
+ for (const clock2 of clocks.current.values()) {
1794
+ clearTimeout(clock2.handle);
1795
+ clock2.remaining -= Date.now() - clock2.startedAt;
1796
+ }
1797
+ }, []);
1798
+ const release = useCallback(() => {
1799
+ if (--held.current > 0) return;
1800
+ held.current = 0;
1801
+ for (const [id, clock2] of clocks.current) {
1802
+ start(id, Math.max(clock2.remaining, 0));
1803
+ }
1804
+ }, [start]);
1717
1805
  const toast = useCallback(
1718
1806
  ({ title, description, tone = "neutral", duration = 5e3, action, onDismiss }) => {
1719
1807
  const id = nextId.current++;
1720
1808
  if (onDismiss !== void 0) leaving.current.set(id, onDismiss);
1721
1809
  setToasts((current) => [...current, { id, title, description, tone, action }]);
1722
- if (duration > 0) setTimeout(() => dismiss(id), duration);
1810
+ if (duration > 0) {
1811
+ start(id, duration);
1812
+ if (held.current > 0) clearTimeout(clocks.current.get(id)?.handle);
1813
+ }
1723
1814
  return id;
1724
1815
  },
1725
- [dismiss]
1816
+ [start]
1726
1817
  );
1727
1818
  const value = useMemo(() => ({ toast, dismiss }), [toast, dismiss]);
1728
1819
  return /* @__PURE__ */ jsxs(ToastContext.Provider, { value, children: [
1729
1820
  children,
1730
- /* @__PURE__ */ jsx("div", { role: "region", "aria-label": regionLabel, className: Toast_module_default.viewport, children: toasts.map((entry) => /* @__PURE__ */ jsxs(
1821
+ /* @__PURE__ */ jsx(
1731
1822
  "div",
1732
1823
  {
1733
- role: entry.tone === "danger" ? "alert" : "status",
1734
- className: cx(Toast_module_default.toast, Toast_module_default[`tone-${entry.tone}`]),
1735
- children: [
1736
- /* @__PURE__ */ jsxs("span", { className: Toast_module_default.body, children: [
1737
- /* @__PURE__ */ jsx("span", { className: Toast_module_default.title, children: entry.title }),
1738
- entry.description !== void 0 && /* @__PURE__ */ jsx("span", { className: Toast_module_default.description, children: entry.description })
1739
- ] }),
1740
- entry.action !== void 0 && /* @__PURE__ */ jsx(
1741
- "button",
1742
- {
1743
- type: "button",
1744
- className: Toast_module_default.action,
1745
- onClick: () => {
1746
- entry.action?.onClick();
1747
- dismiss(entry.id);
1748
- },
1749
- children: entry.action.label
1750
- }
1751
- ),
1752
- /* @__PURE__ */ jsx(
1753
- "button",
1754
- {
1755
- type: "button",
1756
- "aria-label": `${dismissLabel}: ${entry.title}`,
1757
- className: Toast_module_default.close,
1758
- onClick: () => dismiss(entry.id),
1759
- children: /* @__PURE__ */ jsx(CloseGlyph, { className: Toast_module_default.closeGlyph })
1760
- }
1761
- )
1762
- ]
1763
- },
1764
- entry.id
1765
- )) })
1824
+ role: "region",
1825
+ "aria-label": regionLabel,
1826
+ className: Toast_module_default.viewport,
1827
+ onPointerEnter: hold,
1828
+ onPointerLeave: release,
1829
+ onFocus: hold,
1830
+ onBlur: release,
1831
+ children: toasts.map((entry) => /* @__PURE__ */ jsxs(
1832
+ "div",
1833
+ {
1834
+ ref: (node) => {
1835
+ if (node !== null) surfaces.current.set(entry.id, node);
1836
+ else surfaces.current.delete(entry.id);
1837
+ },
1838
+ role: entry.tone === "danger" ? "alert" : "status",
1839
+ className: cx(Toast_module_default.toast, Toast_module_default[`tone-${entry.tone}`]),
1840
+ children: [
1841
+ /* @__PURE__ */ jsxs("span", { className: Toast_module_default.body, children: [
1842
+ /* @__PURE__ */ jsx("span", { className: Toast_module_default.title, children: entry.title }),
1843
+ entry.description !== void 0 && /* @__PURE__ */ jsx("span", { className: Toast_module_default.description, children: entry.description })
1844
+ ] }),
1845
+ entry.action !== void 0 && /* @__PURE__ */ jsx(
1846
+ "button",
1847
+ {
1848
+ type: "button",
1849
+ className: Toast_module_default.action,
1850
+ onClick: () => {
1851
+ entry.action?.onClick();
1852
+ dismiss(entry.id);
1853
+ },
1854
+ children: entry.action.label
1855
+ }
1856
+ ),
1857
+ /* @__PURE__ */ jsx(
1858
+ "button",
1859
+ {
1860
+ type: "button",
1861
+ "aria-label": `${dismissLabel}: ${entry.title}`,
1862
+ className: Toast_module_default.close,
1863
+ onClick: () => dismiss(entry.id),
1864
+ children: /* @__PURE__ */ jsx(CloseGlyph, { className: Toast_module_default.closeGlyph })
1865
+ }
1866
+ )
1867
+ ]
1868
+ },
1869
+ entry.id
1870
+ ))
1871
+ }
1872
+ )
1766
1873
  ] });
1767
1874
  }
1768
1875
 
@@ -1827,9 +1934,23 @@ function Container(props) {
1827
1934
  var Card_module_default = { "card": "mds-Card__card", "variant-raised": "mds-Card__variant-raised", "variant-sunken": "mds-Card__variant-sunken", "emphasis": "mds-Card__emphasis", "interactive": "mds-Card__interactive", "header": "mds-Card__header", "body": "mds-Card__body", "footer": "mds-Card__footer", "clipped": "mds-Card__clipped" };
1828
1935
  function Card({ children, variant = "card", emphasis = false, onClick, href, as, className, ref, ...rest }) {
1829
1936
  const cardClass = cx(Card_module_default.card, Card_module_default[`variant-${variant}`], emphasis && Card_module_default.emphasis, className);
1937
+ const own = useRef(null);
1938
+ const interactive = href !== void 0 || onClick !== void 0;
1939
+ useEffect(() => {
1940
+ if (!interactive) return;
1941
+ const nested = own.current?.querySelector(
1942
+ "a[href], button, input, select, textarea, [tabindex]"
1943
+ );
1944
+ if (nested != null) {
1945
+ console.warn(
1946
+ "Card: this card is a link or button, and it contains another interactive element. Move the control outside the card, or drop onClick/href.",
1947
+ nested
1948
+ );
1949
+ }
1950
+ });
1830
1951
  if (href !== void 0) {
1831
1952
  const Element2 = as ?? "a";
1832
- return /* @__PURE__ */ jsx(Element2, { className: cx(cardClass, Card_module_default.interactive), href, ref, ...rest, children });
1953
+ return /* @__PURE__ */ jsx(Element2, { className: cx(cardClass, Card_module_default.interactive), href, ref: forkRef(own, ref), ...rest, children });
1833
1954
  }
1834
1955
  if (onClick !== void 0) {
1835
1956
  return /* @__PURE__ */ jsx(
@@ -1838,7 +1959,7 @@ function Card({ children, variant = "card", emphasis = false, onClick, href, as,
1838
1959
  type: "button",
1839
1960
  className: cx(cardClass, Card_module_default.interactive),
1840
1961
  onClick,
1841
- ref,
1962
+ ref: forkRef(own, ref),
1842
1963
  ...rest,
1843
1964
  children
1844
1965
  }
@@ -1867,7 +1988,7 @@ function CardFooter({ className, ...rest }) {
1867
1988
 
1868
1989
  // mds-cssmod:/home/runner/work/mond-design-system/mond-design-system/packages/react/src/components/Modal/Modal.module.css?mds-scoped
1869
1990
  var Modal_module_default = { "panel": "mds-Modal__panel", "header": "mds-Modal__header", "headerTitle": "mds-Modal__headerTitle", "body": "mds-Modal__body", "footer": "mds-Modal__footer" };
1870
- function Modal({ open, onClose, label, closeOnScrimClick = true, role = "dialog", children }) {
1991
+ function Modal({ open, onClose, label, closeOnScrimClick = true, role = "dialog", describedBy, children }) {
1871
1992
  return /* @__PURE__ */ jsx(
1872
1993
  Overlay,
1873
1994
  {
@@ -1876,6 +1997,7 @@ function Modal({ open, onClose, label, closeOnScrimClick = true, role = "dialog"
1876
1997
  label,
1877
1998
  variant: "modal",
1878
1999
  role,
2000
+ describedBy,
1879
2001
  closeOnScrimClick,
1880
2002
  panelClassName: Modal_module_default.panel,
1881
2003
  children
@@ -1959,12 +2081,13 @@ function Popover({
1959
2081
  onClose,
1960
2082
  anchorRef,
1961
2083
  label,
2084
+ id,
1962
2085
  placement = "bottom-start",
1963
2086
  className,
1964
2087
  children
1965
2088
  }) {
1966
2089
  const { mounted, visible } = usePresence(open, POPOVER_EXIT_MS);
1967
- const panelRef = useOverlay({ open, onClose, lockScroll: false });
2090
+ const panelRef = useOverlay({ open, onClose, lockScroll: false, modal: false });
1968
2091
  useAnchoredPosition({ open: mounted, anchorRef, floatingRef: panelRef, placement, gap: GAP });
1969
2092
  useDismissOnOutside(open, onClose, [panelRef, anchorRef]);
1970
2093
  if (!mounted) return null;
@@ -1973,6 +2096,7 @@ function Popover({
1973
2096
  "div",
1974
2097
  {
1975
2098
  ref: panelRef,
2099
+ id,
1976
2100
  role: "dialog",
1977
2101
  "aria-label": label,
1978
2102
  tabIndex: -1,
@@ -2028,7 +2152,9 @@ var GAP2 = 8;
2028
2152
  var ITEM_SELECTOR = '[role="menuitem"]';
2029
2153
  var MenuContext = createContext(null);
2030
2154
  function Menu({ label, trigger, placement = "bottom-end", className, children }) {
2155
+ const id = useId();
2031
2156
  const [open, setOpen] = useState(false);
2157
+ const enterAt = useRef("first");
2032
2158
  const { mounted, visible } = usePresence(open, MENU_EXIT_MS);
2033
2159
  const triggerRef = useRef(null);
2034
2160
  const panelRef = useRef(null);
@@ -2045,14 +2171,16 @@ function Menu({ label, trigger, placement = "bottom-end", className, children })
2045
2171
  });
2046
2172
  useEffect(() => {
2047
2173
  if (!open) return;
2048
- const first = panelRef.current?.querySelector(
2174
+ const items = panelRef.current?.querySelectorAll(
2049
2175
  `${ITEM_SELECTOR}:not([disabled])`
2050
2176
  );
2051
- first?.focus();
2177
+ if (items === void 0 || items.length === 0) return;
2178
+ items[enterAt.current === "last" ? items.length - 1 : 0]?.focus();
2052
2179
  }, [open]);
2053
2180
  const openWithKeyboard = (event) => {
2054
2181
  if (event.key !== "ArrowDown" && event.key !== "ArrowUp") return;
2055
2182
  event.preventDefault();
2183
+ enterAt.current = event.key === "ArrowUp" ? "last" : "first";
2056
2184
  setOpen(true);
2057
2185
  };
2058
2186
  const { type: Trigger, props: triggerProps } = trigger;
@@ -2064,8 +2192,10 @@ function Menu({ label, trigger, placement = "bottom-end", className, children })
2064
2192
  ref: forkRef(triggerRef, triggerProps.ref),
2065
2193
  "aria-haspopup": "menu",
2066
2194
  "aria-expanded": open,
2195
+ "aria-controls": open ? id : void 0,
2067
2196
  onClick: (event) => {
2068
2197
  triggerProps.onClick?.(event);
2198
+ enterAt.current = "first";
2069
2199
  setOpen((v) => !v);
2070
2200
  },
2071
2201
  onKeyDown: (event) => {
@@ -2079,6 +2209,7 @@ function Menu({ label, trigger, placement = "bottom-end", className, children })
2079
2209
  "div",
2080
2210
  {
2081
2211
  ref: panelRef,
2212
+ id,
2082
2213
  role: "menu",
2083
2214
  "aria-label": label,
2084
2215
  className: cx(Menu_module_default.panel, className),
@@ -2090,10 +2221,22 @@ function Menu({ label, trigger, placement = "bottom-end", className, children })
2090
2221
  return;
2091
2222
  }
2092
2223
  if (event.key === "Tab") {
2093
- event.preventDefault();
2094
2224
  close();
2095
2225
  return;
2096
2226
  }
2227
+ if (event.key.length === 1 && !event.ctrlKey && !event.metaKey && !event.altKey) {
2228
+ const items = [
2229
+ ...panelRef.current?.querySelectorAll(
2230
+ `${ITEM_SELECTOR}:not([disabled])`
2231
+ ) ?? []
2232
+ ];
2233
+ const current = items.indexOf(event.target);
2234
+ const fromNext = [...items.slice(current + 1), ...items.slice(0, current + 1)];
2235
+ fromNext.find(
2236
+ (item) => item.textContent?.trim().toLowerCase().startsWith(event.key.toLowerCase())
2237
+ )?.focus();
2238
+ return;
2239
+ }
2097
2240
  onArrowKeys(event);
2098
2241
  },
2099
2242
  children
@@ -2126,6 +2269,7 @@ function MenuItem({ onSelect, disabled = false, tone = "default", children }) {
2126
2269
  var Tooltip_module_default = { "surface": "mds-Tooltip__surface" };
2127
2270
  var GAP3 = 8;
2128
2271
  var DEFAULT_DELAY_MS = 400;
2272
+ var HOVER_GRACE_MS = 150;
2129
2273
  function Tooltip({
2130
2274
  content,
2131
2275
  placement = "top",
@@ -2145,6 +2289,10 @@ function Tooltip({
2145
2289
  clearTimeout(timer.current);
2146
2290
  setOpen(false);
2147
2291
  };
2292
+ const hideAfterGrace = () => {
2293
+ clearTimeout(timer.current);
2294
+ timer.current = setTimeout(hide, HOVER_GRACE_MS);
2295
+ };
2148
2296
  useEffect(() => clearTimeout(timer.current), []);
2149
2297
  useAnchoredPosition({ open, anchorRef, floatingRef: surfaceRef, placement, gap: GAP3 });
2150
2298
  useEffect(() => {
@@ -2171,7 +2319,7 @@ function Tooltip({
2171
2319
  },
2172
2320
  onPointerLeave: (event) => {
2173
2321
  triggerProps.onPointerLeave?.(event);
2174
- hide();
2322
+ hideAfterGrace();
2175
2323
  },
2176
2324
  onFocus: (event) => {
2177
2325
  triggerProps.onFocus?.(event);
@@ -2184,7 +2332,18 @@ function Tooltip({
2184
2332
  }
2185
2333
  ),
2186
2334
  open && createPortal(
2187
- /* @__PURE__ */ jsx("div", { ref: surfaceRef, id, role: "tooltip", className: Tooltip_module_default.surface, children: content }),
2335
+ /* @__PURE__ */ jsx(
2336
+ "div",
2337
+ {
2338
+ ref: surfaceRef,
2339
+ id,
2340
+ role: "tooltip",
2341
+ className: Tooltip_module_default.surface,
2342
+ onPointerEnter: show,
2343
+ onPointerLeave: hideAfterGrace,
2344
+ children: content
2345
+ }
2346
+ ),
2188
2347
  document.body
2189
2348
  )
2190
2349
  ] });
@@ -2209,6 +2368,7 @@ function ConfirmDialog({
2209
2368
  tone = "default",
2210
2369
  errorMessage
2211
2370
  }) {
2371
+ const descriptionId = useId();
2212
2372
  const [busy, setBusy] = useState(false);
2213
2373
  const [error, setError] = useState(null);
2214
2374
  const [prevOpen, setPrevOpen] = useState(open);
@@ -2247,11 +2407,12 @@ function ConfirmDialog({
2247
2407
  onClose: close,
2248
2408
  label: view.title,
2249
2409
  role: "alertdialog",
2410
+ describedBy: view.description != null ? descriptionId : void 0,
2250
2411
  closeOnScrimClick: false,
2251
2412
  children: [
2252
2413
  /* @__PURE__ */ jsx(ModalHeader, { children: view.title }),
2253
2414
  /* @__PURE__ */ jsxs(ModalBody, { children: [
2254
- view.description != null ? /* @__PURE__ */ jsx("p", { className: ConfirmDialog_module_default.description, children: view.description }) : null,
2415
+ view.description != null ? /* @__PURE__ */ jsx("p", { id: descriptionId, className: ConfirmDialog_module_default.description, children: view.description }) : null,
2255
2416
  error != null ? /* @__PURE__ */ jsx("p", { role: "alert", className: ConfirmDialog_module_default.error, children: error }) : null
2256
2417
  ] }),
2257
2418
  /* @__PURE__ */ jsxs(ModalFooter, { children: [
@@ -2298,6 +2459,7 @@ function TabBarItem({
2298
2459
  onClick,
2299
2460
  active = false,
2300
2461
  badge = false,
2462
+ badgeLabel,
2301
2463
  as,
2302
2464
  hideLabel = false
2303
2465
  }) {
@@ -2307,7 +2469,8 @@ function TabBarItem({
2307
2469
  icon,
2308
2470
  badge ? /* @__PURE__ */ jsx("span", { className: TabBar_module_default.badge, "data-testid": "mds-tabbar-badge" }) : null
2309
2471
  ] }),
2310
- hideLabel ? /* @__PURE__ */ jsx(VisuallyHidden, { children: label }) : /* @__PURE__ */ jsx("span", { className: TabBar_module_default.label, children: label })
2472
+ hideLabel ? /* @__PURE__ */ jsx(VisuallyHidden, { children: label }) : /* @__PURE__ */ jsx("span", { className: TabBar_module_default.label, children: label }),
2473
+ badge && badgeLabel !== void 0 && /* @__PURE__ */ jsx(VisuallyHidden, { children: badgeLabel })
2311
2474
  ] });
2312
2475
  if (href !== void 0) {
2313
2476
  const Element = as ?? "a";
@@ -2406,8 +2569,8 @@ function MediaPlaceholder({
2406
2569
  decoding: "async",
2407
2570
  onError: () => setFailed(src)
2408
2571
  }
2409
- ) : /* @__PURE__ */ jsxs("div", { className: MediaPlaceholder_module_default.fill, "aria-hidden": "true", children: [
2410
- glyph,
2572
+ ) : /* @__PURE__ */ jsxs("div", { className: MediaPlaceholder_module_default.fill, children: [
2573
+ glyph != null && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: glyph }),
2411
2574
  caption !== void 0 && /* @__PURE__ */ jsx(Text, { variant: "meta", children: caption })
2412
2575
  ] }),
2413
2576
  picture && caption !== void 0 && /* @__PURE__ */ jsx(Text, { variant: "meta", tone: "on-media", className: MediaPlaceholder_module_default.caption, children: caption }),
@@ -2624,7 +2787,7 @@ function UploadProgress({
2624
2787
  return /* @__PURE__ */ jsxs("div", { className: cx(UploadProgress_module_default.upload, UploadProgress_module_default[`status-${status}`], className), ...rest, children: [
2625
2788
  /* @__PURE__ */ jsxs("div", { className: UploadProgress_module_default.head, children: [
2626
2789
  preview !== void 0 && /* @__PURE__ */ jsx("div", { className: UploadProgress_module_default.preview, children: preview }),
2627
- mark !== void 0 && /* @__PURE__ */ jsx("span", { className: UploadProgress_module_default.mark, children: mark }),
2790
+ mark !== void 0 && /* @__PURE__ */ jsx("span", { className: UploadProgress_module_default.mark, "aria-hidden": "true", children: mark }),
2628
2791
  /* @__PURE__ */ jsx(Text, { variant: "label", truncate: true, className: UploadProgress_module_default.name, children: name }),
2629
2792
  running && onCancel !== void 0 && /* @__PURE__ */ jsx(Button, { iconOnly: true, size: "sm", variant: "ghost", "aria-label": labels.cancel, onClick: onCancel, children: /* @__PURE__ */ jsx(CloseGlyph, {}) }),
2630
2793
  !running && onRemove !== void 0 && /* @__PURE__ */ jsx(Button, { iconOnly: true, size: "sm", variant: "ghost", "aria-label": labels.remove, onClick: onRemove, children: /* @__PURE__ */ jsx(CloseGlyph, {}) }),
@@ -2640,7 +2803,7 @@ function UploadProgress({
2640
2803
  }
2641
2804
  ),
2642
2805
  /* @__PURE__ */ jsx("div", { className: UploadProgress_module_default.foot, children: status === "error" ? /* @__PURE__ */ jsx(Text, { variant: "meta", tone: "danger", role: "alert", children: error ?? labels.error }) : /* @__PURE__ */ jsxs(Fragment, { children: [
2643
- status !== "uploading" && /* @__PURE__ */ jsx(Text, { variant: "meta", children: labels[status] }),
2806
+ /* @__PURE__ */ jsx(Text, { variant: "meta", role: "status", children: status === "uploading" ? null : labels[status] }),
2644
2807
  detail !== void 0 && /* @__PURE__ */ jsx(Text, { variant: "meta", children: detail })
2645
2808
  ] }) })
2646
2809
  ] });
@@ -2693,7 +2856,7 @@ function FileDrop({
2693
2856
  disabled,
2694
2857
  onClick: () => picker.current?.click(),
2695
2858
  children: [
2696
- icon !== void 0 && /* @__PURE__ */ jsx("span", { className: FileDrop_module_default.icon, children: icon }),
2859
+ icon !== void 0 && /* @__PURE__ */ jsx("span", { className: FileDrop_module_default.icon, "aria-hidden": "true", children: icon }),
2697
2860
  /* @__PURE__ */ jsx(Text, { variant: "label", children: label }),
2698
2861
  hint !== void 0 && /* @__PURE__ */ jsx(Text, { variant: "meta", children: hint })
2699
2862
  ]
@@ -2727,6 +2890,13 @@ var MAX_SCALE = 4;
2727
2890
  var STEP2 = 1;
2728
2891
  var DOUBLE = 2.5;
2729
2892
  var WHEEL = 0.01;
2893
+ var KEY_PAN_PX = 48;
2894
+ var ARROWS = {
2895
+ ArrowLeft: { x: 1, y: 0 },
2896
+ ArrowRight: { x: -1, y: 0 },
2897
+ ArrowUp: { x: 0, y: 1 },
2898
+ ArrowDown: { x: 0, y: -1 }
2899
+ };
2730
2900
  var ORIGIN = { x: 0, y: 0 };
2731
2901
  var clamp = (value, low, high) => Math.min(Math.max(value, low), high);
2732
2902
  var round = (value) => Math.round(value * 1e3) / 1e3;
@@ -2777,6 +2947,19 @@ function Lightbox({ open, onClose, src, alt = "", caption, labels }) {
2777
2947
  surface.addEventListener("wheel", onWheel, { passive: false });
2778
2948
  return () => surface.removeEventListener("wheel", onWheel);
2779
2949
  }, [surface, zoom]);
2950
+ useEffect(() => {
2951
+ if (!open) return;
2952
+ const onKeyDown = (event) => {
2953
+ const step = ARROWS[event.key];
2954
+ if (step === void 0 || at.current <= MIN_SCALE) return;
2955
+ event.preventDefault();
2956
+ setOffset(
2957
+ (current) => hold({ x: current.x + step.x * KEY_PAN_PX, y: current.y + step.y * KEY_PAN_PX }, at.current)
2958
+ );
2959
+ };
2960
+ document.addEventListener("keydown", onKeyDown);
2961
+ return () => document.removeEventListener("keydown", onKeyDown);
2962
+ }, [open, hold]);
2780
2963
  function onPointerDown(event) {
2781
2964
  points.current.set(event.pointerId, { x: event.clientX, y: event.clientY });
2782
2965
  const [first, second] = [...points.current.values()];
@@ -2911,6 +3094,7 @@ function VideoPlayer({
2911
3094
  const [at, setAt] = useState(0);
2912
3095
  const [length, setLength] = useState(0);
2913
3096
  const [isFullscreen, setFullscreen] = useState(false);
3097
+ const [captionsOn, setCaptionsOn] = useState(captions?.defaultOn ?? false);
2914
3098
  function toggle() {
2915
3099
  const el = media.current;
2916
3100
  if (!el) return;
@@ -2936,7 +3120,10 @@ function VideoPlayer({
2936
3120
  }
2937
3121
  function toggleCaptions() {
2938
3122
  const track = media.current?.textTracks?.[0];
2939
- if (track) track.mode = track.mode === "showing" ? "disabled" : "showing";
3123
+ if (!track) return;
3124
+ const next = track.mode !== "showing";
3125
+ track.mode = next ? "showing" : "disabled";
3126
+ setCaptionsOn(next);
2940
3127
  }
2941
3128
  function selectChapter(chapter) {
2942
3129
  goTo(chapter.at);
@@ -3017,6 +3204,7 @@ function VideoPlayer({
3017
3204
  type: "range",
3018
3205
  className: VideoPlayer_module_default.scrub,
3019
3206
  "aria-label": labels.seek,
3207
+ "aria-valuetext": `${clock(Math.min(at, length || 0))} / ${clock(length)}`,
3020
3208
  min: 0,
3021
3209
  max: length || 0,
3022
3210
  step: 1,
@@ -3043,7 +3231,7 @@ function VideoPlayer({
3043
3231
  "aria-label": captionsLabel,
3044
3232
  variant: "ghost",
3045
3233
  size: "sm",
3046
- "aria-pressed": captions.defaultOn ?? false,
3234
+ "aria-pressed": captionsOn,
3047
3235
  onClick: toggleCaptions,
3048
3236
  children: /* @__PURE__ */ jsx(CaptionsGlyph, {})
3049
3237
  }
@@ -3083,7 +3271,12 @@ function Breadcrumb({
3083
3271
  const last = index === items.length - 1;
3084
3272
  return /* @__PURE__ */ jsxs("li", { className: Breadcrumb_module_default.item, children: [
3085
3273
  index > 0 && /* @__PURE__ */ jsx(ChevronRightGlyph, { className: Breadcrumb_module_default.separator }),
3086
- last || item.href === void 0 ? /* @__PURE__ */ jsx(Text, { variant: "meta", tone: last ? "primary" : "muted", "aria-current": last ? "page" : void 0, children: item.label }) : /* @__PURE__ */ jsx(Link, { variant: "plain", size: "xs", as: linkAs, href: item.href, className: Breadcrumb_module_default.link, children: item.label })
3274
+ last || item.href === void 0 ? /* @__PURE__ */ jsx(Text, { variant: "meta", tone: last ? "primary" : "muted", "aria-current": last ? "page" : void 0, children: item.label }) : (
3275
+ /* inline, not plain: the underline is what separates a step
3276
+ you can go back to from the one you are standing on, for a
3277
+ reader who cannot tell the two text colors apart. */
3278
+ /* @__PURE__ */ jsx(Link, { variant: "inline", size: "xs", as: linkAs, href: item.href, className: Breadcrumb_module_default.link, children: item.label })
3279
+ )
3087
3280
  ] }, `${item.label}-${index}`);
3088
3281
  }) }) });
3089
3282
  }