@ttt-productions/ui-core 0.11.17 → 0.11.19

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 (55) hide show
  1. package/dist/react/components/consequence-dialog.d.ts.map +1 -1
  2. package/dist/react/components/consequence-dialog.js +7 -2
  3. package/dist/react/components/consequence-dialog.js.map +1 -1
  4. package/package.json +2 -4
  5. package/src/index.ts +0 -3
  6. package/src/lib/format-numbers.ts +0 -17
  7. package/src/lib/utils.ts +0 -9
  8. package/src/react/components/accordion.tsx +0 -53
  9. package/src/react/components/alert-dialog.tsx +0 -131
  10. package/src/react/components/alert.tsx +0 -59
  11. package/src/react/components/avatar.tsx +0 -51
  12. package/src/react/components/badge.tsx +0 -31
  13. package/src/react/components/button.tsx +0 -102
  14. package/src/react/components/card.tsx +0 -54
  15. package/src/react/components/checkbox.tsx +0 -28
  16. package/src/react/components/chunk-error-recovery.tsx +0 -57
  17. package/src/react/components/collapsible.tsx +0 -11
  18. package/src/react/components/consequence-dialog.tsx +0 -274
  19. package/src/react/components/date-picker.tsx +0 -567
  20. package/src/react/components/dialog.tsx +0 -114
  21. package/src/react/components/dropdown-menu.tsx +0 -200
  22. package/src/react/components/end-of-list-indicator.tsx +0 -27
  23. package/src/react/components/form.tsx +0 -174
  24. package/src/react/components/input.tsx +0 -23
  25. package/src/react/components/label.tsx +0 -21
  26. package/src/react/components/layout/screen-adaptive-view.tsx +0 -106
  27. package/src/react/components/list-pagination.tsx +0 -101
  28. package/src/react/components/menubar.tsx +0 -220
  29. package/src/react/components/popover.tsx +0 -31
  30. package/src/react/components/progress.tsx +0 -27
  31. package/src/react/components/radio-group.tsx +0 -36
  32. package/src/react/components/relative-time.tsx +0 -56
  33. package/src/react/components/scroll-area.tsx +0 -56
  34. package/src/react/components/scroll-to-top-button.tsx +0 -85
  35. package/src/react/components/search-dropdown.tsx +0 -233
  36. package/src/react/components/select.tsx +0 -165
  37. package/src/react/components/separator.tsx +0 -26
  38. package/src/react/components/sheet.tsx +0 -140
  39. package/src/react/components/skeleton.tsx +0 -16
  40. package/src/react/components/slider.tsx +0 -25
  41. package/src/react/components/spinner.tsx +0 -49
  42. package/src/react/components/switch.tsx +0 -41
  43. package/src/react/components/table.tsx +0 -121
  44. package/src/react/components/tabs.tsx +0 -59
  45. package/src/react/components/textarea.tsx +0 -22
  46. package/src/react/components/toast.tsx +0 -193
  47. package/src/react/components/toaster.tsx +0 -43
  48. package/src/react/components/tooltip.tsx +0 -28
  49. package/src/react/hooks/use-async-action.ts +0 -63
  50. package/src/react/hooks/use-cursor-page.ts +0 -139
  51. package/src/react/hooks/use-media-query.tsx +0 -26
  52. package/src/react/hooks/use-paged-list.ts +0 -106
  53. package/src/react/hooks/use-return-scroll.ts +0 -149
  54. package/src/react/hooks/use-toast.tsx +0 -176
  55. package/src/react/index.ts +0 -85
