@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.cjs CHANGED
@@ -131,7 +131,7 @@ function Icon({ name, size: size2, label, className, ...rest }) {
131
131
  }
132
132
 
133
133
  // mds-cssmod:/home/runner/work/mond-design-system/mond-design-system/packages/react/src/components/Button/Button.module.css?mds-scoped
134
- 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" };
134
+ 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" };
135
135
  var ICON_PX = { sm: 16, md: 20, lg: 24 };
136
136
  function Button({
137
137
  variant = "primary",
@@ -149,13 +149,14 @@ function Button({
149
149
  as,
150
150
  className,
151
151
  children,
152
+ onClick,
152
153
  ...rest
153
154
  }) {
154
155
  const glyph = iconOnly && children != null ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: Button_module_default.slot, children }) : children;
155
156
  const body = loading && iconOnly ? null : glyph;
156
157
  const Element = as ?? (href !== void 0 ? "a" : "button");
157
158
  const isButton = Element === "button";
158
- const own = isButton ? { type, disabled: disabled || loading } : { href };
159
+ const own = isButton ? { type, disabled } : { href };
159
160
  return /* @__PURE__ */ jsxRuntime.jsxs(
160
161
  Element,
161
162
  {
@@ -170,6 +171,8 @@ function Button({
170
171
  className
171
172
  ),
172
173
  "aria-busy": loading || void 0,
174
+ "aria-disabled": loading || void 0,
175
+ onClick: loading ? (event) => event.preventDefault() : onClick,
173
176
  ...own,
174
177
  ...rest,
175
178
  children: [
@@ -182,19 +185,21 @@ function Button({
182
185
  }
183
186
 
184
187
  // mds-cssmod:/home/runner/work/mond-design-system/mond-design-system/packages/react/src/components/Link/Link.module.css?mds-scoped
185
- 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" };
188
+ 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" };
186
189
  function Link({
187
190
  variant = "inline",
188
191
  size: size2,
189
192
  external = false,
193
+ externalLabel,
190
194
  as,
195
+ children,
191
196
  ...rest
192
197
  }) {
193
198
  const Element = as ?? "a";
194
199
  const { className, type, ...others } = rest;
195
200
  const externalProps = external ? { target: "_blank", rel: "noopener noreferrer" } : {};
196
201
  const typeProps = Element === "button" ? { type: type ?? "button" } : type !== void 0 ? { type } : {};
197
- return /* @__PURE__ */ jsxRuntime.jsx(
202
+ return /* @__PURE__ */ jsxRuntime.jsxs(
198
203
  Element,
199
204
  {
200
205
  className: cx(
@@ -205,7 +210,13 @@ function Link({
205
210
  ),
206
211
  ...externalProps,
207
212
  ...others,
208
- ...typeProps
213
+ ...typeProps,
214
+ children: [
215
+ children,
216
+ external && externalLabel !== void 0 && /* The leading space keeps the name "Docs opens in new tab", not
217
+ "Docsopens…" — inline text concatenates without one. */
218
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: Link_module_default.hiddenNote, children: ` ${externalLabel}` })
219
+ ]
209
220
  }
210
221
  );
211
222
  }
@@ -356,7 +367,7 @@ function ChipBar({
356
367
  }
357
368
 
358
369
  // mds-cssmod:/home/runner/work/mond-design-system/mond-design-system/packages/react/src/components/CountButton/CountButton.module.css?mds-scoped
359
- var CountButton_module_default = { "button": "mds-CountButton__button", "tone-accent": "mds-CountButton__tone-accent", "tone-danger": "mds-CountButton__tone-danger", "glyph": "mds-CountButton__glyph" };
370
+ 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" };
360
371
  var SPINNER_PX = 20;
361
372
  function CountButton({
362
373
  icon,
@@ -367,16 +378,20 @@ function CountButton({
367
378
  loading = false,
368
379
  className,
369
380
  children,
381
+ onClick,
370
382
  ...rest
371
383
  }) {
372
384
  const isToggle = active !== void 0;
385
+ const spoken = typeof children === "string" || typeof children === "number" ? `${label} ${children}` : label;
373
386
  return /* @__PURE__ */ jsxRuntime.jsxs(
374
387
  "button",
375
388
  {
376
389
  type: "button",
377
- disabled: disabled || loading,
378
- "aria-label": label,
390
+ disabled,
391
+ onClick: loading ? (event) => event.preventDefault() : onClick,
392
+ "aria-label": spoken,
379
393
  "aria-pressed": isToggle ? active : void 0,
394
+ "aria-disabled": loading || void 0,
380
395
  "aria-busy": loading || void 0,
381
396
  className: cx(CountButton_module_default.button, active && CountButton_module_default[`tone-${tone}`], className),
382
397
  ...rest,
@@ -502,7 +517,7 @@ function Field({ label, children, hint, error, required = false, className }) {
502
517
  /* @__PURE__ */ jsxRuntime.jsx(
503
518
  FieldContext.Provider,
504
519
  {
505
- value: { id, describedBy: message ? messageId : void 0, invalid: error !== void 0 },
520
+ value: { id, describedBy: message ? messageId : void 0, invalid: error !== void 0, required },
506
521
  children
507
522
  }
508
523
  ),
@@ -529,6 +544,7 @@ function Select({ size: size2 = "md", className, children, ...rest }) {
529
544
  id: rest.id ?? field?.id,
530
545
  "aria-describedby": rest["aria-describedby"] ?? field?.describedBy,
531
546
  "aria-invalid": rest["aria-invalid"] ?? (field?.invalid || void 0),
547
+ "aria-required": rest["aria-required"] ?? (field?.required || void 0),
532
548
  className: cx(Select_module_default.select, Select_module_default[`size-${size2}`], className),
533
549
  ...rest,
534
550
  children
@@ -540,10 +556,10 @@ function Select({ size: size2 = "md", className, children, ...rest }) {
540
556
  var OverlayHistoryContext = react.createContext(null);
541
557
 
542
558
  // src/hooks/useOverlay.ts
543
- var FOCUSABLE = 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
559
+ 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"])';
544
560
  function useOverlay(options) {
545
561
  const ref = react.useRef(null);
546
- const { open, onClose, lockScroll = true } = options;
562
+ const { open, onClose, lockScroll = true, modal = true } = options;
547
563
  const close = react.useRef(onClose);
548
564
  react.useEffect(() => {
549
565
  close.current = onClose;
@@ -558,13 +574,24 @@ function useOverlay(options) {
558
574
  const panel = ref.current;
559
575
  const previous = document.activeElement instanceof HTMLElement ? document.activeElement : null;
560
576
  panel?.focus();
577
+ let marked = [];
578
+ if (modal && panel) {
579
+ let portal = panel;
580
+ while (portal.parentElement && portal.parentElement !== document.body) {
581
+ portal = portal.parentElement;
582
+ }
583
+ marked = [...document.body.children].filter(
584
+ (sibling) => sibling !== portal && !sibling.hasAttribute("inert")
585
+ );
586
+ for (const sibling of marked) sibling.setAttribute("inert", "");
587
+ }
561
588
  const onKeyDown = (event) => {
562
589
  if (event.key === "Escape") {
563
590
  event.stopPropagation();
564
591
  close.current();
565
592
  return;
566
593
  }
567
- if (event.key !== "Tab" || !panel) return;
594
+ if (!modal || event.key !== "Tab" || !panel) return;
568
595
  const focusable = [...panel.querySelectorAll(FOCUSABLE)];
569
596
  if (focusable.length === 0) {
570
597
  event.preventDefault();
@@ -588,9 +615,13 @@ function useOverlay(options) {
588
615
  return () => {
589
616
  document.removeEventListener("keydown", onKeyDown);
590
617
  if (lockScroll) document.body.style.overflow = bodyOverflow;
591
- previous?.focus();
618
+ for (const sibling of marked) sibling.removeAttribute("inert");
619
+ const active = document.activeElement;
620
+ if (modal || panel && active instanceof HTMLElement && panel.contains(active)) {
621
+ previous?.focus();
622
+ }
592
623
  };
593
- }, [open, lockScroll]);
624
+ }, [open, lockScroll, modal]);
594
625
  return ref;
595
626
  }
596
627
  function usePresence(open, durationMs) {
@@ -611,7 +642,8 @@ function usePresence(open, durationMs) {
611
642
  }, [open]);
612
643
  react.useEffect(() => {
613
644
  if (open) return;
614
- const timer = setTimeout(() => setMounted(false), durationMs);
645
+ const reduced = typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
646
+ const timer = setTimeout(() => setMounted(false), reduced ? 0 : durationMs);
615
647
  return () => clearTimeout(timer);
616
648
  }, [open, durationMs]);
617
649
  return { mounted: open || mounted, visible };
@@ -621,7 +653,7 @@ function usePresence(open, durationMs) {
621
653
  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" };
622
654
  var OVERLAY_EXIT_MS = 200;
623
655
  var SHEET_EXIT_MS = 320;
624
- function Overlay({ open, onClose, label, variant, role = "dialog", closeOnScrimClick, panelClassName, children }) {
656
+ function Overlay({ open, onClose, label, variant, role = "dialog", describedBy, closeOnScrimClick, panelClassName, children }) {
625
657
  const { mounted, visible } = usePresence(open, variant === "sheet" ? SHEET_EXIT_MS : OVERLAY_EXIT_MS);
626
658
  const ref = useOverlay({ open, onClose });
627
659
  if (!mounted) return null;
@@ -640,6 +672,7 @@ function Overlay({ open, onClose, label, variant, role = "dialog", closeOnScrimC
640
672
  role,
641
673
  "aria-modal": "true",
642
674
  "aria-label": label,
675
+ "aria-describedby": describedBy,
643
676
  tabIndex: -1,
644
677
  className: panelClassName,
645
678
  "data-open": visible || void 0,
@@ -683,7 +716,7 @@ function SheetFooter({ children }) {
683
716
  }
684
717
 
685
718
  // mds-cssmod:/home/runner/work/mond-design-system/mond-design-system/packages/react/src/components/DateTimePicker/DateTimePicker.module.css?mds-scoped
686
- 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" };
719
+ 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" };
687
720
  function localeData(locale) {
688
721
  const resolved = new Intl.DateTimeFormat(locale).resolvedOptions().locale;
689
722
  const intlLocale = new Intl.Locale(resolved);
@@ -910,9 +943,11 @@ function DateTimePicker({
910
943
  if (el) dayRefs.current.set(cellStart, el);
911
944
  else dayRefs.current.delete(cellStart);
912
945
  } : void 0,
946
+ role: "gridcell",
913
947
  "aria-hidden": inMonth ? void 0 : true,
914
948
  "aria-label": intl.dayLabel.format(cell),
915
- "aria-pressed": isSelected,
949
+ "aria-selected": isSelected,
950
+ "aria-current": isToday ? "date" : void 0,
916
951
  onClick: inMonth ? () => pickDay(cell.getDate()) : void 0,
917
952
  onKeyDown: inMonth && !isDisabled ? (e) => onDayKeyDown(e, cell) : void 0,
918
953
  className: cx(DateTimePicker_module_default.day, isToday && DateTimePicker_module_default.today, isSelected && DateTimePicker_module_default.selected, !inMonth && DateTimePicker_module_default.outside),
@@ -930,6 +965,7 @@ function DateTimePicker({
930
965
  type: "button",
931
966
  disabled,
932
967
  "aria-describedby": field?.describedBy,
968
+ "aria-invalid": field?.invalid || void 0,
933
969
  "aria-haspopup": "dialog",
934
970
  "aria-expanded": open,
935
971
  "aria-label": ariaLabel === void 0 ? void 0 : [ariaLabel, shown].filter(Boolean).join(": "),
@@ -974,11 +1010,13 @@ function DateTimePicker({
974
1010
  children: /* @__PURE__ */ jsxRuntime.jsx(ChevronLeftGlyph, { className: DateTimePicker_module_default.glyph })
975
1011
  }
976
1012
  ),
977
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: DateTimePicker_module_default.monthLabel, children: intl.monthTitle.format(first) }),
1013
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: DateTimePicker_module_default.monthLabel, "aria-live": "polite", children: intl.monthTitle.format(first) }),
978
1014
  /* @__PURE__ */ jsxRuntime.jsx(Button, { iconOnly: true, "aria-label": text.nextMonth, variant: "ghost", size: "sm", onClick: () => shiftMonth(1), children: /* @__PURE__ */ jsxRuntime.jsx(ChevronRightGlyph, { className: DateTimePicker_module_default.glyph }) })
979
1015
  ] }),
980
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: DateTimePicker_module_default.weekdays, children: intl.weekdays.map((w, i) => /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", className: DateTimePicker_module_default.weekday, children: w }, `wd-${i}`)) }),
981
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: DateTimePicker_module_default.grid, children: dayCells })
1016
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { role: "grid", "aria-label": intl.monthTitle.format(first), children: [
1017
+ /* @__PURE__ */ jsxRuntime.jsx("div", { role: "row", className: DateTimePicker_module_default.weekdays, children: intl.weekdays.map((w, i) => /* @__PURE__ */ jsxRuntime.jsx("span", { role: "columnheader", className: DateTimePicker_module_default.weekday, children: w }, `wd-${i}`)) }),
1018
+ /* @__PURE__ */ jsxRuntime.jsx("div", { role: "rowgroup", className: DateTimePicker_module_default.grid, children: Array.from({ length: spanCells / 7 }, (_, w) => /* @__PURE__ */ jsxRuntime.jsx("div", { role: "row", className: DateTimePicker_module_default.week, children: dayCells.slice(w * 7, w * 7 + 7) }, `w-${w}`)) })
1019
+ ] })
982
1020
  ] })
983
1021
  ] }),
984
1022
  /* @__PURE__ */ jsxRuntime.jsxs(SheetFooter, { children: [
@@ -1139,6 +1177,7 @@ function Input({
1139
1177
  id: rest.id ?? field?.id,
1140
1178
  "aria-describedby": rest["aria-describedby"] ?? field?.describedBy,
1141
1179
  "aria-invalid": rest["aria-invalid"] ?? ((invalid ?? field?.invalid) || void 0),
1180
+ "aria-required": rest["aria-required"] ?? (field?.required || void 0),
1142
1181
  className: cx(
1143
1182
  Input_module_default.input,
1144
1183
  Input_module_default[`size-${size2}`],
@@ -1161,7 +1200,7 @@ function Input({
1161
1200
  }
1162
1201
 
1163
1202
  // mds-cssmod:/home/runner/work/mond-design-system/mond-design-system/packages/react/src/components/PasswordInput/PasswordInput.module.css?mds-scoped
1164
- var PasswordInput_module_default = { "wrap": "mds-PasswordInput__wrap", "input": "mds-PasswordInput__input", "toggle": "mds-PasswordInput__toggle" };
1203
+ var PasswordInput_module_default = { "wrap": "mds-PasswordInput__wrap", "input": "mds-PasswordInput__input", "toggle": "mds-PasswordInput__toggle mds-hit-area__hitArea" };
1165
1204
  function PasswordInput({
1166
1205
  showLabel,
1167
1206
  hideLabel,
@@ -1203,28 +1242,31 @@ function PasswordInput({
1203
1242
  var Textarea_module_default = { "textarea": "mds-Textarea__textarea", "wrap": "mds-Textarea__wrap", "count": "mds-Textarea__count" };
1204
1243
  function Textarea({ rows = 3, showCount = false, className, ...rest }) {
1205
1244
  const field = useFieldContext();
1245
+ const countId = react.useId();
1206
1246
  const [tracked, setTracked] = react.useState(() => String(rest.defaultValue ?? ""));
1207
1247
  const current = rest.value !== void 0 ? String(rest.value) : tracked;
1208
1248
  const onChange = (event) => {
1209
1249
  setTracked(event.target.value);
1210
1250
  rest.onChange?.(event);
1211
1251
  };
1252
+ const describedBy = [rest["aria-describedby"] ?? field?.describedBy, showCount ? countId : void 0].filter(Boolean).join(" ") || void 0;
1212
1253
  const textarea = /* @__PURE__ */ jsxRuntime.jsx(
1213
1254
  "textarea",
1214
1255
  {
1215
1256
  id: rest.id ?? field?.id,
1216
- "aria-describedby": rest["aria-describedby"] ?? field?.describedBy,
1217
1257
  "aria-invalid": rest["aria-invalid"] ?? (field?.invalid || void 0),
1258
+ "aria-required": rest["aria-required"] ?? (field?.required || void 0),
1218
1259
  rows,
1219
1260
  className: cx(Textarea_module_default.textarea, className),
1220
1261
  ...rest,
1262
+ "aria-describedby": describedBy,
1221
1263
  ...showCount ? { onChange } : {}
1222
1264
  }
1223
1265
  );
1224
1266
  if (!showCount) return textarea;
1225
1267
  return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: Textarea_module_default.wrap, children: [
1226
1268
  textarea,
1227
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: Textarea_module_default.count, "aria-hidden": "true", children: rest.maxLength !== void 0 ? `${current.length}/${rest.maxLength}` : current.length })
1269
+ /* @__PURE__ */ jsxRuntime.jsx("span", { id: countId, className: Textarea_module_default.count, children: rest.maxLength !== void 0 ? `${current.length}/${rest.maxLength}` : current.length })
1228
1270
  ] });
1229
1271
  }
1230
1272
 
@@ -1272,6 +1314,8 @@ function Switch({
1272
1314
  loading = false,
1273
1315
  disabled = false,
1274
1316
  className,
1317
+ onClick,
1318
+ onChange,
1275
1319
  ...rest
1276
1320
  }) {
1277
1321
  return /* @__PURE__ */ jsxRuntime.jsxs("label", { className: cx(Switch_module_default.root, className), children: [
@@ -1282,7 +1326,10 @@ function Switch({
1282
1326
  type: "checkbox",
1283
1327
  role: "switch",
1284
1328
  className: Switch_module_default.track,
1285
- disabled: disabled || loading,
1329
+ disabled,
1330
+ onClick: loading ? (event) => event.preventDefault() : onClick,
1331
+ onChange: loading ? void 0 : onChange,
1332
+ "aria-disabled": loading || void 0,
1286
1333
  "aria-busy": loading || void 0,
1287
1334
  ...rest
1288
1335
  }
@@ -1427,6 +1474,7 @@ function ListGroup({ label, className, ...rest }) {
1427
1474
  const list = /* @__PURE__ */ jsxRuntime.jsx(ListGroupContext.Provider, { value: true, children: /* @__PURE__ */ jsxRuntime.jsx(
1428
1475
  "ul",
1429
1476
  {
1477
+ role: "list",
1430
1478
  className: cx(List_module_default.group, className),
1431
1479
  "aria-labelledby": label != null ? headingId : void 0,
1432
1480
  ...rest
@@ -1593,10 +1641,10 @@ function DataTable({
1593
1641
 
1594
1642
  // mds-cssmod:/home/runner/work/mond-design-system/mond-design-system/packages/react/src/components/EmptyState/EmptyState.module.css?mds-scoped
1595
1643
  var EmptyState_module_default = { "root": "mds-EmptyState__root", "icon": "mds-EmptyState__icon", "action": "mds-EmptyState__action" };
1596
- function EmptyState({ title, description, icon, action, className }) {
1644
+ function EmptyState({ title, level = 3, description, icon, action, className }) {
1597
1645
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cx(EmptyState_module_default.root, className), children: [
1598
1646
  icon != null && /* @__PURE__ */ jsxRuntime.jsx("span", { className: EmptyState_module_default.icon, "aria-hidden": "true", children: icon }),
1599
- /* @__PURE__ */ jsxRuntime.jsx(Heading, { level: 3, children: title }),
1647
+ /* @__PURE__ */ jsxRuntime.jsx(Heading, { level, children: title }),
1600
1648
  description !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(Text, { tone: "secondary", children: description }),
1601
1649
  action != null && /* @__PURE__ */ jsxRuntime.jsx("span", { className: EmptyState_module_default.action, children: action })
1602
1650
  ] });
@@ -1684,6 +1732,7 @@ function TabPanel({ value, className, children, ...rest }) {
1684
1732
  role: "tabpanel",
1685
1733
  id: `${tabs.baseId}-panel-${value}`,
1686
1734
  "aria-labelledby": `${tabs.baseId}-tab-${value}`,
1735
+ tabIndex: 0,
1687
1736
  hidden: !selected,
1688
1737
  className: cx(Tabs_module_default.panel, className),
1689
1738
  ...rest,
@@ -1708,7 +1757,22 @@ function ToastProvider({
1708
1757
  const [toasts, setToasts] = react.useState([]);
1709
1758
  const nextId = react.useRef(1);
1710
1759
  const leaving = react.useRef(/* @__PURE__ */ new Map());
1760
+ const clocks = react.useRef(
1761
+ /* @__PURE__ */ new Map()
1762
+ );
1763
+ const held = react.useRef(0);
1764
+ const surfaces = react.useRef(/* @__PURE__ */ new Map());
1711
1765
  const dismiss = react.useCallback((id) => {
1766
+ const clock2 = clocks.current.get(id);
1767
+ if (clock2 !== void 0) {
1768
+ clearTimeout(clock2.handle);
1769
+ clocks.current.delete(id);
1770
+ }
1771
+ const surface = surfaces.current.get(id);
1772
+ if (surface?.contains(document.activeElement)) {
1773
+ const neighbour = surface.nextElementSibling ?? surface.previousElementSibling;
1774
+ neighbour?.querySelector("button:last-child")?.focus();
1775
+ }
1712
1776
  setToasts((current) => current.filter((entry) => entry.id !== id));
1713
1777
  const said = leaving.current.get(id);
1714
1778
  if (said !== void 0) {
@@ -1716,55 +1780,98 @@ function ToastProvider({
1716
1780
  said();
1717
1781
  }
1718
1782
  }, []);
1783
+ const start = react.useCallback(
1784
+ (id, remaining) => {
1785
+ clocks.current.set(id, {
1786
+ handle: setTimeout(() => dismiss(id), remaining),
1787
+ startedAt: Date.now(),
1788
+ remaining
1789
+ });
1790
+ },
1791
+ [dismiss]
1792
+ );
1793
+ const hold = react.useCallback(() => {
1794
+ if (held.current++ > 0) return;
1795
+ for (const clock2 of clocks.current.values()) {
1796
+ clearTimeout(clock2.handle);
1797
+ clock2.remaining -= Date.now() - clock2.startedAt;
1798
+ }
1799
+ }, []);
1800
+ const release = react.useCallback(() => {
1801
+ if (--held.current > 0) return;
1802
+ held.current = 0;
1803
+ for (const [id, clock2] of clocks.current) {
1804
+ start(id, Math.max(clock2.remaining, 0));
1805
+ }
1806
+ }, [start]);
1719
1807
  const toast = react.useCallback(
1720
1808
  ({ title, description, tone = "neutral", duration = 5e3, action, onDismiss }) => {
1721
1809
  const id = nextId.current++;
1722
1810
  if (onDismiss !== void 0) leaving.current.set(id, onDismiss);
1723
1811
  setToasts((current) => [...current, { id, title, description, tone, action }]);
1724
- if (duration > 0) setTimeout(() => dismiss(id), duration);
1812
+ if (duration > 0) {
1813
+ start(id, duration);
1814
+ if (held.current > 0) clearTimeout(clocks.current.get(id)?.handle);
1815
+ }
1725
1816
  return id;
1726
1817
  },
1727
- [dismiss]
1818
+ [start]
1728
1819
  );
1729
1820
  const value = react.useMemo(() => ({ toast, dismiss }), [toast, dismiss]);
1730
1821
  return /* @__PURE__ */ jsxRuntime.jsxs(ToastContext.Provider, { value, children: [
1731
1822
  children,
1732
- /* @__PURE__ */ jsxRuntime.jsx("div", { role: "region", "aria-label": regionLabel, className: Toast_module_default.viewport, children: toasts.map((entry) => /* @__PURE__ */ jsxRuntime.jsxs(
1823
+ /* @__PURE__ */ jsxRuntime.jsx(
1733
1824
  "div",
1734
1825
  {
1735
- role: entry.tone === "danger" ? "alert" : "status",
1736
- className: cx(Toast_module_default.toast, Toast_module_default[`tone-${entry.tone}`]),
1737
- children: [
1738
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: Toast_module_default.body, children: [
1739
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: Toast_module_default.title, children: entry.title }),
1740
- entry.description !== void 0 && /* @__PURE__ */ jsxRuntime.jsx("span", { className: Toast_module_default.description, children: entry.description })
1741
- ] }),
1742
- entry.action !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(
1743
- "button",
1744
- {
1745
- type: "button",
1746
- className: Toast_module_default.action,
1747
- onClick: () => {
1748
- entry.action?.onClick();
1749
- dismiss(entry.id);
1750
- },
1751
- children: entry.action.label
1752
- }
1753
- ),
1754
- /* @__PURE__ */ jsxRuntime.jsx(
1755
- "button",
1756
- {
1757
- type: "button",
1758
- "aria-label": `${dismissLabel}: ${entry.title}`,
1759
- className: Toast_module_default.close,
1760
- onClick: () => dismiss(entry.id),
1761
- children: /* @__PURE__ */ jsxRuntime.jsx(CloseGlyph, { className: Toast_module_default.closeGlyph })
1762
- }
1763
- )
1764
- ]
1765
- },
1766
- entry.id
1767
- )) })
1826
+ role: "region",
1827
+ "aria-label": regionLabel,
1828
+ className: Toast_module_default.viewport,
1829
+ onPointerEnter: hold,
1830
+ onPointerLeave: release,
1831
+ onFocus: hold,
1832
+ onBlur: release,
1833
+ children: toasts.map((entry) => /* @__PURE__ */ jsxRuntime.jsxs(
1834
+ "div",
1835
+ {
1836
+ ref: (node) => {
1837
+ if (node !== null) surfaces.current.set(entry.id, node);
1838
+ else surfaces.current.delete(entry.id);
1839
+ },
1840
+ role: entry.tone === "danger" ? "alert" : "status",
1841
+ className: cx(Toast_module_default.toast, Toast_module_default[`tone-${entry.tone}`]),
1842
+ children: [
1843
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: Toast_module_default.body, children: [
1844
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: Toast_module_default.title, children: entry.title }),
1845
+ entry.description !== void 0 && /* @__PURE__ */ jsxRuntime.jsx("span", { className: Toast_module_default.description, children: entry.description })
1846
+ ] }),
1847
+ entry.action !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(
1848
+ "button",
1849
+ {
1850
+ type: "button",
1851
+ className: Toast_module_default.action,
1852
+ onClick: () => {
1853
+ entry.action?.onClick();
1854
+ dismiss(entry.id);
1855
+ },
1856
+ children: entry.action.label
1857
+ }
1858
+ ),
1859
+ /* @__PURE__ */ jsxRuntime.jsx(
1860
+ "button",
1861
+ {
1862
+ type: "button",
1863
+ "aria-label": `${dismissLabel}: ${entry.title}`,
1864
+ className: Toast_module_default.close,
1865
+ onClick: () => dismiss(entry.id),
1866
+ children: /* @__PURE__ */ jsxRuntime.jsx(CloseGlyph, { className: Toast_module_default.closeGlyph })
1867
+ }
1868
+ )
1869
+ ]
1870
+ },
1871
+ entry.id
1872
+ ))
1873
+ }
1874
+ )
1768
1875
  ] });
1769
1876
  }
1770
1877
 
@@ -1829,9 +1936,23 @@ function Container(props) {
1829
1936
  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" };
1830
1937
  function Card({ children, variant = "card", emphasis = false, onClick, href, as, className, ref, ...rest }) {
1831
1938
  const cardClass = cx(Card_module_default.card, Card_module_default[`variant-${variant}`], emphasis && Card_module_default.emphasis, className);
1939
+ const own = react.useRef(null);
1940
+ const interactive = href !== void 0 || onClick !== void 0;
1941
+ react.useEffect(() => {
1942
+ if (!interactive) return;
1943
+ const nested = own.current?.querySelector(
1944
+ "a[href], button, input, select, textarea, [tabindex]"
1945
+ );
1946
+ if (nested != null) {
1947
+ console.warn(
1948
+ "Card: this card is a link or button, and it contains another interactive element. Move the control outside the card, or drop onClick/href.",
1949
+ nested
1950
+ );
1951
+ }
1952
+ });
1832
1953
  if (href !== void 0) {
1833
1954
  const Element2 = as ?? "a";
1834
- return /* @__PURE__ */ jsxRuntime.jsx(Element2, { className: cx(cardClass, Card_module_default.interactive), href, ref, ...rest, children });
1955
+ return /* @__PURE__ */ jsxRuntime.jsx(Element2, { className: cx(cardClass, Card_module_default.interactive), href, ref: forkRef(own, ref), ...rest, children });
1835
1956
  }
1836
1957
  if (onClick !== void 0) {
1837
1958
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -1840,7 +1961,7 @@ function Card({ children, variant = "card", emphasis = false, onClick, href, as,
1840
1961
  type: "button",
1841
1962
  className: cx(cardClass, Card_module_default.interactive),
1842
1963
  onClick,
1843
- ref,
1964
+ ref: forkRef(own, ref),
1844
1965
  ...rest,
1845
1966
  children
1846
1967
  }
@@ -1869,7 +1990,7 @@ function CardFooter({ className, ...rest }) {
1869
1990
 
1870
1991
  // mds-cssmod:/home/runner/work/mond-design-system/mond-design-system/packages/react/src/components/Modal/Modal.module.css?mds-scoped
1871
1992
  var Modal_module_default = { "panel": "mds-Modal__panel", "header": "mds-Modal__header", "headerTitle": "mds-Modal__headerTitle", "body": "mds-Modal__body", "footer": "mds-Modal__footer" };
1872
- function Modal({ open, onClose, label, closeOnScrimClick = true, role = "dialog", children }) {
1993
+ function Modal({ open, onClose, label, closeOnScrimClick = true, role = "dialog", describedBy, children }) {
1873
1994
  return /* @__PURE__ */ jsxRuntime.jsx(
1874
1995
  Overlay,
1875
1996
  {
@@ -1878,6 +1999,7 @@ function Modal({ open, onClose, label, closeOnScrimClick = true, role = "dialog"
1878
1999
  label,
1879
2000
  variant: "modal",
1880
2001
  role,
2002
+ describedBy,
1881
2003
  closeOnScrimClick,
1882
2004
  panelClassName: Modal_module_default.panel,
1883
2005
  children
@@ -1961,12 +2083,13 @@ function Popover({
1961
2083
  onClose,
1962
2084
  anchorRef,
1963
2085
  label,
2086
+ id,
1964
2087
  placement = "bottom-start",
1965
2088
  className,
1966
2089
  children
1967
2090
  }) {
1968
2091
  const { mounted, visible } = usePresence(open, POPOVER_EXIT_MS);
1969
- const panelRef = useOverlay({ open, onClose, lockScroll: false });
2092
+ const panelRef = useOverlay({ open, onClose, lockScroll: false, modal: false });
1970
2093
  useAnchoredPosition({ open: mounted, anchorRef, floatingRef: panelRef, placement, gap: GAP });
1971
2094
  useDismissOnOutside(open, onClose, [panelRef, anchorRef]);
1972
2095
  if (!mounted) return null;
@@ -1975,6 +2098,7 @@ function Popover({
1975
2098
  "div",
1976
2099
  {
1977
2100
  ref: panelRef,
2101
+ id,
1978
2102
  role: "dialog",
1979
2103
  "aria-label": label,
1980
2104
  tabIndex: -1,
@@ -2030,7 +2154,9 @@ var GAP2 = 8;
2030
2154
  var ITEM_SELECTOR = '[role="menuitem"]';
2031
2155
  var MenuContext = react.createContext(null);
2032
2156
  function Menu({ label, trigger, placement = "bottom-end", className, children }) {
2157
+ const id = react.useId();
2033
2158
  const [open, setOpen] = react.useState(false);
2159
+ const enterAt = react.useRef("first");
2034
2160
  const { mounted, visible } = usePresence(open, MENU_EXIT_MS);
2035
2161
  const triggerRef = react.useRef(null);
2036
2162
  const panelRef = react.useRef(null);
@@ -2047,14 +2173,16 @@ function Menu({ label, trigger, placement = "bottom-end", className, children })
2047
2173
  });
2048
2174
  react.useEffect(() => {
2049
2175
  if (!open) return;
2050
- const first = panelRef.current?.querySelector(
2176
+ const items = panelRef.current?.querySelectorAll(
2051
2177
  `${ITEM_SELECTOR}:not([disabled])`
2052
2178
  );
2053
- first?.focus();
2179
+ if (items === void 0 || items.length === 0) return;
2180
+ items[enterAt.current === "last" ? items.length - 1 : 0]?.focus();
2054
2181
  }, [open]);
2055
2182
  const openWithKeyboard = (event) => {
2056
2183
  if (event.key !== "ArrowDown" && event.key !== "ArrowUp") return;
2057
2184
  event.preventDefault();
2185
+ enterAt.current = event.key === "ArrowUp" ? "last" : "first";
2058
2186
  setOpen(true);
2059
2187
  };
2060
2188
  const { type: Trigger, props: triggerProps } = trigger;
@@ -2066,8 +2194,10 @@ function Menu({ label, trigger, placement = "bottom-end", className, children })
2066
2194
  ref: forkRef(triggerRef, triggerProps.ref),
2067
2195
  "aria-haspopup": "menu",
2068
2196
  "aria-expanded": open,
2197
+ "aria-controls": open ? id : void 0,
2069
2198
  onClick: (event) => {
2070
2199
  triggerProps.onClick?.(event);
2200
+ enterAt.current = "first";
2071
2201
  setOpen((v) => !v);
2072
2202
  },
2073
2203
  onKeyDown: (event) => {
@@ -2081,6 +2211,7 @@ function Menu({ label, trigger, placement = "bottom-end", className, children })
2081
2211
  "div",
2082
2212
  {
2083
2213
  ref: panelRef,
2214
+ id,
2084
2215
  role: "menu",
2085
2216
  "aria-label": label,
2086
2217
  className: cx(Menu_module_default.panel, className),
@@ -2092,10 +2223,22 @@ function Menu({ label, trigger, placement = "bottom-end", className, children })
2092
2223
  return;
2093
2224
  }
2094
2225
  if (event.key === "Tab") {
2095
- event.preventDefault();
2096
2226
  close();
2097
2227
  return;
2098
2228
  }
2229
+ if (event.key.length === 1 && !event.ctrlKey && !event.metaKey && !event.altKey) {
2230
+ const items = [
2231
+ ...panelRef.current?.querySelectorAll(
2232
+ `${ITEM_SELECTOR}:not([disabled])`
2233
+ ) ?? []
2234
+ ];
2235
+ const current = items.indexOf(event.target);
2236
+ const fromNext = [...items.slice(current + 1), ...items.slice(0, current + 1)];
2237
+ fromNext.find(
2238
+ (item) => item.textContent?.trim().toLowerCase().startsWith(event.key.toLowerCase())
2239
+ )?.focus();
2240
+ return;
2241
+ }
2099
2242
  onArrowKeys(event);
2100
2243
  },
2101
2244
  children
@@ -2128,6 +2271,7 @@ function MenuItem({ onSelect, disabled = false, tone = "default", children }) {
2128
2271
  var Tooltip_module_default = { "surface": "mds-Tooltip__surface" };
2129
2272
  var GAP3 = 8;
2130
2273
  var DEFAULT_DELAY_MS = 400;
2274
+ var HOVER_GRACE_MS = 150;
2131
2275
  function Tooltip({
2132
2276
  content,
2133
2277
  placement = "top",
@@ -2147,6 +2291,10 @@ function Tooltip({
2147
2291
  clearTimeout(timer.current);
2148
2292
  setOpen(false);
2149
2293
  };
2294
+ const hideAfterGrace = () => {
2295
+ clearTimeout(timer.current);
2296
+ timer.current = setTimeout(hide, HOVER_GRACE_MS);
2297
+ };
2150
2298
  react.useEffect(() => clearTimeout(timer.current), []);
2151
2299
  useAnchoredPosition({ open, anchorRef, floatingRef: surfaceRef, placement, gap: GAP3 });
2152
2300
  react.useEffect(() => {
@@ -2173,7 +2321,7 @@ function Tooltip({
2173
2321
  },
2174
2322
  onPointerLeave: (event) => {
2175
2323
  triggerProps.onPointerLeave?.(event);
2176
- hide();
2324
+ hideAfterGrace();
2177
2325
  },
2178
2326
  onFocus: (event) => {
2179
2327
  triggerProps.onFocus?.(event);
@@ -2186,7 +2334,18 @@ function Tooltip({
2186
2334
  }
2187
2335
  ),
2188
2336
  open && reactDom.createPortal(
2189
- /* @__PURE__ */ jsxRuntime.jsx("div", { ref: surfaceRef, id, role: "tooltip", className: Tooltip_module_default.surface, children: content }),
2337
+ /* @__PURE__ */ jsxRuntime.jsx(
2338
+ "div",
2339
+ {
2340
+ ref: surfaceRef,
2341
+ id,
2342
+ role: "tooltip",
2343
+ className: Tooltip_module_default.surface,
2344
+ onPointerEnter: show,
2345
+ onPointerLeave: hideAfterGrace,
2346
+ children: content
2347
+ }
2348
+ ),
2190
2349
  document.body
2191
2350
  )
2192
2351
  ] });
@@ -2211,6 +2370,7 @@ function ConfirmDialog({
2211
2370
  tone = "default",
2212
2371
  errorMessage
2213
2372
  }) {
2373
+ const descriptionId = react.useId();
2214
2374
  const [busy, setBusy] = react.useState(false);
2215
2375
  const [error, setError] = react.useState(null);
2216
2376
  const [prevOpen, setPrevOpen] = react.useState(open);
@@ -2249,11 +2409,12 @@ function ConfirmDialog({
2249
2409
  onClose: close,
2250
2410
  label: view.title,
2251
2411
  role: "alertdialog",
2412
+ describedBy: view.description != null ? descriptionId : void 0,
2252
2413
  closeOnScrimClick: false,
2253
2414
  children: [
2254
2415
  /* @__PURE__ */ jsxRuntime.jsx(ModalHeader, { children: view.title }),
2255
2416
  /* @__PURE__ */ jsxRuntime.jsxs(ModalBody, { children: [
2256
- view.description != null ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: ConfirmDialog_module_default.description, children: view.description }) : null,
2417
+ view.description != null ? /* @__PURE__ */ jsxRuntime.jsx("p", { id: descriptionId, className: ConfirmDialog_module_default.description, children: view.description }) : null,
2257
2418
  error != null ? /* @__PURE__ */ jsxRuntime.jsx("p", { role: "alert", className: ConfirmDialog_module_default.error, children: error }) : null
2258
2419
  ] }),
2259
2420
  /* @__PURE__ */ jsxRuntime.jsxs(ModalFooter, { children: [
@@ -2300,6 +2461,7 @@ function TabBarItem({
2300
2461
  onClick,
2301
2462
  active = false,
2302
2463
  badge = false,
2464
+ badgeLabel,
2303
2465
  as,
2304
2466
  hideLabel = false
2305
2467
  }) {
@@ -2309,7 +2471,8 @@ function TabBarItem({
2309
2471
  icon,
2310
2472
  badge ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: TabBar_module_default.badge, "data-testid": "mds-tabbar-badge" }) : null
2311
2473
  ] }),
2312
- hideLabel ? /* @__PURE__ */ jsxRuntime.jsx(VisuallyHidden, { children: label }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: TabBar_module_default.label, children: label })
2474
+ hideLabel ? /* @__PURE__ */ jsxRuntime.jsx(VisuallyHidden, { children: label }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: TabBar_module_default.label, children: label }),
2475
+ badge && badgeLabel !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(VisuallyHidden, { children: badgeLabel })
2313
2476
  ] });
2314
2477
  if (href !== void 0) {
2315
2478
  const Element = as ?? "a";
@@ -2408,8 +2571,8 @@ function MediaPlaceholder({
2408
2571
  decoding: "async",
2409
2572
  onError: () => setFailed(src)
2410
2573
  }
2411
- ) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: MediaPlaceholder_module_default.fill, "aria-hidden": "true", children: [
2412
- glyph,
2574
+ ) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: MediaPlaceholder_module_default.fill, children: [
2575
+ glyph != null && /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", children: glyph }),
2413
2576
  caption !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(Text, { variant: "meta", children: caption })
2414
2577
  ] }),
2415
2578
  picture && caption !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(Text, { variant: "meta", tone: "on-media", className: MediaPlaceholder_module_default.caption, children: caption }),
@@ -2626,7 +2789,7 @@ function UploadProgress({
2626
2789
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cx(UploadProgress_module_default.upload, UploadProgress_module_default[`status-${status}`], className), ...rest, children: [
2627
2790
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: UploadProgress_module_default.head, children: [
2628
2791
  preview !== void 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: UploadProgress_module_default.preview, children: preview }),
2629
- mark !== void 0 && /* @__PURE__ */ jsxRuntime.jsx("span", { className: UploadProgress_module_default.mark, children: mark }),
2792
+ mark !== void 0 && /* @__PURE__ */ jsxRuntime.jsx("span", { className: UploadProgress_module_default.mark, "aria-hidden": "true", children: mark }),
2630
2793
  /* @__PURE__ */ jsxRuntime.jsx(Text, { variant: "label", truncate: true, className: UploadProgress_module_default.name, children: name }),
2631
2794
  running && onCancel !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(Button, { iconOnly: true, size: "sm", variant: "ghost", "aria-label": labels.cancel, onClick: onCancel, children: /* @__PURE__ */ jsxRuntime.jsx(CloseGlyph, {}) }),
2632
2795
  !running && onRemove !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(Button, { iconOnly: true, size: "sm", variant: "ghost", "aria-label": labels.remove, onClick: onRemove, children: /* @__PURE__ */ jsxRuntime.jsx(CloseGlyph, {}) }),
@@ -2642,7 +2805,7 @@ function UploadProgress({
2642
2805
  }
2643
2806
  ),
2644
2807
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: UploadProgress_module_default.foot, children: status === "error" ? /* @__PURE__ */ jsxRuntime.jsx(Text, { variant: "meta", tone: "danger", role: "alert", children: error ?? labels.error }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2645
- status !== "uploading" && /* @__PURE__ */ jsxRuntime.jsx(Text, { variant: "meta", children: labels[status] }),
2808
+ /* @__PURE__ */ jsxRuntime.jsx(Text, { variant: "meta", role: "status", children: status === "uploading" ? null : labels[status] }),
2646
2809
  detail !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(Text, { variant: "meta", children: detail })
2647
2810
  ] }) })
2648
2811
  ] });
@@ -2695,7 +2858,7 @@ function FileDrop({
2695
2858
  disabled,
2696
2859
  onClick: () => picker.current?.click(),
2697
2860
  children: [
2698
- icon !== void 0 && /* @__PURE__ */ jsxRuntime.jsx("span", { className: FileDrop_module_default.icon, children: icon }),
2861
+ icon !== void 0 && /* @__PURE__ */ jsxRuntime.jsx("span", { className: FileDrop_module_default.icon, "aria-hidden": "true", children: icon }),
2699
2862
  /* @__PURE__ */ jsxRuntime.jsx(Text, { variant: "label", children: label }),
2700
2863
  hint !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(Text, { variant: "meta", children: hint })
2701
2864
  ]
@@ -2729,6 +2892,13 @@ var MAX_SCALE = 4;
2729
2892
  var STEP2 = 1;
2730
2893
  var DOUBLE = 2.5;
2731
2894
  var WHEEL = 0.01;
2895
+ var KEY_PAN_PX = 48;
2896
+ var ARROWS = {
2897
+ ArrowLeft: { x: 1, y: 0 },
2898
+ ArrowRight: { x: -1, y: 0 },
2899
+ ArrowUp: { x: 0, y: 1 },
2900
+ ArrowDown: { x: 0, y: -1 }
2901
+ };
2732
2902
  var ORIGIN = { x: 0, y: 0 };
2733
2903
  var clamp = (value, low, high) => Math.min(Math.max(value, low), high);
2734
2904
  var round = (value) => Math.round(value * 1e3) / 1e3;
@@ -2779,6 +2949,19 @@ function Lightbox({ open, onClose, src, alt = "", caption, labels }) {
2779
2949
  surface.addEventListener("wheel", onWheel, { passive: false });
2780
2950
  return () => surface.removeEventListener("wheel", onWheel);
2781
2951
  }, [surface, zoom]);
2952
+ react.useEffect(() => {
2953
+ if (!open) return;
2954
+ const onKeyDown = (event) => {
2955
+ const step = ARROWS[event.key];
2956
+ if (step === void 0 || at.current <= MIN_SCALE) return;
2957
+ event.preventDefault();
2958
+ setOffset(
2959
+ (current) => hold({ x: current.x + step.x * KEY_PAN_PX, y: current.y + step.y * KEY_PAN_PX }, at.current)
2960
+ );
2961
+ };
2962
+ document.addEventListener("keydown", onKeyDown);
2963
+ return () => document.removeEventListener("keydown", onKeyDown);
2964
+ }, [open, hold]);
2782
2965
  function onPointerDown(event) {
2783
2966
  points.current.set(event.pointerId, { x: event.clientX, y: event.clientY });
2784
2967
  const [first, second] = [...points.current.values()];
@@ -2913,6 +3096,7 @@ function VideoPlayer({
2913
3096
  const [at, setAt] = react.useState(0);
2914
3097
  const [length, setLength] = react.useState(0);
2915
3098
  const [isFullscreen, setFullscreen] = react.useState(false);
3099
+ const [captionsOn, setCaptionsOn] = react.useState(captions?.defaultOn ?? false);
2916
3100
  function toggle() {
2917
3101
  const el = media.current;
2918
3102
  if (!el) return;
@@ -2938,7 +3122,10 @@ function VideoPlayer({
2938
3122
  }
2939
3123
  function toggleCaptions() {
2940
3124
  const track = media.current?.textTracks?.[0];
2941
- if (track) track.mode = track.mode === "showing" ? "disabled" : "showing";
3125
+ if (!track) return;
3126
+ const next = track.mode !== "showing";
3127
+ track.mode = next ? "showing" : "disabled";
3128
+ setCaptionsOn(next);
2942
3129
  }
2943
3130
  function selectChapter(chapter) {
2944
3131
  goTo(chapter.at);
@@ -3019,6 +3206,7 @@ function VideoPlayer({
3019
3206
  type: "range",
3020
3207
  className: VideoPlayer_module_default.scrub,
3021
3208
  "aria-label": labels.seek,
3209
+ "aria-valuetext": `${clock(Math.min(at, length || 0))} / ${clock(length)}`,
3022
3210
  min: 0,
3023
3211
  max: length || 0,
3024
3212
  step: 1,
@@ -3045,7 +3233,7 @@ function VideoPlayer({
3045
3233
  "aria-label": captionsLabel,
3046
3234
  variant: "ghost",
3047
3235
  size: "sm",
3048
- "aria-pressed": captions.defaultOn ?? false,
3236
+ "aria-pressed": captionsOn,
3049
3237
  onClick: toggleCaptions,
3050
3238
  children: /* @__PURE__ */ jsxRuntime.jsx(CaptionsGlyph, {})
3051
3239
  }
@@ -3085,7 +3273,12 @@ function Breadcrumb({
3085
3273
  const last = index === items.length - 1;
3086
3274
  return /* @__PURE__ */ jsxRuntime.jsxs("li", { className: Breadcrumb_module_default.item, children: [
3087
3275
  index > 0 && /* @__PURE__ */ jsxRuntime.jsx(ChevronRightGlyph, { className: Breadcrumb_module_default.separator }),
3088
- last || item.href === void 0 ? /* @__PURE__ */ jsxRuntime.jsx(Text, { variant: "meta", tone: last ? "primary" : "muted", "aria-current": last ? "page" : void 0, children: item.label }) : /* @__PURE__ */ jsxRuntime.jsx(Link, { variant: "plain", size: "xs", as: linkAs, href: item.href, className: Breadcrumb_module_default.link, children: item.label })
3276
+ last || item.href === void 0 ? /* @__PURE__ */ jsxRuntime.jsx(Text, { variant: "meta", tone: last ? "primary" : "muted", "aria-current": last ? "page" : void 0, children: item.label }) : (
3277
+ /* inline, not plain: the underline is what separates a step
3278
+ you can go back to from the one you are standing on, for a
3279
+ reader who cannot tell the two text colors apart. */
3280
+ /* @__PURE__ */ jsxRuntime.jsx(Link, { variant: "inline", size: "xs", as: linkAs, href: item.href, className: Breadcrumb_module_default.link, children: item.label })
3281
+ )
3089
3282
  ] }, `${item.label}-${index}`);
3090
3283
  }) }) });
3091
3284
  }