@tangle-network/ui 11.34.0 → 11.36.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.
- package/CHANGELOG.md +12 -0
- package/README.md +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.js +2 -1
- package/dist/lib/control-presentation.d.ts +34 -1
- package/dist/lib/control-presentation.js +1 -1
- package/dist/lib/focus.d.ts +2 -2
- package/dist/lib/focus.js +3 -2
- package/dist/lib/motion.d.ts +4 -0
- package/dist/lib/motion.js +7 -0
- package/dist/lib/surface-motion.js +84 -0
- package/dist/presentation.d.ts +88 -0
- package/dist/presentation.js +183 -0
- package/dist/primitives/alert-dialog.js +1 -1
- package/dist/primitives/alert.d.ts +1 -2
- package/dist/primitives/button.d.ts +2 -5
- package/dist/primitives/button.js +3 -30
- package/dist/primitives/card.js +3 -7
- package/dist/primitives/date-picker.d.ts +1 -1
- package/dist/primitives/dialog.js +29 -16
- package/dist/primitives/empty-state.js +6 -5
- package/dist/primitives/heading.d.ts +1 -0
- package/dist/primitives/heading.js +3 -2
- package/dist/primitives/help-text.js +2 -1
- package/dist/primitives/input.d.ts +1 -4
- package/dist/primitives/input.js +3 -24
- package/dist/primitives/label.js +1 -2
- package/dist/primitives/metric-strip.js +7 -12
- package/dist/primitives/page-header.d.ts +1 -0
- package/dist/primitives/page-header.js +7 -6
- package/dist/primitives/page-shell.d.ts +8 -2
- package/dist/primitives/page-shell.js +21 -2
- package/dist/primitives/pagination.js +1 -1
- package/dist/primitives/segmented-control.d.ts +6 -3
- package/dist/primitives/segmented-control.js +105 -56
- package/dist/primitives/sheet.js +29 -19
- package/dist/primitives/skeleton.js +1 -1
- package/dist/primitives/status-pill.d.ts +3 -1
- package/dist/primitives/status-pill.js +12 -3
- package/dist/primitives/table.js +10 -9
- package/dist/primitives/tabs.js +3 -12
- package/dist/primitives/tooltip.js +11 -3
- package/dist/primitives.d.ts +3 -2
- package/dist/primitives.js +2 -1
- package/dist/utils.d.ts +1 -1
- package/package.json +15 -5
- package/src/lib/control-presentation.ts +2 -4
- package/src/lib/focus.ts +3 -2
- package/src/lib/motion.ts +7 -0
- package/src/lib/surface-motion.tsx +78 -0
- package/src/presentation.ts +205 -0
- package/src/primitives/alert.tsx +1 -2
- package/src/primitives/button.tsx +4 -40
- package/src/primitives/card.tsx +5 -10
- package/src/primitives/checkbox-separator-sheet-tooltip.test.tsx +2 -1
- package/src/primitives/control-presentation.test.tsx +2 -7
- package/src/primitives/dialog.tsx +19 -18
- package/src/primitives/empty-state.tsx +6 -5
- package/src/primitives/heading.tsx +4 -2
- package/src/primitives/help-text.tsx +3 -2
- package/src/primitives/input.tsx +3 -31
- package/src/primitives/label.tsx +2 -4
- package/src/primitives/metric-strip.tsx +8 -17
- package/src/primitives/page-header.tsx +8 -5
- package/src/primitives/page-shell.tsx +13 -2
- package/src/primitives/segmented-control.stories.tsx +89 -0
- package/src/primitives/segmented-control.test.tsx +20 -7
- package/src/primitives/segmented-control.tsx +90 -29
- package/src/primitives/sheet.tsx +16 -11
- package/src/primitives/skeleton.tsx +1 -1
- package/src/primitives/status-pill.test.tsx +1 -0
- package/src/primitives/status-pill.tsx +10 -4
- package/src/primitives/table.tsx +10 -9
- package/src/primitives/tabs.tsx +5 -17
- package/src/primitives/tooltip.tsx +6 -5
- package/src/styles/compat.css +12 -0
- package/src/styles/presentation.css +30 -0
- package/src/primitives/page-shell.test.tsx +0 -24
|
@@ -5,14 +5,39 @@ import { cn } from "../lib/utils.js";
|
|
|
5
5
|
import "../lib/control-presentation.js";
|
|
6
6
|
import * as React from "react";
|
|
7
7
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
|
+
import { ChevronLeft, ChevronRight } from "lucide-react";
|
|
8
9
|
//#region src/primitives/segmented-control.tsx
|
|
9
|
-
/**
|
|
10
|
-
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
10
|
+
/** Size of the edge chevron, in px. The selected option is kept this far
|
|
11
|
+
* (plus the strip's own padding) from an edge so the chevron never covers it. */
|
|
12
|
+
const SCROLL_CUE_SIZE = 32;
|
|
13
|
+
/**
|
|
14
|
+
* A chevron pinned to one edge of the scrolling strip. It is a sticky flex
|
|
15
|
+
* item with a negative margin equal to its width plus the gap, so it takes no room in the
|
|
16
|
+
* row (showing it never reflows the options) and stays at the visible edge
|
|
17
|
+
* while the strip scrolls. A solid surface keeps it legible on any plane,
|
|
18
|
+
* which an edge fade is not: a fade over the gap between two options, or over
|
|
19
|
+
* a host's inline padding, shows nothing.
|
|
20
|
+
*/
|
|
21
|
+
function ScrollCue({ side, onPage }) {
|
|
22
|
+
const Icon = side === "start" ? ChevronLeft : ChevronRight;
|
|
23
|
+
return /* @__PURE__ */ jsx("button", {
|
|
24
|
+
type: "button",
|
|
25
|
+
tabIndex: -1,
|
|
26
|
+
"aria-hidden": "true",
|
|
27
|
+
"data-scroll-cue": side,
|
|
28
|
+
onClick: onPage,
|
|
29
|
+
className: cn("sticky z-[1] flex shrink-0 items-center justify-center self-center rounded-full border border-border bg-card text-muted-foreground shadow-sm transition-colors hover:text-foreground", side === "start" ? "left-0" : "right-0"),
|
|
30
|
+
style: {
|
|
31
|
+
width: SCROLL_CUE_SIZE,
|
|
32
|
+
height: SCROLL_CUE_SIZE,
|
|
33
|
+
[side === "start" ? "marginRight" : "marginLeft"]: -36
|
|
34
|
+
},
|
|
35
|
+
children: /* @__PURE__ */ jsx(Icon, {
|
|
36
|
+
className: "h-4 w-4",
|
|
37
|
+
"aria-hidden": "true"
|
|
38
|
+
})
|
|
39
|
+
});
|
|
40
|
+
}
|
|
16
41
|
function SegmentedControl({ value, onValueChange, options, variant = "row", className, ...rest }) {
|
|
17
42
|
if (typeof process !== "undefined" && process?.env?.NODE_ENV !== "production" && !rest["aria-label"] && !rest["aria-labelledby"]) console.warn("[SegmentedControl] role=\"radiogroup\" requires either aria-label or aria-labelledby for accessibility.");
|
|
18
43
|
const optionRefs = React.useRef(/* @__PURE__ */ new Map());
|
|
@@ -21,7 +46,7 @@ function SegmentedControl({ value, onValueChange, options, variant = "row", clas
|
|
|
21
46
|
start: false,
|
|
22
47
|
end: false
|
|
23
48
|
});
|
|
24
|
-
const
|
|
49
|
+
const optionKey = options.map((o) => o.value).join("\0");
|
|
25
50
|
const measureOverflow = React.useCallback(() => {
|
|
26
51
|
const strip = stripRef.current;
|
|
27
52
|
if (!strip) return;
|
|
@@ -32,31 +57,45 @@ function SegmentedControl({ value, onValueChange, options, variant = "row", clas
|
|
|
32
57
|
end
|
|
33
58
|
});
|
|
34
59
|
}, []);
|
|
35
|
-
React.
|
|
36
|
-
|
|
60
|
+
const valueRef = React.useRef(value);
|
|
61
|
+
valueRef.current = value;
|
|
62
|
+
const revealSelected = React.useCallback(() => {
|
|
37
63
|
const strip = stripRef.current;
|
|
38
|
-
const option = optionRefs.current.get(
|
|
39
|
-
if (strip && option) {
|
|
64
|
+
const option = optionRefs.current.get(valueRef.current);
|
|
65
|
+
if (strip && option && strip.scrollWidth > strip.clientWidth) {
|
|
66
|
+
const style = getComputedStyle(strip);
|
|
67
|
+
const reserveStart = 36 + (Number.parseFloat(style.paddingLeft) || 0);
|
|
68
|
+
const reserveEnd = 36 + (Number.parseFloat(style.paddingRight) || 0);
|
|
40
69
|
const start = option.offsetLeft;
|
|
41
70
|
const end = start + option.offsetWidth;
|
|
42
|
-
if (start < strip.scrollLeft
|
|
43
|
-
else if (end > strip.scrollLeft + strip.clientWidth
|
|
71
|
+
if (start - reserveStart < strip.scrollLeft) strip.scrollLeft = Math.max(0, start - reserveStart);
|
|
72
|
+
else if (end + reserveEnd > strip.scrollLeft + strip.clientWidth) strip.scrollLeft = end + reserveEnd - strip.clientWidth;
|
|
44
73
|
}
|
|
45
74
|
measureOverflow();
|
|
46
|
-
}, [
|
|
47
|
-
|
|
75
|
+
}, [measureOverflow]);
|
|
76
|
+
React.useLayoutEffect(revealSelected, [
|
|
48
77
|
value,
|
|
49
78
|
options.length,
|
|
50
|
-
|
|
79
|
+
revealSelected
|
|
51
80
|
]);
|
|
52
81
|
React.useEffect(() => {
|
|
53
|
-
if (
|
|
82
|
+
if (typeof ResizeObserver === "undefined") return;
|
|
54
83
|
const strip = stripRef.current;
|
|
55
84
|
if (!strip) return;
|
|
56
|
-
const observer = new ResizeObserver(
|
|
85
|
+
const observer = new ResizeObserver(revealSelected);
|
|
57
86
|
observer.observe(strip);
|
|
87
|
+
for (const option of optionRefs.current.values()) observer.observe(option);
|
|
58
88
|
return () => observer.disconnect();
|
|
59
|
-
}, [
|
|
89
|
+
}, [optionKey, revealSelected]);
|
|
90
|
+
const page = (direction) => {
|
|
91
|
+
const strip = stripRef.current;
|
|
92
|
+
if (!strip) return;
|
|
93
|
+
const reduce = typeof window !== "undefined" && window.matchMedia?.("(prefers-reduced-motion: reduce)").matches;
|
|
94
|
+
strip.scrollBy({
|
|
95
|
+
left: direction * strip.clientWidth * .75,
|
|
96
|
+
behavior: reduce ? "auto" : "smooth"
|
|
97
|
+
});
|
|
98
|
+
};
|
|
60
99
|
const hasMatch = options.some((o) => o.value === value);
|
|
61
100
|
const handleKeyDown = (e) => {
|
|
62
101
|
if (options.length === 0) return;
|
|
@@ -73,7 +112,7 @@ function SegmentedControl({ value, onValueChange, options, variant = "row", clas
|
|
|
73
112
|
optionRefs.current.get(options[next].value)?.focus();
|
|
74
113
|
}
|
|
75
114
|
};
|
|
76
|
-
return /* @__PURE__ */
|
|
115
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
77
116
|
role: "radiogroup",
|
|
78
117
|
id: rest.id,
|
|
79
118
|
"aria-label": rest["aria-label"],
|
|
@@ -83,41 +122,51 @@ function SegmentedControl({ value, onValueChange, options, variant = "row", clas
|
|
|
83
122
|
"data-overflow-end": overflow.end || void 0,
|
|
84
123
|
ref: stripRef,
|
|
85
124
|
onKeyDown: handleKeyDown,
|
|
86
|
-
onScroll:
|
|
87
|
-
className: cn("flex gap-1", variant === "row" && "flex-wrap items-center rounded-lg border border-[var(--field-border)] bg-[var(--well)] p-1", variant === "tabs" &&
|
|
88
|
-
children:
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
125
|
+
onScroll: measureOverflow,
|
|
126
|
+
className: cn("relative flex gap-1", variant === "row" && "flex-wrap items-center rounded-lg border border-[var(--field-border)] bg-[var(--well)] p-1", variant === "tabs" && "flex-nowrap items-end border-b border-border pb-0 overflow-x-auto", className),
|
|
127
|
+
children: [
|
|
128
|
+
overflow.start && /* @__PURE__ */ jsx(ScrollCue, {
|
|
129
|
+
side: "start",
|
|
130
|
+
onPage: () => page(-1)
|
|
131
|
+
}),
|
|
132
|
+
options.map((option, i) => {
|
|
133
|
+
const active = option.value === value;
|
|
134
|
+
const focusable = active || !hasMatch && i === 0;
|
|
135
|
+
return /* @__PURE__ */ jsxs("button", {
|
|
136
|
+
ref: (el) => {
|
|
137
|
+
if (el) optionRefs.current.set(option.value, el);
|
|
138
|
+
else optionRefs.current.delete(option.value);
|
|
139
|
+
},
|
|
140
|
+
type: "button",
|
|
141
|
+
role: "radio",
|
|
142
|
+
"aria-checked": active,
|
|
143
|
+
"data-testid": option.testId,
|
|
144
|
+
tabIndex: focusable ? 0 : -1,
|
|
145
|
+
onClick: () => {
|
|
146
|
+
if (!active) onValueChange(option.value);
|
|
147
|
+
},
|
|
148
|
+
className: cn("relative inline-flex items-center gap-2 whitespace-nowrap text-sm font-medium transition-[color,background-color,border-color,box-shadow]", variant === "row" && [
|
|
149
|
+
"rounded-md border border-transparent px-3 py-1.5",
|
|
150
|
+
focusRing,
|
|
151
|
+
"hover:bg-[var(--ix-hover-bg)] active:bg-[var(--ix-sel-bg)]",
|
|
152
|
+
"aria-pressed:bg-[var(--ix-sel-bg)] aria-pressed:text-[var(--accent-text)] aria-pressed:shadow-[inset_0_0_0_1px_var(--ix-sel-ring)] aria-checked:bg-[var(--ix-sel-bg)] aria-checked:text-[var(--accent-text)] aria-checked:shadow-[inset_0_0_0_1px_var(--ix-sel-ring)] aria-[current=page]:bg-[var(--ix-sel-bg)] aria-[current=page]:text-[var(--accent-text)] aria-[current=page]:shadow-[inset_0_0_0_1px_var(--ix-sel-ring)] data-[state=on]:bg-[var(--ix-sel-bg)] data-[state=on]:shadow-[inset_0_0_0_1px_var(--ix-sel-ring)] data-[state=active]:bg-[var(--ix-sel-bg)] data-[state=active]:text-[var(--accent-text)] data-[state=active]:shadow-[inset_0_0_0_1px_var(--ix-sel-ring)]"
|
|
153
|
+
], variant === "tabs" && [
|
|
154
|
+
"shrink-0 rounded-none border-b-2 border-transparent -mb-px px-4 py-2",
|
|
155
|
+
"pointer-coarse:min-h-11",
|
|
156
|
+
focusRingInset,
|
|
157
|
+
"hover:shadow-[inset_0_-2px_0_hsl(var(--hsl-ring)/0.4)] data-[state=active]:bg-[hsl(var(--hsl-ring)/0.07)] data-[state=active]:text-[var(--accent-text)] data-[state=active]:shadow-[inset_0_-2px_0_var(--ix-sel-ring)] aria-checked:bg-[hsl(var(--hsl-ring)/0.07)] aria-checked:text-[var(--accent-text)] aria-checked:shadow-[inset_0_-2px_0_var(--ix-sel-ring)]"
|
|
158
|
+
], active ? "text-[var(--accent-text)]" : "text-muted-foreground hover:text-foreground"),
|
|
159
|
+
children: [/* @__PURE__ */ jsx("span", { children: option.label }), option.adornment && /* @__PURE__ */ jsx("span", {
|
|
160
|
+
className: cn("text-xs", active ? "text-[var(--accent-text)]/80" : "text-muted-foreground"),
|
|
161
|
+
children: option.adornment
|
|
162
|
+
})]
|
|
163
|
+
}, option.value);
|
|
164
|
+
}),
|
|
165
|
+
overflow.end && /* @__PURE__ */ jsx(ScrollCue, {
|
|
166
|
+
side: "end",
|
|
167
|
+
onPage: () => page(1)
|
|
168
|
+
})
|
|
169
|
+
]
|
|
121
170
|
});
|
|
122
171
|
}
|
|
123
172
|
//#endregion
|
package/dist/primitives/sheet.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
import { surfaceMotion } from "../lib/motion.js";
|
|
2
3
|
import { focusRing } from "../lib/focus.js";
|
|
3
4
|
import { cn } from "../lib/utils.js";
|
|
5
|
+
import { SurfaceMotion } from "../lib/surface-motion.js";
|
|
4
6
|
import { cva } from "class-variance-authority";
|
|
5
7
|
import * as React from "react";
|
|
6
8
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -12,36 +14,44 @@ const Sheet = SheetPrimitive.Root;
|
|
|
12
14
|
const SheetTrigger = SheetPrimitive.Trigger;
|
|
13
15
|
const SheetClose = SheetPrimitive.Close;
|
|
14
16
|
const SheetPortal = SheetPrimitive.Portal;
|
|
15
|
-
const SheetOverlay = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(SheetPrimitive.Overlay, {
|
|
17
|
+
const SheetOverlay = React.forwardRef(({ className, asChild, children, ...props }, ref) => /* @__PURE__ */ jsx(SheetPrimitive.Overlay, {
|
|
16
18
|
ref,
|
|
17
|
-
className: cn("fixed inset-0 z-50 bg-black/40",
|
|
18
|
-
...props
|
|
19
|
+
className: cn("fixed inset-0 z-50 bg-black/40", surfaceMotion, className),
|
|
20
|
+
...props,
|
|
21
|
+
asChild: true,
|
|
22
|
+
children: /* @__PURE__ */ jsx(SurfaceMotion, {
|
|
23
|
+
kind: "overlay",
|
|
24
|
+
asChild,
|
|
25
|
+
children
|
|
26
|
+
})
|
|
19
27
|
}));
|
|
20
28
|
SheetOverlay.displayName = SheetPrimitive.Overlay.displayName;
|
|
21
|
-
const sheetVariants = cva([
|
|
22
|
-
"fixed z-50 flex flex-col gap-4 border-border bg-card shadow-lg",
|
|
23
|
-
"data-[state=closed]:animate-out data-[state=open]:animate-in",
|
|
24
|
-
"data-[state=closed]:duration-200 data-[state=open]:duration-300 ease-out motion-reduce:animate-none"
|
|
25
|
-
], {
|
|
29
|
+
const sheetVariants = cva(["fixed z-50 flex flex-col gap-4 border-border bg-card shadow-lg", surfaceMotion], {
|
|
26
30
|
variants: { side: {
|
|
27
|
-
right: "inset-y-0 right-0 h-full w-3/4 border-l
|
|
28
|
-
left: "inset-y-0 left-0 h-full w-3/4 border-r
|
|
29
|
-
top: "inset-x-0 top-0 h-auto border-b
|
|
30
|
-
bottom: "inset-x-0 bottom-0 h-auto border-t
|
|
31
|
+
right: "inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm",
|
|
32
|
+
left: "inset-y-0 left-0 h-full w-3/4 border-r sm:max-w-sm",
|
|
33
|
+
top: "inset-x-0 top-0 h-auto border-b",
|
|
34
|
+
bottom: "inset-x-0 bottom-0 h-auto border-t"
|
|
31
35
|
} },
|
|
32
36
|
defaultVariants: { side: "right" }
|
|
33
37
|
});
|
|
34
|
-
const SheetContent = React.forwardRef(({ className, children, side = "right", hideCloseButton = false, ...props }, ref) => /* @__PURE__ */ jsxs(SheetPortal, { children: [/* @__PURE__ */ jsx(SheetOverlay, {}), /* @__PURE__ */
|
|
38
|
+
const SheetContent = React.forwardRef(({ className, children, side = "right", hideCloseButton = false, asChild, ...props }, ref) => /* @__PURE__ */ jsxs(SheetPortal, { children: [/* @__PURE__ */ jsx(SheetOverlay, {}), /* @__PURE__ */ jsx(SheetPrimitive.Content, {
|
|
35
39
|
ref,
|
|
36
40
|
className: cn(sheetVariants({ side }), className),
|
|
37
41
|
...props,
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
42
|
+
asChild: true,
|
|
43
|
+
children: /* @__PURE__ */ jsxs(SurfaceMotion, {
|
|
44
|
+
kind: "sheet",
|
|
45
|
+
side: side ?? "right",
|
|
46
|
+
asChild,
|
|
47
|
+
children: [children, !hideCloseButton && /* @__PURE__ */ jsxs(SheetPrimitive.Close, {
|
|
48
|
+
className: cn("absolute top-4 right-4 rounded-sm text-muted-foreground opacity-70 transition-[opacity,box-shadow] hover:opacity-100 focus-visible:opacity-100 disabled:pointer-events-none", "[transition:color_var(--motion-control),background-color_var(--motion-control),border-color_var(--motion-control),box-shadow_var(--motion-control),transform_var(--motion-control),scale_var(--motion-control),opacity_var(--motion-control)] motion-reduce:transition-none", focusRing),
|
|
49
|
+
children: [/* @__PURE__ */ jsx(X, { className: "size-4" }), /* @__PURE__ */ jsx("span", {
|
|
50
|
+
className: "sr-only",
|
|
51
|
+
children: "Close"
|
|
52
|
+
})]
|
|
43
53
|
})]
|
|
44
|
-
})
|
|
54
|
+
})
|
|
45
55
|
})] }));
|
|
46
56
|
SheetContent.displayName = SheetPrimitive.Content.displayName;
|
|
47
57
|
const SheetHeader = ({ className, ...props }) => /* @__PURE__ */ jsx("div", {
|
|
@@ -3,7 +3,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
//#region src/primitives/skeleton.tsx
|
|
4
4
|
function Skeleton({ className, ...props }) {
|
|
5
5
|
return /* @__PURE__ */ jsx("div", {
|
|
6
|
-
className: cn("animate-pulse rounded-lg bg-muted/50", className),
|
|
6
|
+
className: cn("animate-pulse motion-reduce:animate-none rounded-lg bg-muted/50", className),
|
|
7
7
|
...props
|
|
8
8
|
});
|
|
9
9
|
}
|
|
@@ -19,7 +19,7 @@ import * as React from "react";
|
|
|
19
19
|
* that always brings its own background cannot be placed onto a plane that
|
|
20
20
|
* breaks it.
|
|
21
21
|
*/
|
|
22
|
-
export type StatusTone = "success" | "warning" | "danger" | "info" | "neutral" | "running";
|
|
22
|
+
export type StatusTone = "success" | "warning" | "danger" | "info" | "neutral" | "running" | "violet";
|
|
23
23
|
export interface StatusPillProps extends Omit<React.HTMLAttributes<HTMLSpanElement>, "children"> {
|
|
24
24
|
tone: StatusTone;
|
|
25
25
|
children: React.ReactNode;
|
|
@@ -37,6 +37,8 @@ export interface StatusPillProps extends Omit<React.HTMLAttributes<HTMLSpanEleme
|
|
|
37
37
|
* For a control that supplies its own surface (a chip, a selected row).
|
|
38
38
|
*/
|
|
39
39
|
bare?: boolean;
|
|
40
|
+
/** Decorative caller glyph; the visible text remains the accessible status. */
|
|
41
|
+
icon?: React.ReactNode;
|
|
40
42
|
size?: "sm" | "md";
|
|
41
43
|
}
|
|
42
44
|
declare const StatusPill: React.ForwardRefExoticComponent<StatusPillProps & React.RefAttributes<HTMLSpanElement>>;
|
|
@@ -3,6 +3,7 @@ import * as React from "react";
|
|
|
3
3
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
4
|
//#region src/primitives/status-pill.tsx
|
|
5
5
|
const TONE_SURFACE = {
|
|
6
|
+
violet: "bg-[var(--surface-violet-bg)] text-[var(--surface-violet-text)] border-[var(--surface-violet-border)]",
|
|
6
7
|
success: "bg-[var(--surface-success-bg)] text-[var(--surface-success-text)] border-[var(--surface-success-border)]",
|
|
7
8
|
warning: "bg-[var(--surface-warning-bg)] text-[var(--surface-warning-text)] border-[var(--surface-warning-border)]",
|
|
8
9
|
danger: "bg-[var(--surface-danger-bg)] text-[var(--surface-danger-text)] border-[var(--surface-danger-border)]",
|
|
@@ -18,6 +19,7 @@ const TONE_SURFACE = {
|
|
|
18
19
|
* has left — the glyph — with nothing to signal that it happened.
|
|
19
20
|
*/
|
|
20
21
|
const TONE_TEXT = {
|
|
22
|
+
violet: "text-[var(--surface-violet-text)]",
|
|
21
23
|
success: "text-[var(--surface-success-text)]",
|
|
22
24
|
warning: "text-[var(--surface-warning-text)]",
|
|
23
25
|
danger: "text-[var(--surface-danger-text)]",
|
|
@@ -35,6 +37,12 @@ function ToneGlyph({ tone }) {
|
|
|
35
37
|
className: "size-2 shrink-0"
|
|
36
38
|
};
|
|
37
39
|
switch (tone) {
|
|
40
|
+
case "violet": return /* @__PURE__ */ jsx("svg", {
|
|
41
|
+
...common,
|
|
42
|
+
"aria-hidden": "true",
|
|
43
|
+
fill: "currentColor",
|
|
44
|
+
children: /* @__PURE__ */ jsx("path", { d: "M4 .5 7.5 4 4 7.5 .5 4Z" })
|
|
45
|
+
});
|
|
38
46
|
case "success": return /* @__PURE__ */ jsx("svg", {
|
|
39
47
|
...common,
|
|
40
48
|
"aria-hidden": "true",
|
|
@@ -99,13 +107,14 @@ function ToneGlyph({ tone }) {
|
|
|
99
107
|
});
|
|
100
108
|
}
|
|
101
109
|
}
|
|
102
|
-
const StatusPill = React.forwardRef(({ className, tone, children, bare = false, size = "sm", ...props }, ref) => /* @__PURE__ */ jsxs("span", {
|
|
110
|
+
const StatusPill = React.forwardRef(({ className, tone, children, icon, bare = false, size = "sm", ...props }, ref) => /* @__PURE__ */ jsxs("span", {
|
|
103
111
|
ref,
|
|
104
112
|
className: cn("inline-flex items-center gap-1.5 whitespace-nowrap rounded-full font-medium", size === "sm" ? "px-2 py-0.5 text-xs" : "px-2.5 py-1 text-sm", bare ? "border border-transparent bg-transparent px-0" : cn("border", TONE_SURFACE[tone]), className),
|
|
105
113
|
...props,
|
|
106
114
|
children: [/* @__PURE__ */ jsx("span", {
|
|
107
|
-
|
|
108
|
-
|
|
115
|
+
"aria-hidden": "true",
|
|
116
|
+
className: cn("inline-flex [&>svg]:size-3 [&>svg]:shrink-0", bare && TONE_TEXT[tone]),
|
|
117
|
+
children: icon ?? /* @__PURE__ */ jsx(ToneGlyph, { tone })
|
|
109
118
|
}), children]
|
|
110
119
|
}));
|
|
111
120
|
StatusPill.displayName = "StatusPill";
|
package/dist/primitives/table.js
CHANGED
|
@@ -1,61 +1,62 @@
|
|
|
1
1
|
import { cn } from "../lib/utils.js";
|
|
2
|
+
import { tablePresentation } from "../presentation.js";
|
|
2
3
|
import * as React from "react";
|
|
3
4
|
import { jsx } from "react/jsx-runtime";
|
|
4
5
|
//#region src/primitives/table.tsx
|
|
5
6
|
const Table = React.forwardRef(({ className, wrapper = true, wrapperProps, ...props }, ref) => {
|
|
6
7
|
const table = /* @__PURE__ */ jsx("table", {
|
|
7
8
|
ref,
|
|
8
|
-
className: cn(
|
|
9
|
+
className: cn(tablePresentation.root, className),
|
|
9
10
|
...props
|
|
10
11
|
});
|
|
11
12
|
if (!wrapper) return table;
|
|
12
13
|
const { className: wrapperClassName, ...attributes } = wrapperProps ?? {};
|
|
13
14
|
return /* @__PURE__ */ jsx("div", {
|
|
14
15
|
...attributes,
|
|
15
|
-
className: cn(
|
|
16
|
+
className: cn(tablePresentation.wrapper, wrapperClassName),
|
|
16
17
|
children: table
|
|
17
18
|
});
|
|
18
19
|
});
|
|
19
20
|
Table.displayName = "Table";
|
|
20
21
|
const TableHeader = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("thead", {
|
|
21
22
|
ref,
|
|
22
|
-
className: cn(
|
|
23
|
+
className: cn(tablePresentation.head, className),
|
|
23
24
|
...props
|
|
24
25
|
}));
|
|
25
26
|
TableHeader.displayName = "TableHeader";
|
|
26
27
|
const TableBody = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("tbody", {
|
|
27
28
|
ref,
|
|
28
|
-
className: cn(
|
|
29
|
+
className: cn(tablePresentation.body, className),
|
|
29
30
|
...props
|
|
30
31
|
}));
|
|
31
32
|
TableBody.displayName = "TableBody";
|
|
32
33
|
const TableFooter = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("tfoot", {
|
|
33
34
|
ref,
|
|
34
|
-
className: cn(
|
|
35
|
+
className: cn(tablePresentation.footer, className),
|
|
35
36
|
...props
|
|
36
37
|
}));
|
|
37
38
|
TableFooter.displayName = "TableFooter";
|
|
38
39
|
const TableRow = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("tr", {
|
|
39
40
|
ref,
|
|
40
|
-
className: cn(
|
|
41
|
+
className: cn(tablePresentation.row, className),
|
|
41
42
|
...props
|
|
42
43
|
}));
|
|
43
44
|
TableRow.displayName = "TableRow";
|
|
44
45
|
const TableHead = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("th", {
|
|
45
46
|
ref,
|
|
46
|
-
className: cn(
|
|
47
|
+
className: cn(tablePresentation.columnHeader, className),
|
|
47
48
|
...props
|
|
48
49
|
}));
|
|
49
50
|
TableHead.displayName = "TableHead";
|
|
50
51
|
const TableCell = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("td", {
|
|
51
52
|
ref,
|
|
52
|
-
className: cn(
|
|
53
|
+
className: cn(tablePresentation.cell, className),
|
|
53
54
|
...props
|
|
54
55
|
}));
|
|
55
56
|
TableCell.displayName = "TableCell";
|
|
56
57
|
const TableCaption = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("caption", {
|
|
57
58
|
ref,
|
|
58
|
-
className: cn(
|
|
59
|
+
className: cn(tablePresentation.caption, className),
|
|
59
60
|
...props
|
|
60
61
|
}));
|
|
61
62
|
TableCaption.displayName = "TableCaption";
|
package/dist/primitives/tabs.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { ixControlSoft, ixSelected, ixTabUnderline } from "../lib/interaction.js";
|
|
2
1
|
import { focusRing } from "../lib/focus.js";
|
|
3
2
|
import { cn } from "../lib/utils.js";
|
|
3
|
+
import { tabsPresentation } from "../presentation.js";
|
|
4
4
|
import * as React from "react";
|
|
5
5
|
import { jsx } from "react/jsx-runtime";
|
|
6
6
|
import * as TabsPrimitive from "@radix-ui/react-tabs";
|
|
@@ -9,24 +9,15 @@ const Tabs = TabsPrimitive.Root;
|
|
|
9
9
|
const TabsList = React.forwardRef(({ className, variant = "default", ...props }, ref) => {
|
|
10
10
|
return /* @__PURE__ */ jsx(TabsPrimitive.List, {
|
|
11
11
|
ref,
|
|
12
|
-
className: cn(
|
|
13
|
-
default: "inline-flex h-10 items-center justify-center rounded-lg bg-muted p-1 text-muted-foreground",
|
|
14
|
-
pills: "inline-flex items-center gap-2",
|
|
15
|
-
underline: "inline-flex items-center gap-4 border-b border-border"
|
|
16
|
-
}[variant], className),
|
|
12
|
+
className: cn(tabsPresentation.list[variant], className),
|
|
17
13
|
...props
|
|
18
14
|
});
|
|
19
15
|
});
|
|
20
16
|
TabsList.displayName = TabsPrimitive.List.displayName;
|
|
21
17
|
const TabsTrigger = React.forwardRef(({ className, variant = "default", ...props }, ref) => {
|
|
22
|
-
const variants = {
|
|
23
|
-
default: `inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1.5 text-sm font-medium transition-all ${focusRing} disabled:pointer-events-none disabled:opacity-50 ${ixControlSoft} ${ixSelected}`,
|
|
24
|
-
pills: `inline-flex items-center justify-center whitespace-nowrap rounded-full px-4 py-2 text-sm font-medium transition-all ${focusRing} disabled:pointer-events-none disabled:opacity-50 data-[state=inactive]:text-muted-foreground ${ixControlSoft} ${ixSelected}`,
|
|
25
|
-
underline: `inline-flex items-center justify-center whitespace-nowrap rounded-t-md pb-3 text-sm font-medium transition-all ${focusRing} disabled:pointer-events-none disabled:opacity-50 border-b-2 border-transparent data-[state=inactive]:text-muted-foreground ${ixTabUnderline}`
|
|
26
|
-
};
|
|
27
18
|
return /* @__PURE__ */ jsx(TabsPrimitive.Trigger, {
|
|
28
19
|
ref,
|
|
29
|
-
className: cn(
|
|
20
|
+
className: cn(tabsPresentation.trigger[variant], className),
|
|
30
21
|
...props
|
|
31
22
|
});
|
|
32
23
|
});
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
import { surfaceMotion } from "../lib/motion.js";
|
|
2
3
|
import { cn } from "../lib/utils.js";
|
|
4
|
+
import { SurfaceMotion } from "../lib/surface-motion.js";
|
|
3
5
|
import * as React from "react";
|
|
4
6
|
import { jsx } from "react/jsx-runtime";
|
|
5
7
|
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
|
|
@@ -21,11 +23,17 @@ const TooltipTrigger = TooltipPrimitive.Trigger;
|
|
|
21
23
|
* Inverse-surface label for a control whose name is otherwise only an icon.
|
|
22
24
|
* It is supplementary: the trigger still needs its own accessible name.
|
|
23
25
|
*/
|
|
24
|
-
const TooltipContent = React.forwardRef(({ className, sideOffset = 6, ...props }, ref) => /* @__PURE__ */ jsx(TooltipPrimitive.Portal, { children: /* @__PURE__ */ jsx(TooltipPrimitive.Content, {
|
|
26
|
+
const TooltipContent = React.forwardRef(({ className, sideOffset = 6, asChild, children, ...props }, ref) => /* @__PURE__ */ jsx(TooltipPrimitive.Portal, { children: /* @__PURE__ */ jsx(TooltipPrimitive.Content, {
|
|
25
27
|
ref,
|
|
26
28
|
sideOffset,
|
|
27
|
-
className: cn("z-50 max-w-xs origin-(--radix-tooltip-content-transform-origin) rounded-md bg-foreground px-2.5 py-1.5 text-background text-xs leading-snug shadow-md",
|
|
28
|
-
...props
|
|
29
|
+
className: cn("z-50 max-w-xs origin-(--radix-tooltip-content-transform-origin) rounded-md bg-foreground px-2.5 py-1.5 text-background text-xs leading-snug shadow-md", surfaceMotion, className),
|
|
30
|
+
...props,
|
|
31
|
+
asChild: true,
|
|
32
|
+
children: /* @__PURE__ */ jsx(SurfaceMotion, {
|
|
33
|
+
kind: "tooltip",
|
|
34
|
+
asChild,
|
|
35
|
+
children
|
|
36
|
+
})
|
|
29
37
|
}) }));
|
|
30
38
|
TooltipContent.displayName = TooltipPrimitive.Content.displayName;
|
|
31
39
|
//#endregion
|
package/dist/primitives.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { buttonVariants } from "./presentation.js";
|
|
2
|
+
import { Button, ButtonProps } from "./primitives/button.js";
|
|
2
3
|
import { ArtifactPane, ArtifactPaneProps } from "./primitives/artifact-pane.js";
|
|
3
4
|
import { CATEGORY_TONES, CategoryTone, TONE_CLASSES, Tone, ToneClasses, toneFor } from "./primitives/tone.js";
|
|
4
5
|
import { IconTile, IconTileProps, initialsOf } from "./primitives/icon-tile.js";
|
|
@@ -44,9 +45,9 @@ import { ThemeToggle, useTheme } from "./primitives/theme-toggle.js";
|
|
|
44
45
|
import { Heading, HeadingProps, HeadingVariant } from "./primitives/heading.js";
|
|
45
46
|
import { PageShell, PageShellProps } from "./primitives/page-shell.js";
|
|
46
47
|
import { PageHeader, PageHeaderProps } from "./primitives/page-header.js";
|
|
47
|
-
import { StatusPill, StatusPillProps, StatusTone } from "./primitives/status-pill.js";
|
|
48
48
|
import { Alert, AlertDescription, AlertProps, AlertTitle, AlertTone } from "./primitives/alert.js";
|
|
49
49
|
import { Pagination, PaginationProps } from "./primitives/pagination.js";
|
|
50
|
+
import { StatusPill, StatusPillProps, StatusTone } from "./primitives/status-pill.js";
|
|
50
51
|
import { Tag, TagProps } from "./primitives/tag.js";
|
|
51
52
|
import { Chip, ChipProps } from "./primitives/chip.js";
|
|
52
53
|
import { Metric, MetricProps, MetricStrip, MetricStripProps } from "./primitives/metric-strip.js";
|
package/dist/primitives.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { buttonVariants } from "./presentation.js";
|
|
2
|
+
import { Button } from "./primitives/button.js";
|
|
2
3
|
import { Heading } from "./primitives/heading.js";
|
|
3
4
|
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "./primitives/card.js";
|
|
4
5
|
import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger } from "./primitives/dialog.js";
|
package/dist/utils.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import { ixControl, ixControlAfter, ixControlSoft, ixField, ixRow, ixSelected, ixTabUnderline } from "./lib/interaction.js";
|
|
1
2
|
import { copyText } from "./utils/copy-text.js";
|
|
2
3
|
import { formatBytes, formatDuration, formatUptime, plainTextFromMarkdown, truncateText } from "./utils/format.js";
|
|
3
4
|
import { timeAgo } from "./utils/time-ago.js";
|
|
4
5
|
import { TOOL_CATEGORY_ICONS, getToolCategory, getToolDisplayMetadata, getToolErrorText } from "./utils/tool-display.js";
|
|
5
6
|
import { cn } from "./lib/utils.js";
|
|
6
7
|
import { focusField, focusFieldInvalid, focusFieldWithin, focusRing, focusRingInset } from "./lib/focus.js";
|
|
7
|
-
import { ixControl, ixControlAfter, ixControlSoft, ixField, ixRow, ixSelected, ixTabUnderline } from "./lib/interaction.js";
|
|
8
8
|
export { TOOL_CATEGORY_ICONS, cn, copyText, focusField, focusFieldInvalid, focusFieldWithin, focusRing, focusRingInset, formatBytes, formatDuration, formatUptime, getToolCategory, getToolDisplayMetadata, getToolErrorText, ixControl, ixControlAfter, ixControlSoft, ixField, ixRow, ixSelected, ixTabUnderline, plainTextFromMarkdown, timeAgo, truncateText };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tangle-network/ui",
|
|
3
|
-
"version": "11.
|
|
3
|
+
"version": "11.36.0",
|
|
4
4
|
"description": "Generic React UI components for Tangle products — primitives, chat, run, files, editor, markdown.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -13,7 +13,9 @@
|
|
|
13
13
|
"url": "https://github.com/tangle-network/brand/issues"
|
|
14
14
|
},
|
|
15
15
|
"homepage": "https://github.com/tangle-network/brand",
|
|
16
|
-
"sideEffects":
|
|
16
|
+
"sideEffects": [
|
|
17
|
+
"./src/styles/*.css"
|
|
18
|
+
],
|
|
17
19
|
"files": [
|
|
18
20
|
"dist",
|
|
19
21
|
"src",
|
|
@@ -111,7 +113,14 @@
|
|
|
111
113
|
"types": "./dist/redaction.d.ts",
|
|
112
114
|
"import": "./dist/redaction.js",
|
|
113
115
|
"default": "./dist/redaction.js"
|
|
114
|
-
}
|
|
116
|
+
},
|
|
117
|
+
"./presentation": {
|
|
118
|
+
"types": "./dist/presentation.d.ts",
|
|
119
|
+
"import": "./dist/presentation.js",
|
|
120
|
+
"default": "./dist/presentation.js"
|
|
121
|
+
},
|
|
122
|
+
"./styles/presentation.css": "./src/styles/presentation.css",
|
|
123
|
+
"./styles/compat.css": "./src/styles/compat.css"
|
|
115
124
|
},
|
|
116
125
|
"dependencies": {
|
|
117
126
|
"@pierre/theme": "1.1.0",
|
|
@@ -139,6 +148,7 @@
|
|
|
139
148
|
"github-slugger": "^2.0.0",
|
|
140
149
|
"lucide-react": "^1.48.0",
|
|
141
150
|
"marked": "^18.0.14",
|
|
151
|
+
"motion": "14.2.0",
|
|
142
152
|
"react-markdown": "^10.1.0",
|
|
143
153
|
"rehype-sanitize": "^6.0.0",
|
|
144
154
|
"rehype-slug": "^6.0.0",
|
|
@@ -150,7 +160,7 @@
|
|
|
150
160
|
"peerDependencies": {
|
|
151
161
|
"@hocuspocus/provider": ">=2.15.0 <5.0.0",
|
|
152
162
|
"@nanostores/react": ">=0.8.4 <3.0.0",
|
|
153
|
-
"@tangle-network/brand": "^1.
|
|
163
|
+
"@tangle-network/brand": "^1.25.1",
|
|
154
164
|
"@tiptap/core": ">=3.20.4 <4.0.0",
|
|
155
165
|
"@tiptap/extension-collaboration": ">=3.20.4 <4.0.0",
|
|
156
166
|
"@tiptap/extension-collaboration-caret": ">=3.7.0 <4.0.0",
|
|
@@ -228,7 +238,7 @@
|
|
|
228
238
|
"design-system"
|
|
229
239
|
],
|
|
230
240
|
"scripts": {
|
|
231
|
-
"build": "node scripts/generate-openui-validators.mjs --check && tsdown && node ../../scripts/validate-dist.mjs && node scripts/probe-openui-worker.mjs",
|
|
241
|
+
"build": "node scripts/generate-openui-validators.mjs --check && tsdown && node scripts/generate-presentation-css.mjs --check && node ../../scripts/validate-dist.mjs && node scripts/probe-openui-worker.mjs",
|
|
232
242
|
"gen:openui-schema": "node scripts/generate-openui-validators.mjs",
|
|
233
243
|
"dev": "tsdown --watch"
|
|
234
244
|
}
|
|
@@ -2,10 +2,8 @@ import { focusField } from "./focus";
|
|
|
2
2
|
import { ixField } from "./interaction";
|
|
3
3
|
import { cn } from "./utils";
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
export const controlMotion =
|
|
8
|
-
"duration-[var(--duration-fast)] ease-[var(--ease-standard)] motion-reduce:transition-none motion-reduce:duration-0";
|
|
5
|
+
import { controlMotion } from "./motion";
|
|
6
|
+
export { controlMotion } from "./motion";
|
|
9
7
|
|
|
10
8
|
/**
|
|
11
9
|
* The control scale. Button, Input, Textarea and SelectTrigger take the same
|