@@ -1,28 +0,0 @@
1
- "use client"
2
-
3
- import * as React from "react"
4
- import * as CheckboxPrimitive from "@radix-ui/react-checkbox"
5
- import { Check } from "lucide-react"
6
-
7
- import { cn } from "../../lib/utils.js"
8
-
9
- const Checkbox = React.forwardRef<
10
- React.ElementRef<typeof CheckboxPrimitive.Root>,
11
- React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root>
12
- >(({ className, ...props }, ref) => (
13
- <CheckboxPrimitive.Root
14
- ref={ref}
15
- className={cn(
16
- "peer h-4 w-4 shrink-0 rounded-xs border border-primary ring-offset-background focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
17
- className
18
- )}
19
- {...props}
20
- >
21
- <CheckboxPrimitive.Indicator className={cn("flex items-center justify-center text-current")}>
22
- <Check className="h-4 w-4" />
23
- </CheckboxPrimitive.Indicator>
24
- </CheckboxPrimitive.Root>
25
- ))
26
- Checkbox.displayName = CheckboxPrimitive.Root.displayName
27
-
28
- export { Checkbox }
@@ -1,57 +0,0 @@
1
- "use client";
2
-
3
- import * as React from "react";
4
- import { Spinner } from "./spinner.js";
5
-
6
- export interface ChunkErrorRecoveryProps {
7
- children: React.ReactNode;
8
- /** Custom message shown during recovery reload. */
9
- loadingMessage?: React.ReactNode;
10
- /**
11
- * Predicate to decide whether an error event indicates a stale-chunk failure.
12
- * Defaults to matching "Failed to load chunk" in the message.
13
- */
14
- isChunkError?: (event: ErrorEvent) => boolean;
15
- }
16
-
17
- function defaultIsChunkError(event: ErrorEvent): boolean {
18
- const msg = event.message ?? "";
19
- if (msg.includes("Failed to load chunk")) return true;
20
- const nested =
21
- event.error && typeof event.error === "object"
22
- ? (event.error as { message?: string }).message
23
- : undefined;
24
- return typeof nested === "string" && nested.includes("Failed to load chunk");
25
- }
26
-
27
- export function ChunkErrorRecovery({
28
- children,
29
- loadingMessage = "Updating…",
30
- isChunkError = defaultIsChunkError,
31
- }: ChunkErrorRecoveryProps) {
32
- const [hasError, setHasError] = React.useState(false);
33
-
34
- React.useEffect(() => {
35
- const handler = (event: ErrorEvent) => {
36
- if (!isChunkError(event)) return;
37
-
38
- console.error("Stale chunk detected — reloading.");
39
- setHasError(true);
40
- window.location.reload();
41
- event.preventDefault();
42
- event.stopPropagation();
43
- };
44
- window.addEventListener("error", handler);
45
- return () => window.removeEventListener("error", handler);
46
- }, [isChunkError]);
47
-
48
- if (hasError) {
49
- return (
50
- <div className="flex flex-col h-screen w-screen items-center justify-center gap-4 bg-background">
51
- <Spinner size="xl" />
52
- <p className="text-muted-foreground">{loadingMessage}</p>
53
- </div>
54
- );
55
- }
56
- return <>{children}</>;
57
- }
@@ -1,11 +0,0 @@
1
- "use client"
2
-
3
- import * as CollapsiblePrimitive from "@radix-ui/react-collapsible"
4
-
5
- const Collapsible = CollapsiblePrimitive.Root
6
-
7
- const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger
8
-
9
- const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent
10
-
11
- export { Collapsible, CollapsibleTrigger, CollapsibleContent }
@@ -1,274 +0,0 @@
1
- 'use client';
2
-
3
- import { useCallback, useId, useState } from 'react';
4
- import {
5
- AlertDialog,
6
- AlertDialogTrigger,
7
- AlertDialogContent,
8
- AlertDialogHeader,
9
- AlertDialogFooter,
10
- AlertDialogTitle,
11
- AlertDialogDescription,
12
- } from './alert-dialog.js';
13
- import { Button } from './button.js';
14
- import { Textarea } from './textarea.js';
15
- import { Input } from './input.js';
16
- import { Label } from './label.js';
17
- import { Zap, Clock, RotateCcw } from 'lucide-react';
18
-
19
- /**
20
- * The inline required-reason config. The caller OWNS the reason value/onChange (draft state lives at
21
- * the call site). Any `minLength`/`maxLength` the caller supplies MUST come from a caller-owned
22
- * constant — this component declares no business numbers and no copy; it only derives the gate + the
23
- * Textarea `maxLength` from the values it is handed.
24
- */
25
- export interface ConsequenceDialogReason {
26
- required?: boolean;
27
- minLength?: number;
28
- maxLength?: number;
29
- placeholder?: string;
30
- label?: React.ReactNode;
31
- value: string;
32
- onChange: (value: string) => void;
33
- rows?: number;
34
- }
35
-
36
- /**
37
- * The exact-match typed-confirmation gate. `phrase` is business copy owned by the CALLER; the
38
- * component only compares the typed value against it.
39
- */
40
- export interface ConsequenceDialogTypedConfirmation {
41
- phrase: string;
42
- label?: React.ReactNode;
43
- }
44
-
45
- export interface ConsequenceDialogProps {
46
- /** Uncontrolled open model: the element that opens the dialog (rendered `asChild`). */
47
- trigger?: React.ReactNode;
48
- /** Controlled open model. Provide with `onOpenChange`. */
49
- open?: boolean;
50
- onOpenChange?: (open: boolean) => void;
51
-
52
- /** Heading — a string or node so callers can interpolate amounts / names. */
53
- title: React.ReactNode;
54
-
55
- /** SLOT 1 — what happens right now (e.g. "initiates a Stripe refund of $X now"). */
56
- immediateEffect?: React.ReactNode;
57
- /** SLOT 2 — what settles later, out of band (webhook / worker / async confirmation). */
58
- delayedEffect?: React.ReactNode;
59
- /** SLOT 3 — whether/how this can be undone or superseded. */
60
- reversibility?: React.ReactNode;
61
-
62
- /** Inline required-reason field. Optional. */
63
- reason?: ConsequenceDialogReason;
64
- /** Exact-phrase typed-confirmation gate. Optional. */
65
- typedConfirmation?: ConsequenceDialogTypedConfirmation;
66
-
67
- confirmLabel: React.ReactNode;
68
- cancelLabel?: React.ReactNode;
69
- /** `true` styles the confirm button with `bg-destructive`. */
70
- destructive?: boolean;
71
-
72
- /**
73
- * Runs on confirm. May be async — while it is pending the dialog STAYS OPEN, both buttons disable,
74
- * and the confirm button shows its pending spinner. Closes on resolve. If it rejects the dialog
75
- * stays open so the caller's own surface (error toast, TOTP step-up) can show and the operator can
76
- * retry or cancel. This component takes no Sentry/monitoring dependency — every error path
77
- * belongs to the caller's `onConfirm`.
78
- */
79
- onConfirm: () => void | Promise<void>;
80
- }
81
-
82
- const SLOT_ICON_CLASS = 'icon-xs mt-0.5 shrink-0 text-muted-foreground';
83
-
84
- function ConsequenceRow({
85
- icon: Icon,
86
- label,
87
- children,
88
- }: {
89
- icon: typeof Zap;
90
- label: string;
91
- children: React.ReactNode;
92
- }) {
93
- return (
94
- <span className="flex items-start gap-2">
95
- <Icon className={SLOT_ICON_CLASS} aria-hidden="true" />
96
- <span className="stack-1 min-w-0">
97
- <span className="text-caption font-medium uppercase tracking-wide text-muted-foreground">
98
- {label}
99
- </span>
100
- <span className="text-small text-foreground">{children}</span>
101
- </span>
102
- </span>
103
- );
104
- }
105
-
106
- /**
107
- * A reusable confirm-before-a-consequential-action dialog. Three optional, consistently-styled
108
- * consequence slots (immediate / afterward / reversibility), an optional inline required-reason field,
109
- * and an optional exact-phrase typed-confirmation gate. Composes the ui-core AlertDialog primitives
110
- * (Radix) — it never re-implements them. Supports BOTH a `trigger` (uncontrolled) and `open` /
111
- * `onOpenChange` (controlled).
112
- *
113
- * Pending model: the confirm button is a plain `Button` (NOT `AlertDialogAction`), so Radix does not
114
- * auto-close on click; open state is managed here so the dialog stays open with a visible spinner
115
- * until `onConfirm` resolves.
116
- */
117
- export function ConsequenceDialog({
118
- trigger,
119
- open,
120
- onOpenChange,
121
- title,
122
- immediateEffect,
123
- delayedEffect,
124
- reversibility,
125
- reason,
126
- typedConfirmation,
127
- confirmLabel,
128
- cancelLabel = 'Cancel',
129
- destructive = false,
130
- onConfirm,
131
- }: ConsequenceDialogProps) {
132
- const isControlled = open !== undefined;
133
- const [internalOpen, setInternalOpen] = useState(false);
134
- const [typedValue, setTypedValue] = useState('');
135
- const [pending, setPending] = useState(false);
136
-
137
- const actualOpen = isControlled ? open : internalOpen;
138
-
139
- const reasonId = useId();
140
- const typedId = useId();
141
-
142
- const setOpenState = useCallback(
143
- (next: boolean) => {
144
- if (isControlled) onOpenChange?.(next);
145
- else setInternalOpen(next);
146
- },
147
- [isControlled, onOpenChange],
148
- );
149
-
150
- // Never allow an open-state change (Escape, cancel, controlled parent) to fight the pending action.
151
- const handleOpenChange = useCallback(
152
- (next: boolean) => {
153
- if (pending) return;
154
- setTypedValue(''); // reset the typed gate on every open/close
155
- setOpenState(next);
156
- },
157
- [pending, setOpenState],
158
- );
159
-
160
- const handleConfirm = useCallback(async () => {
161
- if (pending) return;
162
- setPending(true);
163
- try {
164
- await onConfirm();
165
- setTypedValue('');
166
- setOpenState(false);
167
- } catch {
168
- // Keep the dialog open — the caller's onConfirm surfaced the error (toast / step-up). The
169
- // operator can retry or cancel. Never rethrow, never monitor here.
170
- } finally {
171
- setPending(false);
172
- }
173
- }, [pending, onConfirm, setOpenState]);
174
-
175
- const reasonTrimmedLength = reason ? reason.value.trim().length : 0;
176
- const reasonProvided = reasonTrimmedLength > 0;
177
- const reasonOk = !reason
178
- ? true
179
- : reason.required && !reasonProvided
180
- ? false
181
- : !reasonProvided
182
- ? true // optional + empty is fine
183
- : (reason.minLength === undefined || reasonTrimmedLength >= reason.minLength) &&
184
- (reason.maxLength === undefined || reason.value.length <= reason.maxLength);
185
-
186
- const typedOk = !typedConfirmation ? true : typedValue === typedConfirmation.phrase;
187
-
188
- return (
189
- <AlertDialog open={actualOpen} onOpenChange={handleOpenChange}>
190
- {trigger ? <AlertDialogTrigger asChild>{trigger}</AlertDialogTrigger> : null}
191
- <AlertDialogContent
192
- onEscapeKeyDown={(e) => {
193
- if (pending) e.preventDefault();
194
- }}
195
- >
196
- <AlertDialogHeader>
197
- <AlertDialogTitle>{title}</AlertDialogTitle>
198
- <AlertDialogDescription asChild>
199
- <span className="stack-3">
200
- {immediateEffect ? (
201
- <ConsequenceRow icon={Zap} label="Immediately">
202
- {immediateEffect}
203
- </ConsequenceRow>
204
- ) : null}
205
- {delayedEffect ? (
206
- <ConsequenceRow icon={Clock} label="Afterward">
207
- {delayedEffect}
208
- </ConsequenceRow>
209
- ) : null}
210
- {reversibility ? (
211
- <ConsequenceRow icon={RotateCcw} label="Reversibility">
212
- {reversibility}
213
- </ConsequenceRow>
214
- ) : null}
215
- </span>
216
- </AlertDialogDescription>
217
- </AlertDialogHeader>
218
-
219
- {reason ? (
220
- <div className="stack-2">
221
- <Label htmlFor={reasonId}>{reason.label ?? 'Reason'}</Label>
222
- <Textarea
223
- id={reasonId}
224
- value={reason.value}
225
- onChange={(e) => reason.onChange(e.target.value)}
226
- placeholder={reason.placeholder}
227
- maxLength={reason.maxLength}
228
- rows={reason.rows ?? 3}
229
- disabled={pending}
230
- />
231
- </div>
232
- ) : null}
233
-
234
- {typedConfirmation ? (
235
- <div className="stack-2">
236
- <Label htmlFor={typedId}>
237
- {typedConfirmation.label ?? (
238
- <>
239
- Type <span className="font-mono">{typedConfirmation.phrase}</span> to confirm
240
- </>
241
- )}
242
- </Label>
243
- <Input
244
- id={typedId}
245
- value={typedValue}
246
- onChange={(e) => setTypedValue(e.target.value)}
247
- placeholder={typedConfirmation.phrase}
248
- disabled={pending}
249
- autoComplete="off"
250
- />
251
- </div>
252
- ) : null}
253
-
254
- <AlertDialogFooter>
255
- <Button
256
- variant="outline"
257
- onClick={() => handleOpenChange(false)}
258
- disabled={pending}
259
- >
260
- {cancelLabel}
261
- </Button>
262
- <Button
263
- variant={destructive ? 'destructive' : 'default'}
264
- onClick={handleConfirm}
265
- disabled={!reasonOk || !typedOk}
266
- pending={pending}
267
- >
268
- {confirmLabel}
269
- </Button>
270
- </AlertDialogFooter>
271
- </AlertDialogContent>
272
- </AlertDialog>
273
- );
274
- }