@tensor_1/react 2.31.0 → 2.32.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 (114) hide show
  1. package/README.md +350 -0
  2. package/dist/attachment-labels.d.ts +16 -0
  3. package/dist/attachment-labels.js +9 -0
  4. package/dist/calendar-model.d.ts +141 -1
  5. package/dist/calendar-model.js +200 -11
  6. package/dist/chart-preset.cjs +127 -7
  7. package/dist/chart-preset.d.cts +2 -2
  8. package/dist/chart-preset.d.ts +1 -0
  9. package/dist/chart-preset.js +6 -1
  10. package/dist/chart-theme-Cl0IrkKQ.d.cts +112 -0
  11. package/dist/chart-theme.d.ts +51 -0
  12. package/dist/chart-theme.js +99 -0
  13. package/dist/chart.cjs +127 -7
  14. package/dist/chart.d.cts +2 -2
  15. package/dist/chart.d.ts +1 -0
  16. package/dist/chart.js +6 -2
  17. package/dist/components/Alert.d.ts +17 -1
  18. package/dist/components/Alert.js +9 -4
  19. package/dist/components/AlertDismiss.d.ts +14 -0
  20. package/dist/components/AlertDismiss.js +11 -0
  21. package/dist/components/Attachment.d.ts +15 -12
  22. package/dist/components/Attachment.js +17 -17
  23. package/dist/components/AttachmentParts.d.ts +24 -0
  24. package/dist/components/AttachmentParts.js +20 -0
  25. package/dist/components/Chart.d.ts +1 -0
  26. package/dist/components/Chart.js +6 -1
  27. package/dist/components/ChartCore.d.ts +6 -1
  28. package/dist/components/ChartCore.js +23 -5
  29. package/dist/components/Chip.d.ts +17 -2
  30. package/dist/components/Chip.js +35 -11
  31. package/dist/components/ChipRemove.d.ts +17 -0
  32. package/dist/components/ChipRemove.js +13 -0
  33. package/dist/components/CollapsibleParts.d.ts +29 -0
  34. package/dist/components/CollapsibleParts.js +20 -0
  35. package/dist/components/CollapsibleSection.d.ts +30 -3
  36. package/dist/components/CollapsibleSection.js +7 -11
  37. package/dist/components/CollapsibleSectionState.d.ts +1 -1
  38. package/dist/components/CollapsibleSectionState.js +54 -27
  39. package/dist/components/DashboardGrid.d.ts +60 -0
  40. package/dist/components/DashboardGrid.js +40 -0
  41. package/dist/components/DateRangePicker.d.ts +7 -1
  42. package/dist/components/DateRangePicker.js +5 -1
  43. package/dist/components/DescriptionList.d.ts +12 -1
  44. package/dist/components/DescriptionList.js +5 -5
  45. package/dist/components/Dialogs.d.ts +24 -3
  46. package/dist/components/Dialogs.js +74 -16
  47. package/dist/components/FileUpload.d.ts +10 -2
  48. package/dist/components/FileUpload.js +10 -4
  49. package/dist/components/FilterEditor.d.ts +4 -0
  50. package/dist/components/FilterEditor.js +13 -2
  51. package/dist/components/Heartbeat.d.ts +31 -0
  52. package/dist/components/Heartbeat.js +28 -0
  53. package/dist/components/Menu.d.ts +53 -5
  54. package/dist/components/Menu.js +104 -4
  55. package/dist/components/Meter.d.ts +7 -3
  56. package/dist/components/Meter.js +2 -1
  57. package/dist/components/Popover.d.ts +21 -2
  58. package/dist/components/Popover.js +94 -17
  59. package/dist/components/Prose.d.ts +6 -2
  60. package/dist/components/Prose.js +1 -1
  61. package/dist/components/RuleBuilder.d.ts +52 -4
  62. package/dist/components/RuleBuilder.js +109 -24
  63. package/dist/components/SchedulingCalendar.d.ts +142 -11
  64. package/dist/components/SchedulingCalendar.js +234 -57
  65. package/dist/components/SchedulingTimeline.d.ts +1 -0
  66. package/dist/components/Segmented.d.ts +17 -3
  67. package/dist/components/Segmented.js +59 -5
  68. package/dist/components/Select.d.ts +66 -2
  69. package/dist/components/Select.js +800 -10
  70. package/dist/components/Spinner.d.ts +2 -1
  71. package/dist/components/Spinner.js +1 -1
  72. package/dist/components/StatusPill.d.ts +8 -2
  73. package/dist/components/StatusPill.js +2 -2
  74. package/dist/components/Steps.d.ts +28 -4
  75. package/dist/components/Steps.js +46 -16
  76. package/dist/components/TableControls.js +4 -3
  77. package/dist/components/Tabs.d.ts +7 -2
  78. package/dist/components/Tabs.js +14 -1
  79. package/dist/components/TextDiff.d.ts +49 -0
  80. package/dist/components/TextDiff.js +124 -0
  81. package/dist/{emoji-image-6mPZ15Zy.d.cts → emoji-image-DjCKpdwE.d.cts} +1 -1
  82. package/dist/emoji.cjs +169 -27
  83. package/dist/emoji.d.cts +3 -3
  84. package/dist/file-model.d.ts +22 -3
  85. package/dist/file-model.js +6 -4
  86. package/dist/filter-model.d.ts +26 -2
  87. package/dist/filter-model.js +13 -1
  88. package/dist/hooks/useAnchoredPosition.d.ts +24 -1
  89. package/dist/hooks/useAnchoredPosition.js +58 -8
  90. package/dist/hooks/useVirtualRows.d.ts +17 -2
  91. package/dist/hooks/useVirtualRows.js +6 -5
  92. package/dist/icons/Icon.d.ts +14 -14
  93. package/dist/index.cjs +4179 -1678
  94. package/dist/index.d.cts +1706 -660
  95. package/dist/index.d.ts +16 -9
  96. package/dist/index.js +53 -4
  97. package/dist/labels-context.d.ts +28 -0
  98. package/dist/{overlay-position-Cjv5E7mn.d.cts → overlay-position-2Afke51l.d.cts} +20 -0
  99. package/dist/overlay-position.d.ts +20 -0
  100. package/dist/overlay-position.js +17 -2
  101. package/dist/rich-text.cjs +169 -27
  102. package/dist/rich-text.d.cts +3 -3
  103. package/dist/rule-builder-model.d.ts +34 -2
  104. package/dist/rule-builder-model.js +48 -12
  105. package/dist/scheduling-calendar-model.d.ts +66 -0
  106. package/dist/scheduling-calendar-model.js +82 -0
  107. package/dist/select-model.d.ts +80 -0
  108. package/dist/select-model.js +109 -0
  109. package/dist/table-engine.d.ts +7 -0
  110. package/dist/table-engine.js +2 -1
  111. package/dist/text-diff-model.d.ts +71 -0
  112. package/dist/text-diff-model.js +261 -0
  113. package/package.json +1 -1
  114. package/dist/ChartCore-DzWe10bN.d.cts +0 -59
