@tensor_1/react 2.6.0 → 2.8.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.
@@ -0,0 +1,183 @@
1
+ import * as react from 'react';
2
+ import { HTMLAttributes, ReactNode, ButtonHTMLAttributes } from 'react';
3
+
4
+ interface PageIntroProps extends Omit<HTMLAttributes<HTMLElement>, 'title'> {
5
+ /** Small line above the title (e.g. today's date). */
6
+ kicker?: ReactNode;
7
+ title: ReactNode;
8
+ /** Supporting copy under the title. */
9
+ lede?: ReactNode;
10
+ /** Label shown above the search well. */
11
+ searchLabel?: ReactNode;
12
+ /** The search well itself (form + `.uix-searchbar` markup, or `SearchBar`-style composition). */
13
+ search?: ReactNode;
14
+ /** Shortcut buttons; rendered inside the `.uix-shortcut-grid` full-width row. */
15
+ shortcuts?: ReactNode;
16
+ /** Accessible label for the shortcuts group. */
17
+ shortcutsLabel?: string;
18
+ }
19
+ /**
20
+ * Editorial page intro — kicker / title / lede beside a search well, with an
21
+ * optional full-width shortcut grid underneath (`.uix-page-intro`).
22
+ */
23
+ declare function PageIntro({ kicker, title, lede, searchLabel, search, shortcuts, shortcutsLabel, className, children, ...props }: PageIntroProps): react.JSX.Element;
24
+ interface SectionHeadProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'> {
25
+ title: ReactNode;
26
+ /** id for the `<h2>`, for `aria-labelledby` wiring on the enclosing section. */
27
+ titleId?: string;
28
+ /** Right-aligned slot — a `.uix-section-link` anchor, a button, a pill. */
29
+ action?: ReactNode;
30
+ }
31
+ /** Section head — title row with an optional trailing action (`.uix-section-head`). */
32
+ declare function SectionHead({ title, titleId, action, className, ...props }: SectionHeadProps): react.JSX.Element;
33
+ interface NoticeQueueProps extends Omit<HTMLAttributes<HTMLElement>, 'title'> {
34
+ /** Leading icon slot. */
35
+ icon?: ReactNode;
36
+ /** Bolded lead-in of the notice copy. */
37
+ title?: ReactNode;
38
+ /** Rest of the notice copy, after the title. */
39
+ summary?: ReactNode;
40
+ /** Meta line under the copy (e.g. "Needs action · Required by Friday"). */
41
+ meta?: ReactNode;
42
+ /** Queue position (e.g. "1 of 4 important updates"). */
43
+ position?: ReactNode;
44
+ /**
45
+ * Trailing controls — previous/next arrows (put `uix-notice__arrow--previous`
46
+ * on the previous button to flip its icon), read/view-all buttons.
47
+ */
48
+ actions?: ReactNode;
49
+ }
50
+ /**
51
+ * Notice queue — the rotating "important updates" banner (`.uix-notice`). The
52
+ * copy region is `aria-live="polite"` so queue rotation is announced.
53
+ */
54
+ declare function NoticeQueue({ icon, title, summary, meta, position, actions, className, ...props }: NoticeQueueProps): react.JSX.Element;
55
+ interface FeaturedStageProps extends Omit<HTMLAttributes<HTMLElement>, 'title'> {
56
+ /** Decorative visual (story signal + diagram). Hidden from AT unless `visualLabel` is set. */
57
+ visual?: ReactNode;
58
+ /** Accessible description of the visual; when set the visual gets `role="img"`. */
59
+ visualLabel?: string;
60
+ /**
61
+ * Ordinal of the story on stage (e.g. "01"), rendered as the filled
62
+ * `.uix-featured__now` chip in the eyebrow. Pair it with the matching
63
+ * `FeaturedRundownItem`'s `number` — the selected item's number renders as
64
+ * the same chip, and that pairing is what links rundown to stage on every
65
+ * breakpoint (the divider caret only exists while the zones sit side by side).
66
+ */
67
+ ordinal?: ReactNode;
68
+ eyebrow?: ReactNode;
69
+ title: ReactNode;
70
+ /** id for the `<h2>` title, for `aria-labelledby` wiring. */
71
+ titleId?: string;
72
+ description?: ReactNode;
73
+ meta?: ReactNode;
74
+ /** Call-to-action slot under the meta line. */
75
+ action?: ReactNode;
76
+ }
77
+ /**
78
+ * Featured stage — the hero story zone of the featured briefing
79
+ * (`.uix-featured__stage`). The briefing is ONE `.uix-featured` card: put this
80
+ * and `FeaturedRundown` directly inside it and the internal hairline, the
81
+ * selected item's caret, and the ordinal chips connect the two zones. Content
82
+ * is `aria-live="polite"` so swapping the featured story (from the rundown)
83
+ * is announced.
84
+ */
85
+ declare function FeaturedStage({ visual, visualLabel, ordinal, eyebrow, title, titleId, description, meta, action, className, ...props }: FeaturedStageProps): react.JSX.Element;
86
+ interface FeaturedRundownProps extends Omit<HTMLAttributes<HTMLElement>, 'title'> {
87
+ /** Uppercase line above the title (e.g. "Editorial briefing"). */
88
+ eyebrow?: ReactNode;
89
+ title: ReactNode;
90
+ /** id for the `<h2>` title, for `aria-labelledby` wiring. */
91
+ titleId?: string;
92
+ /** Trailing head slot (e.g. a count pill). */
93
+ meta?: ReactNode;
94
+ /** `FeaturedRundownItem`s. */
95
+ children?: ReactNode;
96
+ }
97
+ /**
98
+ * Featured rundown — the story-list zone of the featured briefing
99
+ * (`.uix-rundown`), sitting inside the single `.uix-featured` card beside (or,
100
+ * stacked, below) the `FeaturedStage`.
101
+ */
102
+ declare function FeaturedRundown({ eyebrow, title, titleId, meta, children, className, ...props }: FeaturedRundownProps): react.JSX.Element;
103
+ interface FeaturedRundownItemProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'title'> {
104
+ /** Ordinal label (e.g. "01"). */
105
+ number?: ReactNode;
106
+ /** Topic/duration line above the item title (e.g. "Company · 5 min"). */
107
+ topic?: ReactNode;
108
+ /** The story title. */
109
+ title: ReactNode;
110
+ /** Whether this story is on the stage — sets `aria-pressed` + `data-selected`. */
111
+ selected?: boolean;
112
+ }
113
+ /**
114
+ * One rundown story — a toggle button (`.uix-rundown__item`) whose selection
115
+ * is conveyed with `aria-pressed` and styled through `[data-selected]`.
116
+ */
117
+ declare function FeaturedRundownItem({ number, topic, title, selected, className, ...props }: FeaturedRundownItemProps): react.JSX.Element;
118
+ interface NewsLeadProps extends Omit<HTMLAttributes<HTMLElement>, 'title'> {
119
+ /** Meta line above the title (pill + date). */
120
+ meta?: ReactNode;
121
+ title: ReactNode;
122
+ summary?: ReactNode;
123
+ }
124
+ /** News lead — the top story of a news column (`.uix-news-lead`). */
125
+ declare function NewsLead({ meta, title, summary, className, ...props }: NewsLeadProps): react.JSX.Element;
126
+ interface ContentListProps extends HTMLAttributes<HTMLUListElement> {
127
+ /** `ContentListItem`s. */
128
+ children?: ReactNode;
129
+ }
130
+ /** Content list — hairline-divided rows of titled items (`.uix-content-list`). */
131
+ declare function ContentList({ children, className, ...props }: ContentListProps): react.JSX.Element;
132
+ interface ContentListItemProps extends Omit<HTMLAttributes<HTMLLIElement>, 'title'> {
133
+ title: ReactNode;
134
+ meta?: ReactNode;
135
+ /** Trailing slot — an arrow glyph, a pill. */
136
+ trailing?: ReactNode;
137
+ }
138
+ /** One content-list row — title + meta with an optional trailing slot. */
139
+ declare function ContentListItem({ title, meta, trailing, className, ...props }: ContentListItemProps): react.JSX.Element;
140
+ interface ResourceGridProps extends HTMLAttributes<HTMLDivElement> {
141
+ /** Resource buttons/links (typically `.uix-btn--outline`). */
142
+ children?: ReactNode;
143
+ }
144
+ /** Resource grid — a three-up grid of resource shortcuts (`.uix-resource-grid`). */
145
+ declare function ResourceGrid({ children, className, ...props }: ResourceGridProps): react.JSX.Element;
146
+ interface StatLineItem {
147
+ /** Stable identity for the item; falls back to the index when omitted. */
148
+ id?: string;
149
+ value: ReactNode;
150
+ label: ReactNode;
151
+ }
152
+ interface StatLineProps extends HTMLAttributes<HTMLDivElement> {
153
+ items?: StatLineItem[];
154
+ children?: ReactNode;
155
+ }
156
+ /**
157
+ * Stat line — a compact value/label summary strip over a hairline
158
+ * (`.uix-stat-line`). Use `items` for data-driven stats or hand-author
159
+ * `.uix-stat-line__item` children.
160
+ */
161
+ declare function StatLine({ items, children, className, ...props }: StatLineProps): react.JSX.Element;
162
+ interface EventRowProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'> {
163
+ /** Calendar-block month (e.g. "Jul"). */
164
+ month: ReactNode;
165
+ /** Calendar-block day (e.g. "30"). */
166
+ day: ReactNode;
167
+ title: ReactNode;
168
+ meta?: ReactNode;
169
+ }
170
+ /** Event row — date block + title/meta (`.uix-event-row`). */
171
+ declare function EventRow({ month, day, title, meta, className, ...props }: EventRowProps): react.JSX.Element;
172
+ interface StatusRowProps extends HTMLAttributes<HTMLDivElement> {
173
+ /** Leading indicator slot (e.g. a `.uix-dot`). */
174
+ indicator?: ReactNode;
175
+ /** Service name — takes the flexible middle (`.uix-status-row__name`). */
176
+ name: ReactNode;
177
+ /** Trailing meta (e.g. "Healthy"), rendered as `.uix-list-meta`. */
178
+ meta?: ReactNode;
179
+ }
180
+ /** Status row — indicator + name + trailing meta (`.uix-status-row`). */
181
+ declare function StatusRow({ indicator, name, meta, className, ...props }: StatusRowProps): react.JSX.Element;
182
+
183
+ export { ContentList, ContentListItem, type ContentListItemProps, type ContentListProps, EventRow, type EventRowProps, FeaturedRundown, FeaturedRundownItem, type FeaturedRundownItemProps, type FeaturedRundownProps, FeaturedStage, type FeaturedStageProps, NewsLead, type NewsLeadProps, NoticeQueue, type NoticeQueueProps, PageIntro, type PageIntroProps, ResourceGrid, type ResourceGridProps, SectionHead, type SectionHeadProps, StatLine, type StatLineItem, type StatLineProps, StatusRow, type StatusRowProps };
@@ -0,0 +1,183 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { cx } from "../cx.js";
3
+ function PageIntro({
4
+ kicker,
5
+ title,
6
+ lede,
7
+ searchLabel,
8
+ search,
9
+ shortcuts,
10
+ shortcutsLabel,
11
+ className,
12
+ children,
13
+ ...props
14
+ }) {
15
+ return /* @__PURE__ */ jsxs("section", { className: cx("uix-page-intro", className), ...props, children: [
16
+ /* @__PURE__ */ jsxs("div", { children: [
17
+ kicker != null && /* @__PURE__ */ jsx("p", { className: "uix-page-kicker", children: kicker }),
18
+ /* @__PURE__ */ jsx("h1", { className: "uix-page-title", children: title }),
19
+ lede != null && /* @__PURE__ */ jsx("p", { className: "uix-page-lede", children: lede })
20
+ ] }),
21
+ search != null && /* @__PURE__ */ jsxs("div", { children: [
22
+ searchLabel != null && /* @__PURE__ */ jsx("p", { className: "uix-intro-search__label", children: searchLabel }),
23
+ search
24
+ ] }),
25
+ shortcuts != null && /* @__PURE__ */ jsx("div", { className: "uix-shortcut-grid", "aria-label": shortcutsLabel, children: shortcuts }),
26
+ children
27
+ ] });
28
+ }
29
+ function SectionHead({ title, titleId, action, className, ...props }) {
30
+ return /* @__PURE__ */ jsxs("div", { className: cx("uix-section-head", className), ...props, children: [
31
+ /* @__PURE__ */ jsx("h2", { className: "uix-section-title", id: titleId, children: title }),
32
+ action
33
+ ] });
34
+ }
35
+ function NoticeQueue({ icon, title, summary, meta, position, actions, className, ...props }) {
36
+ return /* @__PURE__ */ jsxs("section", { className: cx("uix-notice", className), ...props, children: [
37
+ icon,
38
+ /* @__PURE__ */ jsxs("div", { className: "uix-notice__content", "aria-live": "polite", children: [
39
+ /* @__PURE__ */ jsxs("p", { className: "uix-notice__copy", children: [
40
+ title != null && /* @__PURE__ */ jsx("strong", { children: title }),
41
+ title != null && summary != null && " ",
42
+ summary
43
+ ] }),
44
+ meta != null && /* @__PURE__ */ jsx("p", { className: "uix-notice__meta", children: meta })
45
+ ] }),
46
+ (position != null || actions != null) && /* @__PURE__ */ jsxs("div", { className: "uix-notice__actions", children: [
47
+ position != null && /* @__PURE__ */ jsx("span", { className: "uix-notice__position", children: position }),
48
+ actions
49
+ ] })
50
+ ] });
51
+ }
52
+ function FeaturedStage({
53
+ visual,
54
+ visualLabel,
55
+ ordinal,
56
+ eyebrow,
57
+ title,
58
+ titleId,
59
+ description,
60
+ meta,
61
+ action,
62
+ className,
63
+ ...props
64
+ }) {
65
+ return /* @__PURE__ */ jsxs("article", { className: cx("uix-featured__stage", className), ...props, children: [
66
+ /* @__PURE__ */ jsx(
67
+ "div",
68
+ {
69
+ className: "uix-featured__visual",
70
+ role: visualLabel != null ? "img" : void 0,
71
+ "aria-label": visualLabel,
72
+ "aria-hidden": visualLabel == null || void 0,
73
+ children: visual
74
+ }
75
+ ),
76
+ /* @__PURE__ */ jsxs("div", { className: "uix-featured__content", "aria-live": "polite", children: [
77
+ (eyebrow != null || ordinal != null) && /* @__PURE__ */ jsxs("p", { className: "uix-featured__eyebrow", children: [
78
+ ordinal != null && /* @__PURE__ */ jsx("span", { className: "uix-featured__now", children: ordinal }),
79
+ eyebrow
80
+ ] }),
81
+ /* @__PURE__ */ jsx("h2", { className: "uix-featured__title", id: titleId, children: title }),
82
+ description != null && /* @__PURE__ */ jsx("p", { className: "uix-featured__description", children: description }),
83
+ meta != null && /* @__PURE__ */ jsx("p", { className: "uix-featured__meta", children: meta }),
84
+ action
85
+ ] })
86
+ ] });
87
+ }
88
+ function FeaturedRundown({ eyebrow, title, titleId, meta, children, className, ...props }) {
89
+ return /* @__PURE__ */ jsxs("aside", { className: cx("uix-rundown", className), ...props, children: [
90
+ /* @__PURE__ */ jsxs("div", { className: "uix-rundown__head", children: [
91
+ /* @__PURE__ */ jsxs("div", { children: [
92
+ eyebrow != null && /* @__PURE__ */ jsx("p", { className: "uix-rundown__eyebrow", children: eyebrow }),
93
+ /* @__PURE__ */ jsx("h2", { className: "uix-rundown__title", id: titleId, children: title })
94
+ ] }),
95
+ meta
96
+ ] }),
97
+ /* @__PURE__ */ jsx("div", { className: "uix-rundown__items", children })
98
+ ] });
99
+ }
100
+ function FeaturedRundownItem({ number, topic, title, selected = false, className, ...props }) {
101
+ return /* @__PURE__ */ jsxs(
102
+ "button",
103
+ {
104
+ type: "button",
105
+ className: cx("uix-rundown__item", className),
106
+ "aria-pressed": selected,
107
+ "data-selected": selected ? "true" : "false",
108
+ ...props,
109
+ children: [
110
+ number != null && /* @__PURE__ */ jsx("span", { className: "uix-rundown__number", children: number }),
111
+ /* @__PURE__ */ jsxs("span", { children: [
112
+ topic != null && /* @__PURE__ */ jsx("span", { className: "uix-rundown__topic", children: topic }),
113
+ /* @__PURE__ */ jsx("span", { className: "uix-rundown__item-title", children: title })
114
+ ] })
115
+ ]
116
+ }
117
+ );
118
+ }
119
+ function NewsLead({ meta, title, summary, className, ...props }) {
120
+ return /* @__PURE__ */ jsxs("article", { className: cx("uix-news-lead", className), ...props, children: [
121
+ meta != null && /* @__PURE__ */ jsx("p", { className: "uix-news-lead__meta", children: meta }),
122
+ /* @__PURE__ */ jsx("h3", { className: "uix-news-lead__title", children: title }),
123
+ summary != null && /* @__PURE__ */ jsx("p", { className: "uix-news-lead__summary", children: summary })
124
+ ] });
125
+ }
126
+ function ContentList({ children, className, ...props }) {
127
+ return /* @__PURE__ */ jsx("ul", { className: cx("uix-content-list", className), ...props, children });
128
+ }
129
+ function ContentListItem({ title, meta, trailing, className, ...props }) {
130
+ return /* @__PURE__ */ jsxs("li", { className: cx("uix-content-list__item", className), ...props, children: [
131
+ /* @__PURE__ */ jsxs("div", { children: [
132
+ /* @__PURE__ */ jsx("p", { className: "uix-content-list__title", children: title }),
133
+ meta != null && /* @__PURE__ */ jsx("p", { className: "uix-content-list__meta", children: meta })
134
+ ] }),
135
+ trailing
136
+ ] });
137
+ }
138
+ function ResourceGrid({ children, className, ...props }) {
139
+ return /* @__PURE__ */ jsx("div", { className: cx("uix-resource-grid", className), ...props, children });
140
+ }
141
+ function StatLine({ items, children, className, ...props }) {
142
+ return /* @__PURE__ */ jsxs("div", { className: cx("uix-stat-line", className), ...props, children: [
143
+ items?.map((item, i) => /* @__PURE__ */ jsxs("div", { className: "uix-stat-line__item", children: [
144
+ /* @__PURE__ */ jsx("span", { className: "uix-stat-line__value", children: item.value }),
145
+ /* @__PURE__ */ jsx("span", { className: "uix-stat-line__label", children: item.label })
146
+ ] }, item.id ?? i)),
147
+ children
148
+ ] });
149
+ }
150
+ function EventRow({ month, day, title, meta, className, ...props }) {
151
+ return /* @__PURE__ */ jsxs("div", { className: cx("uix-event-row", className), ...props, children: [
152
+ /* @__PURE__ */ jsxs("div", { className: "uix-event-date", children: [
153
+ /* @__PURE__ */ jsx("span", { className: "uix-event-date__month", children: month }),
154
+ /* @__PURE__ */ jsx("span", { className: "uix-event-date__day", children: day })
155
+ ] }),
156
+ /* @__PURE__ */ jsxs("div", { children: [
157
+ /* @__PURE__ */ jsx("p", { className: "uix-event-row__title", children: title }),
158
+ meta != null && /* @__PURE__ */ jsx("p", { className: "uix-event-row__meta", children: meta })
159
+ ] })
160
+ ] });
161
+ }
162
+ function StatusRow({ indicator, name, meta, className, ...props }) {
163
+ return /* @__PURE__ */ jsxs("div", { className: cx("uix-status-row", className), ...props, children: [
164
+ indicator,
165
+ /* @__PURE__ */ jsx("span", { className: "uix-status-row__name", children: name }),
166
+ meta != null && /* @__PURE__ */ jsx("span", { className: "uix-list-meta", children: meta })
167
+ ] });
168
+ }
169
+ export {
170
+ ContentList,
171
+ ContentListItem,
172
+ EventRow,
173
+ FeaturedRundown,
174
+ FeaturedRundownItem,
175
+ FeaturedStage,
176
+ NewsLead,
177
+ NoticeQueue,
178
+ PageIntro,
179
+ ResourceGrid,
180
+ SectionHead,
181
+ StatLine,
182
+ StatusRow
183
+ };
@@ -1,12 +1,18 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { cloneElement, isValidElement, useId } from "react";
2
3
  import { cx } from "../cx.js";
