@tensor_1/react 2.13.0 → 2.15.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 (39) hide show
  1. package/dist/combobox-model.d.ts +9 -0
  2. package/dist/combobox-model.js +10 -0
  3. package/dist/components/AsyncOperationStatus.d.ts +16 -0
  4. package/dist/components/AsyncOperationStatus.js +24 -0
  5. package/dist/components/Breadcrumbs.d.ts +18 -0
  6. package/dist/components/Breadcrumbs.js +14 -0
  7. package/dist/components/Card.d.ts +11 -2
  8. package/dist/components/Card.js +16 -1
  9. package/dist/components/CollapsibleSection.d.ts +11 -0
  10. package/dist/components/CollapsibleSection.js +17 -0
  11. package/dist/components/Combobox.d.ts +19 -0
  12. package/dist/components/Combobox.js +120 -0
  13. package/dist/components/DetailPage.d.ts +33 -0
  14. package/dist/components/DetailPage.js +19 -0
  15. package/dist/components/Dialogs.d.ts +35 -0
  16. package/dist/components/Dialogs.js +55 -0
  17. package/dist/components/Modal.d.ts +3 -1
  18. package/dist/components/Modal.js +6 -4
  19. package/dist/components/RelatedLinks.d.ts +16 -0
  20. package/dist/components/RelatedLinks.js +14 -0
  21. package/dist/components/RelativeTime.d.ts +23 -0
  22. package/dist/components/RelativeTime.js +40 -0
  23. package/dist/components/Sidebar.d.ts +7 -2
  24. package/dist/components/Sidebar.js +27 -9
  25. package/dist/components/States.d.ts +23 -4
  26. package/dist/components/States.js +28 -6
  27. package/dist/components/TableControls.d.ts +77 -0
  28. package/dist/components/TableControls.js +61 -0
  29. package/dist/components/ToggleRow.d.ts +14 -0
  30. package/dist/components/ToggleRow.js +16 -0
  31. package/dist/index.cjs +1277 -786
  32. package/dist/index.d.cts +280 -8
  33. package/dist/index.d.ts +14 -2
  34. package/dist/index.js +34 -2
  35. package/dist/relative-time-clock.d.ts +4 -0
  36. package/dist/relative-time-clock.js +33 -0
  37. package/dist/relative-time-model.d.ts +9 -0
  38. package/dist/relative-time-model.js +16 -0
  39. package/package.json +1 -1
