@arbidocs/blocks 0.4.119 → 0.4.120

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,207 @@
1
+ "use client";
2
+ 'use strict';
3
+
4
+ var ui = require('@arbidocs/react/ui');
5
+ var jsxRuntime = require('react/jsx-runtime');
6
+
7
+ // src/components/UsageMeter.tsx
8
+ function usageMeterState(used, allowance) {
9
+ if (allowance <= 0) return used > 0 ? "over" : "under";
10
+ const ratio = used / allowance;
11
+ return ratio >= 1 ? "over" : ratio >= 0.8 ? "near" : "under";
12
+ }
13
+ var STATE_TONE = {
14
+ under: "bg-primary",
15
+ near: "bg-yellow-500",
16
+ over: "bg-red-500"
17
+ };
18
+ var pctOf = (n, scale) => scale > 0 ? Math.max(0, Math.min(100, n / scale * 100)) : 0;
19
+ var SWATCH = {
20
+ plan: "bg-muted-foreground/40",
21
+ topUps: "bg-primary/30",
22
+ autoRoom: "bg-muted"
23
+ };
24
+ var HATCH = {
25
+ backgroundImage: "repeating-linear-gradient(135deg, currentColor 0 1.5px, transparent 1.5px 5px)"
26
+ };
27
+ function AllowanceBar({ allowance, label }) {
28
+ const { used, plan } = allowance;
29
+ const topUps = Math.max(0, allowance.topUps ?? 0);
30
+ const room = Math.max(0, allowance.autoRoom ?? 0);
31
+ const total = plan + topUps;
32
+ const scale = Math.max(total + room, used, Number.EPSILON);
33
+ const state = usageMeterState(used, total);
34
+ const usedPct = total > 0 ? Math.round(used / total * 100) : used > 0 ? 100 : 0;
35
+ const markPlan = plan > 0 && plan < scale && (topUps > 0 || room > 0);
36
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative h-2 overflow-hidden rounded-full bg-muted", "data-slot": "usage-meter-bar", children: [
37
+ topUps > 0 && /* @__PURE__ */ jsxRuntime.jsx(
38
+ "div",
39
+ {
40
+ className: ui.cn("absolute inset-y-0", SWATCH.topUps),
41
+ style: { left: `${pctOf(plan, scale)}%`, width: `${pctOf(topUps, scale)}%` },
42
+ "data-slot": "usage-meter-topups"
43
+ }
44
+ ),
45
+ room > 0 && /* @__PURE__ */ jsxRuntime.jsx(
46
+ "div",
47
+ {
48
+ className: "absolute inset-y-0 text-muted-foreground/35",
49
+ style: { left: `${pctOf(total, scale)}%`, width: `${pctOf(room, scale)}%`, ...HATCH },
50
+ "data-slot": "usage-meter-auto-room"
51
+ }
52
+ ),
53
+ /* @__PURE__ */ jsxRuntime.jsx(
54
+ "div",
55
+ {
56
+ className: ui.cn(
57
+ "absolute inset-y-0 left-0 rounded-full transition-[width]",
58
+ STATE_TONE[state]
59
+ ),
60
+ style: { width: `${pctOf(Math.min(used, scale), scale)}%` },
61
+ role: "progressbar",
62
+ "aria-valuenow": Math.min(usedPct, 100),
63
+ "aria-valuemin": 0,
64
+ "aria-valuemax": 100,
65
+ "aria-label": label,
66
+ "data-state": state
67
+ }
68
+ ),
69
+ markPlan && /* @__PURE__ */ jsxRuntime.jsx(
70
+ "div",
71
+ {
72
+ className: "absolute inset-y-0 w-0.5 -translate-x-1/2 bg-foreground/70",
73
+ style: { left: `${pctOf(plan, scale)}%` },
74
+ "aria-hidden": true,
75
+ "data-slot": "usage-meter-plan-mark"
76
+ }
77
+ ),
78
+ room > 0 && total > 0 && /* @__PURE__ */ jsxRuntime.jsx(
79
+ "div",
80
+ {
81
+ className: "absolute inset-y-0 w-px bg-foreground/40",
82
+ style: { left: `${pctOf(total, scale)}%` },
83
+ "aria-hidden": true
84
+ }
85
+ )
86
+ ] });
87
+ }
88
+ function UsageMeter({
89
+ icon: Icon,
90
+ label,
91
+ value,
92
+ pct = 0,
93
+ hint,
94
+ allowance,
95
+ legend,
96
+ details,
97
+ actions,
98
+ className,
99
+ testId
100
+ }) {
101
+ const clamped = Math.max(0, Math.min(100, pct));
102
+ const tone = clamped >= 100 ? "bg-red-500" : clamped >= 50 ? "bg-yellow-500" : "bg-primary";
103
+ const state = allowance ? usageMeterState(allowance.used, allowance.plan + Math.max(0, allowance.topUps ?? 0)) : void 0;
104
+ const legendItems = legend ? [
105
+ ["plan", legend.plan],
106
+ ["topUps", legend.topUps],
107
+ ["autoRoom", legend.autoRoom]
108
+ ].filter(([, text]) => !!text) : [];
109
+ const meter = /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1 space-y-1.5", children: [
110
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-3 text-sm", children: [
111
+ /* @__PURE__ */ jsxRuntime.jsxs(
112
+ "span",
113
+ {
114
+ className: ui.cn(
115
+ "flex min-w-0 items-center gap-1.5",
116
+ allowance ? "font-medium text-foreground" : "text-muted-foreground"
117
+ ),
118
+ children: [
119
+ Icon && /* @__PURE__ */ jsxRuntime.jsx(Icon, { className: "size-3.5 shrink-0" }),
120
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "truncate", children: [
121
+ label,
122
+ hint ? ` (${hint})` : ""
123
+ ] })
124
+ ]
125
+ }
126
+ ),
127
+ /* @__PURE__ */ jsxRuntime.jsx(
128
+ "span",
129
+ {
130
+ className: "shrink-0 font-medium tabular-nums text-foreground",
131
+ "data-slot": "usage-meter-value",
132
+ children: value
133
+ }
134
+ )
135
+ ] }),
136
+ allowance ? /* @__PURE__ */ jsxRuntime.jsx(AllowanceBar, { allowance, label }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-1.5 overflow-hidden rounded-full bg-muted", children: /* @__PURE__ */ jsxRuntime.jsx(
137
+ "div",
138
+ {
139
+ className: ui.cn("h-full rounded-full transition-[width]", tone),
140
+ style: { width: `${clamped}%` },
141
+ role: "progressbar",
142
+ "aria-valuenow": clamped,
143
+ "aria-valuemin": 0,
144
+ "aria-valuemax": 100,
145
+ "aria-label": label
146
+ }
147
+ ) }),
148
+ legendItems.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
149
+ "ul",
150
+ {
151
+ className: "flex flex-wrap gap-x-4 gap-y-1 text-xs tabular-nums text-muted-foreground",
152
+ "data-slot": "usage-meter-legend",
153
+ children: legendItems.map(([key, text]) => /* @__PURE__ */ jsxRuntime.jsxs(
154
+ "li",
155
+ {
156
+ className: "flex items-center gap-1.5",
157
+ "data-slot": `usage-meter-legend-${key}`,
158
+ children: [
159
+ /* @__PURE__ */ jsxRuntime.jsx(
160
+ "span",
161
+ {
162
+ className: ui.cn(
163
+ "inline-block size-2 rounded-sm",
164
+ SWATCH[key],
165
+ key === "autoRoom" && "text-muted-foreground/60"
166
+ ),
167
+ style: key === "autoRoom" ? HATCH : void 0,
168
+ "aria-hidden": true
169
+ }
170
+ ),
171
+ text
172
+ ]
173
+ },
174
+ key
175
+ ))
176
+ }
177
+ ),
178
+ details && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-1 text-xs text-muted-foreground", children: details })
179
+ ] });
180
+ return /* @__PURE__ */ jsxRuntime.jsxs(
181
+ "div",
182
+ {
183
+ className: ui.cn(
184
+ actions ? "flex flex-wrap items-start gap-x-6 gap-y-3" : "space-y-1.5",
185
+ className
186
+ ),
187
+ "data-testid": testId,
188
+ "data-state": state,
189
+ children: [
190
+ meter,
191
+ actions && /* @__PURE__ */ jsxRuntime.jsx(
192
+ "div",
193
+ {
194
+ className: "flex shrink-0 flex-col items-stretch gap-2 sm:w-64",
195
+ "data-slot": "usage-meter-actions",
196
+ children: actions
197
+ }
198
+ )
199
+ ]
200
+ }
201
+ );
202
+ }
203
+
204
+ exports.UsageMeter = UsageMeter;
205
+ exports.usageMeterState = usageMeterState;
206
+ //# sourceMappingURL=chunk-A7NCSZAP.cjs.map
207
+ //# sourceMappingURL=chunk-A7NCSZAP.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/UsageMeter.tsx"],"names":["jsxs","jsx","cn"],"mappings":"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","file":"chunk-A7NCSZAP.cjs","sourcesContent":["import type { ReactNode } from 'react'\nimport type { LucideIcon } from 'lucide-react'\nimport { cn } from '@arbidocs/react/ui'\n\n/**\n * What an allowance is made of, in the meter's own unit (£ of AI usage, GB, users): what was used, the plan's part\n * of the allowance, the top-ups on top of it, and how much more automatic top-ups may still add before a usage cap\n * (or budget) stops them.\n */\nexport interface UsageMeterAllowance {\n used: number\n /** The plan's part of the allowance (marked on the bar when top-ups or automatic room follow it). */\n plan: number\n /** Top-ups on top of the plan: the allowance now is `plan + topUps`. */\n topUps?: number\n /**\n * Room automatic top-ups may still add before the cap stops them (drawn hatched after the allowance).\n * `null` = no limit (nothing drawn: the room is unbounded); omitted = not applicable.\n */\n autoRoom?: number | null\n}\n\n/** Pressure on an allowance: under 80% used, 80% to under 100%, or at / over it. */\nexport type UsageMeterState = 'under' | 'near' | 'over'\n\n/** The allowance meter's pressure state for `used` of `allowance`. */\nexport function usageMeterState(used: number, allowance: number): UsageMeterState {\n if (allowance <= 0) return used > 0 ? 'over' : 'under'\n const ratio = used / allowance\n return ratio >= 1 ? 'over' : ratio >= 0.8 ? 'near' : 'under'\n}\n\nconst STATE_TONE: Record<UsageMeterState, string> = {\n under: 'bg-primary',\n near: 'bg-yellow-500',\n over: 'bg-red-500',\n}\n\nconst pctOf = (n: number, scale: number) =>\n scale > 0 ? Math.max(0, Math.min(100, (n / scale) * 100)) : 0\n\n/** Legend swatches, matching the bar's segments. */\nconst SWATCH = {\n plan: 'bg-muted-foreground/40',\n topUps: 'bg-primary/30',\n autoRoom: 'bg-muted',\n}\n\nconst HATCH = {\n backgroundImage: 'repeating-linear-gradient(135deg, currentColor 0 1.5px, transparent 1.5px 5px)',\n} as const\n\n/** The segmented bar of an allowance meter: used, the plan mark, top-ups, and the automatic room. */\nfunction AllowanceBar({ allowance, label }: { allowance: UsageMeterAllowance; label: string }) {\n const { used, plan } = allowance\n const topUps = Math.max(0, allowance.topUps ?? 0)\n const room = Math.max(0, allowance.autoRoom ?? 0)\n const total = plan + topUps\n const scale = Math.max(total + room, used, Number.EPSILON)\n const state = usageMeterState(used, total)\n const usedPct = total > 0 ? Math.round((used / total) * 100) : used > 0 ? 100 : 0\n const markPlan = plan > 0 && plan < scale && (topUps > 0 || room > 0)\n return (\n <div className=\"relative h-2 overflow-hidden rounded-full bg-muted\" data-slot=\"usage-meter-bar\">\n {topUps > 0 && (\n <div\n className={cn('absolute inset-y-0', SWATCH.topUps)}\n style={{ left: `${pctOf(plan, scale)}%`, width: `${pctOf(topUps, scale)}%` }}\n data-slot=\"usage-meter-topups\"\n />\n )}\n {room > 0 && (\n <div\n className=\"absolute inset-y-0 text-muted-foreground/35\"\n style={{ left: `${pctOf(total, scale)}%`, width: `${pctOf(room, scale)}%`, ...HATCH }}\n data-slot=\"usage-meter-auto-room\"\n />\n )}\n <div\n className={cn(\n 'absolute inset-y-0 left-0 rounded-full transition-[width]',\n STATE_TONE[state]\n )}\n style={{ width: `${pctOf(Math.min(used, scale), scale)}%` }}\n role=\"progressbar\"\n aria-valuenow={Math.min(usedPct, 100)}\n aria-valuemin={0}\n aria-valuemax={100}\n aria-label={label}\n data-state={state}\n />\n {markPlan && (\n <div\n className=\"absolute inset-y-0 w-0.5 -translate-x-1/2 bg-foreground/70\"\n style={{ left: `${pctOf(plan, scale)}%` }}\n aria-hidden\n data-slot=\"usage-meter-plan-mark\"\n />\n )}\n {room > 0 && total > 0 && (\n <div\n className=\"absolute inset-y-0 w-px bg-foreground/40\"\n style={{ left: `${pctOf(total, scale)}%` }}\n aria-hidden\n />\n )}\n </div>\n )\n}\n\n/**\n * One labelled quota meter: icon + label on the left, used-of-limit on the right, a thin bar underneath.\n *\n * Simple mode (`pct`): the bar's tone follows pressure with the shared green → yellow → red gradation — green while\n * there's room, yellow past the halfway mark, red at the limit — so a stack of meters reads at a glance which quota\n * is the one to think about.\n *\n * Allowance mode (`allowance`): the bar spans the allowance plus what automatic top-ups may still add; it marks the\n * plan's part, shades the top-ups, hatches the automatic room, and fills what was used (red at the allowance, yellow\n * from 80%). `legend` names the segments; `details` (next period, cap) sit under the bar and `actions` (+ / −) beside\n * it. The meter's `data-state` is `under` / `near` / `over`.\n */\nexport function UsageMeter({\n icon: Icon,\n label,\n value,\n pct = 0,\n hint,\n allowance,\n legend,\n details,\n actions,\n className,\n testId,\n}: {\n icon?: LucideIcon\n label: string\n /** Formatted used-of-limit text, e.g. \"1,200 / 25,000\" or \"40 / 500 MB\". */\n value: string\n /** Simple mode: fill percentage, 0–100 (values are clamped). Ignored with `allowance`. */\n pct?: number\n /** Inline parenthetical after the label, e.g. \"resets May 31, 2025\" —\n * same font as the label, so the meter never grows an extra row. */\n hint?: string\n /** Allowance mode: the allowance's parts, in the meter's unit. */\n allowance?: UsageMeterAllowance\n /** Formatted legend entries for the allowance's segments (each shown only when set). */\n legend?: { plan?: string; topUps?: string; autoRoom?: string }\n details?: ReactNode\n actions?: ReactNode\n className?: string\n testId?: string\n}) {\n const clamped = Math.max(0, Math.min(100, pct))\n // Shared green → yellow → red gradation (same thresholds as the project-card\n // quota badges): green with room, yellow past the halfway mark, red at limit.\n const tone = clamped >= 100 ? 'bg-red-500' : clamped >= 50 ? 'bg-yellow-500' : 'bg-primary'\n const state = allowance\n ? usageMeterState(allowance.used, allowance.plan + Math.max(0, allowance.topUps ?? 0))\n : undefined\n const legendItems = legend\n ? (\n [\n ['plan', legend.plan],\n ['topUps', legend.topUps],\n ['autoRoom', legend.autoRoom],\n ] as const\n ).filter(([, text]) => !!text)\n : []\n\n const meter = (\n <div className=\"min-w-0 flex-1 space-y-1.5\">\n <div className=\"flex items-center justify-between gap-3 text-sm\">\n <span\n className={cn(\n 'flex min-w-0 items-center gap-1.5',\n allowance ? 'font-medium text-foreground' : 'text-muted-foreground'\n )}\n >\n {Icon && <Icon className=\"size-3.5 shrink-0\" />}\n <span className=\"truncate\">\n {label}\n {hint ? ` (${hint})` : ''}\n </span>\n </span>\n <span\n className=\"shrink-0 font-medium tabular-nums text-foreground\"\n data-slot=\"usage-meter-value\"\n >\n {value}\n </span>\n </div>\n {allowance ? (\n <AllowanceBar allowance={allowance} label={label} />\n ) : (\n <div className=\"h-1.5 overflow-hidden rounded-full bg-muted\">\n <div\n className={cn('h-full rounded-full transition-[width]', tone)}\n style={{ width: `${clamped}%` }}\n role=\"progressbar\"\n aria-valuenow={clamped}\n aria-valuemin={0}\n aria-valuemax={100}\n aria-label={label}\n />\n </div>\n )}\n {legendItems.length > 0 && (\n <ul\n className=\"flex flex-wrap gap-x-4 gap-y-1 text-xs tabular-nums text-muted-foreground\"\n data-slot=\"usage-meter-legend\"\n >\n {legendItems.map(([key, text]) => (\n <li\n key={key}\n className=\"flex items-center gap-1.5\"\n data-slot={`usage-meter-legend-${key}`}\n >\n <span\n className={cn(\n 'inline-block size-2 rounded-sm',\n SWATCH[key],\n key === 'autoRoom' && 'text-muted-foreground/60'\n )}\n style={key === 'autoRoom' ? HATCH : undefined}\n aria-hidden\n />\n {text}\n </li>\n ))}\n </ul>\n )}\n {details && <div className=\"space-y-1 text-xs text-muted-foreground\">{details}</div>}\n </div>\n )\n\n return (\n <div\n className={cn(\n actions ? 'flex flex-wrap items-start gap-x-6 gap-y-3' : 'space-y-1.5',\n className\n )}\n data-testid={testId}\n data-state={state}\n >\n {meter}\n {actions && (\n <div\n className=\"flex shrink-0 flex-col items-stretch gap-2 sm:w-64\"\n data-slot=\"usage-meter-actions\"\n >\n {actions}\n </div>\n )}\n </div>\n )\n}\n"]}
@@ -0,0 +1,204 @@
1
+ "use client";
2
+ import { cn } from '@arbidocs/react/ui';
3
+ import { jsxs, jsx } from 'react/jsx-runtime';
4
+
5
+ // src/components/UsageMeter.tsx
6
+ function usageMeterState(used, allowance) {
7
+ if (allowance <= 0) return used > 0 ? "over" : "under";
8
+ const ratio = used / allowance;
9
+ return ratio >= 1 ? "over" : ratio >= 0.8 ? "near" : "under";
10
+ }
11
+ var STATE_TONE = {
12
+ under: "bg-primary",
13
+ near: "bg-yellow-500",
14
+ over: "bg-red-500"
15
+ };
16
+ var pctOf = (n, scale) => scale > 0 ? Math.max(0, Math.min(100, n / scale * 100)) : 0;
17
+ var SWATCH = {
18
+ plan: "bg-muted-foreground/40",
19
+ topUps: "bg-primary/30",
20
+ autoRoom: "bg-muted"
21
+ };
22
+ var HATCH = {
23
+ backgroundImage: "repeating-linear-gradient(135deg, currentColor 0 1.5px, transparent 1.5px 5px)"
24
+ };
25
+ function AllowanceBar({ allowance, label }) {
26
+ const { used, plan } = allowance;
27
+ const topUps = Math.max(0, allowance.topUps ?? 0);
28
+ const room = Math.max(0, allowance.autoRoom ?? 0);
29
+ const total = plan + topUps;
30
+ const scale = Math.max(total + room, used, Number.EPSILON);
31
+ const state = usageMeterState(used, total);
32
+ const usedPct = total > 0 ? Math.round(used / total * 100) : used > 0 ? 100 : 0;
33
+ const markPlan = plan > 0 && plan < scale && (topUps > 0 || room > 0);
34
+ return /* @__PURE__ */ jsxs("div", { className: "relative h-2 overflow-hidden rounded-full bg-muted", "data-slot": "usage-meter-bar", children: [
35
+ topUps > 0 && /* @__PURE__ */ jsx(
36
+ "div",
37
+ {
38
+ className: cn("absolute inset-y-0", SWATCH.topUps),
39
+ style: { left: `${pctOf(plan, scale)}%`, width: `${pctOf(topUps, scale)}%` },
40
+ "data-slot": "usage-meter-topups"
41
+ }
42
+ ),
43
+ room > 0 && /* @__PURE__ */ jsx(
44
+ "div",
45
+ {
46
+ className: "absolute inset-y-0 text-muted-foreground/35",
47
+ style: { left: `${pctOf(total, scale)}%`, width: `${pctOf(room, scale)}%`, ...HATCH },
48
+ "data-slot": "usage-meter-auto-room"
49
+ }
50
+ ),
51
+ /* @__PURE__ */ jsx(
52
+ "div",
53
+ {
54
+ className: cn(
55
+ "absolute inset-y-0 left-0 rounded-full transition-[width]",
56
+ STATE_TONE[state]
57
+ ),
58
+ style: { width: `${pctOf(Math.min(used, scale), scale)}%` },
59
+ role: "progressbar",
60
+ "aria-valuenow": Math.min(usedPct, 100),
61
+ "aria-valuemin": 0,
62
+ "aria-valuemax": 100,
63
+ "aria-label": label,
64
+ "data-state": state
65
+ }
66
+ ),
67
+ markPlan && /* @__PURE__ */ jsx(
68
+ "div",
69
+ {
70
+ className: "absolute inset-y-0 w-0.5 -translate-x-1/2 bg-foreground/70",
71
+ style: { left: `${pctOf(plan, scale)}%` },
72
+ "aria-hidden": true,
73
+ "data-slot": "usage-meter-plan-mark"
74
+ }
75
+ ),
76
+ room > 0 && total > 0 && /* @__PURE__ */ jsx(
77
+ "div",
78
+ {
79
+ className: "absolute inset-y-0 w-px bg-foreground/40",
80
+ style: { left: `${pctOf(total, scale)}%` },
81
+ "aria-hidden": true
82
+ }
83
+ )
84
+ ] });
85
+ }
86
+ function UsageMeter({
87
+ icon: Icon,
88
+ label,
89
+ value,
90
+ pct = 0,
91
+ hint,
92
+ allowance,
93
+ legend,
94
+ details,
95
+ actions,
96
+ className,
97
+ testId
98
+ }) {
99
+ const clamped = Math.max(0, Math.min(100, pct));
100
+ const tone = clamped >= 100 ? "bg-red-500" : clamped >= 50 ? "bg-yellow-500" : "bg-primary";
101
+ const state = allowance ? usageMeterState(allowance.used, allowance.plan + Math.max(0, allowance.topUps ?? 0)) : void 0;
102
+ const legendItems = legend ? [
103
+ ["plan", legend.plan],
104
+ ["topUps", legend.topUps],
105
+ ["autoRoom", legend.autoRoom]
106
+ ].filter(([, text]) => !!text) : [];
107
+ const meter = /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1 space-y-1.5", children: [
108
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3 text-sm", children: [
109
+ /* @__PURE__ */ jsxs(
110
+ "span",
111
+ {
112
+ className: cn(
113
+ "flex min-w-0 items-center gap-1.5",
114
+ allowance ? "font-medium text-foreground" : "text-muted-foreground"
115
+ ),
116
+ children: [
117
+ Icon && /* @__PURE__ */ jsx(Icon, { className: "size-3.5 shrink-0" }),
118
+ /* @__PURE__ */ jsxs("span", { className: "truncate", children: [
119
+ label,
120
+ hint ? ` (${hint})` : ""
121
+ ] })
122
+ ]
123
+ }
124
+ ),
125
+ /* @__PURE__ */ jsx(
126
+ "span",
127
+ {
128
+ className: "shrink-0 font-medium tabular-nums text-foreground",
129
+ "data-slot": "usage-meter-value",
130
+ children: value
131
+ }
132
+ )
133
+ ] }),
134
+ allowance ? /* @__PURE__ */ jsx(AllowanceBar, { allowance, label }) : /* @__PURE__ */ jsx("div", { className: "h-1.5 overflow-hidden rounded-full bg-muted", children: /* @__PURE__ */ jsx(
135
+ "div",
136
+ {
137
+ className: cn("h-full rounded-full transition-[width]", tone),
138
+ style: { width: `${clamped}%` },
139
+ role: "progressbar",
140
+ "aria-valuenow": clamped,
141
+ "aria-valuemin": 0,
142
+ "aria-valuemax": 100,
143
+ "aria-label": label
144
+ }
145
+ ) }),
146
+ legendItems.length > 0 && /* @__PURE__ */ jsx(
147
+ "ul",
148
+ {
149
+ className: "flex flex-wrap gap-x-4 gap-y-1 text-xs tabular-nums text-muted-foreground",
150
+ "data-slot": "usage-meter-legend",
151
+ children: legendItems.map(([key, text]) => /* @__PURE__ */ jsxs(
152
+ "li",
153
+ {
154
+ className: "flex items-center gap-1.5",
155
+ "data-slot": `usage-meter-legend-${key}`,
156
+ children: [
157
+ /* @__PURE__ */ jsx(
158
+ "span",
159
+ {
160
+ className: cn(
161
+ "inline-block size-2 rounded-sm",
162
+ SWATCH[key],
163
+ key === "autoRoom" && "text-muted-foreground/60"
164
+ ),
165
+ style: key === "autoRoom" ? HATCH : void 0,
166
+ "aria-hidden": true
167
+ }
168
+ ),
169
+ text
170
+ ]
171
+ },
172
+ key
173
+ ))
174
+ }
175
+ ),
176
+ details && /* @__PURE__ */ jsx("div", { className: "space-y-1 text-xs text-muted-foreground", children: details })
177
+ ] });
178
+ return /* @__PURE__ */ jsxs(
179
+ "div",
180
+ {
181
+ className: cn(
182
+ actions ? "flex flex-wrap items-start gap-x-6 gap-y-3" : "space-y-1.5",
183
+ className
184
+ ),
185
+ "data-testid": testId,
186
+ "data-state": state,
187
+ children: [
188
+ meter,
189
+ actions && /* @__PURE__ */ jsx(
190
+ "div",
191
+ {
192
+ className: "flex shrink-0 flex-col items-stretch gap-2 sm:w-64",
193
+ "data-slot": "usage-meter-actions",
194
+ children: actions
195
+ }
196
+ )
197
+ ]
198
+ }
199
+ );
200
+ }
201
+
202
+ export { UsageMeter, usageMeterState };
203
+ //# sourceMappingURL=chunk-IBZJYCYM.js.map
204
+ //# sourceMappingURL=chunk-IBZJYCYM.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/UsageMeter.tsx"],"names":[],"mappings":"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","file":"chunk-IBZJYCYM.js","sourcesContent":["import type { ReactNode } from 'react'\nimport type { LucideIcon } from 'lucide-react'\nimport { cn } from '@arbidocs/react/ui'\n\n/**\n * What an allowance is made of, in the meter's own unit (£ of AI usage, GB, users): what was used, the plan's part\n * of the allowance, the top-ups on top of it, and how much more automatic top-ups may still add before a usage cap\n * (or budget) stops them.\n */\nexport interface UsageMeterAllowance {\n used: number\n /** The plan's part of the allowance (marked on the bar when top-ups or automatic room follow it). */\n plan: number\n /** Top-ups on top of the plan: the allowance now is `plan + topUps`. */\n topUps?: number\n /**\n * Room automatic top-ups may still add before the cap stops them (drawn hatched after the allowance).\n * `null` = no limit (nothing drawn: the room is unbounded); omitted = not applicable.\n */\n autoRoom?: number | null\n}\n\n/** Pressure on an allowance: under 80% used, 80% to under 100%, or at / over it. */\nexport type UsageMeterState = 'under' | 'near' | 'over'\n\n/** The allowance meter's pressure state for `used` of `allowance`. */\nexport function usageMeterState(used: number, allowance: number): UsageMeterState {\n if (allowance <= 0) return used > 0 ? 'over' : 'under'\n const ratio = used / allowance\n return ratio >= 1 ? 'over' : ratio >= 0.8 ? 'near' : 'under'\n}\n\nconst STATE_TONE: Record<UsageMeterState, string> = {\n under: 'bg-primary',\n near: 'bg-yellow-500',\n over: 'bg-red-500',\n}\n\nconst pctOf = (n: number, scale: number) =>\n scale > 0 ? Math.max(0, Math.min(100, (n / scale) * 100)) : 0\n\n/** Legend swatches, matching the bar's segments. */\nconst SWATCH = {\n plan: 'bg-muted-foreground/40',\n topUps: 'bg-primary/30',\n autoRoom: 'bg-muted',\n}\n\nconst HATCH = {\n backgroundImage: 'repeating-linear-gradient(135deg, currentColor 0 1.5px, transparent 1.5px 5px)',\n} as const\n\n/** The segmented bar of an allowance meter: used, the plan mark, top-ups, and the automatic room. */\nfunction AllowanceBar({ allowance, label }: { allowance: UsageMeterAllowance; label: string }) {\n const { used, plan } = allowance\n const topUps = Math.max(0, allowance.topUps ?? 0)\n const room = Math.max(0, allowance.autoRoom ?? 0)\n const total = plan + topUps\n const scale = Math.max(total + room, used, Number.EPSILON)\n const state = usageMeterState(used, total)\n const usedPct = total > 0 ? Math.round((used / total) * 100) : used > 0 ? 100 : 0\n const markPlan = plan > 0 && plan < scale && (topUps > 0 || room > 0)\n return (\n <div className=\"relative h-2 overflow-hidden rounded-full bg-muted\" data-slot=\"usage-meter-bar\">\n {topUps > 0 && (\n <div\n className={cn('absolute inset-y-0', SWATCH.topUps)}\n style={{ left: `${pctOf(plan, scale)}%`, width: `${pctOf(topUps, scale)}%` }}\n data-slot=\"usage-meter-topups\"\n />\n )}\n {room > 0 && (\n <div\n className=\"absolute inset-y-0 text-muted-foreground/35\"\n style={{ left: `${pctOf(total, scale)}%`, width: `${pctOf(room, scale)}%`, ...HATCH }}\n data-slot=\"usage-meter-auto-room\"\n />\n )}\n <div\n className={cn(\n 'absolute inset-y-0 left-0 rounded-full transition-[width]',\n STATE_TONE[state]\n )}\n style={{ width: `${pctOf(Math.min(used, scale), scale)}%` }}\n role=\"progressbar\"\n aria-valuenow={Math.min(usedPct, 100)}\n aria-valuemin={0}\n aria-valuemax={100}\n aria-label={label}\n data-state={state}\n />\n {markPlan && (\n <div\n className=\"absolute inset-y-0 w-0.5 -translate-x-1/2 bg-foreground/70\"\n style={{ left: `${pctOf(plan, scale)}%` }}\n aria-hidden\n data-slot=\"usage-meter-plan-mark\"\n />\n )}\n {room > 0 && total > 0 && (\n <div\n className=\"absolute inset-y-0 w-px bg-foreground/40\"\n style={{ left: `${pctOf(total, scale)}%` }}\n aria-hidden\n />\n )}\n </div>\n )\n}\n\n/**\n * One labelled quota meter: icon + label on the left, used-of-limit on the right, a thin bar underneath.\n *\n * Simple mode (`pct`): the bar's tone follows pressure with the shared green → yellow → red gradation — green while\n * there's room, yellow past the halfway mark, red at the limit — so a stack of meters reads at a glance which quota\n * is the one to think about.\n *\n * Allowance mode (`allowance`): the bar spans the allowance plus what automatic top-ups may still add; it marks the\n * plan's part, shades the top-ups, hatches the automatic room, and fills what was used (red at the allowance, yellow\n * from 80%). `legend` names the segments; `details` (next period, cap) sit under the bar and `actions` (+ / −) beside\n * it. The meter's `data-state` is `under` / `near` / `over`.\n */\nexport function UsageMeter({\n icon: Icon,\n label,\n value,\n pct = 0,\n hint,\n allowance,\n legend,\n details,\n actions,\n className,\n testId,\n}: {\n icon?: LucideIcon\n label: string\n /** Formatted used-of-limit text, e.g. \"1,200 / 25,000\" or \"40 / 500 MB\". */\n value: string\n /** Simple mode: fill percentage, 0–100 (values are clamped). Ignored with `allowance`. */\n pct?: number\n /** Inline parenthetical after the label, e.g. \"resets May 31, 2025\" —\n * same font as the label, so the meter never grows an extra row. */\n hint?: string\n /** Allowance mode: the allowance's parts, in the meter's unit. */\n allowance?: UsageMeterAllowance\n /** Formatted legend entries for the allowance's segments (each shown only when set). */\n legend?: { plan?: string; topUps?: string; autoRoom?: string }\n details?: ReactNode\n actions?: ReactNode\n className?: string\n testId?: string\n}) {\n const clamped = Math.max(0, Math.min(100, pct))\n // Shared green → yellow → red gradation (same thresholds as the project-card\n // quota badges): green with room, yellow past the halfway mark, red at limit.\n const tone = clamped >= 100 ? 'bg-red-500' : clamped >= 50 ? 'bg-yellow-500' : 'bg-primary'\n const state = allowance\n ? usageMeterState(allowance.used, allowance.plan + Math.max(0, allowance.topUps ?? 0))\n : undefined\n const legendItems = legend\n ? (\n [\n ['plan', legend.plan],\n ['topUps', legend.topUps],\n ['autoRoom', legend.autoRoom],\n ] as const\n ).filter(([, text]) => !!text)\n : []\n\n const meter = (\n <div className=\"min-w-0 flex-1 space-y-1.5\">\n <div className=\"flex items-center justify-between gap-3 text-sm\">\n <span\n className={cn(\n 'flex min-w-0 items-center gap-1.5',\n allowance ? 'font-medium text-foreground' : 'text-muted-foreground'\n )}\n >\n {Icon && <Icon className=\"size-3.5 shrink-0\" />}\n <span className=\"truncate\">\n {label}\n {hint ? ` (${hint})` : ''}\n </span>\n </span>\n <span\n className=\"shrink-0 font-medium tabular-nums text-foreground\"\n data-slot=\"usage-meter-value\"\n >\n {value}\n </span>\n </div>\n {allowance ? (\n <AllowanceBar allowance={allowance} label={label} />\n ) : (\n <div className=\"h-1.5 overflow-hidden rounded-full bg-muted\">\n <div\n className={cn('h-full rounded-full transition-[width]', tone)}\n style={{ width: `${clamped}%` }}\n role=\"progressbar\"\n aria-valuenow={clamped}\n aria-valuemin={0}\n aria-valuemax={100}\n aria-label={label}\n />\n </div>\n )}\n {legendItems.length > 0 && (\n <ul\n className=\"flex flex-wrap gap-x-4 gap-y-1 text-xs tabular-nums text-muted-foreground\"\n data-slot=\"usage-meter-legend\"\n >\n {legendItems.map(([key, text]) => (\n <li\n key={key}\n className=\"flex items-center gap-1.5\"\n data-slot={`usage-meter-legend-${key}`}\n >\n <span\n className={cn(\n 'inline-block size-2 rounded-sm',\n SWATCH[key],\n key === 'autoRoom' && 'text-muted-foreground/60'\n )}\n style={key === 'autoRoom' ? HATCH : undefined}\n aria-hidden\n />\n {text}\n </li>\n ))}\n </ul>\n )}\n {details && <div className=\"space-y-1 text-xs text-muted-foreground\">{details}</div>}\n </div>\n )\n\n return (\n <div\n className={cn(\n actions ? 'flex flex-wrap items-start gap-x-6 gap-y-3' : 'space-y-1.5',\n className\n )}\n data-testid={testId}\n data-state={state}\n >\n {meter}\n {actions && (\n <div\n className=\"flex shrink-0 flex-col items-stretch gap-2 sm:w-64\"\n data-slot=\"usage-meter-actions\"\n >\n {actions}\n </div>\n )}\n </div>\n )\n}\n"]}
@@ -1,21 +1,58 @@
1
+ import type { ReactNode } from 'react';
1
2
  import type { LucideIcon } from 'lucide-react';
