@tensor_1/react 2.41.0 → 2.43.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +24 -0
  2. package/dist/components/Button.d.ts +19 -1
  3. package/dist/components/Button.js +13 -8
  4. package/dist/components/DescriptionList.d.ts +11 -0
  5. package/dist/components/DiscardPrompt.d.ts +54 -0
  6. package/dist/components/DiscardPrompt.js +245 -0
  7. package/dist/components/Drawer.d.ts +19 -3
  8. package/dist/components/Drawer.js +60 -21
  9. package/dist/components/EditorialHome.d.ts +1 -1
  10. package/dist/components/Field.d.ts +7 -1
  11. package/dist/components/Field.js +5 -4
  12. package/dist/components/Input.d.ts +5 -0
  13. package/dist/components/Input.js +2 -1
  14. package/dist/components/Kbd.d.ts +20 -1
  15. package/dist/components/Kbd.js +30 -7
  16. package/dist/components/Menu.d.ts +11 -4
  17. package/dist/components/Menu.js +17 -3
  18. package/dist/components/MetricInput.d.ts +8 -1
  19. package/dist/components/MetricInput.js +11 -3
  20. package/dist/components/Modal.d.ts +15 -1
  21. package/dist/components/Modal.js +58 -13
  22. package/dist/components/OrgChart.d.ts +121 -0
  23. package/dist/components/OrgChart.js +683 -0
  24. package/dist/components/SearchBar.d.ts +67 -0
  25. package/dist/components/SearchBar.js +117 -0
  26. package/dist/components/Select.js +5 -4
  27. package/dist/components/ShortcutHint.d.ts +29 -0
  28. package/dist/components/ShortcutHint.js +149 -0
  29. package/dist/components/Tooltip.d.ts +10 -1
  30. package/dist/components/Tooltip.js +15 -4
  31. package/dist/emoji.cjs +108 -31
  32. package/dist/hooks/useAnchoredPosition.d.ts +13 -1
  33. package/dist/hooks/useAnchoredPosition.js +19 -11
  34. package/dist/hooks/useDialog.d.ts +6 -1
  35. package/dist/hooks/useDialog.js +10 -1
  36. package/dist/index.cjs +3911 -2567
  37. package/dist/index.d.cts +340 -39
  38. package/dist/index.d.ts +4 -1
  39. package/dist/index.js +11 -1
  40. package/dist/labels-context.d.ts +11 -0
  41. package/dist/rich-text.cjs +17 -10
  42. package/package.json +1 -1
@@ -5,6 +5,11 @@ interface InputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'size'>
5
5
  size?: 'sm' | 'md';
6
6
  invalid?: boolean;
7
7
  valid?: boolean;
8
+ /**
9
+ * "Check this": a value the reader should confirm, not an error, e.g. one an assistant filled
10
+ * in (HAR-1814). Draws the warning border; `invalid` wins when both are set.
11
+ */
12
+ warning?: boolean;
8
13
  }
9
14
  declare const Input: react.ForwardRefExoticComponent<InputProps & react.RefAttributes<HTMLInputElement>>;