@@ -0,0 +1,9 @@
1
+ interface ComboboxOption {
2
+ value: string;
3
+ label: string;
4
+ keywords?: readonly string[];
5
+ disabled?: boolean;
6
+ }
7
+ declare function filterComboboxOptions(options: readonly ComboboxOption[], query: string): ComboboxOption[];
8
+
9
+ export { type ComboboxOption, filterComboboxOptions };
@@ -0,0 +1,10 @@
1
+ function filterComboboxOptions(options, query) {
2
+ const needle = query.trim().toLocaleLowerCase();
3
+ if (!needle) return [...options];
4
+ return options.filter(
5
+ (option) => [option.label, option.value, ...option.keywords ?? []].some((part) => part.toLocaleLowerCase().includes(needle))
6
+ );
7
+ }
8
+ export {
9
+ filterComboboxOptions
10
+ };
@@ -0,0 +1,16 @@
1
+ import * as react from 'react';
2
+ import { HTMLAttributes, ReactNode } from 'react';
3
+
4
+ type AsyncOperationState = 'queued' | 'running' | 'complete' | 'failed';
5
+ interface AsyncOperationStatusProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'> {
6
+ state: AsyncOperationState;
7
+ title: ReactNode;
8
+ statusLabel: ReactNode;
9
+ description?: ReactNode;
10
+ progress?: number;
11
+ progressLabel?: string;
12
+ action?: ReactNode;
13
+ }
14
+ declare function AsyncOperationStatus({ state, title, statusLabel, description, progress, progressLabel, action, className, ...props }: AsyncOperationStatusProps): react.JSX.Element;
15
+
16
+ export { type AsyncOperationState, AsyncOperationStatus, type AsyncOperationStatusProps };
@@ -0,0 +1,24 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { cx } from "../cx.js";
3
+ import { Progress } from "./Progress.js";
4
+ import { StatusPill } from "./StatusPill.js";
5
+ const tones = {
6
+ queued: "neutral",
7
+ running: "info",
8
+ complete: "success",
9
+ failed: "danger"
10
+ };
11
+ function AsyncOperationStatus({ state, title, statusLabel, description, progress, progressLabel, action, className, ...props }) {
12
+ return /* @__PURE__ */ jsxs("div", { className: cx("uix-operation", className), role: state === "failed" ? "alert" : "status", "aria-live": "polite", ...props, children: [
13
+ /* @__PURE__ */ jsxs("div", { className: "uix-operation__head", children: [
14
+ /* @__PURE__ */ jsx("strong", { children: title }),
15
+ /* @__PURE__ */ jsx(StatusPill, { tone: tones[state], children: statusLabel })
16
+ ] }),
17
+ description != null && /* @__PURE__ */ jsx("div", { className: "uix-operation__description", children: description }),
18
+ progress != null && /* @__PURE__ */ jsx(Progress, { value: progress, "aria-label": progressLabel }),
19
+ action != null && /* @__PURE__ */ jsx("div", { className: "uix-operation__action", children: action })
20
+ ] });
21
+ }
22
+ export {
23
+ AsyncOperationStatus
24
+ };
@@ -0,0 +1,18 @@
1
+ import * as react from 'react';
2
+ import { ReactNode, HTMLAttributes } from 'react';
3
+
4
+ interface BreadcrumbItem {
5
+ id?: string;
6
+ label: ReactNode;
7
+ href?: string;
8
+ current?: boolean;
9
+ }
10
+ interface BreadcrumbsProps extends Omit<HTMLAttributes<HTMLElement>, 'children'> {
11
+ items: readonly BreadcrumbItem[];
12
+ label: string;
13
+ separator?: ReactNode;
14
+ }
15
+ /** Framework-neutral breadcrumb navigation. Route adapters can replace anchors at the application seam. */
16
+ declare function Breadcrumbs({ items, label, separator, className, ...props }: BreadcrumbsProps): react.JSX.Element;
17
+
18
+ export { type BreadcrumbItem, Breadcrumbs, type BreadcrumbsProps };
@@ -0,0 +1,14 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { Fragment } from "react";
3
+ function Breadcrumbs({ items, label, separator = "/", className, ...props }) {
4
+ return /* @__PURE__ */ jsx("nav", { "aria-label": label, className, ...props, children: /* @__PURE__ */ jsx("ol", { className: "uix-breadcrumbs", children: items.map((item, index) => {
5
+ const current = item.current ?? index === items.length - 1;
6
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
7
+ index > 0 && /* @__PURE__ */ jsx("li", { className: "uix-breadcrumbs__sep", "aria-hidden": "true", children: separator }),
8
+ /* @__PURE__ */ jsx("li", { children: item.href && !current ? /* @__PURE__ */ jsx("a", { href: item.href, children: item.label }) : /* @__PURE__ */ jsx("span", { "aria-current": current ? "page" : void 0, children: item.label }) })
9
+ ] }, item.id ?? `${item.href ?? "current"}-${index}`);
10
+ }) }) });
11
+ }
12
+ export {
13
+ Breadcrumbs
14
+ };
@@ -1,5 +1,5 @@
1
1
  import * as react from 'react';
2
- import { HTMLAttributes, ReactNode } from 'react';
2
+ import { HTMLAttributes, ReactNode, AnchorHTMLAttributes } from 'react';
3
3
 
4
4
  interface CardProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'> {
5
5
  title?: ReactNode;
@@ -9,5 +9,14 @@ interface CardProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'> {
9
9
  children?: ReactNode;
10
10
  }
11
11
  declare function Card({ title, subtitle, headerAction, footer, children, className, ...props }: CardProps): react.JSX.Element;
12
+ interface CardLinkProps extends Omit<AnchorHTMLAttributes<HTMLAnchorElement>, 'title'> {
13
+ title?: ReactNode;
14
+ subtitle?: ReactNode;
15
+ headerAction?: ReactNode;
16
+ footer?: ReactNode;
17
+ children?: ReactNode;
18
+ }
19
+ /** A whole-card anchor with visible hover and keyboard focus affordances. */
20
+ declare function CardLink({ title, subtitle, headerAction, footer, children, className, ...props }: CardLinkProps): react.JSX.Element;
12
21
 
13
- export { Card, type CardProps };
22
+ export { Card, CardLink, type CardLinkProps, type CardProps };
@@ -14,6 +14,21 @@ function Card({ title, subtitle, headerAction, footer, children, className, ...p
14
14
  footer != null && /* @__PURE__ */ jsx("div", { className: "uix-card__footer", children: footer })
15
15
  ] });