3
4
  function Field({ label, hint, error, success, required, children, className, htmlFor }) {
5
+ const uid = useId();
6
+ const msgId = `${uid}-msg`;
7
+ const describedBy = error || success || hint ? msgId : void 0;
8
+ const control = isValidElement(children) ? cloneElement(children, {
9
+ "aria-describedby": [children.props["aria-describedby"], describedBy].filter(Boolean).join(" ") || void 0,
10
+ "aria-invalid": error ? true : children.props["aria-invalid"]
11
+ }) : children;
4
12
  return /* @__PURE__ */ jsxs("div", { className: cx("uix-field", className), children: [
5
13
  label && /* @__PURE__ */ jsx("label", { className: "uix-field__label", htmlFor, "data-required": required || void 0, children: label }),
6
- children,
7
- error && /* @__PURE__ */ jsx("span", { className: "uix-field__error", children: error }),
8
- success && !error && /* @__PURE__ */ jsx("span", { className: "uix-field__success", children: success }),
9
- hint && !error && !success && /* @__PURE__ */ jsx("span", { className: "uix-field__hint", children: hint })
14
+ control,
15
+ /* @__PURE__ */ jsx("div", { className: "uix-field__msg", id: msgId, children: error ? /* @__PURE__ */ jsx("span", { className: "uix-field__error", role: "alert", children: error }) : success ? /* @__PURE__ */ jsx("span", { className: "uix-field__success", children: success }) : hint ? /* @__PURE__ */ jsx("span", { className: "uix-field__hint", children: hint }) : null })
10
16
  ] });
11
17
  }