@@ -2,7 +2,8 @@ import * as react from 'react';
2
2
  import { HTMLAttributes } from 'react';
3
3
 
4
4
  interface SpinnerProps extends HTMLAttributes<HTMLSpanElement> {
5
- size?: 'md' | 'lg';
5
+ /** `sm` 16 px (inline, beside text or inside a 24 px control), `md` 20 px (default), `lg` 32 px. */
6
+ size?: 'sm' | 'md' | 'lg';
6
7
  accent?: boolean;
7
8
  /** Announced loading text (default "Loading…"). */
8
9
  label?: string;
@@ -4,7 +4,7 @@ function Spinner({ size = "md", accent, label, className, ...props }) {
4
4
  return /* @__PURE__ */ jsx(
5
5
  "span",
6
6
  {
7
- className: cx("uix-spinner", accent && "uix-spinner--accent", size === "lg" && "uix-spinner--lg", className),
7
+ className: cx("uix-spinner", accent && "uix-spinner--accent", size === "sm" && "uix-spinner--sm", size === "lg" && "uix-spinner--lg", className),
8
8
  role: "status",
9
9
  ...props,
10
10
  children: /* @__PURE__ */ jsx("span", { className: "uix-visually-hidden", children: label ?? "Loading\u2026" })
@@ -3,13 +3,19 @@ import { HTMLAttributes, ReactNode } from 'react';
3
3
 
4
4
  type PillTone = 'neutral' | 'success' | 'info' | 'warning' | 'danger' | 'critical' | 'muted' | 'attention' | 'overdue' | 'sla-ok' | 'sla-at-risk' | 'sla-breached' | 'p1' | 'p2' | 'p3' | 'p4' | 'p5';
5
5
  type PillTreatment = 'filled' | 'outline';
6
+ type PillSize = 'sm' | 'md' | 'lg';
6
7
  interface StatusPillProps extends HTMLAttributes<HTMLSpanElement> {
7
8
  tone?: PillTone;
8
9
  /** 'filled' (default) = tinted wash; 'outline' = transparent fill + tone-tinted border. */
9
10
  treatment?: PillTreatment;
11
+ /**
12
+ * `sm` for a dense table cell or beside small text, `lg` for a page header (the public status
13
+ * page). Default `md`, the size every pill had before (HAR-1614).
14
+ */
15
+ size?: PillSize;
10
16
  dot?: boolean;
11
17
  children?: ReactNode;
12
18
  }
13
- declare function StatusPill({ tone, treatment, dot, children, className, ...props }: StatusPillProps): react.JSX.Element;
19
+ declare function StatusPill({ tone, treatment, size, dot, children, className, ...props }: StatusPillProps): react.JSX.Element;
14
20
 
15
- export { type PillTone, type PillTreatment, StatusPill, type StatusPillProps };
21
+ export { type PillSize, type PillTone, type PillTreatment, StatusPill, type StatusPillProps };
@@ -1,10 +1,10 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import { cx } from "../cx.js";
3
- function StatusPill({ tone = "neutral", treatment = "filled", dot, children, className, ...props }) {
3
+ function StatusPill({ tone = "neutral", treatment = "filled", size = "md", dot, children, className, ...props }) {
4
4
  return /* @__PURE__ */ jsxs(
5
5
  "span",
6
6
  {
7
- className: cx("uix-pill", `uix-pill--${tone}`, treatment === "outline" && "uix-pill--outline", className),
7
+ className: cx("uix-pill", `uix-pill--${tone}`, treatment === "outline" && "uix-pill--outline", size === "sm" && "uix-pill--sm", size === "lg" && "uix-pill--lg", className),
8
8
  ...props,
9
9
  children: [
10
10
  dot && /* @__PURE__ */ jsx("span", { className: "uix-pill__dot", "aria-hidden": "true" }),
@@ -15,9 +15,20 @@ declare const DEFAULT_STEP_STATE_LABELS: StepStateLabels;
15
15
  interface StepsProps extends HTMLAttributes<HTMLOListElement> {
16
16
  /**
17
17
  * `horizontal` (default) lays the steps in a row and stacks them below 40rem;
18
- * `vertical` always stacks, with descriptions under each title.
18
+ * `vertical` always stacks, with descriptions under each title. Steps that hold
19
+ * content (`progress={false}`) are always vertical.
19
20
  */
20
21
  orientation?: 'horizontal' | 'vertical';
22
+ /**
23
+ * `true` (default): a progress indicator. Every step has a state, which is read after its
24
+ * title, and the current one carries `aria-current="step"`.
25
+ * `false`: a numbered list of sections that are all on screen at once (an intake form in
26
+ * four parts). No state is drawn or announced, and each `Step`'s children are its content,
27
+ * in a group named by the step's title (HAR-1628).
28
+ */
29
+ progress?: boolean;
30
+ /** Render each step's title as a heading of this level (for `progress={false}` sections). */
31
+ headingLevel?: 2 | 3 | 4 | 5 | 6;
21
32
  /** Names the list, e.g. "Intake progress". */
22
33
  label?: string;
23
34
  stateLabels?: Partial<StepStateLabels>;
@@ -34,15 +45,28 @@ interface StepProps extends Omit<LiHTMLAttributes<HTMLLIElement>, 'title'> {
34
45
  href?: string;
35
46
  renderLink?: UixRenderLink;
36
47
  onSelect?: () => void;
48
+ /**
49
+ * The step's content: fields, text, a table. Rendered under the title in a group named by
50
+ * it. Meant for `<Steps progress={false}>`; in a progress indicator keep steps to a title
51
+ * and a description.
52
+ */
53
+ children?: ReactNode;
37
54
  /** Set by `Steps`. */
38
55
  index?: number;
39
56
  /** Set by `Steps`. */
40
57
  last?: boolean;
41
58
  /** Set by `Steps`. */
42
59
  stateLabels?: StepStateLabels;
60
+ /** Set by `Steps`. */
61
+ progress?: boolean;
62
+ /** Set by `Steps`. */
63
+ headingLevel?: 2 | 3 | 4 | 5 | 6;
43
64
  }
44
- /** Numbered progress through a multi-step flow (HAR-1362; TENSOR C13, MOTUS C-9). */
45
- declare function Steps({ orientation, label, stateLabels, className, children, ...props }: StepsProps): react.JSX.Element;
46
- declare function Step({ title, description, state, marker, href, renderLink, onSelect, index, last, stateLabels, className, ...props }: StepProps): react.JSX.Element;
65
+ /**
66
+ * Numbered progress through a multi-step flow (HAR-1362; TENSOR C13, MOTUS C-9), or with
67
+ * `progress={false}` a numbered list of sections that each hold content (HAR-1628).
68
+ */
69
+ declare function Steps({ orientation, progress, headingLevel, label, stateLabels, className, children, ...props }: StepsProps): react.JSX.Element;
70
+ declare function Step({ title, description, state, marker, href, renderLink, onSelect, children, index, last, stateLabels, progress, headingLevel, className, ...props }: StepProps): react.JSX.Element;
47
71
 
48
72
  export { DEFAULT_STEP_STATE_LABELS, Step, type StepProps, type StepState, type StepStateLabels, Steps, type StepsProps };
@@ -1,5 +1,5 @@
1
1
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
- import { Children, cloneElement, isValidElement } from "react";
2
+ import { Children, cloneElement, isValidElement, useId } from "react";
3
3
  import { cx } from "../cx.js";
4
4
  import { renderUixLink } from "../link.js";
5
5
  const DEFAULT_STEP_STATE_LABELS = {
@@ -11,29 +11,59 @@ const DEFAULT_STEP_STATE_LABELS = {
11
11
  };
12
12
  const CSS_STATE = { complete: "done", current: "active", upcoming: void 0, waiting: "waiting", error: "error" };
13
13
  const Check = () => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 16 16", width: "12", height: "12", fill: "none", stroke: "currentColor", strokeWidth: 2.25, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { d: "M3.5 8.5l3 3 6-7" }) });
14
- function Steps({ orientation = "horizontal", label, stateLabels, className, children, ...props }) {
14
+ function Steps({ orientation = "horizontal", progress = true, headingLevel, label, stateLabels, className, children, ...props }) {
15
15
  const labels = { ...DEFAULT_STEP_STATE_LABELS, ...stateLabels };
16
16
  const steps = Children.toArray(children).filter(isValidElement);
17
- return /* @__PURE__ */ jsx("ol", { "aria-label": label, className: cx("uix-steps", "uix-steps--list", `uix-steps--${orientation}`, className), ...props, children: steps.map((step, index) => cloneElement(step, { index, last: index === steps.length - 1, stateLabels: labels })) });
17
+ const direction = progress ? orientation : "vertical";
18
+ return /* @__PURE__ */ jsx("ol", { "aria-label": label, className: cx("uix-steps", "uix-steps--list", `uix-steps--${direction}`, !progress && "uix-steps--sections", className), ...props, children: steps.map((step, index) => cloneElement(step, { index, last: index === steps.length - 1, stateLabels: labels, progress, headingLevel })) });
18
19
  }
19
- function Step({ title, description, state = "upcoming", marker, href, renderLink, onSelect, index = 0, last = false, stateLabels = DEFAULT_STEP_STATE_LABELS, className, ...props }) {
20
- const glyph = marker ?? (state === "complete" ? /* @__PURE__ */ jsx(Check, {}) : state === "error" ? "!" : index + 1);
20
+ function Step({
21
+ title,
22
+ description,
23
+ state = "upcoming",
24
+ marker,
25
+ href,
26
+ renderLink,
27
+ onSelect,
28
+ children,
29
+ index = 0,
30
+ last = false,
31
+ stateLabels = DEFAULT_STEP_STATE_LABELS,
32
+ progress = true,
33
+ headingLevel,
34
+ className,
35
+ ...props
36
+ }) {
37
+ const titleId = useId();
38
+ const glyph = marker ?? (!progress ? index + 1 : state === "complete" ? /* @__PURE__ */ jsx(Check, {}) : state === "error" ? "!" : index + 1);
39
+ const Title = headingLevel ? `h${headingLevel}` : "span";
21
40
  const name = /* @__PURE__ */ jsxs(Fragment, { children: [
22
- /* @__PURE__ */ jsx("span", { className: "uix-step__title", children: title }),
23
- /* @__PURE__ */ jsxs("span", { className: "uix-visually-hidden", children: [
41
+ /* @__PURE__ */ jsx(Title, { id: children != null ? titleId : void 0, className: "uix-step__title", children: title }),
42
+ progress && /* @__PURE__ */ jsxs("span", { className: "uix-visually-hidden", children: [
24
43
  ", ",
25
44
  stateLabels[state]
26
45
  ] })
27
46
  ] });
28
- const titled = href != null ? renderUixLink(renderLink, { href, className: "uix-step__action", children: name }) : onSelect ? /* @__PURE__ */ jsx("button", { type: "button", className: "uix-step__action", onClick: onSelect, children: name }) : /* @__PURE__ */ jsx("span", { className: "uix-step__action", children: name });
29
- return /* @__PURE__ */ jsxs("li", { className: cx("uix-step", className), "data-state": CSS_STATE[state], "aria-current": state === "current" ? "step" : void 0, ...props, children: [
30
- /* @__PURE__ */ jsx("span", { className: "uix-step__marker", "aria-hidden": "true", children: glyph }),
31
- /* @__PURE__ */ jsxs("span", { className: "uix-step__text", children: [
32
- titled,
33
- description != null && /* @__PURE__ */ jsx("span", { className: "uix-step__desc", children: description })
34
- ] }),
35
- !last && /* @__PURE__ */ jsx("span", { className: "uix-step__connector", "aria-hidden": "true", "data-done": state === "complete" || void 0 })
36
- ] });
47
+ const titled = headingLevel ? name : href != null ? renderUixLink(renderLink, { href, className: "uix-step__action", children: name }) : onSelect ? /* @__PURE__ */ jsx("button", { type: "button", className: "uix-step__action", onClick: onSelect, children: name }) : /* @__PURE__ */ jsx("span", { className: "uix-step__action", children: name });
48
+ const Text = headingLevel || children != null ? "div" : "span";
49
+ return /* @__PURE__ */ jsxs(
50
+ "li",
51
+ {
52
+ className: cx("uix-step", className),
53
+ "data-state": progress ? CSS_STATE[state] : void 0,
54
+ "aria-current": progress && state === "current" ? "step" : void 0,
55
+ ...props,
56
+ children: [
57
+ /* @__PURE__ */ jsx("span", { className: "uix-step__marker", "aria-hidden": "true", children: glyph }),
58
+ /* @__PURE__ */ jsxs(Text, { className: "uix-step__text", children: [
59
+ titled,
60
+ description != null && /* @__PURE__ */ jsx("span", { className: "uix-step__desc", children: description }),
61
+ children != null && /* @__PURE__ */ jsx("div", { className: "uix-step__content", role: "group", "aria-labelledby": titleId, children })
62
+ ] }),
63
+ !last && /* @__PURE__ */ jsx("span", { className: "uix-step__connector", "aria-hidden": "true", "data-done": progress && state === "complete" || void 0 })
64
+ ]
65
+ }
66
+ );
37
67
  }
38
68
  export {
39
69
  DEFAULT_STEP_STATE_LABELS,
@@ -284,10 +284,11 @@ function ViewMenu({
284
284
  ] });
285
285
  }
286
286
  function FilterPopover({ label, type = "text", value, onValueChange, options, applyLabel, clearLabel, onApply, onClear, placeholder }) {
287
+ const controlId = useId();
287
288
  return /* @__PURE__ */ jsxs("div", { className: "uix-filter-popover", children: [
288
- /* @__PURE__ */ jsxs("label", { className: "uix-label", children: [
289
- /* @__PURE__ */ jsx("span", { children: label }),
290
- type === "select" || type === "enum" || type === "boolean" ? /* @__PURE__ */ jsx("select", { className: "uix-select", value, onChange: (event) => onValueChange(event.currentTarget.value), children: options?.map((option) => /* @__PURE__ */ jsx("option", { value: option.value, children: option.label }, option.value)) }) : /* @__PURE__ */ jsx(Input, { type, value, placeholder, onChange: (event) => onValueChange(event.currentTarget.value) })
289
+ /* @__PURE__ */ jsxs("div", { className: "uix-field", children: [
290
+ /* @__PURE__ */ jsx("label", { className: "uix-field__label", htmlFor: controlId, children: label }),
291
+ type === "select" || type === "enum" || type === "boolean" ? /* @__PURE__ */ jsx("select", { id: controlId, className: "uix-select", value, onChange: (event) => onValueChange(event.currentTarget.value), children: options?.map((option) => /* @__PURE__ */ jsx("option", { value: option.value, children: option.label }, option.value)) }) : /* @__PURE__ */ jsx(Input, { id: controlId, type, value, placeholder, onChange: (event) => onValueChange(event.currentTarget.value) })
291
292
  ] }),
292
293
  /* @__PURE__ */ jsxs("div", { className: "uix-filter-popover__actions", children: [
293
294
  /* @__PURE__ */ jsx(Button, { type: "button", onClick: onClear, children: clearLabel }),
@@ -1,7 +1,12 @@
1
1
  import * as react from 'react';
2
2
  import { HTMLAttributes, ReactNode } from 'react';
3
3
 
4
- interface TabsProps {
4
+ /**
5
+ * Besides its own props, `Tabs` takes the HTML attributes of the `role="tablist"` element and
6
+ * renders them there, in both overflow modes: `aria-label` / `aria-labelledby` to name the
7
+ * tablist (HAR-1600), `id`, `data-*`. `role` stays `tablist`.
8
+ */
9
+ interface TabsProps extends Omit<HTMLAttributes<HTMLDivElement>, 'onChange' | 'role'> {
5
10
  variant?: 'line' | 'enclosed' | 'pill';
6
11
  value?: string;
7
12
  onChange?: (value: string) => void;
@@ -21,7 +26,7 @@ interface TabsProps {
21
26
  */
22
27
  activation?: 'automatic' | 'manual';
23
28
  }
24
- declare function Tabs({ variant, value, onChange, children, className, overflow, activation }: TabsProps): react.JSX.Element;
29
+ declare function Tabs({ variant, value, onChange, children, className, overflow, activation, onKeyDown: onKeyDownProp, ...listProps }: TabsProps): react.JSX.Element;
25
30
  interface TabProps extends Omit<HTMLAttributes<HTMLButtonElement>, 'onChange'> {
26
31
  value: string;
27
32
  children?: ReactNode;
@@ -5,7 +5,17 @@ import { cx } from "../cx.js";
5
5
  const TabsCtx = createContext({});
6
6
  const Chevron = ({ d }) => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { d }) });
7
7
  const inlineOffset = (el, rtl) => rtl ? -el.scrollLeft : el.scrollLeft;
8
- function Tabs({ variant = "line", value, onChange, children, className, overflow = "wrap", activation = "automatic" }) {
8
+ function Tabs({
9
+ variant = "line",
10
+ value,
11
+ onChange,
12
+ children,
13
+ className,
14
+ overflow = "wrap",
15
+ activation = "automatic",
16
+ onKeyDown: onKeyDownProp,
17
+ ...listProps
18
+ }) {
9
19
  const baseId = useId();
10
20
  const listRef = useRef(null);
11
21
  const contextValue = useMemo(() => ({ value, onChange, baseId }), [value, onChange, baseId]);
@@ -60,6 +70,8 @@ function Tabs({ variant = "line", value, onChange, children, className, overflow
60
70
  list2.scrollBy({ left: direction * (rtl ? -1 : 1) * list2.clientWidth * 0.8 });
61
71
  };
62
72
  const onKeyDown = (e) => {
73
+ onKeyDownProp?.(e);
74
+ if (e.defaultPrevented) return;
63
75
  const items = Array.from(
64
76
  listRef.current?.querySelectorAll('[role="tab"]:not(:disabled)') ?? []
65
77
  );
@@ -92,6 +104,7 @@ function Tabs({ variant = "line", value, onChange, children, className, overflow
92
104
  const list = /* @__PURE__ */ jsx(
93
105
  "div",
94
106
  {
107
+ ...listProps,
95
108
  ref: listRef,
96
109
  role: "tablist",
97
110
  className: cx("uix-tabs", `uix-tabs--${variant}`, scroll && "uix-tabs--scroll", className),
@@ -0,0 +1,49 @@
1
+ import * as react from 'react';
2
+ import { HTMLAttributes, ReactNode } from 'react';
3
+ import { TextDiffGranularity } from '../text-diff-model.js';
4
+
5
+ interface TextDiffLabels {
6
+ before: string;
7
+ after: string;
8
+ /** `{added}`, `{removed}`: lines or words, by granularity. */
9
+ summary: string;
10
+ noChanges: string;
11
+ /** Read before removed text. */
12
+ removed: string;
13
+ /** Read before added text. */
14
+ added: string;
15
+ /** Fold button. `{count}`. */
16
+ showUnchanged: string;
17
+ tooLarge: string;
18
+ }
19
+ declare const DEFAULT_TEXT_DIFF_LABELS: TextDiffLabels;
20
+ interface TextDiffProps extends Omit<HTMLAttributes<HTMLDivElement>, 'children'> {
21
+ before: string;
22
+ after: string;
23
+ /** Column heading for `before`, e.g. "Version 3". Defaults to `labels.before`. */
24
+ beforeLabel?: ReactNode;
25
+ afterLabel?: ReactNode;
26
+ /** `line` (default) pairs changed lines and marks the words inside; `word` diffs prose as one flow. */
27
+ granularity?: TextDiffGranularity;
28
+ /** `split` (default) puts the versions side by side, stacking on narrow containers; `unified` interleaves them. */
29
+ view?: 'split' | 'unified';
30
+ /** Line diffs: unchanged lines kept around each change; longer runs fold behind a button. Default: show all. */
31
+ context?: number;
32
+ /** Names the diff for assistive technology, e.g. "Changes to the article body". */
33
+ label?: string;
34
+ /** Hide the added/removed summary. */
35
+ hideSummary?: boolean;
36
+ /** Passed to the model (`maxTokens`, `maxEdits`); see `diffText`. */
37
+ maxTokens?: number;
38
+ maxEdits?: number;
39
+ labels?: Partial<TextDiffLabels>;
40
+ }
41
+ /**
42
+ * A two-way diff of two texts (HAR-1368; TENSOR C11 knowledge version diff): by line with word
43
+ * marks inside changed lines, or by word for prose; side by side or unified. Changes are `<del>` /
44
+ * `<ins>` with a −/+ sign and a spoken "Removed"/"Added", never colour alone. `DiffViewer` stays
45
+ * the three-way JSON configuration diff.
46
+ */
47
+ declare function TextDiff({ before, after, beforeLabel, afterLabel, granularity, view, context, label, hideSummary, maxTokens, maxEdits, labels: labelOverrides, className, ...props }: TextDiffProps): react.JSX.Element;
48
+
49
+ export { DEFAULT_TEXT_DIFF_LABELS, TextDiff, type TextDiffLabels, type TextDiffProps };
@@ -0,0 +1,124 @@
1
+ "use client";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
+ import { Fragment as Fragment2, useEffect, useMemo, useRef, useState } from "react";
4
+ import { cx } from "../cx.js";
5
+ import { fillLabel } from "../fill-label.js";
6
+ import { useUixLabels } from "../labels-context.js";
7
+ import { diffText, textDiffRows } from "../text-diff-model.js";
8
+ const DEFAULT_TEXT_DIFF_LABELS = {
9
+ before: "Before",
10
+ after: "After",
11
+ summary: "{added} added, {removed} removed",
12
+ noChanges: "No changes",
13
+ removed: "Removed",
14
+ added: "Added",
15
+ showUnchanged: "Show {count} unchanged lines",
16
+ tooLarge: "These versions are too large to compare here."
17
+ };
18
+ function Marked({ segments, side, labels, whole }) {
19
+ const Tag = side === "before" ? "del" : "ins";
20
+ return /* @__PURE__ */ jsx(Fragment, { children: segments.map((segment, i) => segment.changed ? /* @__PURE__ */ jsxs(Tag, { "data-whole": whole || void 0, children: [
21
+ /* @__PURE__ */ jsxs("span", { className: "uix-visually-hidden", children: [
22
+ side === "before" ? labels.removed : labels.added,
23
+ ": "
24
+ ] }),
25
+ segment.text
26
+ ] }, i) : /* @__PURE__ */ jsx(Fragment2, { children: segment.text }, i)) });
27
+ }
28
+ function Cell({ line, side, kind, labels }) {
29
+ if (!line) return /* @__PURE__ */ jsx("div", { className: "uix-text-diff__cell", "data-side": side, "data-empty": "", "aria-hidden": "true" });
30
+ const changed = kind !== "equal";
31
+ return /* @__PURE__ */ jsxs("div", { className: "uix-text-diff__cell", "data-side": side, "data-kind": changed ? side === "before" ? "delete" : "insert" : "equal", children: [
32
+ /* @__PURE__ */ jsx("span", { className: "uix-text-diff__num", "aria-hidden": "true", children: line.number }),
33
+ /* @__PURE__ */ jsx("span", { className: "uix-text-diff__sign", "aria-hidden": "true", children: changed ? side === "before" ? "\u2212" : "+" : "" }),
34
+ /* @__PURE__ */ jsx("span", { className: "uix-text-diff__text", children: line.segments.length === 1 && line.segments[0].text === "" ? "\u200B" : /* @__PURE__ */ jsx(Marked, { segments: line.segments, side, labels, whole: line.segments.length === 1 }) })
35
+ ] });
36
+ }
37
+ function Rows({ rows, view, labels, focusFirst }) {
38
+ const [open, setOpen] = useState(() => /* @__PURE__ */ new Set());
39
+ const first = useRef(null);
40
+ useEffect(() => {
41
+ if (focusFirst) first.current?.focus();
42
+ }, [focusFirst]);
43
+ const firstProps = (index) => focusFirst && index === 0 ? { ref: first, tabIndex: -1 } : {};
44
+ return /* @__PURE__ */ jsx(Fragment, { children: rows.map((row, index) => {
45
+ if (row.kind === "fold") {
46
+ if (open.has(index)) return /* @__PURE__ */ jsx(Rows, { rows: row.rows, view, labels, focusFirst: true }, index);
47
+ return /* @__PURE__ */ jsx("div", { className: "uix-text-diff__row", "data-kind": "fold", children: /* @__PURE__ */ jsx("button", { type: "button", className: "uix-text-diff__fold", onClick: () => setOpen((s) => new Set(s).add(index)), children: fillLabel(labels.showUnchanged, { count: row.count }) }) }, index);
48
+ }
49
+ const before = row.kind === "insert" ? void 0 : row.before;
50
+ const after = row.kind === "delete" ? void 0 : row.after;
51
+ if (view === "unified") {
52
+ if (row.kind === "equal") return /* @__PURE__ */ jsx("div", { className: "uix-text-diff__row", "data-kind": "equal", ...firstProps(index), children: /* @__PURE__ */ jsx(Cell, { line: after, side: "after", kind: "equal", labels }) }, index);
53
+ return /* @__PURE__ */ jsxs(Fragment2, { children: [
54
+ before && /* @__PURE__ */ jsx("div", { className: "uix-text-diff__row", "data-kind": "delete", children: /* @__PURE__ */ jsx(Cell, { line: before, side: "before", kind: row.kind, labels }) }),
55
+ after && /* @__PURE__ */ jsx("div", { className: "uix-text-diff__row", "data-kind": "insert", children: /* @__PURE__ */ jsx(Cell, { line: after, side: "after", kind: row.kind, labels }) })
56
+ ] }, index);
57
+ }
58
+ return /* @__PURE__ */ jsxs("div", { className: "uix-text-diff__row", "data-kind": row.kind, ...firstProps(index), children: [
59
+ /* @__PURE__ */ jsx(Cell, { line: before, side: "before", kind: row.kind, labels }),
60
+ /* @__PURE__ */ jsx(Cell, { line: after, side: "after", kind: row.kind, labels })
61
+ ] }, index);
62
+ }) });
63
+ }
64
+ function TextDiff({
65
+ before,
66
+ after,
67
+ beforeLabel,
68
+ afterLabel,
69
+ granularity = "line",
70
+ view = "split",
71
+ context,
72
+ label,
73
+ hideSummary,
74
+ maxTokens,
75
+ maxEdits,
76
+ labels: labelOverrides,
77
+ className,
78
+ ...props
79
+ }) {
80
+ const uixLabels = useUixLabels();
81
+ const labels = { ...DEFAULT_TEXT_DIFF_LABELS, ...uixLabels.textDiff, ...labelOverrides };
82
+ const result = useMemo(() => diffText(before, after, { granularity, maxTokens, maxEdits }), [before, after, granularity, maxTokens, maxEdits]);
83
+ const rows = useMemo(() => granularity === "line" && result.status === "ok" ? textDiffRows(result, { context }) : [], [result, granularity, context]);
84
+ const heads = /* @__PURE__ */ jsxs("div", { className: "uix-text-diff__heads", "aria-hidden": view === "unified" || void 0, children: [
85
+ /* @__PURE__ */ jsxs("span", { className: "uix-text-diff__head", "data-side": "before", children: [
86
+ view === "unified" && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "\u2212 " }),
87
+ beforeLabel ?? labels.before
88
+ ] }),
89
+ /* @__PURE__ */ jsxs("span", { className: "uix-text-diff__head", "data-side": "after", children: [
90
+ view === "unified" && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "+ " }),
91
+ afterLabel ?? labels.after
92
+ ] })
93
+ ] });
94
+ const unchanged = result.status === "ok" && result.added === 0 && result.removed === 0 && result.ops.every((op) => op.type === "equal");
95
+ let body;
96
+ if (result.status === "too-large") body = /* @__PURE__ */ jsx("div", { className: "uix-text-diff__note", children: labels.tooLarge });
97
+ else if (granularity === "line") body = /* @__PURE__ */ jsx("div", { className: "uix-text-diff__body", children: /* @__PURE__ */ jsx(Rows, { rows, view, labels }) });
98
+ else {
99
+ const prose = (side2) => /* @__PURE__ */ jsx("div", { className: "uix-text-diff__prose", "data-side": side2, children: result.ops.map((op, i) => {
100
+ const text = op.tokens.join("");
101
+ if (op.type === "equal") return /* @__PURE__ */ jsx(Fragment2, { children: text }, i);
102
+ if (op.type === "delete" && side2 === "after") return null;
103
+ if (op.type === "insert" && side2 === "before") return null;
104
+ return /* @__PURE__ */ jsx(Marked, { segments: [{ text, changed: true }], side: op.type === "delete" ? "before" : "after", labels }, i);
105
+ }) });
106
+ const side = (which) => /* @__PURE__ */ jsxs("div", { className: "uix-text-diff__side", children: [
107
+ /* @__PURE__ */ jsx("div", { className: "uix-text-diff__head", children: which === "before" ? beforeLabel ?? labels.before : afterLabel ?? labels.after }),
108
+ prose(which)
109
+ ] });
110
+ body = /* @__PURE__ */ jsx("div", { className: "uix-text-diff__body", children: view === "unified" ? prose("both") : /* @__PURE__ */ jsxs("div", { className: "uix-text-diff__row", "data-kind": "prose", children: [
111
+ side("before"),
112
+ side("after")
113
+ ] }) });
114
+ }
115
+ return /* @__PURE__ */ jsxs("div", { role: "group", "aria-label": label, className: cx("uix-text-diff", className), "data-view": view, "data-granularity": granularity, ...props, children: [
116
+ !hideSummary && result.status === "ok" && /* @__PURE__ */ jsx("div", { className: "uix-text-diff__summary", children: unchanged ? labels.noChanges : fillLabel(labels.summary, { added: result.added, removed: result.removed }) }),
117
+ !(granularity === "word" && view === "split" && result.status === "ok") && heads,
118
+ body
119
+ ] });
120
+ }
121
+ export {
122
+ DEFAULT_TEXT_DIFF_LABELS,
123
+ TextDiff
124
+ };
@@ -1,6 +1,6 @@
1
1
  import * as react from 'react';
2
2
  import { ReactElement } from 'react';
3
- import { P as Placement } from './overlay-position-Cjv5E7mn.cjs';
3
+ import { P as Placement } from './overlay-position-2Afke51l.cjs';
4
4
 
5
5
  type EmojiLocale = 'en' | 'de';
6
6
  interface EmojiEntry {