16
16
  }
17
+ function CardLink({ title, subtitle, headerAction, footer, children, className, ...props }) {
18
+ const hasHeader = title != null || subtitle != null || headerAction != null;
19
+ return /* @__PURE__ */ jsxs("a", { className: cx("uix-card", "uix-card--interactive", className), ...props, children: [
20
+ hasHeader && /* @__PURE__ */ jsxs("div", { className: "uix-card__header", children: [
21
+ /* @__PURE__ */ jsxs("div", { style: { flex: 1 }, children: [
22
+ title != null && /* @__PURE__ */ jsx("div", { className: "uix-card__title", children: title }),
23
+ subtitle != null && /* @__PURE__ */ jsx("div", { className: "uix-card__subtitle", children: subtitle })
24
+ ] }),
25
+ headerAction
26
+ ] }),
27
+ children != null && /* @__PURE__ */ jsx("div", { className: "uix-card__body", children }),
28
+ footer != null && /* @__PURE__ */ jsx("div", { className: "uix-card__footer", children: footer })
29
+ ] });
30
+ }
17
31
  export {
18
- Card
32
+ Card,
33
+ CardLink
19
34
  };
@@ -0,0 +1,11 @@
1
+ import * as react from 'react';
2
+ import { DetailsHTMLAttributes, ReactNode } from 'react';
3
+
4
+ interface CollapsibleSectionProps extends Omit<DetailsHTMLAttributes<HTMLDetailsElement>, 'title'> {
5
+ title: ReactNode;
6
+ summary?: ReactNode;
7
+ children?: ReactNode;
8
+ }
9
+ declare function CollapsibleSection({ title, summary, children, className, ...props }: CollapsibleSectionProps): react.JSX.Element;
10
+
11
+ export { CollapsibleSection, type CollapsibleSectionProps };
@@ -0,0 +1,17 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { cx } from "../cx.js";
3
+ function CollapsibleSection({ title, summary, children, className, ...props }) {
4
+ return /* @__PURE__ */ jsxs("details", { className: cx("uix-collapsible", className), ...props, children: [
5
+ /* @__PURE__ */ jsxs("summary", { className: "uix-collapsible__summary", children: [
6
+ /* @__PURE__ */ jsxs("span", { children: [
7
+ /* @__PURE__ */ jsx("span", { className: "uix-collapsible__title", children: title }),
8
+ summary != null && /* @__PURE__ */ jsx("span", { className: "uix-collapsible__meta", children: summary })
9
+ ] }),
10
+ /* @__PURE__ */ jsx("span", { className: "uix-collapsible__chevron", "aria-hidden": "true", children: "\u2304" })
11
+ ] }),
12
+ /* @__PURE__ */ jsx("div", { className: "uix-collapsible__body", children })
13
+ ] });
14
+ }
15
+ export {
16
+ CollapsibleSection
17
+ };
@@ -0,0 +1,19 @@
1
+ import * as react from 'react';
2
+ import { HTMLAttributes } from 'react';
3
+ import { ComboboxOption } from '../combobox-model.js';
4
+
5
+ interface ComboboxProps extends Omit<HTMLAttributes<HTMLDivElement>, 'onChange'> {
6
+ options: readonly ComboboxOption[];
7
+ value?: string;
8
+ defaultValue?: string;
9
+ onValueChange?: (value: string, option: ComboboxOption) => void;
10
+ label: string;
11
+ placeholder?: string;
12
+ emptyLabel: string;
13
+ name?: string;
14
+ disabled?: boolean;
15
+ }
16
+ /** Searchable single-select combobox with keyboard navigation and no framework dependencies. */
17
+ declare function Combobox({ options, value, defaultValue, onValueChange, label, placeholder, emptyLabel, name, disabled, className, ...props }: ComboboxProps): react.JSX.Element;
18
+
19
+ export { Combobox, ComboboxOption, type ComboboxProps };
@@ -0,0 +1,120 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { useEffect, useId, useMemo, useRef, useState } from "react";
4
+ import { cx } from "../cx.js";
5
+ import { filterComboboxOptions } from "../combobox-model.js";
6
+ function Combobox({
7
+ options,
8
+ value,
9
+ defaultValue,
10
+ onValueChange,
11
+ label,
12
+ placeholder,
13
+ emptyLabel,
14
+ name,
15
+ disabled,
16
+ className,
17
+ ...props
18
+ }) {
19
+ const generatedId = useId();
20
+ const listboxId = `${generatedId}-listbox`;
21
+ const rootRef = useRef(null);
22
+ const [internalValue, setInternalValue] = useState(defaultValue ?? "");
23
+ const selectedValue = value ?? internalValue;
24
+ const selected = options.find((option) => option.value === selectedValue);
25
+ const [query, setQuery] = useState(selected?.label ?? "");
26
+ const [open, setOpen] = useState(false);
27
+ const [activeIndex, setActiveIndex] = useState(0);
28
+ const filtered = useMemo(() => filterComboboxOptions(options, open ? query : ""), [open, options, query]);
29
+ const activeOption = filtered[activeIndex];
30
+ useEffect(() => {
31
+ if (!open) setQuery(options.find((option) => option.value === selectedValue)?.label ?? "");
32
+ }, [open, options, selectedValue]);
33
+ const select = (option) => {
34
+ if (option.disabled) return;
35
+ if (value == null) setInternalValue(option.value);
36
+ setQuery(option.label);
37
+ setOpen(false);
38
+ onValueChange?.(option.value, option);
39
+ };
40
+ const move = (step) => {
41
+ if (!filtered.length) return;
42
+ let next = activeIndex;
43
+ for (let attempts = 0; attempts < filtered.length; attempts += 1) {
44
+ next = (next + step + filtered.length) % filtered.length;
45
+ if (!filtered[next]?.disabled) break;
46
+ }
47
+ setActiveIndex(next);
48
+ };
49
+ const handleKeyDown = (event) => {
50
+ if (event.key === "ArrowDown" || event.key === "ArrowUp") {
51
+ event.preventDefault();
52
+ if (!open) setOpen(true);
53
+ else move(event.key === "ArrowDown" ? 1 : -1);
54
+ } else if (event.key === "Enter" && open && activeOption) {
55
+ event.preventDefault();
56
+ select(activeOption);
57
+ } else if (event.key === "Escape") {
58
+ setOpen(false);
59
+ setQuery(selected?.label ?? "");
60
+ }
61
+ };
62
+ const handleBlur = (event) => {
63
+ if (!rootRef.current?.contains(event.relatedTarget)) {
64
+ setOpen(false);
65
+ setQuery(options.find((option) => option.value === selectedValue)?.label ?? "");
66
+ }
67
+ };
68
+ const handleChange = (event) => {
69
+ setQuery(event.target.value);
70
+ setActiveIndex(0);
71
+ setOpen(true);
72
+ };
73
+ return /* @__PURE__ */ jsxs("div", { ref: rootRef, className: cx("uix-combobox", className), onBlur: handleBlur, ...props, children: [
74
+ name && /* @__PURE__ */ jsx("input", { type: "hidden", name, value: selectedValue }),
75
+ /* @__PURE__ */ jsx(
76
+ "input",
77
+ {
78
+ className: "uix-input",
79
+ role: "combobox",
80
+ "aria-label": label,
81
+ "aria-autocomplete": "list",
82
+ "aria-expanded": open,
83
+ "aria-controls": listboxId,
84
+ "aria-activedescendant": open && activeOption ? `${listboxId}-${activeIndex}` : void 0,
85
+ value: query,
86
+ placeholder,
87
+ disabled,
88
+ onChange: handleChange,
89
+ onFocus: () => {
90
+ setQuery("");
91
+ setOpen(true);
92
+ setActiveIndex(Math.max(0, options.findIndex((option) => option.value === selectedValue)));
93
+ },
94
+ onKeyDown: handleKeyDown
95
+ }
96
+ ),
97
+ open && /* @__PURE__ */ jsxs("ul", { id: listboxId, className: "uix-listbox uix-combobox__listbox", role: "listbox", children: [
98
+ filtered.length === 0 && /* @__PURE__ */ jsx("li", { className: "uix-listbox__empty", children: emptyLabel }),
99
+ filtered.map((option, index) => /* @__PURE__ */ jsx(
100
+ "li",
101
+ {
102
+ id: `${listboxId}-${index}`,
103
+ className: "uix-listbox__option",
104
+ role: "option",
105
+ "aria-selected": option.value === selectedValue,
106
+ "aria-disabled": option.disabled || void 0,
107
+ "data-active": index === activeIndex || void 0,
108
+ onMouseDown: (event) => event.preventDefault(),
109
+ onMouseEnter: () => setActiveIndex(index),
110
+ onClick: () => select(option),
111
+ children: option.label
112
+ },
113
+ option.value
114
+ ))
115
+ ] })
116
+ ] });
117
+ }
118
+ export {
119
+ Combobox
120
+ };
@@ -0,0 +1,33 @@
1
+ import * as react from 'react';
2
+ import { HTMLAttributes, ReactNode } from 'react';
3
+
4
+ interface DetailPageTab {
5
+ id: string;
6
+ label: ReactNode;
7
+ href: string;
8
+ active?: boolean;
9
+ }
10
+ interface DetailPageMetric {
11
+ id: string;
12
+ label: ReactNode;
13
+ value: ReactNode;
14
+ }
15
+ interface DetailPageProps extends Omit<HTMLAttributes<HTMLElement>, 'title'> {
16
+ back?: {
17
+ href: string;
18
+ label: ReactNode;
19
+ };
20
+ eyebrow?: ReactNode;
21
+ title: ReactNode;
22
+ subtitle?: ReactNode;
23
+ actions?: ReactNode;
24
+ tabs?: readonly DetailPageTab[];
25
+ metrics?: readonly DetailPageMetric[];
26
+ side?: ReactNode;
27
+ children?: ReactNode;
28
+ tabsLabel?: string;
29
+ }
30
+ /** Framework-neutral record-detail scaffold; routing and data policy stay with the application. */
31
+ declare function DetailPage({ back, eyebrow, title, subtitle, actions, tabs, metrics, side, children, tabsLabel, className, ...props }: DetailPageProps): react.JSX.Element;
32
+
33
+ export { DetailPage, type DetailPageMetric, type DetailPageProps, type DetailPageTab };
@@ -0,0 +1,19 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { cx } from "../cx.js";
3
+ import { DetailLayout } from "./DetailLayout.js";
4
+ import { PageHeader } from "./PageHeader.js";
5
+ function DetailPage({ back, eyebrow, title, subtitle, actions, tabs, metrics, side, children, tabsLabel, className, ...props }) {
6
+ return /* @__PURE__ */ jsxs("section", { className: cx("uix-detail-page", className), ...props, children: [
7
+ back && /* @__PURE__ */ jsx("a", { className: "uix-link uix-link--quiet uix-detail-page__back", href: back.href, children: back.label }),
8
+ /* @__PURE__ */ jsx(PageHeader, { eyebrow, title, subtitle, actions }),
9
+ metrics && metrics.length > 0 && /* @__PURE__ */ jsx("dl", { className: "uix-detail-page__metrics", children: metrics.map((metric) => /* @__PURE__ */ jsxs("div", { children: [
10
+ /* @__PURE__ */ jsx("dt", { children: metric.label }),
11
+ /* @__PURE__ */ jsx("dd", { children: metric.value })
12
+ ] }, metric.id)) }),
13
+ tabs && tabs.length > 0 && /* @__PURE__ */ jsx("nav", { className: "uix-tabs", "aria-label": tabsLabel, children: tabs.map((tab) => /* @__PURE__ */ jsx("a", { className: "uix-tab", "aria-current": tab.active ? "page" : void 0, href: tab.href, children: tab.label }, tab.id)) }),
14
+ /* @__PURE__ */ jsx(DetailLayout, { side, children })
15
+ ] });
16
+ }
17
+ export {
18
+ DetailPage
19
+ };
@@ -0,0 +1,35 @@
1
+ import * as react from 'react';
2
+ import { ReactNode } from 'react';
3
+
4
+ interface ConfirmDialogProps {
5
+ open: boolean;
6
+ title: ReactNode;
7
+ description?: ReactNode;
8
+ confirmLabel: ReactNode;
9
+ cancelLabel: ReactNode;
10
+ closeLabel: string;
11
+ onConfirm: () => void | Promise<void>;
12
+ onCancel: () => void;
13
+ pending?: boolean;
14
+ destructive?: boolean;
15
+ }
16
+ /** Generic confirmation shell. Audit, authorization and compensation policy remain application-owned. */
17
+ declare function ConfirmDialog({ open, title, description, confirmLabel, cancelLabel, closeLabel, onConfirm, onCancel, pending, destructive }: ConfirmDialogProps): react.JSX.Element;
18
+ interface PromptDialogProps {
19
+ open: boolean;
20
+ title: ReactNode;
21
+ description?: ReactNode;
22
+ inputLabel: string;
23
+ defaultValue?: string;
24
+ placeholder?: string;
25
+ submitLabel: ReactNode;
26
+ cancelLabel: ReactNode;
27
+ closeLabel: string;
28
+ onSubmit: (value: string) => void | Promise<void>;
29
+ onCancel: () => void;
30
+ validate?: (value: string) => ReactNode | undefined;
31
+ pending?: boolean;
32
+ }
33
+ declare function PromptDialog({ open, title, description, inputLabel, defaultValue, placeholder, submitLabel, cancelLabel, closeLabel, onSubmit, onCancel, validate, pending }: PromptDialogProps): react.JSX.Element;
34
+
35
+ export { ConfirmDialog, type ConfirmDialogProps, PromptDialog, type PromptDialogProps };
@@ -0,0 +1,55 @@
1
+ "use client";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
+ import { useEffect, useId, useState } from "react";
4
+ import { Button } from "./Button.js";
5
+ import { Input } from "./Input.js";
6
+ import { Modal } from "./Modal.js";
7
+ function ConfirmDialog({ open, title, description, confirmLabel, cancelLabel, closeLabel, onConfirm, onCancel, pending, destructive }) {
8
+ return /* @__PURE__ */ jsx(
9
+ Modal,
10
+ {
11
+ open,
12
+ onClose: onCancel,
13
+ title,
14
+ closeLabel,
15
+ role: "alertdialog",
16
+ footer: /* @__PURE__ */ jsxs(Fragment, { children: [
17
+ /* @__PURE__ */ jsx(Button, { type: "button", onClick: onCancel, disabled: pending, children: cancelLabel }),
18
+ /* @__PURE__ */ jsx(Button, { type: "button", variant: destructive ? "danger" : "primary", onClick: () => void onConfirm(), loading: pending, children: confirmLabel })
19
+ ] }),
20
+ children: description
21
+ }
22
+ );
23
+ }
24
+ function PromptDialog({ open, title, description, inputLabel, defaultValue = "", placeholder, submitLabel, cancelLabel, closeLabel, onSubmit, onCancel, validate, pending }) {
25
+ const inputId = useId();
26
+ const errorId = `${inputId}-error`;
27
+ const [value, setValue] = useState(defaultValue);
28
+ const [error, setError] = useState();
29
+ useEffect(() => {
30
+ if (open) {
31
+ setValue(defaultValue);
32
+ setError(void 0);
33
+ }
34
+ }, [defaultValue, open]);
35
+ const handleSubmit = (event) => {
36
+ event.preventDefault();
37
+ const issue = validate?.(value);
38
+ setError(issue);
39
+ if (issue == null) void onSubmit(value);
40
+ };
41
+ return /* @__PURE__ */ jsx(Modal, { open, onClose: onCancel, title, closeLabel, children: /* @__PURE__ */ jsxs("form", { className: "uix-prompt", onSubmit: handleSubmit, children: [
42
+ description != null && /* @__PURE__ */ jsx("div", { children: description }),
43
+ /* @__PURE__ */ jsx("label", { className: "uix-label", htmlFor: inputId, children: inputLabel }),
44
+ /* @__PURE__ */ jsx(Input, { id: inputId, value, placeholder, "aria-describedby": error ? errorId : void 0, invalid: error != null, onChange: (event) => setValue(event.currentTarget.value), autoFocus: true }),
45
+ error != null && /* @__PURE__ */ jsx("div", { id: errorId, className: "uix-field__error", role: "alert", children: error }),
46
+ /* @__PURE__ */ jsxs("div", { className: "uix-dialog__footer uix-prompt__actions", children: [
47
+ /* @__PURE__ */ jsx(Button, { type: "button", onClick: onCancel, disabled: pending, children: cancelLabel }),
48
+ /* @__PURE__ */ jsx(Button, { type: "submit", variant: "primary", loading: pending, children: submitLabel })
49
+ ] })
50
+ ] }) });
51
+ }
52
+ export {
53
+ ConfirmDialog,
54
+ PromptDialog
55
+ };
@@ -9,7 +9,9 @@ interface ModalProps {
9
9
  footer?: ReactNode;
10
10
  className?: string;
11
11
  style?: CSSProperties;
12
+ closeLabel?: string;
13
+ role?: 'dialog' | 'alertdialog';
12
14
  }