12
18
  export {
@@ -1,5 +1,6 @@
1
1
  import * as react from 'react';
2
- import { HTMLAttributes, ReactNode } from 'react';
2
+ import { HTMLAttributes, RefObject, ReactNode } from 'react';
3
+ import { Placement } from '../overlay-position.js';
3
4
 
4
5
  interface PopoverProps extends HTMLAttributes<HTMLDivElement> {
5
6
  /**
@@ -7,12 +8,25 @@ interface PopoverProps extends HTMLAttributes<HTMLDivElement> {
7
8
  * wires to it via `popoverTarget={id}`. Pass `"manual"` to control open state yourself.
8
9
  */
9
10
  popover?: 'auto' | 'manual';
11
+ /**
12
+ * Element (or ref) to anchor against. When set, the popover is placed with
13
+ * cross-browser JS positioning — flip when it won't fit, shift to stay on-screen —
14
+ * instead of relying on CSS `anchor()`, which is Chromium-only and detaches the
15
+ * popover from its trigger everywhere else. The native Popover API still provides
16
+ * the top layer (so it escapes `overflow` clipping) and light-dismiss.
17
+ */
18
+ anchor?: RefObject<HTMLElement | null> | HTMLElement | null;
19
+ /** Preferred placement when `anchor` is set. Default `'bottom-start'`. */
20
+ placement?: Placement;
21
+ /** Gap between the trigger and the popover, in px. Default 6. */
22
+ offset?: number;
10
23
  children?: ReactNode;
11
24
  }
12
25
  /**
13
- * Anchored surface over `.uix-popover`, using the native Popover API.
14
- * Compose ITSM things like a filter popover by putting controls inside.
26
+ * Anchored surface over `.uix-popover`, using the native Popover API. Compose ITSM
27
+ * things like a filter popover, menu, or rich select by putting controls inside.
28
+ * Pass `anchor` to get collision-aware, cross-browser placement (UIX-FIX-02).
15
29
  */
16
- declare function Popover({ popover, children, className, ...props }: PopoverProps): react.JSX.Element;
30
+ declare function Popover({ popover, anchor, placement, offset, className, children, ...props }: PopoverProps): react.JSX.Element;
17
31
 
18
32
  export { Popover, type PopoverProps };
@@ -1,8 +1,37 @@
1
+ "use client";
1
2
  import { jsx } from "react/jsx-runtime";
3
+ import { useEffect, useRef, useState } from "react";
2
4
  import { cx } from "../cx.js";
3
- function Popover({ popover = "auto", children, className, ...props }) {
5
+ import { useAnchoredPosition } from "../hooks/useAnchoredPosition.js";
6
+ function Popover({
7
+ popover = "auto",
8
+ anchor,
9
+ placement = "bottom-start",
10
+ offset = 6,
11
+ className,
12
+ children,
13
+ ...props
14
+ }) {
15
+ const ref = useRef(null);
16
+ const [open, setOpen] = useState(false);
4
17
  const popoverAttr = { popover };
5
- return /* @__PURE__ */ jsx("div", { className: cx("uix-popover", className), ...popoverAttr, ...props, children });
18
+ const reposition = useAnchoredPosition(anchor ?? null, ref, {
19
+ open: open && !!anchor,
20
+ placement,
21
+ offset
22
+ });
23
+ useEffect(() => {
24
+ const el = ref.current;
25
+ if (!el || !anchor) return;
26
+ const sync = () => {
27
+ const isOpen = el.matches(":popover-open");
28
+ if (isOpen) reposition();
29
+ setOpen(isOpen);
30
+ };
31
+ el.addEventListener("toggle", sync);
32
+ return () => el.removeEventListener("toggle", sync);
33
+ }, [anchor, reposition]);
34
+ return /* @__PURE__ */ jsx("div", { ref, className: cx("uix-popover", className), ...popoverAttr, ...props, children });
6
35
  }
7
36
  export {
8
37
  Popover
@@ -1,16 +1,17 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import { cx } from "../cx.js";
3
3
  function Toast({ title, message, tone, icon, onClose, leaving, className, ...props }) {
4
+ const assertive = tone === "danger";
4
5
  return /* @__PURE__ */ jsxs(
5
6
  "div",
6
7
  {
7
8
  className: cx("uix-toast", tone && `uix-toast--${tone}`, className),
8
9
  "data-leaving": leaving || void 0,
9
- role: "status",
10
- "aria-live": "polite",
10
+ role: assertive ? "alert" : "status",
11
+ "aria-live": assertive ? "assertive" : "polite",
11
12
  ...props,
12
13
  children: [
13
- icon && /* @__PURE__ */ jsx("div", { className: "uix-toast__icon", children: icon }),
14
+ icon && /* @__PURE__ */ jsx("div", { className: "uix-toast__icon", "aria-hidden": "true", children: icon }),
14
15
  /* @__PURE__ */ jsxs("div", { className: "uix-toast__body", children: [
15
16
  title && /* @__PURE__ */ jsx("div", { className: "uix-toast__title", children: title }),
16
17
  message && /* @__PURE__ */ jsx("div", { className: "uix-toast__msg", children: message })
@@ -21,7 +22,7 @@ function Toast({ title, message, tone, icon, onClose, leaving, className, ...pro
21
22
  );
22
23
  }
23
24
  function Toaster({ children, className, ...props }) {
24
- return /* @__PURE__ */ jsx("div", { className: cx("uix-toaster", className), "aria-live": "polite", "aria-atomic": "false", ...props, children });
25
+ return /* @__PURE__ */ jsx("div", { className: cx("uix-toaster", className), ...props, children });
25
26
  }
26
27
  export {
27
28
  Toast,
@@ -1,16 +1,20 @@
1
1
  import * as react from 'react';
2
2
  import { HTMLAttributes, ReactNode } from 'react';
3
+ import { Placement } from '../overlay-position.js';
3
4
 
4
5
  interface TooltipProps extends HTMLAttributes<HTMLSpanElement> {
5
- /** Tooltip text. Rendered via the CSS `[data-uix-tip]` hover/focus tooltip. */
6
+ /** Tooltip text. */
6
7
  label: string;
8
+ /** Preferred placement. Default `'top'`. */
9
+ placement?: Placement;
7
10
  children?: ReactNode;
8
11
  }
9
12
  /**
10
- * Thin wrapper over the CSS-only tooltip (`[data-uix-tip]`). Wraps its children
11
- * in a span carrying the attribute; the bubble is drawn entirely in CSS on
12
- * hover/focus. For keyboard reveal, ensure a focusable child or pass `tabIndex`.
13
+ * Tooltip whose bubble renders in the top layer via the native Popover API, so it
14
+ * escapes any `overflow: hidden/scroll` ancestor that would clip the old CSS-only
15
+ * `[data-uix-tip]` tooltip (UIX-FIX-02). Positioned by the shared overlay engine
16
+ * (flip/shift), shown on hover and keyboard focus, dismissed on blur or Escape.
13
17
  */
14
- declare function Tooltip({ label, children, ...props }: TooltipProps): react.JSX.Element;
18
+ declare function Tooltip({ label, placement, className, children, ...props }: TooltipProps): react.JSX.Element;
15
19
 
16
20
  export { Tooltip, type TooltipProps };
@@ -1,6 +1,64 @@
1
- import { jsx } from "react/jsx-runtime";
2
- function Tooltip({ label, children, ...props }) {
3
- return /* @__PURE__ */ jsx("span", { "data-uix-tip": label, ...props, children });
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { useEffect, useId, useRef, useState } from "react";
4
+ import { cx } from "../cx.js";
5
+ import { useAnchoredPosition } from "../hooks/useAnchoredPosition.js";
6
+ function Tooltip({ label, placement = "top", className, children, ...props }) {
7
+ const triggerRef = useRef(null);
8
+ const bubbleRef = useRef(null);
9
+ const [open, setOpen] = useState(false);
10
+ const id = useId();
11
+ useAnchoredPosition(triggerRef, bubbleRef, { open, placement, offset: 6 });
12
+ const show = () => {
13
+ const b = bubbleRef.current;
14
+ if (b && !b.matches(":popover-open")) {
15
+ try {
16
+ b.showPopover();
17
+ } catch {
18
+ }
19
+ }
20
+ setOpen(true);
21
+ };
22
+ const hide = () => {
23
+ const b = bubbleRef.current;
24
+ if (b && b.matches(":popover-open")) {
25
+ try {
26
+ b.hidePopover();
27
+ } catch {
28
+ }
29
+ }
30
+ setOpen(false);
31
+ };
32
+ useEffect(() => () => {
33
+ const b = bubbleRef.current;
34
+ if (b && b.matches(":popover-open")) {
35
+ try {
36
+ b.hidePopover();
37
+ } catch {
38
+ }
39
+ }
40
+ }, []);
41
+ const bubbleAttr = { popover: "manual" };
42
+ return /* @__PURE__ */ jsxs(
43
+ "span",
44
+ {
45
+ ref: triggerRef,
46
+ className: cx("uix-tooltip", className),
47
+ "aria-describedby": id,
48
+ onMouseEnter: show,
49
+ onMouseLeave: hide,
50
+ onFocus: show,
51
+ onBlur: hide,
52
+ onKeyDown: (e) => {
53
+ if (e.key === "Escape") hide();
54
+ },
55
+ ...props,
56
+ children: [
57
+ children,
58
+ /* @__PURE__ */ jsx("span", { ref: bubbleRef, id, role: "tooltip", className: "uix-tooltip__bubble", ...bubbleAttr, children: label })
59
+ ]
60
+ }
61
+ );
4
62
  }
5
63
  export {
6
64
  Tooltip
@@ -11,8 +11,11 @@ interface TreeNodeProps {
11
11
  node: TreeNodeData;
12
12
  expanded: Set<string>;
13
13
  selected?: string;
14
+ selectable: boolean;
15
+ tabbableId?: string;
14
16
  onToggle: (id: string) => void;
15
17
  onSelect?: (id: string) => void;
18
+ onFocusNode: (id: string) => void;
16
19
  level?: number;
17
20
  }
18
21
  interface TreeProps extends Omit<HTMLAttributes<HTMLUListElement>, 'onSelect' | 'onChange'> {
@@ -22,7 +25,18 @@ interface TreeProps extends Omit<HTMLAttributes<HTMLUListElement>, 'onSelect' |
22
25
  selected?: string;
23
26
  onToggle?: (id: string) => void;
24
27
  onSelect?: (id: string) => void;
28
+ /**
29
+ * Virtualize a large tree — render only the rows in view (UIX-FIX-05). Defaults to auto
30
+ * (on past the engine's `shouldVirtualize` threshold of visible rows). When on, the tree is
31
+ * flattened to a scrollable list of `maxHeight` with fixed-height rows; a11y is preserved via
32
+ * `aria-level` / `aria-setsize` / `aria-posinset` instead of nested `role="group"`s.
33
+ */
34
+ virtualize?: boolean;
35
+ /** Fixed row height (px) used for virtualization. Default 32. */
36
+ rowHeight?: number;
37
+ /** Scroll-viewport height (px) when virtualizing. Default 384. */
38
+ maxHeight?: number;
25
39
  }
26
- declare function Tree({ nodes, expanded: controlledExpanded, defaultExpanded, selected, onToggle, onSelect, className, ...props }: TreeProps): react.JSX.Element;
40
+ declare function Tree({ nodes, expanded: controlledExpanded, defaultExpanded, selected, onToggle, onSelect, virtualize, rowHeight, maxHeight, className, ...props }: TreeProps): react.JSX.Element;
27
41
 
28
42
  export { Tree, type TreeNodeData, type TreeNodeProps, type TreeProps };