@olympusoss/canvas 2.20.2 → 3.2.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/README.md +73 -35
- package/package.json +46 -177
- package/src/cn.ts +3 -0
- package/src/index.ts +12 -603
- package/src/theme.ts +62 -0
- package/src/tokens.ts +11 -0
- package/styles/atoms/avatar.css +22 -0
- package/styles/atoms/badge.css +83 -0
- package/styles/atoms/breadcrumb.css +35 -0
- package/styles/atoms/button-group.css +23 -0
- package/styles/atoms/button.css +107 -0
- package/styles/atoms/checkbox.css +55 -0
- package/styles/atoms/combobox.css +75 -0
- package/styles/atoms/dropdown.css +54 -0
- package/styles/atoms/icon.css +8 -0
- package/styles/atoms/input-group.css +45 -0
- package/styles/atoms/input.css +56 -0
- package/styles/atoms/kbd.css +15 -0
- package/styles/atoms/pagination.css +48 -0
- package/styles/atoms/popover.css +14 -0
- package/styles/atoms/radio.css +28 -0
- package/styles/atoms/select.css +57 -0
- package/styles/atoms/separator.css +32 -0
- package/styles/atoms/skeleton.css +32 -0
- package/styles/atoms/spinner.css +26 -0
- package/styles/atoms/switch.css +45 -0
- package/styles/atoms/textarea.css +31 -0
- package/styles/atoms/tooltip.css +53 -0
- package/styles/atoms/typography.css +105 -0
- package/styles/base.css +17 -0
- package/styles/canvas.css +77 -52
- package/styles/molecules/alert.css +66 -0
- package/styles/molecules/card.css +58 -0
- package/styles/molecules/code-block.css +18 -0
- package/styles/molecules/empty-state.css +17 -0
- package/styles/molecules/field.css +27 -0
- package/styles/molecules/form.css +27 -0
- package/styles/molecules/page-header.css +52 -0
- package/styles/molecules/section-card.css +49 -0
- package/styles/molecules/stat-card.css +71 -0
- package/styles/molecules/toast.css +95 -0
- package/styles/organisms/app-shell.css +46 -0
- package/styles/organisms/calendar.css +73 -0
- package/styles/organisms/command.css +94 -0
- package/styles/organisms/data-table.css +142 -0
- package/styles/organisms/dialog.css +72 -0
- package/styles/organisms/filter-panel.css +58 -0
- package/styles/organisms/row-menu.css +69 -0
- package/styles/organisms/sheet.css +70 -0
- package/styles/organisms/sidebar.css +146 -0
- package/styles/organisms/stepper.css +63 -0
- package/styles/organisms/tabs.css +40 -0
- package/styles/organisms/topbar.css +24 -0
- package/styles/patterns/backdrops.css +35 -0
- package/styles/patterns/density.css +66 -0
- package/styles/patterns/focus.css +22 -0
- package/styles/patterns/glass.css +85 -0
- package/styles/patterns/high-contrast.css +70 -0
- package/styles/patterns/reduced-motion.css +12 -0
- package/styles/patterns/scrollbar.css +10 -0
- package/styles/reset.css +89 -0
- package/styles/tokens/colors.css +106 -0
- package/styles/tokens/motion.css +33 -0
- package/styles/tokens/radius.css +10 -0
- package/styles/tokens/shadows.css +35 -0
- package/styles/tokens/spacing.css +19 -0
- package/styles/tokens/typography.css +6 -0
- package/styles/tokens/z-index.css +12 -0
- package/styles/utilities/display.css +66 -0
- package/styles/utilities/flexbox.css +240 -0
- package/styles/utilities/gap.css +288 -0
- package/styles/utilities/grid.css +138 -0
- package/styles/utilities/position.css +78 -0
- package/styles/utilities/sizing.css +138 -0
- package/tsconfig.json +20 -21
- package/src/components/atoms/README.md +0 -11
- package/src/components/atoms/aspect-ratio.tsx +0 -32
- package/src/components/atoms/avatar.tsx +0 -98
- package/src/components/atoms/badge.tsx +0 -44
- package/src/components/atoms/brand-mark.tsx +0 -74
- package/src/components/atoms/button.tsx +0 -105
- package/src/components/atoms/checkbox.tsx +0 -63
- package/src/components/atoms/flex-box.tsx +0 -105
- package/src/components/atoms/icon.tsx +0 -34
- package/src/components/atoms/input.tsx +0 -92
- package/src/components/atoms/label.tsx +0 -41
- package/src/components/atoms/logo.tsx +0 -89
- package/src/components/atoms/progress.tsx +0 -55
- package/src/components/atoms/radio-group.tsx +0 -122
- package/src/components/atoms/scroll-area.tsx +0 -106
- package/src/components/atoms/section.tsx +0 -48
- package/src/components/atoms/separator.tsx +0 -45
- package/src/components/atoms/skeleton.tsx +0 -17
- package/src/components/atoms/slider.tsx +0 -93
- package/src/components/atoms/spinner.tsx +0 -47
- package/src/components/atoms/switch.tsx +0 -60
- package/src/components/atoms/textarea.tsx +0 -78
- package/src/components/atoms/toggle.tsx +0 -80
- package/src/components/charts/activity-heatmap.tsx +0 -186
- package/src/components/charts/axes.tsx +0 -21
- package/src/components/charts/chart-container.tsx +0 -254
- package/src/components/charts/chart-legend.tsx +0 -67
- package/src/components/charts/chart-tooltip.tsx +0 -161
- package/src/components/charts/chart-types.tsx +0 -49
- package/src/components/charts/containers.tsx +0 -11
- package/src/components/charts/data.tsx +0 -16
- package/src/components/charts/details.tsx +0 -25
- package/src/components/charts/dot-pulse.tsx +0 -61
- package/src/components/charts/gauge.tsx +0 -106
- package/src/components/charts/grids.tsx +0 -8
- package/src/components/charts/index.ts +0 -62
- package/src/components/charts/labeled-bar-list.tsx +0 -85
- package/src/components/charts/metric-breakdown.tsx +0 -316
- package/src/components/charts/references.tsx +0 -8
- package/src/components/charts/service-health-list.tsx +0 -85
- package/src/components/charts/sparkline-area.tsx +0 -80
- package/src/components/charts/sparkline.tsx +0 -52
- package/src/components/charts/stacked-bar.tsx +0 -104
- package/src/components/charts/text.tsx +0 -10
- package/src/components/charts/world-heat-map-inner.tsx +0 -317
- package/src/components/charts/world-heat-map.tsx +0 -184
- package/src/components/molecules/README.md +0 -12
- package/src/components/molecules/action-bar.tsx +0 -73
- package/src/components/molecules/activity-item.tsx +0 -74
- package/src/components/molecules/alert.tsx +0 -86
- package/src/components/molecules/animated-background.tsx +0 -92
- package/src/components/molecules/auth-shell.tsx +0 -95
- package/src/components/molecules/brand-lockup.tsx +0 -48
- package/src/components/molecules/breadcrumb.tsx +0 -157
- package/src/components/molecules/button-group.tsx +0 -104
- package/src/components/molecules/calendar.tsx +0 -217
- package/src/components/molecules/card.tsx +0 -102
- package/src/components/molecules/client-brand.tsx +0 -95
- package/src/components/molecules/code-block.tsx +0 -86
- package/src/components/molecules/countdown-button.tsx +0 -92
- package/src/components/molecules/empty-state.tsx +0 -56
- package/src/components/molecules/error-state.tsx +0 -42
- package/src/components/molecules/field-display.tsx +0 -35
- package/src/components/molecules/input-otp.tsx +0 -74
- package/src/components/molecules/launcher-card.tsx +0 -152
- package/src/components/molecules/loading-state.tsx +0 -36
- package/src/components/molecules/notification-item.tsx +0 -67
- package/src/components/molecules/notification-list.tsx +0 -45
- package/src/components/molecules/number-badge.tsx +0 -53
- package/src/components/molecules/or-separator.tsx +0 -38
- package/src/components/molecules/page-header.tsx +0 -88
- package/src/components/molecules/page-tabs.tsx +0 -94
- package/src/components/molecules/pagination.tsx +0 -150
- package/src/components/molecules/password-input.tsx +0 -83
- package/src/components/molecules/password-strength-meter.tsx +0 -104
- package/src/components/molecules/phone-input.tsx +0 -200
- package/src/components/molecules/search-bar.tsx +0 -64
- package/src/components/molecules/secret-field.tsx +0 -158
- package/src/components/molecules/section-card.tsx +0 -91
- package/src/components/molecules/social-buttons.tsx +0 -165
- package/src/components/molecules/stat-card.tsx +0 -100
- package/src/components/molecules/status-badge.tsx +0 -42
- package/src/components/molecules/stepper.tsx +0 -96
- package/src/components/molecules/table.tsx +0 -157
- package/src/components/molecules/terminal.tsx +0 -74
- package/src/components/molecules/toggle-group.tsx +0 -145
- package/src/components/molecules/tooltip.tsx +0 -155
- package/src/components/molecules/user-avatar-chip.tsx +0 -71
- package/src/components/organisms/README.md +0 -14
- package/src/components/organisms/accordion.tsx +0 -154
- package/src/components/organisms/alert-dialog.tsx +0 -277
- package/src/components/organisms/carousel.tsx +0 -244
- package/src/components/organisms/collapsible.tsx +0 -69
- package/src/components/organisms/command.tsx +0 -144
- package/src/components/organisms/context-menu.tsx +0 -339
- package/src/components/organisms/dashboard-grid.tsx +0 -369
- package/src/components/organisms/data-table.tsx +0 -330
- package/src/components/organisms/dialog.tsx +0 -312
- package/src/components/organisms/drawer.tsx +0 -123
- package/src/components/organisms/dropdown-menu.tsx +0 -440
- package/src/components/organisms/editors/code-editor.tsx +0 -144
- package/src/components/organisms/editors/index.ts +0 -4
- package/src/components/organisms/editors/markdown-editor.tsx +0 -153
- package/src/components/organisms/editors/markdown-renderer.ts +0 -27
- package/src/components/organisms/editors/prose-canvas-classes.ts +0 -45
- package/src/components/organisms/editors/rich-text-editor.tsx +0 -126
- package/src/components/organisms/editors/toolbar/md-toolbar.tsx +0 -129
- package/src/components/organisms/editors/toolbar/rte-toolbar.tsx +0 -211
- package/src/components/organisms/editors/toolbar/toolbar-shell.tsx +0 -45
- package/src/components/organisms/editors/use-codemirror-theme.ts +0 -61
- package/src/components/organisms/error-boundary.tsx +0 -61
- package/src/components/organisms/form.tsx +0 -174
- package/src/components/organisms/hover-card.tsx +0 -115
- package/src/components/organisms/menubar.tsx +0 -498
- package/src/components/organisms/navbar.tsx +0 -104
- package/src/components/organisms/navigation-menu.tsx +0 -235
- package/src/components/organisms/popover.tsx +0 -149
- package/src/components/organisms/resizable.tsx +0 -58
- package/src/components/organisms/schema-form.tsx +0 -232
- package/src/components/organisms/select.tsx +0 -309
- package/src/components/organisms/sheet.tsx +0 -265
- package/src/components/organisms/sidebar.tsx +0 -1040
- package/src/components/organisms/sonner.tsx +0 -96
- package/src/components/organisms/tabs.tsx +0 -133
- package/src/components/organisms/theme-provider.tsx +0 -101
- package/src/hooks/use-mobile.tsx +0 -19
- package/src/lib/portal-container.tsx +0 -35
- package/src/lib/utils.ts +0 -6
- package/src/native.ts +0 -23
- package/src/tokens/colors.ts +0 -91
- package/src/tokens/index.ts +0 -3
- package/src/tokens/spacing.ts +0 -55
- package/src/tokens/typography.ts +0 -27
- package/styles/dashboard-grid.css +0 -47
- package/styles/fonts/Roboto-VariableFont_wdth_wght.ttf +0 -0
- package/styles/glass.css +0 -175
- package/styles/leaflet.css +0 -13
- package/styles/tokens.css +0 -317
- package/tailwind.config.ts +0 -70
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import { Check, Copy } from "lucide-react";
|
|
4
|
-
import * as React from "react";
|
|
5
|
-
|
|
6
|
-
import { cn } from "../../lib/utils";
|
|
7
|
-
import { Button } from "../atoms/button";
|
|
8
|
-
|
|
9
|
-
export interface CodeBlockProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
10
|
-
code: string;
|
|
11
|
-
language?: string;
|
|
12
|
-
showCopy?: boolean;
|
|
13
|
-
/**
|
|
14
|
-
* `"light"` (default) renders against `bg-muted`; `"dark"` switches to a
|
|
15
|
-
* near-black terminal palette (`#0a0a0b` background, `#e4e4e7` text) for
|
|
16
|
-
* use on marketing surfaces.
|
|
17
|
-
*/
|
|
18
|
-
theme?: "light" | "dark";
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const CodeBlock = React.forwardRef<HTMLDivElement, CodeBlockProps>(
|
|
22
|
-
({ code, language, showCopy = true, theme = "light", className, ...props }, ref) => {
|
|
23
|
-
const [copied, setCopied] = React.useState(false);
|
|
24
|
-
const isDark = theme === "dark";
|
|
25
|
-
|
|
26
|
-
const handleCopy = React.useCallback(async () => {
|
|
27
|
-
await navigator.clipboard.writeText(code);
|
|
28
|
-
setCopied(true);
|
|
29
|
-
setTimeout(() => setCopied(false), 2000);
|
|
30
|
-
}, [code]);
|
|
31
|
-
|
|
32
|
-
return (
|
|
33
|
-
<div
|
|
34
|
-
ref={ref}
|
|
35
|
-
data-slot="code-block"
|
|
36
|
-
className={cn(
|
|
37
|
-
"relative overflow-hidden rounded-md",
|
|
38
|
-
isDark ? "border border-border" : "bg-muted",
|
|
39
|
-
className,
|
|
40
|
-
)}
|
|
41
|
-
style={isDark ? { background: "#0a0a0b" } : undefined}
|
|
42
|
-
{...props}
|
|
43
|
-
>
|
|
44
|
-
{(language || showCopy) && (
|
|
45
|
-
<div
|
|
46
|
-
className={cn(
|
|
47
|
-
"flex items-center justify-between px-4 py-2",
|
|
48
|
-
isDark ? undefined : "border-b",
|
|
49
|
-
)}
|
|
50
|
-
style={isDark ? { borderBottom: "1px solid #222" } : undefined}
|
|
51
|
-
>
|
|
52
|
-
{language ? (
|
|
53
|
-
<span
|
|
54
|
-
className={cn("text-xs font-medium", !isDark && "text-muted-foreground")}
|
|
55
|
-
style={isDark ? { color: "#6b7280" } : undefined}
|
|
56
|
-
>
|
|
57
|
-
{language}
|
|
58
|
-
</span>
|
|
59
|
-
) : (
|
|
60
|
-
<span />
|
|
61
|
-
)}
|
|
62
|
-
{showCopy && (
|
|
63
|
-
<Button
|
|
64
|
-
variant="ghost"
|
|
65
|
-
size="icon"
|
|
66
|
-
className={cn(
|
|
67
|
-
"h-6 w-6",
|
|
68
|
-
isDark && "text-zinc-400 hover:bg-white/5 hover:text-zinc-100",
|
|
69
|
-
)}
|
|
70
|
-
onClick={handleCopy}
|
|
71
|
-
>
|
|
72
|
-
{copied ? <Check className="h-3 w-3" /> : <Copy className="h-3 w-3" />}
|
|
73
|
-
</Button>
|
|
74
|
-
)}
|
|
75
|
-
</div>
|
|
76
|
-
)}
|
|
77
|
-
<pre className="overflow-x-auto p-4" style={isDark ? { color: "#e4e4e7" } : undefined}>
|
|
78
|
-
<code className="text-sm">{code}</code>
|
|
79
|
-
</pre>
|
|
80
|
-
</div>
|
|
81
|
-
);
|
|
82
|
-
},
|
|
83
|
-
);
|
|
84
|
-
CodeBlock.displayName = "CodeBlock";
|
|
85
|
-
|
|
86
|
-
export { CodeBlock };
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import * as React from "react";
|
|
4
|
-
|
|
5
|
-
import { cn } from "../../lib/utils";
|
|
6
|
-
import { Button, type ButtonProps } from "../atoms/button";
|
|
7
|
-
|
|
8
|
-
export interface CountdownButtonProps
|
|
9
|
-
extends Omit<ButtonProps, "disabled" | "onClick" | "children"> {
|
|
10
|
-
/**
|
|
11
|
-
* Seconds to wait before the button becomes clickable. The countdown
|
|
12
|
-
* starts when the component mounts and resets every time `triggerKey`
|
|
13
|
-
* changes.
|
|
14
|
-
* @default 30
|
|
15
|
-
*/
|
|
16
|
-
duration?: number;
|
|
17
|
-
/**
|
|
18
|
-
* Bump this value to restart the countdown (e.g. after a successful
|
|
19
|
-
* resend, set it to `Date.now()` so the button locks out again).
|
|
20
|
-
*/
|
|
21
|
-
triggerKey?: string | number;
|
|
22
|
-
/** Called when the button is clicked and the countdown has elapsed. */
|
|
23
|
-
onClick?: () => void;
|
|
24
|
-
/**
|
|
25
|
-
* Render-prop for the button label. Receives `secondsLeft` (0 when
|
|
26
|
-
* ready). Default renders `"Resend"` / `"Resend in 23s"`.
|
|
27
|
-
*/
|
|
28
|
-
children?: React.ReactNode | ((secondsLeft: number) => React.ReactNode);
|
|
29
|
-
/**
|
|
30
|
-
* Action verb shown in the default label. Used as `${verb}` when ready
|
|
31
|
-
* and `${verb} in Ns` while counting down.
|
|
32
|
-
* @default "Resend"
|
|
33
|
-
*/
|
|
34
|
-
verb?: string;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Button that locks out for a duration after mount or after `triggerKey`
|
|
39
|
-
* changes. Renders countdown text while disabled.
|
|
40
|
-
*
|
|
41
|
-
* Use for "resend code" / "resend email" flows where the user shouldn't be
|
|
42
|
-
* able to spam the action. Triggering a new send from elsewhere should
|
|
43
|
-
* update `triggerKey` to restart the lockout.
|
|
44
|
-
*/
|
|
45
|
-
const CountdownButton = React.forwardRef<HTMLButtonElement, CountdownButtonProps>(
|
|
46
|
-
(
|
|
47
|
-
{ duration = 30, triggerKey, onClick, children, verb = "Resend", className, ...buttonProps },
|
|
48
|
-
ref,
|
|
49
|
-
) => {
|
|
50
|
-
const [secondsLeft, setSecondsLeft] = React.useState(duration);
|
|
51
|
-
|
|
52
|
-
React.useEffect(() => {
|
|
53
|
-
setSecondsLeft(duration);
|
|
54
|
-
const interval = setInterval(() => {
|
|
55
|
-
setSecondsLeft((s) => {
|
|
56
|
-
if (s <= 1) {
|
|
57
|
-
clearInterval(interval);
|
|
58
|
-
return 0;
|
|
59
|
-
}
|
|
60
|
-
return s - 1;
|
|
61
|
-
});
|
|
62
|
-
}, 1000);
|
|
63
|
-
return () => clearInterval(interval);
|
|
64
|
-
}, [duration, triggerKey]);
|
|
65
|
-
|
|
66
|
-
const ready = secondsLeft <= 0;
|
|
67
|
-
const labelNode =
|
|
68
|
-
typeof children === "function"
|
|
69
|
-
? children(secondsLeft)
|
|
70
|
-
: children !== undefined
|
|
71
|
-
? children
|
|
72
|
-
: ready
|
|
73
|
-
? verb
|
|
74
|
-
: `${verb} in ${secondsLeft}s`;
|
|
75
|
-
|
|
76
|
-
return (
|
|
77
|
-
<Button
|
|
78
|
-
ref={ref}
|
|
79
|
-
type="button"
|
|
80
|
-
disabled={!ready}
|
|
81
|
-
onClick={() => ready && onClick?.()}
|
|
82
|
-
className={cn(className)}
|
|
83
|
-
{...buttonProps}
|
|
84
|
-
>
|
|
85
|
-
{labelNode}
|
|
86
|
-
</Button>
|
|
87
|
-
);
|
|
88
|
-
},
|
|
89
|
-
);
|
|
90
|
-
CountdownButton.displayName = "CountdownButton";
|
|
91
|
-
|
|
92
|
-
export { CountdownButton };
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import { Inbox } from "lucide-react";
|
|
4
|
-
import * as React from "react";
|
|
5
|
-
|
|
6
|
-
import { cn } from "../../lib/utils";
|
|
7
|
-
import { Button } from "../atoms/button";
|
|
8
|
-
|
|
9
|
-
export interface EmptyStateProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
10
|
-
icon?: React.ReactNode;
|
|
11
|
-
/** Primary message. Renders below the icon pill, 15px font-medium. */
|
|
12
|
-
title?: string;
|
|
13
|
-
/**
|
|
14
|
-
* @deprecated Use `title`. Retained as an alias so existing call sites
|
|
15
|
-
* (e.g. DataTable's empty fallback) keep working.
|
|
16
|
-
*/
|
|
17
|
-
message?: string;
|
|
18
|
-
description?: string;
|
|
19
|
-
action?: {
|
|
20
|
-
label: string;
|
|
21
|
-
onClick: () => void;
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const EmptyState = React.forwardRef<HTMLDivElement, EmptyStateProps>(
|
|
26
|
-
({ icon, title, message, description, action, className, ...props }, ref) => {
|
|
27
|
-
const heading = title ?? message ?? "No items found";
|
|
28
|
-
return (
|
|
29
|
-
<div
|
|
30
|
-
ref={ref}
|
|
31
|
-
data-slot="empty-state"
|
|
32
|
-
className={cn(
|
|
33
|
-
"flex flex-col items-center justify-center gap-3 py-12 text-muted-foreground",
|
|
34
|
-
className,
|
|
35
|
-
)}
|
|
36
|
-
{...props}
|
|
37
|
-
>
|
|
38
|
-
<div className="mb-1 inline-flex rounded-full bg-muted p-3 text-muted-foreground">
|
|
39
|
-
{icon ?? <Inbox className="h-5 w-5" />}
|
|
40
|
-
</div>
|
|
41
|
-
<div className="text-center">
|
|
42
|
-
<p className="text-[15px] font-medium text-foreground">{heading}</p>
|
|
43
|
-
{description && <p className="mt-1 text-xs text-muted-foreground">{description}</p>}
|
|
44
|
-
</div>
|
|
45
|
-
{action && (
|
|
46
|
-
<Button variant="outline" size="sm" onClick={action.onClick}>
|
|
47
|
-
{action.label}
|
|
48
|
-
</Button>
|
|
49
|
-
)}
|
|
50
|
-
</div>
|
|
51
|
-
);
|
|
52
|
-
},
|
|
53
|
-
);
|
|
54
|
-
EmptyState.displayName = "EmptyState";
|
|
55
|
-
|
|
56
|
-
export { EmptyState };
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import { AlertCircle } from "lucide-react";
|
|
4
|
-
import * as React from "react";
|
|
5
|
-
|
|
6
|
-
import { cn } from "../../lib/utils";
|
|
7
|
-
import { Button } from "../atoms/button";
|
|
8
|
-
|
|
9
|
-
export interface ErrorStateProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
10
|
-
message?: string;
|
|
11
|
-
onRetry?: () => void;
|
|
12
|
-
retryLabel?: string;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const ErrorState = React.forwardRef<HTMLDivElement, ErrorStateProps>(
|
|
16
|
-
(
|
|
17
|
-
{ message = "Something went wrong.", onRetry, retryLabel = "Try again", className, ...props },
|
|
18
|
-
ref,
|
|
19
|
-
) => {
|
|
20
|
-
return (
|
|
21
|
-
<div
|
|
22
|
-
ref={ref}
|
|
23
|
-
className={cn(
|
|
24
|
-
"flex flex-col items-center justify-center gap-3 py-8 text-muted-foreground",
|
|
25
|
-
className,
|
|
26
|
-
)}
|
|
27
|
-
{...props}
|
|
28
|
-
>
|
|
29
|
-
<AlertCircle className="h-8 w-8 text-destructive" />
|
|
30
|
-
<p className="text-sm">{message}</p>
|
|
31
|
-
{onRetry && (
|
|
32
|
-
<Button variant="outline" size="sm" onClick={onRetry}>
|
|
33
|
-
{retryLabel}
|
|
34
|
-
</Button>
|
|
35
|
-
)}
|
|
36
|
-
</div>
|
|
37
|
-
);
|
|
38
|
-
},
|
|
39
|
-
);
|
|
40
|
-
ErrorState.displayName = "ErrorState";
|
|
41
|
-
|
|
42
|
-
export { ErrorState };
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
|
|
3
|
-
import { cn } from "../../lib/utils";
|
|
4
|
-
|
|
5
|
-
export interface FieldDisplayProps extends React.HTMLAttributes<HTMLDListElement> {
|
|
6
|
-
label: string;
|
|
7
|
-
value?: React.ReactNode;
|
|
8
|
-
mono?: boolean;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
const FieldDisplay = React.forwardRef<HTMLDListElement, FieldDisplayProps>(
|
|
12
|
-
({ label, value, mono = false, className, ...props }, ref) => {
|
|
13
|
-
return (
|
|
14
|
-
<dl
|
|
15
|
-
ref={ref}
|
|
16
|
-
className={cn("grid grid-cols-[180px_1fr] items-baseline gap-4 py-2", className)}
|
|
17
|
-
{...props}
|
|
18
|
-
>
|
|
19
|
-
<dt className="text-[13px] font-medium text-muted-foreground">{label}</dt>
|
|
20
|
-
<dd
|
|
21
|
-
className={cn(
|
|
22
|
-
"break-words text-[13px]",
|
|
23
|
-
mono && "font-mono text-[12.5px]",
|
|
24
|
-
value ? "text-foreground" : "text-muted-foreground",
|
|
25
|
-
)}
|
|
26
|
-
>
|
|
27
|
-
{value ?? "—"}
|
|
28
|
-
</dd>
|
|
29
|
-
</dl>
|
|
30
|
-
);
|
|
31
|
-
},
|
|
32
|
-
);
|
|
33
|
-
FieldDisplay.displayName = "FieldDisplay";
|
|
34
|
-
|
|
35
|
-
export { FieldDisplay };
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import { OTPInput, OTPInputContext } from "input-otp";
|
|
4
|
-
import { Minus } from "lucide-react";
|
|
5
|
-
import * as React from "react";
|
|
6
|
-
|
|
7
|
-
import { cn } from "../../lib/utils";
|
|
8
|
-
|
|
9
|
-
const InputOTP = React.forwardRef<
|
|
10
|
-
React.ElementRef<typeof OTPInput>,
|
|
11
|
-
React.ComponentPropsWithoutRef<typeof OTPInput>
|
|
12
|
-
>(({ className, containerClassName, ...props }, ref) => (
|
|
13
|
-
<OTPInput
|
|
14
|
-
ref={ref}
|
|
15
|
-
containerClassName={cn(
|
|
16
|
-
"flex items-center gap-2 has-[:disabled]:opacity-50",
|
|
17
|
-
containerClassName,
|
|
18
|
-
)}
|
|
19
|
-
className={cn("disabled:cursor-not-allowed", className)}
|
|
20
|
-
{...props}
|
|
21
|
-
/>
|
|
22
|
-
));
|
|
23
|
-
InputOTP.displayName = "InputOTP";
|
|
24
|
-
|
|
25
|
-
const InputOTPGroup = React.forwardRef<
|
|
26
|
-
React.ElementRef<"div">,
|
|
27
|
-
React.ComponentPropsWithoutRef<"div">
|
|
28
|
-
>(({ className, ...props }, ref) => (
|
|
29
|
-
<div ref={ref} className={cn("flex items-center", className)} {...props} />
|
|
30
|
-
));
|
|
31
|
-
InputOTPGroup.displayName = "InputOTPGroup";
|
|
32
|
-
|
|
33
|
-
const InputOTPSlot = React.forwardRef<
|
|
34
|
-
React.ElementRef<"div">,
|
|
35
|
-
React.ComponentPropsWithoutRef<"div"> & { index: number }
|
|
36
|
-
>(({ index, className, ...props }, ref) => {
|
|
37
|
-
const inputOTPContext = React.useContext(OTPInputContext);
|
|
38
|
-
const { char, hasFakeCaret, isActive } = inputOTPContext.slots[index];
|
|
39
|
-
|
|
40
|
-
/* c8 ignore next -- hasFakeCaret is only set by the input-otp library for a live selection range, which jsdom's fireEvent doesn't produce */
|
|
41
|
-
const fakeCaret = hasFakeCaret ? (
|
|
42
|
-
<div className="pointer-events-none absolute inset-0 flex items-center justify-center">
|
|
43
|
-
<div className="h-4 w-px animate-caret-blink bg-foreground duration-1000" />
|
|
44
|
-
</div>
|
|
45
|
-
) : null;
|
|
46
|
-
|
|
47
|
-
return (
|
|
48
|
-
<div
|
|
49
|
-
ref={ref}
|
|
50
|
-
className={cn(
|
|
51
|
-
"relative flex h-9 w-9 items-center justify-center border-y border-r border-input text-sm shadow-sm transition-all first:rounded-l-md first:border-l last:rounded-r-md",
|
|
52
|
-
isActive && "z-10 ring-1 ring-ring",
|
|
53
|
-
className,
|
|
54
|
-
)}
|
|
55
|
-
{...props}
|
|
56
|
-
>
|
|
57
|
-
{char}
|
|
58
|
-
{fakeCaret}
|
|
59
|
-
</div>
|
|
60
|
-
);
|
|
61
|
-
});
|
|
62
|
-
InputOTPSlot.displayName = "InputOTPSlot";
|
|
63
|
-
|
|
64
|
-
const InputOTPSeparator = React.forwardRef<
|
|
65
|
-
React.ElementRef<"div">,
|
|
66
|
-
React.ComponentPropsWithoutRef<"div">
|
|
67
|
-
>(({ ...props }, ref) => (
|
|
68
|
-
<div ref={ref} role="separator" {...props}>
|
|
69
|
-
<Minus />
|
|
70
|
-
</div>
|
|
71
|
-
));
|
|
72
|
-
InputOTPSeparator.displayName = "InputOTPSeparator";
|
|
73
|
-
|
|
74
|
-
export { InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot };
|
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
// molecules: can import tokens/, lib/utils, atoms/.
|
|
2
|
-
"use client";
|
|
3
|
-
|
|
4
|
-
import * as React from "react";
|
|
5
|
-
|
|
6
|
-
import { cn } from "../../lib/utils";
|
|
7
|
-
|
|
8
|
-
const TONE_STYLES = {
|
|
9
|
-
default: {
|
|
10
|
-
bg: "hsl(var(--primary) / 0.1)",
|
|
11
|
-
fg: "hsl(var(--primary))",
|
|
12
|
-
ring: "hsl(var(--primary) / 0.2)",
|
|
13
|
-
},
|
|
14
|
-
indigo: {
|
|
15
|
-
bg: "hsl(231 92% 96%)",
|
|
16
|
-
fg: "hsl(231 60% 38%)",
|
|
17
|
-
ring: "hsl(231 80% 60% / 0.25)",
|
|
18
|
-
},
|
|
19
|
-
violet: {
|
|
20
|
-
bg: "hsl(262 90% 96%)",
|
|
21
|
-
fg: "hsl(262 55% 42%)",
|
|
22
|
-
ring: "hsl(262 80% 60% / 0.25)",
|
|
23
|
-
},
|
|
24
|
-
slate: {
|
|
25
|
-
bg: "hsl(230 25% 95%)",
|
|
26
|
-
fg: "hsl(230 30% 30%)",
|
|
27
|
-
ring: "hsl(230 20% 60% / 0.25)",
|
|
28
|
-
},
|
|
29
|
-
} as const;
|
|
30
|
-
|
|
31
|
-
export type LauncherCardTone = keyof typeof TONE_STYLES;
|
|
32
|
-
|
|
33
|
-
export interface LauncherCardProps {
|
|
34
|
-
/**
|
|
35
|
-
* Top-left identifier — typically a letter (`"C"`) or a small icon.
|
|
36
|
-
*/
|
|
37
|
-
badge: React.ReactNode;
|
|
38
|
-
/** Card heading. */
|
|
39
|
-
title: string;
|
|
40
|
-
/** One-or-two sentence body copy below the title. */
|
|
41
|
-
description: string;
|
|
42
|
-
/**
|
|
43
|
-
* Colour key for the badge background, foreground, and hover ring.
|
|
44
|
-
* Defaults to `"default"`, which uses the `--primary` token.
|
|
45
|
-
*/
|
|
46
|
-
tone?: LauncherCardTone;
|
|
47
|
-
/**
|
|
48
|
-
* If set, the entire card becomes a link with a hover-lift effect.
|
|
49
|
-
* Pair with `linkComponent` to route through a framework-specific Link
|
|
50
|
-
* component (e.g. Next.js).
|
|
51
|
-
*/
|
|
52
|
-
href?: string;
|
|
53
|
-
/**
|
|
54
|
-
* `target` attribute applied when `href` is set. Defaults to `"_self"`.
|
|
55
|
-
*/
|
|
56
|
-
target?: React.HTMLAttributeAnchorTarget;
|
|
57
|
-
/**
|
|
58
|
-
* `rel` attribute applied when `href` is set. Defaults to `"noopener"`
|
|
59
|
-
* when `target === "_blank"`.
|
|
60
|
-
*/
|
|
61
|
-
rel?: string;
|
|
62
|
-
/**
|
|
63
|
-
* Override the rendered link element when `href` is set
|
|
64
|
-
* (e.g. `Link` from `next/link`). Falls back to `<a>`.
|
|
65
|
-
*/
|
|
66
|
-
linkComponent?: React.ElementType;
|
|
67
|
-
/**
|
|
68
|
-
* Footer slot. Typically a small CTA row (e.g. arrow link) or, when the
|
|
69
|
-
* card is non-interactive, an inline status / detail block.
|
|
70
|
-
*/
|
|
71
|
-
children?: React.ReactNode;
|
|
72
|
-
className?: string;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const SHARED_CLASSES =
|
|
76
|
-
"group flex flex-col gap-4 rounded-[14px] border border-border bg-card p-6 text-card-foreground";
|
|
77
|
-
|
|
78
|
-
const INTERACTIVE_CLASSES =
|
|
79
|
-
"no-underline transition-[transform,box-shadow,border-color] duration-200 hover:-translate-y-0.5 hover:shadow-[0_20px_40px_-20px_var(--launcher-tone-ring),0_8px_16px_-8px_rgb(0_0_0/0.08)] hover:[border-color:var(--launcher-tone-fg)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2";
|
|
80
|
-
|
|
81
|
-
const LauncherCard = React.forwardRef<HTMLElement, LauncherCardProps>(
|
|
82
|
-
(
|
|
83
|
-
{
|
|
84
|
-
badge,
|
|
85
|
-
title,
|
|
86
|
-
description,
|
|
87
|
-
tone = "default",
|
|
88
|
-
href,
|
|
89
|
-
target,
|
|
90
|
-
rel,
|
|
91
|
-
linkComponent,
|
|
92
|
-
children,
|
|
93
|
-
className,
|
|
94
|
-
...props
|
|
95
|
-
},
|
|
96
|
-
ref,
|
|
97
|
-
) => {
|
|
98
|
-
const t = TONE_STYLES[tone];
|
|
99
|
-
const interactive = Boolean(href);
|
|
100
|
-
const toneVars = {
|
|
101
|
-
"--launcher-tone-fg": t.fg,
|
|
102
|
-
"--launcher-tone-ring": t.ring,
|
|
103
|
-
} as React.CSSProperties;
|
|
104
|
-
|
|
105
|
-
const inner = (
|
|
106
|
-
<>
|
|
107
|
-
<div className="flex items-center gap-3.5">
|
|
108
|
-
<div
|
|
109
|
-
className="flex h-11 w-11 items-center justify-center rounded-[10px] text-lg font-semibold tracking-tight"
|
|
110
|
-
style={{ background: t.bg, color: t.fg }}
|
|
111
|
-
>
|
|
112
|
-
{badge}
|
|
113
|
-
</div>
|
|
114
|
-
<div className="text-[17px] font-semibold tracking-tight">{title}</div>
|
|
115
|
-
</div>
|
|
116
|
-
<p className="m-0 flex-1 text-sm leading-relaxed text-muted-foreground">{description}</p>
|
|
117
|
-
{children != null && <div className="mt-1">{children}</div>}
|
|
118
|
-
</>
|
|
119
|
-
);
|
|
120
|
-
|
|
121
|
-
if (interactive) {
|
|
122
|
-
const LinkEl = linkComponent || "a";
|
|
123
|
-
const resolvedRel = rel ?? (target === "_blank" ? "noopener" : undefined);
|
|
124
|
-
return (
|
|
125
|
-
<LinkEl
|
|
126
|
-
ref={ref as React.Ref<HTMLAnchorElement>}
|
|
127
|
-
href={href}
|
|
128
|
-
target={target}
|
|
129
|
-
rel={resolvedRel}
|
|
130
|
-
className={cn(SHARED_CLASSES, INTERACTIVE_CLASSES, className)}
|
|
131
|
-
style={toneVars}
|
|
132
|
-
{...props}
|
|
133
|
-
>
|
|
134
|
-
{inner}
|
|
135
|
-
</LinkEl>
|
|
136
|
-
);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
return (
|
|
140
|
-
<div
|
|
141
|
-
ref={ref as React.Ref<HTMLDivElement>}
|
|
142
|
-
className={cn(SHARED_CLASSES, className)}
|
|
143
|
-
{...props}
|
|
144
|
-
>
|
|
145
|
-
{inner}
|
|
146
|
-
</div>
|
|
147
|
-
);
|
|
148
|
-
},
|
|
149
|
-
);
|
|
150
|
-
LauncherCard.displayName = "LauncherCard";
|
|
151
|
-
|
|
152
|
-
export { LauncherCard };
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { Loader2 } from "lucide-react";
|
|
2
|
-
import * as React from "react";
|
|
3
|
-
|
|
4
|
-
import { cn } from "../../lib/utils";
|
|
5
|
-
|
|
6
|
-
export interface LoadingStateProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
7
|
-
message?: string;
|
|
8
|
-
size?: "sm" | "default" | "lg";
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
const sizeMap = {
|
|
12
|
-
sm: "h-4 w-4",
|
|
13
|
-
default: "h-6 w-6",
|
|
14
|
-
lg: "h-8 w-8",
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
const LoadingState = React.forwardRef<HTMLDivElement, LoadingStateProps>(
|
|
18
|
-
({ message = "Loading...", size = "default", className, ...props }, ref) => {
|
|
19
|
-
return (
|
|
20
|
-
<div
|
|
21
|
-
ref={ref}
|
|
22
|
-
className={cn(
|
|
23
|
-
"flex flex-col items-center justify-center gap-3 py-8 text-muted-foreground",
|
|
24
|
-
className,
|
|
25
|
-
)}
|
|
26
|
-
{...props}
|
|
27
|
-
>
|
|
28
|
-
<Loader2 className={cn("animate-spin", sizeMap[size])} />
|
|
29
|
-
{message && <p className="text-sm">{message}</p>}
|
|
30
|
-
</div>
|
|
31
|
-
);
|
|
32
|
-
},
|
|
33
|
-
);
|
|
34
|
-
LoadingState.displayName = "LoadingState";
|
|
35
|
-
|
|
36
|
-
export { LoadingState };
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
|
|
3
|
-
import { cn } from "../../lib/utils";
|
|
4
|
-
|
|
5
|
-
export interface NotificationItemProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "title"> {
|
|
6
|
-
/** Leading icon — typically a Canvas `<Icon name="…" />`. */
|
|
7
|
-
icon?: React.ReactNode;
|
|
8
|
-
/** Tint variant for the icon's circular background. */
|
|
9
|
-
iconTone?: "neutral" | "destructive" | "info" | "success" | "warning";
|
|
10
|
-
/** Bold title line. */
|
|
11
|
-
title: React.ReactNode;
|
|
12
|
-
/** Optional secondary line (description / metadata). */
|
|
13
|
-
description?: React.ReactNode;
|
|
14
|
-
/** Right-aligned timestamp (relative or absolute). */
|
|
15
|
-
timestamp?: React.ReactNode;
|
|
16
|
-
/** When provided, the entire row becomes clickable. */
|
|
17
|
-
onClick?: () => void;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const TONE: Record<NonNullable<NotificationItemProps["iconTone"]>, string> = {
|
|
21
|
-
neutral: "bg-muted text-muted-foreground",
|
|
22
|
-
destructive: "bg-[hsl(var(--stat-destructive)/0.1)] text-[hsl(var(--stat-destructive))]",
|
|
23
|
-
info: "bg-[hsl(var(--stat-blue)/0.1)] text-[hsl(var(--stat-blue))]",
|
|
24
|
-
success: "bg-[hsl(var(--stat-success)/0.1)] text-[hsl(var(--stat-success))]",
|
|
25
|
-
warning: "bg-[hsl(var(--stat-amber)/0.1)] text-[hsl(var(--stat-amber))]",
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
export const NotificationItem = React.forwardRef<HTMLDivElement, NotificationItemProps>(
|
|
29
|
-
(
|
|
30
|
-
{ icon, iconTone = "neutral", title, description, timestamp, onClick, className, ...props },
|
|
31
|
-
ref,
|
|
32
|
-
) => {
|
|
33
|
-
const Comp = onClick ? "button" : "div";
|
|
34
|
-
return (
|
|
35
|
-
<Comp
|
|
36
|
-
ref={ref as never}
|
|
37
|
-
type={onClick ? "button" : undefined}
|
|
38
|
-
onClick={onClick}
|
|
39
|
-
className={cn(
|
|
40
|
-
"flex w-full items-start gap-3 px-3 py-3 text-left transition-colors",
|
|
41
|
-
onClick && "cursor-pointer hover:bg-accent",
|
|
42
|
-
className,
|
|
43
|
-
)}
|
|
44
|
-
{...(props as Record<string, unknown>)}
|
|
45
|
-
>
|
|
46
|
-
{icon && (
|
|
47
|
-
<div
|
|
48
|
-
className={cn(
|
|
49
|
-
"flex h-8 w-8 shrink-0 items-center justify-center rounded-full",
|
|
50
|
-
TONE[iconTone],
|
|
51
|
-
)}
|
|
52
|
-
>
|
|
53
|
-
{icon}
|
|
54
|
-
</div>
|
|
55
|
-
)}
|
|
56
|
-
<div className="min-w-0 flex-1 space-y-0.5">
|
|
57
|
-
<div className="text-[13px] font-semibold text-foreground">{title}</div>
|
|
58
|
-
{description && <div className="text-[12.5px] text-muted-foreground">{description}</div>}
|
|
59
|
-
{timestamp && (
|
|
60
|
-
<div className="font-mono text-[11px] text-muted-foreground">{timestamp}</div>
|
|
61
|
-
)}
|
|
62
|
-
</div>
|
|
63
|
-
</Comp>
|
|
64
|
-
);
|
|
65
|
-
},
|
|
66
|
-
);
|
|
67
|
-
NotificationItem.displayName = "NotificationItem";
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
|
|
3
|
-
import { cn } from "../../lib/utils";
|
|
4
|
-
|
|
5
|
-
export interface NotificationListProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "title"> {
|
|
6
|
-
/** Header title — defaults to "Notifications". */
|
|
7
|
-
title?: React.ReactNode;
|
|
8
|
-
/** Optional count chip rendered to the right of the title (e.g. "3 new"). */
|
|
9
|
-
count?: React.ReactNode;
|
|
10
|
-
/** Footer slot — typically a single full-width "View all" link/button. */
|
|
11
|
-
footer?: React.ReactNode;
|
|
12
|
-
/** Notification rows — typically `<NotificationItem>` instances. */
|
|
13
|
-
children?: React.ReactNode;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export const NotificationList = React.forwardRef<HTMLDivElement, NotificationListProps>(
|
|
17
|
-
({ title = "Notifications", count, footer, children, className, ...props }, ref) => {
|
|
18
|
-
return (
|
|
19
|
-
<div
|
|
20
|
-
ref={ref}
|
|
21
|
-
className={cn(
|
|
22
|
-
"w-80 overflow-hidden rounded-xl border border-border bg-popover text-popover-foreground shadow-lg",
|
|
23
|
-
className,
|
|
24
|
-
)}
|
|
25
|
-
{...props}
|
|
26
|
-
>
|
|
27
|
-
<div className="flex items-center justify-between border-b border-border px-3 py-2.5">
|
|
28
|
-
<span className="text-[13px] font-semibold">{title}</span>
|
|
29
|
-
{count != null && (
|
|
30
|
-
<span className="inline-flex items-center rounded-full bg-muted px-2 py-0.5 text-[11px] font-medium text-muted-foreground">
|
|
31
|
-
{count}
|
|
32
|
-
</span>
|
|
33
|
-
)}
|
|
34
|
-
</div>
|
|
35
|
-
<div className="max-h-96 divide-y divide-border overflow-y-auto">{children}</div>
|
|
36
|
-
{footer && (
|
|
37
|
-
<div className="border-t border-border bg-muted/30 px-3 py-2 text-center text-[12.5px] font-medium">
|
|
38
|
-
{footer}
|
|
39
|
-
</div>
|
|
40
|
-
)}
|
|
41
|
-
</div>
|
|
42
|
-
);
|
|
43
|
-
},
|
|
44
|
-
);
|
|
45
|
-
NotificationList.displayName = "NotificationList";
|