2
3
  /**
3
- * One labelled quota meter: icon + label on the left, used-of-limit on the
4
- * right, a thin bar underneath. The bar's tone follows pressure with the shared
5
- * green → yellow → red gradation — green while there's room, yellow past the
6
- * halfway mark, red at the limit — so a stack of meters reads at a glance which
7
- * quota is the one to think about.
4
+ * What an allowance is made of, in the meter's own unit (£ of AI usage, GB, users): what was used, the plan's part
5
+ * of the allowance, the top-ups on top of it, and how much more automatic top-ups may still add before a usage cap
6
+ * (or budget) stops them.
8
7
  */
9
- export declare function UsageMeter({ icon: Icon, label, value, pct, hint, className, testId, }: {
8
+ export interface UsageMeterAllowance {
9
+ used: number;
10
+ /** The plan's part of the allowance (marked on the bar when top-ups or automatic room follow it). */
11
+ plan: number;
12
+ /** Top-ups on top of the plan: the allowance now is `plan + topUps`. */
13
+ topUps?: number;
14
+ /**
15
+ * Room automatic top-ups may still add before the cap stops them (drawn hatched after the allowance).
16
+ * `null` = no limit (nothing drawn: the room is unbounded); omitted = not applicable.
17
+ */
18
+ autoRoom?: number | null;
19
+ }
20
+ /** Pressure on an allowance: under 80% used, 80% to under 100%, or at / over it. */
21
+ export type UsageMeterState = 'under' | 'near' | 'over';
22
+ /** The allowance meter's pressure state for `used` of `allowance`. */
23
+ export declare function usageMeterState(used: number, allowance: number): UsageMeterState;
24
+ /**
25
+ * One labelled quota meter: icon + label on the left, used-of-limit on the right, a thin bar underneath.
26
+ *
27
+ * Simple mode (`pct`): the bar's tone follows pressure with the shared green → yellow → red gradation — green while
28
+ * there's room, yellow past the halfway mark, red at the limit — so a stack of meters reads at a glance which quota
29
+ * is the one to think about.
30
+ *
31
+ * Allowance mode (`allowance`): the bar spans the allowance plus what automatic top-ups may still add; it marks the
32
+ * plan's part, shades the top-ups, hatches the automatic room, and fills what was used (red at the allowance, yellow
33
+ * from 80%). `legend` names the segments; `details` (next period, cap) sit under the bar and `actions` (+ / −) beside
34
+ * it. The meter's `data-state` is `under` / `near` / `over`.
35
+ */
36
+ export declare function UsageMeter({ icon: Icon, label, value, pct, hint, allowance, legend, details, actions, className, testId, }: {
10
37
  icon?: LucideIcon;
11
38
  label: string;
12
39
  /** Formatted used-of-limit text, e.g. "1,200 / 25,000" or "40 / 500 MB". */
13
40
  value: string;
14
- /** Fill percentage, 0–100 (values are clamped). */
15
- pct: number;
41
+ /** Simple mode: fill percentage, 0–100 (values are clamped). Ignored with `allowance`. */
42
+ pct?: number;
16
43
  /** Inline parenthetical after the label, e.g. "resets May 31, 2025" —
17
44
  * same font as the label, so the meter never grows an extra row. */
18
45
  hint?: string;
46
+ /** Allowance mode: the allowance's parts, in the meter's unit. */
47
+ allowance?: UsageMeterAllowance;
48
+ /** Formatted legend entries for the allowance's segments (each shown only when set). */
49
+ legend?: {
50
+ plan?: string;
51
+ topUps?: string;
52
+ autoRoom?: string;
53
+ };
54
+ details?: ReactNode;
55
+ actions?: ReactNode;
19
56
  className?: string;
20
57
  testId?: string;
21
58
  }): import("react").JSX.Element;
package/dist/index.cjs CHANGED
@@ -3,8 +3,8 @@
3
3
 
4
4
  var chunkXAJIKT6Y_cjs = require('./chunk-XAJIKT6Y.cjs');
5
5
  var chunkQI6AIHWP_cjs = require('./chunk-QI6AIHWP.cjs');
6
+ var chunkA7NCSZAP_cjs = require('./chunk-A7NCSZAP.cjs');
6
7
  var chunkS6BOINC4_cjs = require('./chunk-S6BOINC4.cjs');
7
- var chunk46IU3UPF_cjs = require('./chunk-46IU3UPF.cjs');
8
8
  var chunk65THHQVB_cjs = require('./chunk-65THHQVB.cjs');
9
9
  var chunk7QDX2CO4_cjs = require('./chunk-7QDX2CO4.cjs');
10
10
  var chunkMJCCBMM5_cjs = require('./chunk-MJCCBMM5.cjs');
@@ -971,6 +971,14 @@ Object.defineProperty(exports, "stripMarkdownLinks", {
971
971
  enumerable: true,
972
972
  get: function () { return chunkQI6AIHWP_cjs.stripMarkdownLinks; }
973
973
  });
974
+ Object.defineProperty(exports, "UsageMeter", {
975
+ enumerable: true,
976
+ get: function () { return chunkA7NCSZAP_cjs.UsageMeter; }
977
+ });
978
+ Object.defineProperty(exports, "usageMeterState", {
979
+ enumerable: true,
980
+ get: function () { return chunkA7NCSZAP_cjs.usageMeterState; }
981
+ });
974
982
  Object.defineProperty(exports, "ActionCluster", {
975
983
  enumerable: true,
976
984
  get: function () { return chunkS6BOINC4_cjs.ActionCluster; }
@@ -1147,10 +1155,6 @@ Object.defineProperty(exports, "workspaceKindMeta", {
1147
1155
  enumerable: true,
1148
1156
  get: function () { return chunkS6BOINC4_cjs.workspaceKindMeta; }
1149
1157
  });
1150
- Object.defineProperty(exports, "UsageMeter", {
1151
- enumerable: true,
1152
- get: function () { return chunk46IU3UPF_cjs.UsageMeter; }
1153
- });
1154
1158
  Object.defineProperty(exports, "ArticleList", {
1155
1159
  enumerable: true,
1156
1160
  get: function () { return chunk65THHQVB_cjs.ArticleList; }
package/dist/index.d.ts CHANGED
@@ -13,7 +13,7 @@ export { formatCurrency, initials, fmtDate, fmtDateTime, fromNow, daysUntil, hrs
13
13
  export { DataTable, type Column, type DataTableProps, type DataTableSort, type DataTableTestIds, } from './components/DataTable';
14
14
  export { DataTableBlock, type DataTableBlockProps, type DataTableBlockColumn, } from './components/DataTableBlock';
15
15
  export { EmptyState } from './components/EmptyState';
16
- export { UsageMeter } from './components/UsageMeter';
16
+ export { UsageMeter, usageMeterState, type UsageMeterAllowance, type UsageMeterState, } from './components/UsageMeter';
17
17
  export { Timeline, TimelineCalendar, TimelineGantt, TimelineZoomControl, TIMELINE_ZOOM, formatSpan, parseIso, utcDay, dayKey, monthLabel, type TimelineItem, type TimelineProps, type TimelineViewMode, type TimelineCalendarProps, type TimelineGanttProps, type TimelineZoomControlProps, } from './timeline';
18
18
  export { PageHeader, type PageHeaderProps } from './components/PageHeader';
19
19
  export { SectionHeading } from './components/SectionHeading';
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  "use client";
2
2
  export { MemberAvatarStack } from './chunk-XNOASBGW.js';
3
3
  export { ApplyLabelButton, ApplyLabelDialog, DEFAULT_APPLY_LABEL_DIALOG_LABELS, DEFAULT_RELEVANCE_STEPS, DEFAULT_SEARCH_BAR_LABELS, DEFAULT_SEARCH_EMPTY_STATE_LABELS, DEFAULT_SEARCH_GROUP_HEADER_LABELS, DEFAULT_SEARCH_NO_RESULTS_LABELS, DEFAULT_SEARCH_PAGINATION_LABELS, DEFAULT_SEARCH_TOTALS_LABELS, DOCUMENT_SEARCH_NOUNS, Highlight, RelevanceThreshold, SearchBar, SearchControlsBar, SearchEmptyState, SearchErrorBanner, SearchGroupCard, SearchGroupHeader, SearchNoResults, SearchPageBody, SearchPageFrame, SearchPageHeader, SearchPagination, SearchPillToggle, SearchResultList, SearchResultsInner, SearchResultsMain, SearchToolbarRow, SearchTotalsHeadline, getPageNumbers, highlightTerms, searchNouns, splitByTerms, stripMarkdownLinks } from './chunk-2MJWWPHY.js';
4
+ export { UsageMeter, usageMeterState } from './chunk-IBZJYCYM.js';
4
5
  export { ActionCluster, AskField, AskRow, AskTrigger, DEFAULT_ASK_FIELD_LABELS, DEFAULT_INSTALL_PROMPT_LABELS, DEFAULT_MOBILE_BARS_LABELS, DEFAULT_OFFLINE_PILL_LABELS, DEFAULT_REMOTE_CONTROL_PILL_LABELS, DEFAULT_SIDEBAR_LABELS, DEFAULT_TIP_CARD_LABELS, DEFAULT_WORKSPACE_KIND_LABELS, DEFAULT_WORKSPACE_SWITCHER_LABELS, INSTALL_PROMPT_DISMISSED_KEY, InfoBar, InstallPrompt, MobileAppBar, MobileTabBar, NotificationBell, OfflinePill, RemoteControlPill, STATUS_ROW_BASE_CLASS, STATUS_ROW_TONES, Sidebar, SidebarBadgeRow, SidebarRowAction, SidebarRowHoverContext, SplitLayout, StatusBadge, StatusRow, TipCard, UserMenu, WorkspaceIdentity, WorkspaceSwitcher, getInstallInstructions, initInstallPrompt, isIosDevice, promptAppInstall, resolveSidebarLayout, useInstallPrompt, useSidebarContext, useSidebarSettle, workspaceKindColor, workspaceKindMeta } from './chunk-D3N67Q4D.js';
5
- export { UsageMeter } from './chunk-D5T53XCL.js';
6
6
  export { ArticleList, Chart, CheckList, CollectionDetail, CtaBand, DefinitionList, EditorialHeading, EditorialHero, EditorialProse, EditorialSection, Eyebrow, PanelGrid, ProofStrip, PullQuote, Reveal, TagList } from './chunk-LYUO3E6V.js';
7
7
  export { DataTable, DataTableBlock, MetricCard, PageHeader, StatGroup } from './chunk-ZCF2JJFA.js';
8
8
  export { Accordion, AvatarBlock, Banner, BlockImage, BlockLink, CTABanner, Columns, ContactForm, Container, FAQ, FONT_PAIRINGS, FeatureGrid, Footer, Gallery, Hero, ListBlock, LogoCloud, MediaText, Navbar, PricingTable, ProfileCard, RichTextBlock, SectionHeading, Spacer, Steps, THEME_STYLE_ID, Tabs, TeamGrid, Testimonial, TestimonialGrid, ThemeEditor, VideoEmbed, applyFontVars, applyStoredTheme, applyThemeVars, clearFontVars, clearThemeVars, createThemeStore, fontPackages, getFontPairing, matchFontPairing, tid, toEmbedUrl, toHslTriplet } from './chunk-DLBUFHLY.js';