13
- declare function Modal({ open, onClose, title, children, footer, className, style }: ModalProps): react.JSX.Element;
15
+ declare function Modal({ open, onClose, title, children, footer, className, style, closeLabel, role }: ModalProps): react.JSX.Element;
14
16
 
15
17
  export { Modal, type ModalProps };
@@ -1,14 +1,16 @@
1
1
  "use client";
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { useId } from "react";
3
4
  import { cx } from "../cx.js";
4
5
  import { useDialog } from "../hooks/useDialog.js";
5
6
  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" }) });
6
- function Modal({ open, onClose, title, children, footer, className, style }) {
7
+ function Modal({ open, onClose, title, children, footer, className, style, closeLabel = "Close dialog", role = "dialog" }) {
7
8
  const ref = useDialog(open);
8
- return /* @__PURE__ */ jsxs("dialog", { ref, className: cx("uix-dialog", className), style, onClose, children: [
9
+ const titleId = useId();
10
+ return /* @__PURE__ */ jsxs("dialog", { ref, className: cx("uix-dialog", className), style, onClose, role, "aria-labelledby": title != null ? titleId : void 0, children: [
9
11
  (title != null || onClose) && /* @__PURE__ */ jsxs("div", { className: "uix-dialog__header", children: [
10
- title && /* @__PURE__ */ jsx("div", { className: "uix-dialog__title", children: title }),
11
- onClose && /* @__PURE__ */ jsx("button", { className: "uix-dialog__close", onClick: onClose, "aria-label": "Close dialog", children: /* @__PURE__ */ jsx(CloseIcon, {}) })
12
+ title && /* @__PURE__ */ jsx("div", { id: titleId, className: "uix-dialog__title", children: title }),
13
+ onClose && /* @__PURE__ */ jsx("button", { type: "button", className: "uix-dialog__close", onClick: onClose, "aria-label": closeLabel, children: /* @__PURE__ */ jsx(CloseIcon, {}) })
12
14
  ] }),
13
15
  children != null && /* @__PURE__ */ jsx("div", { className: "uix-dialog__body", children }),
14
16
  footer != null && /* @__PURE__ */ jsx("div", { className: "uix-dialog__footer", children: footer })
@@ -0,0 +1,16 @@
1
+ import * as react from 'react';
2
+ import { ReactNode, HTMLAttributes } from 'react';
3
+
4
+ interface RelatedLinkItem {
5
+ id: string;
6
+ label: ReactNode;
7
+ href: string;
8
+ meta?: ReactNode;
9
+ }
10
+ interface RelatedLinksProps extends Omit<HTMLAttributes<HTMLElement>, 'title' | 'children'> {
11
+ title?: ReactNode;
12
+ items: readonly RelatedLinkItem[];
13
+ }
14
+ declare function RelatedLinks({ title, items, className, ...props }: RelatedLinksProps): react.JSX.Element;
15
+
16
+ export { type RelatedLinkItem, RelatedLinks, type RelatedLinksProps };
@@ -0,0 +1,14 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { cx } from "../cx.js";
3
+ function RelatedLinks({ title, items, className, ...props }) {
4
+ return /* @__PURE__ */ jsxs("section", { className: cx("uix-related-links", className), ...props, children: [
5
+ title != null && /* @__PURE__ */ jsx("h2", { className: "uix-related-links__title", children: title }),
6
+ /* @__PURE__ */ jsx("ul", { className: "uix-related-links__list", children: items.map((item) => /* @__PURE__ */ jsxs("li", { children: [
7
+ /* @__PURE__ */ jsx("a", { className: "uix-link uix-link--quiet", href: item.href, children: item.label }),
8
+ item.meta != null && /* @__PURE__ */ jsx("span", { children: item.meta })
9
+ ] }, item.id)) })
10
+ ] });
11
+ }
12
+ export {
13
+ RelatedLinks
14
+ };
@@ -0,0 +1,23 @@
1
+ import * as react from 'react';
2
+ import { HTMLAttributes, ReactNode } from 'react';
3
+
4
+ interface RelativeTimeFormatContext {
5
+ date: Date;
6
+ now: Date;
7
+ locale?: string | readonly string[];
8
+ timeZone?: string;
9
+ }
10
+ interface RelativeTimeProps extends Omit<HTMLAttributes<HTMLTimeElement>, 'children' | 'dateTime'> {
11
+ value: Date | number | string;
12
+ now?: Date | number;
13
+ locale?: string | readonly string[];
14
+ timeZone?: string;
15
+ numeric?: Intl.RelativeTimeFormatNumeric;
16
+ invalidLabel?: ReactNode;
17
+ refreshInterval?: number | false;
18
+ formatter?: (context: RelativeTimeFormatContext) => ReactNode;
19
+ }
20
+ /** Locale-aware relative time with deterministic `now` support for SSR and tests. */
21
+ declare function RelativeTime({ value, now, locale, timeZone, numeric, invalidLabel, refreshInterval, formatter, title, ...props }: RelativeTimeProps): react.JSX.Element;
22
+
23
+ export { RelativeTime, type RelativeTimeFormatContext, type RelativeTimeProps };
@@ -0,0 +1,40 @@
1
+ "use client";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import { useCallback, useMemo, useSyncExternalStore } from "react";
4
+ import { parseDateValue, relativeTimeValue } from "../relative-time-model.js";
5
+ import { readRelativeClock, subscribeRelativeClock } from "../relative-time-clock.js";
6
+ function RelativeTime({
7
+ value,
8
+ now,
9
+ locale,
10
+ timeZone,
11
+ numeric = "auto",
12
+ invalidLabel = "\u2014",
13
+ refreshInterval = 3e4,
14
+ formatter,
15
+ title,
16
+ ...props
17
+ }) {
18
+ const interval = refreshInterval === false ? 0 : Math.max(0, refreshInterval);
19
+ const fixedNow = now == null ? void 0 : new Date(now).getTime();
20
+ const subscribe = useCallback((listener) => fixedNow == null ? subscribeRelativeClock(interval, listener) : () => {
21
+ }, [fixedNow, interval]);
22
+ const getSnapshot = useCallback(() => fixedNow ?? readRelativeClock(interval), [fixedNow, interval]);
23
+ const getServerSnapshot = useCallback(() => fixedNow ?? Date.now(), [fixedNow]);
24
+ const clock = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
25
+ const date = useMemo(() => parseDateValue(value), [value]);
26
+ if (!date) return /* @__PURE__ */ jsx("time", { "data-invalid": "true", suppressHydrationWarning: true, ...props, children: invalidLabel });
27
+ const current = new Date(clock);
28
+ const content = formatter ? formatter({ date, now: current, locale, timeZone }) : (() => {
29
+ const relative = relativeTimeValue(date, current);
30
+ if (relative.unit === "day" && Math.abs(relative.value) >= 7) {
31
+ return new Intl.DateTimeFormat(locale, { dateStyle: "medium", timeZone }).format(date);
32
+ }
33
+ return new Intl.RelativeTimeFormat(locale, { numeric }).format(relative.value, relative.unit);
34
+ })();
35
+ const absolute = new Intl.DateTimeFormat(locale, { dateStyle: "medium", timeStyle: "short", timeZone }).format(date);
36
+ return /* @__PURE__ */ jsx("time", { dateTime: date.toISOString(), title: title ?? absolute, suppressHydrationWarning: true, ...props, children: content });
37
+ }
38
+ export {
39
+ RelativeTime
40
+ };
@@ -3,12 +3,15 @@ import { ReactNode, AnchorHTMLAttributes, HTMLAttributes } from 'react';
3
3
 
4
4
  interface SidebarProps extends HTMLAttributes<HTMLElement> {
5
5
  collapsed?: boolean;
6
+ nav?: 'full' | 'rail';
6
7
  onToggle?: () => void;
7
8
  brand?: ReactNode;
8
9
  toggleIcon?: ReactNode;
9
10
  children?: ReactNode;
11
+ expandLabel?: string;
12
+ collapseLabel?: string;
10
13
  }
11
- declare function Sidebar({ collapsed, onToggle, brand, toggleIcon, children, className, ...props }: SidebarProps): react.JSX.Element;
14
+ declare function Sidebar({ collapsed, nav, onToggle, brand, toggleIcon, children, expandLabel, collapseLabel, className, ...props }: SidebarProps): react.JSX.Element;
12
15
  interface SidebarSectionProps {
13
16
  label?: string;
14
17
  children?: ReactNode;
@@ -27,8 +30,10 @@ interface NavGroupProps {
27
30
  label: ReactNode;
28
31
  children?: ReactNode;
29
32
  defaultExpanded?: boolean;
33
+ expanded?: boolean;
34
+ onExpandedChange?: (expanded: boolean) => void;
30
35
  }
31
- declare function NavGroup({ icon, label, children, defaultExpanded }: NavGroupProps): react.JSX.Element;
36
+ declare function NavGroup({ icon, label, children, defaultExpanded, expanded: controlledExpanded, onExpandedChange }: NavGroupProps): react.JSX.Element;
32
37
  interface SubNavItemProps extends AnchorHTMLAttributes<HTMLAnchorElement> {
33
38
  active?: boolean;
34
39
  as?: 'a' | 'button';