10
15
  interface InputGroupProps {
@@ -2,13 +2,14 @@ import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import { cloneElement, forwardRef, isValidElement } from "react";
3
3
  import { cx } from "../cx.js";
4
4
  const Input = forwardRef(
5
- ({ size = "md", invalid, valid, className, ...props }, ref) => /* @__PURE__ */ jsx(
5
+ ({ size = "md", invalid, valid, warning, className, ...props }, ref) => /* @__PURE__ */ jsx(
6
6
  "input",
7
7
  {
8
8
  ref,
9
9
  className: cx("uix-input", size === "sm" && "uix-input--sm", className),
10
10
  "aria-invalid": invalid || void 0,
11
11
  "data-valid": valid || void 0,
12
+ "data-warning": warning || void 0,
12
13
  ...props
13
14
  }
14
15
  )
@@ -7,6 +7,25 @@ interface KbdProps extends HTMLAttributes<HTMLElement> {
7
7
  /** One keyboard key over `.uix-kbd` (HAR-1361; TENSOR C14, MOTUS B-A16). */
8
8
  declare function Kbd({ className, children, ...props }: KbdProps): react.JSX.Element;
9
9
  type KbdPlatform = 'mac' | 'other';
10
+ /**
11
+ * The platform for glyphs and `aria-keyshortcuts`: `platform` when given, else the browser's. The
12
+ * server and the hydrating render use `other`; a component mounted later (a menu row on open)
13
+ * reads the platform at once, so it never flashes Ctrl before ⌘.
14
+ */
15
+ declare function useKbdPlatform(platform?: KbdPlatform): KbdPlatform;
16
+ /**
17
+ * A keyboard shortcut (HAR-2031): the keys pressed together, in `KbdCombo`'s names, e.g.
18
+ * `['Mod', 'K']`, `['Shift', '?']` or `['A']`. `Mod` is ⌘ on Apple and Ctrl elsewhere.
19
+ */
20
+ type KeyShortcut = readonly string[];
21
+ /**
22
+ * The `aria-keyshortcuts` value of a shortcut on a platform (HAR-2031): `['Mod', 'K']` is
23
+ * `Meta+K` on Apple and `Control+K` elsewhere; a single letter is upper-cased (`['a']` is `A`).
24
+ * For a control the kit does not render.
25
+ */
26
+ declare function ariaKeyShortcuts(keys: KeyShortcut, platform: KbdPlatform): string;
27
+ /** A `shortcut` prop that is key names rather than a node (HAR-2031). */
28
+ declare const isKeyShortcut: (value: unknown) => value is KeyShortcut;
10
29
  interface KbdComboProps extends Omit<HTMLAttributes<HTMLSpanElement>, 'children'> {
11
30
  /**
12
31
  * Keys in order, e.g. `['Mod', 'K']`. `Mod` is ⌘ on Apple and Ctrl elsewhere; `Shift`,
@@ -24,4 +43,4 @@ interface KbdComboProps extends Omit<HTMLAttributes<HTMLSpanElement>, 'children'
24
43
  */
25
44
  declare function KbdCombo({ keys, platform, keyLabels, className, ...props }: KbdComboProps): react.JSX.Element;
26
45
 
27
- export { Kbd, KbdCombo, type KbdComboProps, type KbdPlatform, type KbdProps };
46
+ export { Kbd, KbdCombo, type KbdComboProps, type KbdPlatform, type KbdProps, type KeyShortcut, ariaKeyShortcuts, isKeyShortcut, useKbdPlatform };
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
- import { useEffect, useState } from "react";
3
+ import { useSyncExternalStore } from "react";
4
4
  import { cx } from "../cx.js";
5
5
  function Kbd({ className, children, ...props }) {
6
6
  return /* @__PURE__ */ jsx("kbd", { className: cx("uix-kbd", className), ...props, children });
@@ -12,6 +12,11 @@ const KEYS = {
12
12
  Alt: { mac: ["\u2325", "Option"], other: ["Alt", "Alt"] },
13
13
  Enter: { mac: ["\u21B5", "Enter"], other: ["Enter", "Enter"] },
14
14
  Escape: { mac: ["Esc", "Escape"], other: ["Esc", "Escape"] },
15
+ Esc: { mac: ["Esc", "Escape"], other: ["Esc", "Escape"] },
16
+ Delete: { mac: ["Del", "Delete"], other: ["Del", "Delete"] },
17
+ Del: { mac: ["Del", "Delete"], other: ["Del", "Delete"] },
18
+ Space: { mac: ["Space", "Space"], other: ["Space", "Space"] },
19
+ " ": { mac: ["Space", "Space"], other: ["Space", "Space"] },
15
20
  Backspace: { mac: ["\u232B", "Backspace"], other: ["Backspace", "Backspace"] },
16
21
  Tab: { mac: ["\u21E5", "Tab"], other: ["Tab", "Tab"] },
17
22
  ArrowUp: { mac: ["\u2191", "Up arrow"], other: ["\u2191", "Up arrow"] },
@@ -24,12 +29,27 @@ const detectPlatform = () => {
24
29
  const nav = navigator;
25
30
  return /mac|iphone|ipad|ipod/i.test(nav.userAgentData?.platform ?? navigator.platform ?? "") ? "mac" : "other";
26
31
  };
32
+ const noSubscription = () => () => {
33
+ };
34
+ const serverPlatform = () => "other";
35
+ function useKbdPlatform(platform) {
36
+ const detected = useSyncExternalStore(noSubscription, detectPlatform, serverPlatform);
37
+ return platform ?? detected;
38
+ }
39
+ const ARIA_KEYS = {
40
+ Mod: { mac: "Meta", other: "Control" },
41
+ Ctrl: { mac: "Control", other: "Control" },
42
+ Esc: { mac: "Escape", other: "Escape" },
43
+ Del: { mac: "Delete", other: "Delete" },
44
+ " ": { mac: "Space", other: "Space" },
45
+ "+": { mac: "Plus", other: "Plus" }
46
+ };
47
+ function ariaKeyShortcuts(keys, platform) {
48
+ return keys.map((key) => ARIA_KEYS[key]?.[platform] ?? (key.length === 1 ? key.toUpperCase() : key)).join("+");
49
+ }
50
+ const isKeyShortcut = (value) => Array.isArray(value) && value.length > 0 && value.every((key) => typeof key === "string");
27
51
  function KbdCombo({ keys, platform, keyLabels, className, ...props }) {
28
- const [detected, setDetected] = useState("other");
29
- useEffect(() => {
30
- if (!platform) setDetected(detectPlatform());
31
- }, [platform]);
32
- const active = platform ?? detected;
52
+ const active = useKbdPlatform(platform);
33
53
  const parts = keys.map((key) => {
34
54
  const known = KEYS[key]?.[active];
35
55
  return { glyph: known?.[0] ?? key, spoken: keyLabels?.[key] ?? known?.[1] ?? key };
@@ -41,5 +61,8 @@ function KbdCombo({ keys, platform, keyLabels, className, ...props }) {
41
61
  }
42
62
  export {
43
63
  Kbd,
44
- KbdCombo
64
+ KbdCombo,
65
+ ariaKeyShortcuts,
66
+ isKeyShortcut,
67
+ useKbdPlatform
45
68
  };
@@ -2,6 +2,7 @@ import * as react from 'react';
2
2
  import { ReactElement, ReactNode } from 'react';
3
3
  import { UixRenderLink } from '../link.js';
4
4
  import { Placement } from '../overlay-position.js';
5
+ import { KeyShortcut } from './Kbd.js';
5
6
 
6
7
  /** `id` and `data-*` for the item's own element (HAR-1629): a stable hook for tests, journeys and analytics. */
7
8
  interface MenuItemAttributes {
@@ -40,8 +41,12 @@ interface MenuItemProps extends MenuItemAttributes {
40
41
  href?: string;
41
42
  renderLink?: UixRenderLink;
42
43
  icon?: ReactNode;
43
- /** Shortcut hint at the end of the row, e.g. `<KbdCombo keys={['Mod', 'D']} />`. */
44
- shortcut?: ReactNode;
44
+ /**
45
+ * The row's keyboard shortcut, at its end. Key names (`['Mod', 'D']`, `['A']`) render with
46
+ * `KbdCombo` and set the row's `aria-keyshortcuts` for the viewer's platform (HAR-2031); any
47
+ * other node renders as given.
48
+ */
49
+ shortcut?: KeyShortcut | ReactNode;
45
50
  tone?: 'default' | 'danger';
46
51
  disabled?: boolean;
47
52
  className?: string;
@@ -70,7 +75,8 @@ interface MenuItemRadioProps extends MenuItemAttributes {
70
75
  closeOnSelect?: boolean;
71
76
  children?: ReactNode;
72
77
  icon?: ReactNode;
73
- shortcut?: ReactNode;
78
+ /** Key names (`['Shift', 'W']`) or a node; see `MenuItemProps.shortcut`. */
79
+ shortcut?: KeyShortcut | ReactNode;
74
80
  disabled?: boolean;
75
81
  className?: string;
76
82
  }
@@ -83,7 +89,8 @@ interface MenuItemCheckboxProps extends MenuItemAttributes {
83
89
  closeOnSelect?: boolean;
84
90
  children?: ReactNode;
85
91
  icon?: ReactNode;
86
- shortcut?: ReactNode;
92
+ /** Key names (`['Shift', 'W']`) or a node; see `MenuItemProps.shortcut`. */
93
+ shortcut?: KeyShortcut | ReactNode;
87
94
  disabled?: boolean;
88
95
  className?: string;
89
96
  }
@@ -5,6 +5,16 @@ import { cx } from "../cx.js";
5
5
  import { Popover } from "./Popover.js";
6
6
  import { CheckIcon } from "../icons/components.js";
7
7
  import { renderUixLink } from "../link.js";
8
+ import { KbdCombo, ariaKeyShortcuts, isKeyShortcut, useKbdPlatform } from "./Kbd.js";
9
+ function useRowShortcut(shortcut) {
10
+ const platform = useKbdPlatform();
11
+ if (shortcut == null) return { node: null, aria: {} };
12
+ if (!isKeyShortcut(shortcut)) return { node: /* @__PURE__ */ jsx("span", { className: "uix-menu__shortcut", children: shortcut }), aria: {} };
13
+ return {
14
+ node: /* @__PURE__ */ jsx("span", { className: "uix-menu__shortcut", children: /* @__PURE__ */ jsx(KbdCombo, { keys: [...shortcut], platform }) }),
15
+ aria: { "aria-keyshortcuts": ariaKeyShortcuts(shortcut, platform) }
16
+ };
17
+ }
8
18
  const MenuContext = createContext(null);
9
19
  const MenuRadioContext = createContext(null);
10
20
  const ITEM = '[role^="menuitem"]:not([aria-disabled="true"])';
@@ -150,18 +160,20 @@ function MenuItem({ children, onSelect, href, renderLink, icon, shortcut, tone =
150
160
  const menu = useContext(MenuContext);
151
161
  const classes = cx("uix-menu__item", tone === "danger" && "uix-menu__item--danger", className);
152
162
  const attributes = itemAttributes(rest);
163
+ const keys = useRowShortcut(shortcut);
153
164
  const body = /* @__PURE__ */ jsxs(Fragment, { children: [
154
165
  icon != null && /* @__PURE__ */ jsx("span", { className: "uix-menu__icon", "aria-hidden": "true", children: icon }),
155
166
  /* @__PURE__ */ jsx("span", { className: "uix-menu__text", children }),
156
- shortcut != null && /* @__PURE__ */ jsx("span", { className: "uix-menu__shortcut", children: shortcut })
167
+ keys.node
157
168
  ] });
158
169
  if (href != null && !disabled) {
159
- return /* @__PURE__ */ jsx(Fragment, { children: renderUixLink(renderLink, { ...attributes, href, role: "menuitem", tabIndex: -1, className: classes, onClick: () => menu?.close(false), children: body }) });
170
+ return /* @__PURE__ */ jsx(Fragment, { children: renderUixLink(renderLink, { ...attributes, ...keys.aria, href, role: "menuitem", tabIndex: -1, className: classes, onClick: () => menu?.close(false), children: body }) });
160
171
  }
161
172
  return /* @__PURE__ */ jsx(
162
173
  "button",
163
174
  {
164
175
  ...attributes,
176
+ ...keys.aria,
165
177
  type: "button",
166
178
  role: "menuitem",
167
179
  tabIndex: -1,
@@ -189,10 +201,12 @@ function CheckableItem({
189
201
  ...rest
190
202
  }) {
191
203
  const menu = useContext(MenuContext);
204
+ const keys = useRowShortcut(shortcut);
192
205
  return /* @__PURE__ */ jsxs(
193
206
  "button",
194
207
  {
195
208
  ...itemAttributes(rest),
209
+ ...keys.aria,
196
210
  type: "button",
197
211
  role,
198
212
  "aria-checked": checked,
@@ -216,7 +230,7 @@ function CheckableItem({
216
230
  /* @__PURE__ */ jsx("span", { className: "uix-menu__check", "aria-hidden": "true", children: checked && /* @__PURE__ */ jsx(CheckIcon, {}) }),
217
231
  icon != null && /* @__PURE__ */ jsx("span", { className: "uix-menu__icon", "aria-hidden": "true", children: icon }),
218
232
  /* @__PURE__ */ jsx("span", { className: "uix-menu__text", children }),
219
- shortcut != null && /* @__PURE__ */ jsx("span", { className: "uix-menu__shortcut", children: shortcut })
233
+ keys.node
220
234
  ]
221
235
  }
222
236
  );
@@ -10,6 +10,13 @@ interface MetricInputProps {
10
10
  label: string;
11
11
  invalid?: boolean;
12
12
  errorMessage?: string;
13
+ /**
14
+ * A required value (HAR-1819): the label shows the same marker as `Field`, and the input
15
+ * gets native `required` and `aria-required="true"`.
16
+ */
17
+ required?: boolean;
18
+ /** Guidance under the control (a range, a unit rule), tied to the input by `aria-describedby`. */
19
+ hint?: string;
13
20
  disabled?: boolean;
14
21
  readOnly?: boolean;
15
22
  parse?: (input: string) => number | null;
@@ -18,6 +25,6 @@ interface MetricInputProps {
18
25
  id?: string;
19
26
  }
20
27
  /** Accessible numeric recipe composed from Input and the UIx stepper controls. */
21
- declare function MetricInput({ value, onValueChange, min, max, step, unit, label, invalid, errorMessage, disabled, readOnly, parse, format, className, id: providedId, }: MetricInputProps): react.JSX.Element;
28
+ declare function MetricInput({ value, onValueChange, min, max, step, unit, label, invalid, errorMessage, required, hint, disabled, readOnly, parse, format, className, id: providedId, }: MetricInputProps): react.JSX.Element;
22
29
 
23
30
  export { MetricInput, type MetricInputProps };
@@ -15,6 +15,8 @@ function MetricInput({
15
15
  label,
16
16
  invalid,
17
17
  errorMessage,
18
+ required,
19
+ hint,
18
20
  disabled,
19
21
  readOnly,
20
22
  parse = parseMetricValue,
@@ -25,6 +27,9 @@ function MetricInput({
25
27
  const generatedId = useId();
26
28
  const id = providedId ?? generatedId;
27
29
  const errorId = `${id}-error`;
30
+ const hintId = `${id}-hint`;
31
+ const showError = Boolean(invalid && errorMessage);
32
+ const describedBy = [hint ? hintId : null, showError ? errorId : null].filter(Boolean).join(" ") || void 0;
28
33
  const [text, setText] = useState(() => format(value));
29
34
  useEffect(() => setText(format(value)), [value, format]);
30
35
  const commit = (input) => {
@@ -53,7 +58,7 @@ function MetricInput({
53
58
  setText(parsed === null && event.currentTarget.value.trim() !== "" ? format(value) : format(parsed));
54
59
  };
55
60
  return /* @__PURE__ */ jsxs("div", { className: cx("uix-metric-input", invalid && "uix-metric-input--invalid", className), children: [
56
- /* @__PURE__ */ jsx("label", { className: "uix-field__label", htmlFor: id, children: label }),
61
+ /* @__PURE__ */ jsx("label", { className: "uix-field__label", htmlFor: id, "data-required": required || void 0, children: label }),
57
62
  /* @__PURE__ */ jsxs("div", { className: "uix-metric-input__control", children: [
58
63
  /* @__PURE__ */ jsx(
59
64
  Input,
@@ -68,7 +73,9 @@ function MetricInput({
68
73
  "aria-valuenow": value ?? void 0,
69
74
  "aria-valuemin": min,
70
75
  "aria-valuemax": max,
71
- "aria-describedby": invalid && errorMessage ? errorId : void 0,
76
+ "aria-describedby": describedBy,
77
+ required,
78
+ "aria-required": required || void 0,
72
79
  invalid,
73
80
  disabled,
74
81
  readOnly
@@ -84,7 +91,8 @@ function MetricInput({
84
91
  "Unit: ",
85
92
  unit
86
93
  ] }),
87
- invalid && errorMessage && /* @__PURE__ */ jsx("span", { id: errorId, className: "uix-field__error", children: errorMessage })
94
+ hint && /* @__PURE__ */ jsx("span", { id: hintId, className: "uix-field__hint", children: hint }),
95
+ showError && /* @__PURE__ */ jsx("span", { id: errorId, className: "uix-field__error", children: errorMessage })
88
96
  ] });
89
97
  }
90
98
  export {
@@ -1,9 +1,23 @@
1
1
  import * as react from 'react';
2
2
  import { HTMLAttributes, ReactNode } from 'react';
3
+ import { DiscardPromptLabels } from './DiscardPrompt.js';
4
+ import './Button.js';
5
+ import '../link.js';
6
+ import './Kbd.js';
3
7
 
4
8
  interface ModalProps extends Omit<HTMLAttributes<HTMLDialogElement>, 'title'> {
5
9
  open: boolean;
6
10
  onClose?: () => void;
11
+ /**
12
+ * The dialog holds input that is not saved (HAR-2029). Escape, the close button and a
13
+ * `DialogClose` then open an anchored "Discard changes?" step with focus on "Keep editing"
14
+ * instead of calling `onClose`; "Discard" calls it. A clean dialog closes at once.
15
+ */
16
+ dirty?: boolean;
17
+ /** A save is in flight: Escape, the close button and a `DialogClose` do nothing. */
18
+ pending?: boolean;
19
+ /** The words of the discard step. Default: `UixLabelsProvider` `discardPrompt`, then English. */
20
+ discardLabels?: Partial<DiscardPromptLabels>;
7
21
  title?: ReactNode;
8
22
  children?: ReactNode;
9
23
  footer?: ReactNode;
@@ -11,6 +25,6 @@ interface ModalProps extends Omit<HTMLAttributes<HTMLDialogElement>, 'title'> {
11
25
  closeLabel?: string;
12
26
  role?: 'dialog' | 'alertdialog';
13
27
  }
14
- declare function Modal({ open, onClose, title, children, footer, className, closeLabel: closeLabelProp, role, ...rest }: ModalProps): react.JSX.Element;
28
+ declare function Modal({ open, onClose, dirty, pending, discardLabels, title, children, footer, className, closeLabel: closeLabelProp, role, onKeyDown, ...rest }: ModalProps): react.JSX.Element;
15
29
 
16
30
  export { Modal, type ModalProps };
@@ -1,23 +1,68 @@
1
1
  "use client";
2
- import { jsx, jsxs } from "react/jsx-runtime";
3
- import { useId } from "react";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
+ import { useId, useRef } from "react";
4
4
  import { cx } from "../cx.js";
5
5
  import { useUixLabels } from "../labels-context.js";
6
- import { useDialog } from "../hooks/useDialog.js";
6
+ import { useGuardedDialog } from "./DiscardPrompt.js";
7
7
  const CloseIcon = () => /* @__PURE__ */ jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { d: "M4 4l8 8M12 4l-8 8" }) });
8
- function Modal({ open, onClose, title, children, footer, className, closeLabel: closeLabelProp, role = "dialog", ...rest }) {
8
+ function Modal({
9
+ open,
10
+ onClose,
11
+ dirty = false,
12
+ pending = false,
13
+ discardLabels,
14
+ title,
15
+ children,
16
+ footer,
17
+ className,
18
+ closeLabel: closeLabelProp,
19
+ role = "dialog",
20
+ onKeyDown,
21
+ ...rest
22
+ }) {
9
23
  const uixLabels = useUixLabels();
10
24
  const closeLabel = closeLabelProp ?? uixLabels.modal?.close ?? "Close dialog";
11
- const ref = useDialog(open, onClose);
25
+ const closeRef = useRef(null);
26
+ const guard = useGuardedDialog({ open, onClose, dirty, pending, labels: discardLabels, closeRef });
12
27
  const titleId = useId();
13
- return /* @__PURE__ */ jsxs("dialog", { ref, className: cx("uix-dialog", className), role, "aria-labelledby": title != null ? titleId : void 0, ...rest, children: [
14
- (title != null || onClose) && /* @__PURE__ */ jsxs("div", { className: "uix-dialog__header", children: [
15
- title && /* @__PURE__ */ jsx("h2", { className: "uix-dialog__title", id: titleId, children: title }),
16
- onClose && /* @__PURE__ */ jsx("button", { type: "button", className: "uix-dialog__close", onClick: onClose, "aria-label": closeLabel, children: /* @__PURE__ */ jsx(CloseIcon, {}) })
17
- ] }),
18
- children != null && /* @__PURE__ */ jsx("div", { className: "uix-dialog__body", children }),
19
- footer != null && /* @__PURE__ */ jsx("div", { className: "uix-dialog__footer", children: footer })
20
- ] });
28
+ return /* @__PURE__ */ jsxs(
29
+ "dialog",
30
+ {
31
+ ref: guard.ref,
32
+ className: cx("uix-dialog", className),
33
+ role,
34
+ "aria-labelledby": title != null ? titleId : void 0,
35
+ ...rest,
36
+ onKeyDown: (event) => {
37
+ onKeyDown?.(event);
38
+ guard.onKeyDown(event);
39
+ },
40
+ children: [
41
+ guard.provide(
42
+ /* @__PURE__ */ jsxs(Fragment, { children: [
43
+ (title != null || onClose) && /* @__PURE__ */ jsxs("div", { className: "uix-dialog__header", children: [
44
+ title && /* @__PURE__ */ jsx("h2", { className: "uix-dialog__title", id: titleId, children: title }),
45
+ onClose && /* @__PURE__ */ jsx(
46
+ "button",
47
+ {
48
+ ref: closeRef,
49
+ type: "button",
50
+ className: "uix-dialog__close",
51
+ onClick: () => guard.requestClose(),
52
+ "aria-label": closeLabel,
53
+ "aria-disabled": pending || void 0,
54
+ children: /* @__PURE__ */ jsx(CloseIcon, {})
55
+ }
56
+ )
57
+ ] }),
58
+ children != null && /* @__PURE__ */ jsx("div", { className: "uix-dialog__body", children }),
59
+ footer != null && /* @__PURE__ */ jsx("div", { className: "uix-dialog__footer", children: footer })
60
+ ] })
61
+ ),
62
+ guard.prompt
63
+ ]
64
+ }
65
+ );
21
66
  }
22
67
  export {
23
68
  Modal
@@ -0,0 +1,121 @@
1
+ import * as react from 'react';
2
+ import { HTMLAttributes } from 'react';
3
+
4
+ /** One person (or position) in an {@link OrgChart}. */
5
+ interface OrgChartNode {
6
+ id: string;
7
+ /** The person's name; also the card's accessible name. */
8
+ name: string;
9
+ /** Job title, under the name. */
10
+ title?: string;
11
+ team?: string;
12
+ location?: string;
13
+ /** Photo URL. Without one the card shows {@link OrgChartNode.initials}, else the name's initials. */
14
+ photo?: string;
15
+ initials?: string;
16
+ /** A short marker on the card, such as "You". It is read with the person's description. */
17
+ badge?: string;
18
+ /**
19
+ * How many people report directly to this one. With `loadChildren`, a count above 0 shows the
20
+ * count on the card and makes the person expandable before {@link OrgChartNode.children} is
21
+ * known. Once the reports are known, their number wins.
22
+ */
23
+ reportCount?: number;
24
+ /** The direct reports, when known. Otherwise `loadChildren` loads them on the first expand. */
25
+ children?: OrgChartNode[];
26
+ }
27
+ /** The words of an {@link OrgChart}. `{count}` and `{percent}` are filled in. */
28
+ interface OrgChartLabels {
29
+ /** The tree's name when the chart gets no `aria-label` / `aria-labelledby`. */
30
+ chart: string;
31
+ /** The row in place of reports that are loading. */
32
+ loading: string;
33
+ /** The row in place of reports that could not be loaded; also announced. */
34
+ error: string;
35
+ /** The action in that row. Enter, Space or a click on the row runs it. */
36
+ retry: string;
37
+ /** Read after the person: one direct report. */
38
+ reportsOne: string;
39
+ /** Read after the person: `{count}` direct reports. */
40
+ reportsMany: string;
41
+ /** The report count's tooltip while the reports are hidden. */
42
+ expand: string;
43
+ /** The report count's tooltip while the reports are shown. */
44
+ collapse: string;
45
+ /** The zoom controls' group name. */
46
+ toolbar: string;
47
+ zoomIn: string;
48
+ zoomOut: string;
49
+ /** Fit the chart's width into the frame. */
50
+ fit: string;
51
+ /** The current zoom, shown on the reset button: `{percent}`. */
52
+ zoomLevel: string;
53
+ /** The reset button's name; it should start with the visible level. `{percent}`. */
54
+ resetZoom: string;
55
+ }
56
+ declare const DEFAULT_ORG_CHART_LABELS: OrgChartLabels;
57
+ interface OrgChartProps extends Omit<HTMLAttributes<HTMLDivElement>, 'onSelect' | 'onToggle' | 'children'> {
58
+ /** The top of the chart: the people without a manager, or wherever the chart should start. */
59
+ nodes: OrgChartNode[];
60
+ /** Expanded people (controlled). Expanding one whose reports are not known loads them. */
61
+ expanded?: ReadonlySet<string>;
62
+ /** Expanded people at first render (uncontrolled). */
63
+ defaultExpanded?: ReadonlySet<string>;
64
+ /** A person was expanded or collapsed, by a click on the report count or by the keyboard. */
65
+ onToggle?: (id: string, expanded: boolean) => void;
66
+ /** The chosen person: highlighted, and the tree's entry point for the Tab key. */
67
+ selected?: string | null;
68
+ /** A card was clicked, or Enter pressed on it: open the person. */
69
+ onSelect?: (id: string) => void;
70
+ /**
71
+ * Loads a person's direct reports the first time they are expanded, for a chart that grows on
72
+ * demand. While it runs a loading row stands in for the reports; a rejection shows an error row
73
+ * whose Enter, Space or click retries, and is announced. The result is kept for the life of the
74
+ * chart.
75
+ */
76
+ loadChildren?: (node: OrgChartNode) => Promise<OrgChartNode[]>;
77
+ /**
78
+ * Bring this person into view and focus them once they are rendered: the target of a search.
79
+ * Expand their reporting line first (`expanded`); the chart waits until the card exists. Set it
80
+ * back to null in `onReveal`, so the same person can be revealed again later.
81
+ */
82
+ revealId?: string | null;
83
+ /** Called once the {@link OrgChartProps.revealId} person has been scrolled to and focused. */
84
+ onReveal?: (id: string) => void;
85
+ /**
86
+ * Stack a row of people who have no reports of their own in one column under their manager,
87
+ * instead of side by side. Default true: a team of eight then takes one card's width.
88
+ */
89
+ stackLeaves?: boolean;
90
+ /**
91
+ * Start at this zoom (0.25–1.5), or `'fit'` to fit the chart's width into the frame; the fit is
92
+ * kept as reports load until the reader zooms, pans or opens someone. Default 1.
93
+ */
94
+ defaultZoom?: number | 'fit';
95
+ /** Words of the chart. Default: `UixLabelsProvider` `orgChart`, then English. */
96
+ labels?: Partial<OrgChartLabels>;
97
+ }
98
+ /** The next zoom step above (`dir` 1) or below (`dir` -1) `zoom`. */
99
+ declare const orgChartZoomStep: (zoom: number, dir: 1 | -1) => number;
100
+ /** "Mira Kovač" → "MK"; one word → its first letter. */
101
+ declare const orgChartInitials: (name: string) => string;
102
+ /**
103
+ * An org chart over `.uix-org-chart` (HAR-1807): managers above their direct reports, joined by
104
+ * lines, each person a card with photo or initials, name, title, team, location and their number
105
+ * of direct reports. Reports load on expand (`loadChildren`); a person can be revealed by id for
106
+ * search. The frame zooms (buttons, and Fit for the chart's width) around its centre, pans by
107
+ * dragging its background, scrolls, and keeps an opened manager's card in place while their
108
+ * reports appear. Below 48rem of its own width it becomes an indented vertical tree.
109
+ *
110
+ * A WAI-ARIA tree with one tab stop. In the chart: ↑ manager, ↓ first report (expanding them
111
+ * first), ← → the previous and next person on the same level (from a stacked column, the next
112
+ * column); in a stacked column ↑ ↓ move along it. In the list: the tree keys (↑ ↓ visible order,
113
+ * → expand / first report, ← collapse / manager). Both: Home / End, a letter jumps to the next
114
+ * visible person whose name starts with it, Enter opens the person (`onSelect`), Space shows or
115
+ * hides their reports.
116
+ *
117
+ * The root is an inline-size container: give it a width (a block, or `flex: 1` in a flex row).
118
+ */
119
+ declare function OrgChart({ nodes, expanded: controlledExpanded, defaultExpanded, onToggle, selected, onSelect, loadChildren, revealId, onReveal, stackLeaves, defaultZoom, labels: labelOverrides, className, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledby, ...props }: OrgChartProps): react.JSX.Element;
120
+
121
+ export { DEFAULT_ORG_CHART_LABELS, OrgChart, type OrgChartLabels, type OrgChartNode, type OrgChartProps, orgChartInitials, orgChartZoomStep };