@tangle-network/ui 11.13.1 → 11.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.
- package/CHANGELOG.md +16 -0
- package/dist/lib/control-presentation.js +11 -0
- package/dist/primitives/button.d.ts +2 -2
- package/dist/primitives/button.js +11 -9
- package/dist/primitives/dialog.d.ts +6 -0
- package/dist/primitives/dialog.js +2 -2
- package/dist/primitives/input.d.ts +3 -1
- package/dist/primitives/input.js +7 -5
- package/dist/primitives/metric-strip.d.ts +6 -28
- package/dist/primitives/metric-strip.js +15 -18
- package/dist/primitives/select.d.ts +4 -1
- package/dist/primitives/select.js +7 -7
- package/package.json +1 -1
- package/src/lib/control-presentation.ts +26 -0
- package/src/primitives/button.tsx +11 -9
- package/src/primitives/control-presentation.stories.tsx +79 -0
- package/src/primitives/control-presentation.test.tsx +67 -0
- package/src/primitives/dialog-dismissal.stories.tsx +48 -0
- package/src/primitives/dialog.test.tsx +75 -0
- package/src/primitives/dialog.tsx +18 -10
- package/src/primitives/input.tsx +12 -6
- package/src/primitives/metric-strip.stories.tsx +39 -0
- package/src/primitives/metric-strip.test.tsx +82 -144
- package/src/primitives/metric-strip.tsx +27 -61
- package/src/primitives/select.tsx +16 -9
|
@@ -2,32 +2,32 @@ import * as React from "react";
|
|
|
2
2
|
import { cn } from "../lib/utils";
|
|
3
3
|
import { StatusPill, type StatusTone } from "./status-pill";
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
* parent, because these wrap. `divide-x` draws from DOM order and leaves a
|
|
17
|
-
* stray rule at the start of every wrapped line; a per-item leading border
|
|
18
|
-
* suppressed at each row start does not.
|
|
19
|
-
*/
|
|
5
|
+
// Each finite preset owns its grid AND its row-start rules. Keep the strings
|
|
6
|
+
// literal so host Tailwind consumers scanning the published source emit them.
|
|
7
|
+
// Ranges do not overlap: no drawn border is subsequently suppressed.
|
|
8
|
+
const layouts = {
|
|
9
|
+
3: "grid-cols-2 sm:grid-cols-3 max-sm:[&>div:not(:nth-child(2n+1))]:border-l sm:[&>div:not(:nth-child(3n+1))]:border-l",
|
|
10
|
+
4: "grid-cols-2 sm:grid-cols-4 max-sm:[&>div:not(:nth-child(2n+1))]:border-l sm:[&>div:not(:nth-child(4n+1))]:border-l",
|
|
11
|
+
5: "grid-cols-2 sm:grid-cols-3 lg:grid-cols-5 max-sm:[&>div:not(:nth-child(2n+1))]:border-l sm:max-lg:[&>div:not(:nth-child(3n+1))]:border-l lg:[&>div:not(:nth-child(5n+1))]:border-l",
|
|
12
|
+
6: "grid-cols-2 sm:grid-cols-3 lg:grid-cols-6 max-sm:[&>div:not(:nth-child(2n+1))]:border-l sm:max-lg:[&>div:not(:nth-child(3n+1))]:border-l lg:[&>div:not(:nth-child(6n+1))]:border-l",
|
|
13
|
+
} as const;
|
|
14
|
+
|
|
15
|
+
/** Headline readings on one plane, with native definition-list semantics. */
|
|
20
16
|
export interface MetricStripProps
|
|
21
17
|
extends React.HTMLAttributes<HTMLDListElement> {
|
|
22
18
|
children: React.ReactNode;
|
|
19
|
+
/** Desktop columns. Default retains the existing two / sm:four layout.
|
|
20
|
+
* Five and six use three columns between sm and lg. Never inferred from data. */
|
|
21
|
+
columns?: 3 | 4 | 5 | 6;
|
|
23
22
|
}
|
|
24
23
|
|
|
25
24
|
const MetricStrip = React.forwardRef<HTMLDListElement, MetricStripProps>(
|
|
26
|
-
({ className, children, ...props }, ref) => (
|
|
25
|
+
({ className, children, columns = 4, ...props }, ref) => (
|
|
27
26
|
<dl
|
|
28
27
|
ref={ref}
|
|
29
28
|
className={cn(
|
|
30
|
-
"grid
|
|
29
|
+
"grid min-w-0 rounded-[var(--radius-lg)] border border-border bg-card shadow-[var(--shadow-card)]",
|
|
30
|
+
layouts[columns],
|
|
31
31
|
className,
|
|
32
32
|
)}
|
|
33
33
|
{...props}
|
|
@@ -43,72 +43,38 @@ export interface MetricProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
|
43
43
|
value: React.ReactNode;
|
|
44
44
|
/** The qualifying line: which wallet, which window, what the limit is. */
|
|
45
45
|
hint?: React.ReactNode;
|
|
46
|
-
/**
|
|
47
|
-
* `attention` only when a person has to DO something — not merely when a
|
|
48
|
-
* number is zero. */
|
|
46
|
+
/** Attention is a caller-owned action requirement, never inferred from zero. */
|
|
49
47
|
attention?: { tone: StatusTone; label: string };
|
|
50
48
|
}
|
|
51
49
|
|
|
52
|
-
/**
|
|
53
|
-
* One reading inside a `MetricStrip`.
|
|
54
|
-
*
|
|
55
|
-
* `<dt>` carries the label and `<dd>` the value, so the pair is announced as a
|
|
56
|
-
* described term rather than as two loose strings — the semantic that makes a
|
|
57
|
-
* figure legible without the visual grouping.
|
|
58
|
-
*
|
|
59
|
-
* The value is `tabular-nums`: these sit in a row and change on a timer, and
|
|
60
|
-
* proportional digits make the column jitter every time a 1 becomes an 8.
|
|
61
|
-
*/
|
|
50
|
+
/** A described term; the strip, not this item, decides where separators belong. */
|
|
62
51
|
const Metric = React.forwardRef<HTMLDivElement, MetricProps>(
|
|
63
52
|
({ className, label, value, hint, attention, ...props }, ref) => (
|
|
64
53
|
<div
|
|
65
54
|
ref={ref}
|
|
66
|
-
className={cn(
|
|
67
|
-
// Leading hairline, DRAWN only where a divider belongs — never drawn
|
|
68
|
-
// and then suppressed. Stating it as `border-l` plus `border-l-0`
|
|
69
|
-
// variants puts both in one conflict group, where the winner is decided
|
|
70
|
-
// by emitted-rule order rather than by intent; that resolved the wrong
|
|
71
|
-
// way at every breakpoint and left a rule down the start of each row.
|
|
72
|
-
// `:not(:nth-child(Nn+1))` is the row-start test for the column count
|
|
73
|
-
// at that breakpoint, so no rule ever contradicts another.
|
|
74
|
-
//
|
|
75
|
-
// N is COUPLED to the grid template on `MetricStrip`: 2 columns below
|
|
76
|
-
// `sm`, 4 from `sm`. A new breakpoint there — `md:grid-cols-3`, say —
|
|
77
|
-
// needs its `md:[&:not(:nth-child(3n+1))]:border-l` here in the same
|
|
78
|
-
// change, or the dividers land mid-row at that width.
|
|
79
|
-
"border-border p-4 sm:p-5",
|
|
80
|
-
"max-sm:[&:not(:nth-child(2n+1))]:border-l",
|
|
81
|
-
"sm:[&:not(:nth-child(4n+1))]:border-l",
|
|
82
|
-
className,
|
|
83
|
-
)}
|
|
55
|
+
className={cn("min-w-0 border-border p-4 sm:p-5", className)}
|
|
84
56
|
{...props}
|
|
85
57
|
>
|
|
86
|
-
<dt className="flex items-center gap-2 text-muted-foreground text-sm">
|
|
87
|
-
<span className="
|
|
58
|
+
<dt className="flex min-w-0 flex-wrap items-center gap-2 text-muted-foreground text-sm">
|
|
59
|
+
<span className="min-w-0 whitespace-normal [overflow-wrap:anywhere]">{label}</span>
|
|
88
60
|
{attention && (
|
|
89
61
|
<StatusPill tone={attention.tone}>{attention.label}</StatusPill>
|
|
90
62
|
)}
|
|
91
63
|
</dt>
|
|
92
|
-
{/*
|
|
93
|
-
|
|
94
|
-
funded account and as "$1,284,003.10" on a busy one, and at four
|
|
95
|
-
columns the wide case has nowhere to go — it either overflows its cell
|
|
96
|
-
or pushes the whole strip past the page. */}
|
|
64
|
+
{/* Values and qualifiers must be readable on touch, not only in a hover
|
|
65
|
+
title. Wrap even unbroken values without making the grid wider. */}
|
|
97
66
|
<dd
|
|
98
67
|
className={cn(
|
|
99
|
-
"mt-1
|
|
68
|
+
"mt-1 whitespace-normal [overflow-wrap:anywhere] font-semibold text-xl tabular-nums tracking-tight lg:text-2xl",
|
|
100
69
|
attention?.tone === "danger" && "text-[var(--surface-danger-text)]",
|
|
101
70
|
)}
|
|
102
71
|
title={typeof value === "string" ? value : undefined}
|
|
103
72
|
>
|
|
104
73
|
{value}
|
|
105
74
|
</dd>
|
|
106
|
-
{hint && (
|
|
107
|
-
// Titled for the same reason the value is: the line truncates, and a
|
|
108
|
-
// hint is where the qualifying detail lives — which wallet, which
|
|
109
|
-
// window — so a clipped one is the case most worth recovering.
|
|
75
|
+
{hint !== undefined && hint !== null && hint !== false && hint !== "" && (
|
|
110
76
|
<dd
|
|
111
|
-
className="mt-0.5
|
|
77
|
+
className="mt-0.5 whitespace-normal [overflow-wrap:anywhere] text-[var(--text-dim)] text-xs"
|
|
112
78
|
title={typeof hint === "string" ? hint : undefined}
|
|
113
79
|
>
|
|
114
80
|
{hint}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import * as SelectPrimitive from "@radix-ui/react-select";
|
|
4
4
|
import { Check, ChevronDown, ChevronUp } from "lucide-react";
|
|
5
5
|
import * as React from "react";
|
|
6
|
-
import {
|
|
6
|
+
import { controlSizes, fieldPresentation } from "../lib/control-presentation";
|
|
7
7
|
import { cn } from "../lib/utils";
|
|
8
8
|
|
|
9
9
|
const Select = SelectPrimitive.Root;
|
|
@@ -14,20 +14,27 @@ const SelectValue = SelectPrimitive.Value;
|
|
|
14
14
|
|
|
15
15
|
const SelectTrigger = React.forwardRef<
|
|
16
16
|
React.ElementRef<typeof SelectPrimitive.Trigger>,
|
|
17
|
-
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
|
|
18
|
-
|
|
17
|
+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger> & {
|
|
18
|
+
/** Default remains 36px; compact follows density, touch is at least 44px. */
|
|
19
|
+
size?: "default" | "compact" | "touch";
|
|
20
|
+
}
|
|
21
|
+
>(({ className, children, size = "default", ...props }, ref) => (
|
|
19
22
|
<SelectPrimitive.Trigger
|
|
20
23
|
ref={ref}
|
|
21
24
|
className={cn(
|
|
22
|
-
"flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border
|
|
23
|
-
|
|
25
|
+
"flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border px-3 py-2 text-sm shadow-sm placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
|
|
26
|
+
fieldPresentation,
|
|
27
|
+
"data-[placeholder]:text-muted-foreground [&>span]:min-w-0 [&>span]:text-left",
|
|
28
|
+
// The designed open state: accent border and surface while the menu is up.
|
|
29
|
+
"data-[state=open]:border-primary data-[state=open]:bg-[var(--accent-surface-strong)] [&[data-state=open]>svg]:rotate-180 [&>svg]:transition-transform",
|
|
30
|
+
size !== "default" && controlSizes[size],
|
|
24
31
|
className,
|
|
25
32
|
)}
|
|
26
33
|
{...props}
|
|
27
34
|
>
|
|
28
35
|
{children}
|
|
29
36
|
<SelectPrimitive.Icon asChild>
|
|
30
|
-
<ChevronDown className="h-4 w-4 opacity-50" />
|
|
37
|
+
<ChevronDown className="h-4 w-4 shrink-0 opacity-50" />
|
|
31
38
|
</SelectPrimitive.Icon>
|
|
32
39
|
</SelectPrimitive.Trigger>
|
|
33
40
|
));
|
|
@@ -76,7 +83,7 @@ const SelectContent = React.forwardRef<
|
|
|
76
83
|
<SelectPrimitive.Content
|
|
77
84
|
ref={ref}
|
|
78
85
|
className={cn(
|
|
79
|
-
"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-
|
|
86
|
+
"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-lg border border-border bg-card text-foreground shadow-[var(--shadow-dropdown)] data-[state=closed]:animate-out data-[state=open]:animate-in",
|
|
80
87
|
position === "popper" &&
|
|
81
88
|
"data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=bottom]:translate-y-1 data-[side=top]:-translate-y-1",
|
|
82
89
|
className,
|
|
@@ -119,14 +126,14 @@ const SelectItem = React.forwardRef<
|
|
|
119
126
|
<SelectPrimitive.Item
|
|
120
127
|
ref={ref}
|
|
121
128
|
className={cn(
|
|
122
|
-
"relative flex w-full cursor-default select-none items-center rounded-
|
|
129
|
+
"relative flex w-full cursor-default select-none items-center rounded-md py-1.5 pr-8 pl-2 text-sm outline-none transition-colors focus:bg-[var(--accent-surface-strong)] focus:text-foreground data-[state=checked]:bg-[var(--accent-surface-strong)] data-[state=checked]:font-medium data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
123
130
|
className,
|
|
124
131
|
)}
|
|
125
132
|
{...props}
|
|
126
133
|
>
|
|
127
134
|
<span className="absolute right-2 flex h-3.5 w-3.5 items-center justify-center">
|
|
128
135
|
<SelectPrimitive.ItemIndicator>
|
|
129
|
-
<Check className="h-4 w-4" />
|
|
136
|
+
<Check className="h-4 w-4 text-[var(--accent-text)]" />
|
|
130
137
|
</SelectPrimitive.ItemIndicator>
|
|
131
138
|
</span>
|
|
132
139
|
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
|