@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.
Files changed (214) hide show
  1. package/README.md +73 -35
  2. package/package.json +46 -177
  3. package/src/cn.ts +3 -0
  4. package/src/index.ts +12 -603
  5. package/src/theme.ts +62 -0
  6. package/src/tokens.ts +11 -0
  7. package/styles/atoms/avatar.css +22 -0
  8. package/styles/atoms/badge.css +83 -0
  9. package/styles/atoms/breadcrumb.css +35 -0
  10. package/styles/atoms/button-group.css +23 -0
  11. package/styles/atoms/button.css +107 -0
  12. package/styles/atoms/checkbox.css +55 -0
  13. package/styles/atoms/combobox.css +75 -0
  14. package/styles/atoms/dropdown.css +54 -0
  15. package/styles/atoms/icon.css +8 -0
  16. package/styles/atoms/input-group.css +45 -0
  17. package/styles/atoms/input.css +56 -0
  18. package/styles/atoms/kbd.css +15 -0
  19. package/styles/atoms/pagination.css +48 -0
  20. package/styles/atoms/popover.css +14 -0
  21. package/styles/atoms/radio.css +28 -0
  22. package/styles/atoms/select.css +57 -0
  23. package/styles/atoms/separator.css +32 -0
  24. package/styles/atoms/skeleton.css +32 -0
  25. package/styles/atoms/spinner.css +26 -0
  26. package/styles/atoms/switch.css +45 -0
  27. package/styles/atoms/textarea.css +31 -0
  28. package/styles/atoms/tooltip.css +53 -0
  29. package/styles/atoms/typography.css +105 -0
  30. package/styles/base.css +17 -0
  31. package/styles/canvas.css +77 -52
  32. package/styles/molecules/alert.css +66 -0
  33. package/styles/molecules/card.css +58 -0
  34. package/styles/molecules/code-block.css +18 -0
  35. package/styles/molecules/empty-state.css +17 -0
  36. package/styles/molecules/field.css +27 -0
  37. package/styles/molecules/form.css +27 -0
  38. package/styles/molecules/page-header.css +52 -0
  39. package/styles/molecules/section-card.css +49 -0
  40. package/styles/molecules/stat-card.css +71 -0
  41. package/styles/molecules/toast.css +95 -0
  42. package/styles/organisms/app-shell.css +46 -0
  43. package/styles/organisms/calendar.css +73 -0
  44. package/styles/organisms/command.css +94 -0
  45. package/styles/organisms/data-table.css +142 -0
  46. package/styles/organisms/dialog.css +72 -0
  47. package/styles/organisms/filter-panel.css +58 -0
  48. package/styles/organisms/row-menu.css +69 -0
  49. package/styles/organisms/sheet.css +70 -0
  50. package/styles/organisms/sidebar.css +146 -0
  51. package/styles/organisms/stepper.css +63 -0
  52. package/styles/organisms/tabs.css +40 -0
  53. package/styles/organisms/topbar.css +24 -0
  54. package/styles/patterns/backdrops.css +35 -0
  55. package/styles/patterns/density.css +66 -0
  56. package/styles/patterns/focus.css +22 -0
  57. package/styles/patterns/glass.css +85 -0
  58. package/styles/patterns/high-contrast.css +70 -0
  59. package/styles/patterns/reduced-motion.css +12 -0
  60. package/styles/patterns/scrollbar.css +10 -0
  61. package/styles/reset.css +89 -0
  62. package/styles/tokens/colors.css +106 -0
  63. package/styles/tokens/motion.css +33 -0
  64. package/styles/tokens/radius.css +10 -0
  65. package/styles/tokens/shadows.css +35 -0
  66. package/styles/tokens/spacing.css +19 -0
  67. package/styles/tokens/typography.css +6 -0
  68. package/styles/tokens/z-index.css +12 -0
  69. package/styles/utilities/display.css +66 -0
  70. package/styles/utilities/flexbox.css +240 -0
  71. package/styles/utilities/gap.css +288 -0
  72. package/styles/utilities/grid.css +138 -0
  73. package/styles/utilities/position.css +78 -0
  74. package/styles/utilities/sizing.css +138 -0
  75. package/tsconfig.json +20 -21
  76. package/src/components/atoms/README.md +0 -11
  77. package/src/components/atoms/aspect-ratio.tsx +0 -32
  78. package/src/components/atoms/avatar.tsx +0 -98
  79. package/src/components/atoms/badge.tsx +0 -44
  80. package/src/components/atoms/brand-mark.tsx +0 -74
  81. package/src/components/atoms/button.tsx +0 -105
  82. package/src/components/atoms/checkbox.tsx +0 -63
  83. package/src/components/atoms/flex-box.tsx +0 -105
  84. package/src/components/atoms/icon.tsx +0 -34
  85. package/src/components/atoms/input.tsx +0 -92
  86. package/src/components/atoms/label.tsx +0 -41
  87. package/src/components/atoms/logo.tsx +0 -89
  88. package/src/components/atoms/progress.tsx +0 -55
  89. package/src/components/atoms/radio-group.tsx +0 -122
  90. package/src/components/atoms/scroll-area.tsx +0 -106
  91. package/src/components/atoms/section.tsx +0 -48
  92. package/src/components/atoms/separator.tsx +0 -45
  93. package/src/components/atoms/skeleton.tsx +0 -17
  94. package/src/components/atoms/slider.tsx +0 -93
  95. package/src/components/atoms/spinner.tsx +0 -47
  96. package/src/components/atoms/switch.tsx +0 -60
  97. package/src/components/atoms/textarea.tsx +0 -78
  98. package/src/components/atoms/toggle.tsx +0 -80
  99. package/src/components/charts/activity-heatmap.tsx +0 -186
  100. package/src/components/charts/axes.tsx +0 -21
  101. package/src/components/charts/chart-container.tsx +0 -254
  102. package/src/components/charts/chart-legend.tsx +0 -67
  103. package/src/components/charts/chart-tooltip.tsx +0 -161
  104. package/src/components/charts/chart-types.tsx +0 -49
  105. package/src/components/charts/containers.tsx +0 -11
  106. package/src/components/charts/data.tsx +0 -16
  107. package/src/components/charts/details.tsx +0 -25
  108. package/src/components/charts/dot-pulse.tsx +0 -61
  109. package/src/components/charts/gauge.tsx +0 -106
  110. package/src/components/charts/grids.tsx +0 -8
  111. package/src/components/charts/index.ts +0 -62
  112. package/src/components/charts/labeled-bar-list.tsx +0 -85
  113. package/src/components/charts/metric-breakdown.tsx +0 -316
  114. package/src/components/charts/references.tsx +0 -8
  115. package/src/components/charts/service-health-list.tsx +0 -85
  116. package/src/components/charts/sparkline-area.tsx +0 -80
  117. package/src/components/charts/sparkline.tsx +0 -52
  118. package/src/components/charts/stacked-bar.tsx +0 -104
  119. package/src/components/charts/text.tsx +0 -10
  120. package/src/components/charts/world-heat-map-inner.tsx +0 -317
  121. package/src/components/charts/world-heat-map.tsx +0 -184
  122. package/src/components/molecules/README.md +0 -12
  123. package/src/components/molecules/action-bar.tsx +0 -73
  124. package/src/components/molecules/activity-item.tsx +0 -74
  125. package/src/components/molecules/alert.tsx +0 -86
  126. package/src/components/molecules/animated-background.tsx +0 -92
  127. package/src/components/molecules/auth-shell.tsx +0 -95
  128. package/src/components/molecules/brand-lockup.tsx +0 -48
  129. package/src/components/molecules/breadcrumb.tsx +0 -157
  130. package/src/components/molecules/button-group.tsx +0 -104
  131. package/src/components/molecules/calendar.tsx +0 -217
  132. package/src/components/molecules/card.tsx +0 -102
  133. package/src/components/molecules/client-brand.tsx +0 -95
  134. package/src/components/molecules/code-block.tsx +0 -86
  135. package/src/components/molecules/countdown-button.tsx +0 -92
  136. package/src/components/molecules/empty-state.tsx +0 -56
  137. package/src/components/molecules/error-state.tsx +0 -42
  138. package/src/components/molecules/field-display.tsx +0 -35
  139. package/src/components/molecules/input-otp.tsx +0 -74
  140. package/src/components/molecules/launcher-card.tsx +0 -152
  141. package/src/components/molecules/loading-state.tsx +0 -36
  142. package/src/components/molecules/notification-item.tsx +0 -67
  143. package/src/components/molecules/notification-list.tsx +0 -45
  144. package/src/components/molecules/number-badge.tsx +0 -53
  145. package/src/components/molecules/or-separator.tsx +0 -38
  146. package/src/components/molecules/page-header.tsx +0 -88
  147. package/src/components/molecules/page-tabs.tsx +0 -94
  148. package/src/components/molecules/pagination.tsx +0 -150
  149. package/src/components/molecules/password-input.tsx +0 -83
  150. package/src/components/molecules/password-strength-meter.tsx +0 -104
  151. package/src/components/molecules/phone-input.tsx +0 -200
  152. package/src/components/molecules/search-bar.tsx +0 -64
  153. package/src/components/molecules/secret-field.tsx +0 -158
  154. package/src/components/molecules/section-card.tsx +0 -91
  155. package/src/components/molecules/social-buttons.tsx +0 -165
  156. package/src/components/molecules/stat-card.tsx +0 -100
  157. package/src/components/molecules/status-badge.tsx +0 -42
  158. package/src/components/molecules/stepper.tsx +0 -96
  159. package/src/components/molecules/table.tsx +0 -157
  160. package/src/components/molecules/terminal.tsx +0 -74
  161. package/src/components/molecules/toggle-group.tsx +0 -145
  162. package/src/components/molecules/tooltip.tsx +0 -155
  163. package/src/components/molecules/user-avatar-chip.tsx +0 -71
  164. package/src/components/organisms/README.md +0 -14
  165. package/src/components/organisms/accordion.tsx +0 -154
  166. package/src/components/organisms/alert-dialog.tsx +0 -277
  167. package/src/components/organisms/carousel.tsx +0 -244
  168. package/src/components/organisms/collapsible.tsx +0 -69
  169. package/src/components/organisms/command.tsx +0 -144
  170. package/src/components/organisms/context-menu.tsx +0 -339
  171. package/src/components/organisms/dashboard-grid.tsx +0 -369
  172. package/src/components/organisms/data-table.tsx +0 -330
  173. package/src/components/organisms/dialog.tsx +0 -312
  174. package/src/components/organisms/drawer.tsx +0 -123
  175. package/src/components/organisms/dropdown-menu.tsx +0 -440
  176. package/src/components/organisms/editors/code-editor.tsx +0 -144
  177. package/src/components/organisms/editors/index.ts +0 -4
  178. package/src/components/organisms/editors/markdown-editor.tsx +0 -153
  179. package/src/components/organisms/editors/markdown-renderer.ts +0 -27
  180. package/src/components/organisms/editors/prose-canvas-classes.ts +0 -45
  181. package/src/components/organisms/editors/rich-text-editor.tsx +0 -126
  182. package/src/components/organisms/editors/toolbar/md-toolbar.tsx +0 -129
  183. package/src/components/organisms/editors/toolbar/rte-toolbar.tsx +0 -211
  184. package/src/components/organisms/editors/toolbar/toolbar-shell.tsx +0 -45
  185. package/src/components/organisms/editors/use-codemirror-theme.ts +0 -61
  186. package/src/components/organisms/error-boundary.tsx +0 -61
  187. package/src/components/organisms/form.tsx +0 -174
  188. package/src/components/organisms/hover-card.tsx +0 -115
  189. package/src/components/organisms/menubar.tsx +0 -498
  190. package/src/components/organisms/navbar.tsx +0 -104
  191. package/src/components/organisms/navigation-menu.tsx +0 -235
  192. package/src/components/organisms/popover.tsx +0 -149
  193. package/src/components/organisms/resizable.tsx +0 -58
  194. package/src/components/organisms/schema-form.tsx +0 -232
  195. package/src/components/organisms/select.tsx +0 -309
  196. package/src/components/organisms/sheet.tsx +0 -265
  197. package/src/components/organisms/sidebar.tsx +0 -1040
  198. package/src/components/organisms/sonner.tsx +0 -96
  199. package/src/components/organisms/tabs.tsx +0 -133
  200. package/src/components/organisms/theme-provider.tsx +0 -101
  201. package/src/hooks/use-mobile.tsx +0 -19
  202. package/src/lib/portal-container.tsx +0 -35
  203. package/src/lib/utils.ts +0 -6
  204. package/src/native.ts +0 -23
  205. package/src/tokens/colors.ts +0 -91
  206. package/src/tokens/index.ts +0 -3
  207. package/src/tokens/spacing.ts +0 -55
  208. package/src/tokens/typography.ts +0 -27
  209. package/styles/dashboard-grid.css +0 -47
  210. package/styles/fonts/Roboto-VariableFont_wdth_wght.ttf +0 -0
  211. package/styles/glass.css +0 -175
  212. package/styles/leaflet.css +0 -13
  213. package/styles/tokens.css +0 -317
  214. package/tailwind.config.ts +0 -70
@@ -1,184 +0,0 @@
1
- "use client";
2
-
3
- import * as React from "react";
4
-
5
- import { cn } from "../../lib/utils";
6
-
7
- /**
8
- * `<WorldHeatMap>` — Leaflet-based geographic scatter / heat-map.
9
- *
10
- * Public surface lives here; the actual react-leaflet tree is in
11
- * `world-heat-map-inner.tsx` and is loaded via `React.lazy()` so neither
12
- * `leaflet` nor `react-leaflet` lands in canvas's main bundle. Both are
13
- * declared as **peer-optional** in `package.json` — consumers who use the map
14
- * install them explicitly; consumers who don't, don't pay.
15
- *
16
- * Consumers must also import the Leaflet base stylesheet at app entry:
17
- *
18
- * import "@olympusoss/canvas/styles/leaflet.css";
19
- *
20
- * Visual identity matches the deployed admin dashboard reference (CartoDB
21
- * tiles, hidden controls, triple-layered CircleMarker per point).
22
- */
23
-
24
- // ── Public types ─────────────────────────────────────────────
25
-
26
- export interface WorldHeatMapPoint {
27
- /** Latitude. */
28
- lat: number;
29
- /** Longitude. */
30
- lng: number;
31
- /** Tooltip label (e.g. "Munich, DE"). */
32
- label: string;
33
- /** Drives marker radius. */
34
- count: number;
35
- }
36
-
37
- export interface WorldHeatMapProps {
38
- /** Geo-located points to render. Empty array shows the empty-state overlay. */
39
- points: WorldHeatMapPoint[];
40
- /** Container height. Number → px, string passes through. Default `"100%"`. */
41
- height?: number | string;
42
- /** Initial map center `[lat, lng]`. Default `[20, 0]`. */
43
- center?: [number, number];
44
- /** Initial zoom. Default `3`. */
45
- zoom?: number;
46
- /** Tile basemap. `"auto"` follows canvas's `useTheme()`. Default `"auto"`. */
47
- tileTheme?: "auto" | "dark" | "light";
48
- /** Show Leaflet zoom + attribution controls. Default `false`. */
49
- showControls?: boolean;
50
- /** Marker fill (any CSS colour, including `hsl(var(--chart-1))`). Default `"hsl(var(--chart-1))"`. */
51
- markerColor?: string;
52
- /** `[min, max]` marker radius in pixels (log-scaled on `count`). Default `[4, 20]`. */
53
- markerRadiusRange?: [number, number];
54
- /** Per-marker click handler. */
55
- onMarkerClick?: (point: WorldHeatMapPoint) => void;
56
- /** Show "Fewer / More" legend overlay (bottom-left). Default `true`. */
57
- showLegend?: boolean;
58
- /** Optional title overlaid top-left. */
59
- title?: string;
60
- /** Override the empty-state node. Default: a small "No geographic data available" card. */
61
- emptyState?: React.ReactNode;
62
- /** Pass-through className on the outer `.world-heat-map` wrapper. */
63
- className?: string;
64
- }
65
-
66
- // ── Lazy-loaded inner ───────────────────────────────────────
67
-
68
- const WorldHeatMapInner = React.lazy(() => import("./world-heat-map-inner"));
69
-
70
- // ── Error boundary (peer-missing or runtime-load failure) ───
71
-
72
- interface WorldHeatMapErrorBoundaryProps {
73
- fallback: React.ReactNode;
74
- children: React.ReactNode;
75
- }
76
- interface WorldHeatMapErrorBoundaryState {
77
- hasError: boolean;
78
- }
79
-
80
- class WorldHeatMapErrorBoundary extends React.Component<
81
- WorldHeatMapErrorBoundaryProps,
82
- WorldHeatMapErrorBoundaryState
83
- > {
84
- state: WorldHeatMapErrorBoundaryState = { hasError: false };
85
-
86
- /* c8 ignore start -- only triggers when the lazy-loaded Leaflet inner throws (peer missing or runtime load failure); both are jsdom-incompatible to simulate */
87
- static getDerivedStateFromError(): WorldHeatMapErrorBoundaryState {
88
- return { hasError: true };
89
- }
90
-
91
- render(): React.ReactNode {
92
- return this.state.hasError ? this.props.fallback : this.props.children;
93
- }
94
- /* c8 ignore stop */
95
- }
96
-
97
- // ── Shell (SSR / loading / error) ───────────────────────────
98
-
99
- type ShellState = "loading" | "error";
100
-
101
- interface ShellProps {
102
- height?: number | string;
103
- className?: string;
104
- state: ShellState;
105
- }
106
-
107
- function resolveHeight(height: number | string | undefined): string {
108
- if (height === undefined) return "100%";
109
- return typeof height === "number" ? `${height}px` : height;
110
- }
111
-
112
- function Shell({ height, className, state }: ShellProps) {
113
- const heightStr = resolveHeight(height);
114
- return (
115
- <div
116
- className={cn(
117
- "relative flex items-center justify-center overflow-hidden rounded-xl bg-muted/40 text-xs text-muted-foreground world-heat-map",
118
- className,
119
- )}
120
- style={{ height: heightStr }}
121
- >
122
- {state === "loading" ? (
123
- <>
124
- <svg
125
- width="20"
126
- height="20"
127
- viewBox="0 0 20 20"
128
- aria-hidden="true"
129
- className="mr-2 animate-spin opacity-50"
130
- >
131
- <title>Loading</title>
132
- <circle
133
- cx="10"
134
- cy="10"
135
- r="8"
136
- fill="none"
137
- stroke="currentColor"
138
- strokeWidth="2"
139
- strokeDasharray="40"
140
- strokeLinecap="round"
141
- />
142
- </svg>
143
- Loading map…
144
- </>
145
- ) : (
146
- <div className="px-4 py-2 text-center">
147
- Map dependencies missing — install <code>leaflet</code> and <code>react-leaflet</code>.
148
- </div>
149
- )}
150
- </div>
151
- );
152
- }
153
-
154
- // ── Public component ────────────────────────────────────────
155
-
156
- export function WorldHeatMap(props: WorldHeatMapProps) {
157
- const [mounted, setMounted] = React.useState(false);
158
-
159
- React.useEffect(() => {
160
- setMounted(true);
161
- }, []);
162
-
163
- // SSR / hydration guard — prevents `window`/`document` access during the
164
- // server pass and the first client render before hydration completes.
165
- if (!mounted) {
166
- return <Shell height={props.height} className={props.className} state="loading" />;
167
- }
168
-
169
- /* c8 ignore start -- post-mount tree mounts the lazy Leaflet inner; jsdom can't run Leaflet so the Suspense + ErrorBoundary tree is verified visually in the docs */
170
- return (
171
- <WorldHeatMapErrorBoundary
172
- fallback={<Shell height={props.height} className={props.className} state="error" />}
173
- >
174
- <React.Suspense
175
- fallback={<Shell height={props.height} className={props.className} state="loading" />}
176
- >
177
- <WorldHeatMapInner {...props} />
178
- </React.Suspense>
179
- </WorldHeatMapErrorBoundary>
180
- );
181
- /* c8 ignore stop */
182
- }
183
-
184
- WorldHeatMap.displayName = "WorldHeatMap";
@@ -1,12 +0,0 @@
1
- # Molecules
2
-
3
- Small compositions of atoms. Meaningful UI semantics. No app-state model.
4
-
5
- **Can import**: `tokens/`, `lib/utils`, `atoms/`, React.
6
-
7
- **Cannot import**: anything from `organisms/` or `templates/`.
8
-
9
- Molecules don't own interactive state more complex than a local toggle. If it
10
- has open/close state, selection, or form integration, it's an organism.
11
-
12
- See [CONTRIBUTING.md](../../../CONTRIBUTING.md) for the full atomic-design rules.
@@ -1,73 +0,0 @@
1
- "use client";
2
-
3
- import type * as React from "react";
4
-
5
- import { cn } from "../../lib/utils";
6
- import { Button } from "../atoms/button";
7
- import { Icon } from "../atoms/icon";
8
-
9
- export interface ActionBarAction {
10
- label: string;
11
- onClick: () => void;
12
- icon?: React.ReactNode;
13
- loading?: boolean;
14
- disabled?: boolean;
15
- }
16
-
17
- export interface ActionBarSecondary extends ActionBarAction {
18
- variant?: "outline" | "ghost";
19
- }
20
-
21
- export interface ActionBarProps {
22
- primaryAction?: ActionBarAction;
23
- secondaryActions?: ActionBarSecondary[];
24
- align?: "left" | "right" | "center" | "space-between";
25
- className?: string;
26
- }
27
-
28
- const ALIGN: Record<NonNullable<ActionBarProps["align"]>, string> = {
29
- left: "justify-start",
30
- right: "justify-end",
31
- center: "justify-center",
32
- "space-between": "justify-between",
33
- };
34
-
35
- export function ActionBar({
36
- primaryAction,
37
- secondaryActions = [],
38
- align = "right",
39
- className,
40
- }: ActionBarProps) {
41
- return (
42
- <div className={cn("flex items-center gap-2", ALIGN[align], className)}>
43
- {secondaryActions.map((a) => (
44
- <Button
45
- key={a.label}
46
- onClick={a.onClick}
47
- disabled={a.disabled || a.loading}
48
- variant={a.variant ?? "outline"}
49
- size="sm"
50
- >
51
- {a.icon && <span className="mr-1">{a.icon}</span>}
52
- {a.label}
53
- </Button>
54
- ))}
55
- {primaryAction && (
56
- <Button
57
- onClick={primaryAction.onClick}
58
- disabled={primaryAction.disabled || primaryAction.loading}
59
- size="sm"
60
- >
61
- {primaryAction.loading ? (
62
- <Icon name="LoaderCircle" className="mr-1 h-4 w-4 animate-spin" />
63
- ) : (
64
- primaryAction.icon && <span className="mr-1">{primaryAction.icon}</span>
65
- )}
66
- {primaryAction.label}
67
- </Button>
68
- )}
69
- </div>
70
- );
71
- }
72
-
73
- ActionBar.displayName = "ActionBar";
@@ -1,74 +0,0 @@
1
- import * as React from "react";
2
-
3
- import { cn } from "../../lib/utils";
4
-
5
- export interface ActivityItemProps extends React.HTMLAttributes<HTMLLIElement> {
6
- /** Bold subject (the actor). */
7
- subject: React.ReactNode;
8
- /** Muted action verb + object. */
9
- action: React.ReactNode;
10
- /** Right-aligned timestamp. */
11
- timestamp?: React.ReactNode;
12
- /** Leading icon or avatar slot. */
13
- leading?: React.ReactNode;
14
- /** When provided, the row becomes clickable. */
15
- onClick?: () => void;
16
- /**
17
- * Index in a list — when 0, the top border is suppressed so consecutive
18
- * items only render dividers between rows. Defaults to 1 (border on).
19
- */
20
- index?: number;
21
- }
22
-
23
- export const ActivityItem = React.forwardRef<HTMLLIElement, ActivityItemProps>(
24
- ({ subject, action, timestamp, leading, onClick, index = 1, className, ...props }, ref) => {
25
- const Row = onClick ? "button" : "div";
26
- return (
27
- <li
28
- ref={ref}
29
- className={cn(
30
- "flex items-start gap-3 py-3",
31
- index > 0 && "border-t border-border",
32
- className,
33
- )}
34
- {...props}
35
- >
36
- {leading && <div className="shrink-0">{leading}</div>}
37
- <Row
38
- type={onClick ? "button" : undefined}
39
- onClick={onClick}
40
- className={cn(
41
- "flex w-full items-start justify-between gap-4 text-left",
42
- onClick && "transition-colors hover:text-foreground",
43
- )}
44
- >
45
- <p className="m-0 min-w-0 text-[13.5px]">
46
- <span className="font-medium text-foreground">{subject}</span>{" "}
47
- <span className="text-muted-foreground">{action}</span>
48
- </p>
49
- {timestamp != null && (
50
- <span className="shrink-0 font-mono text-[11px] text-muted-foreground">
51
- {timestamp}
52
- </span>
53
- )}
54
- </Row>
55
- </li>
56
- );
57
- },
58
- );
59
- ActivityItem.displayName = "ActivityItem";
60
-
61
- export interface ActivityFeedProps extends React.HTMLAttributes<HTMLUListElement> {
62
- children?: React.ReactNode;
63
- }
64
-
65
- export const ActivityFeed = React.forwardRef<HTMLUListElement, ActivityFeedProps>(
66
- ({ children, className, ...props }, ref) => {
67
- return (
68
- <ul ref={ref} className={cn("m-0 list-none p-0", className)} {...props}>
69
- {children}
70
- </ul>
71
- );
72
- },
73
- );
74
- ActivityFeed.displayName = "ActivityFeed";
@@ -1,86 +0,0 @@
1
- import { cva, type VariantProps } from "class-variance-authority";
2
- import * as React from "react";
3
-
4
- import { cn } from "../../lib/utils";
5
-
6
- const alertVariants = cva(
7
- "relative w-full rounded-lg border border-border px-4 py-3 text-sm [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground [&>svg~*]:pl-7",
8
- {
9
- variants: {
10
- variant: {
11
- default: "bg-background text-foreground",
12
- destructive:
13
- "border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive",
14
- },
15
- },
16
- defaultVariants: {
17
- variant: "default",
18
- },
19
- },
20
- );
21
-
22
- export interface AlertProps
23
- extends React.HTMLAttributes<HTMLDivElement>,
24
- VariantProps<typeof alertVariants> {
25
- /**
26
- * Visual emphasis preset. `default` is informational, `destructive`
27
- * uses the danger palette for errors and warnings.
28
- * @default "default"
29
- */
30
- variant?: "default" | "destructive";
31
- /**
32
- * Optional leading icon (lucide-react), `<AlertTitle>`, and
33
- * `<AlertDescription>`. The icon — when present as a direct child —
34
- * is auto-positioned in the top-left.
35
- */
36
- children?: React.ReactNode;
37
- /** Tailwind / CSS classes merged onto the alert via `cn()`. */
38
- className?: string;
39
- }
40
-
41
- const Alert = React.forwardRef<HTMLDivElement, AlertProps>(
42
- ({ className, variant, ...props }, ref) => (
43
- <div
44
- ref={ref}
45
- role="alert"
46
- data-slot="alert"
47
- className={cn(alertVariants({ variant }), className)}
48
- {...props}
49
- />
50
- ),
51
- );
52
- Alert.displayName = "Alert";
53
-
54
- export interface AlertTitleProps extends React.HTMLAttributes<HTMLHeadingElement> {
55
- /** The alert headline. Renders as an `<h5>`. */
56
- children?: React.ReactNode;
57
- /** Tailwind / CSS classes merged onto the title via `cn()`. */
58
- className?: string;
59
- }
60
-
61
- const AlertTitle = React.forwardRef<HTMLParagraphElement, AlertTitleProps>(
62
- ({ className, ...props }, ref) => (
63
- <h5
64
- ref={ref}
65
- className={cn("mb-1 font-medium leading-none tracking-tight", className)}
66
- {...props}
67
- />
68
- ),
69
- );
70
- AlertTitle.displayName = "AlertTitle";
71
-
72
- export interface AlertDescriptionProps extends React.HTMLAttributes<HTMLParagraphElement> {
73
- /** Body copy of the alert. Renders as a `<div>` so it can hold paragraphs and lists. */
74
- children?: React.ReactNode;
75
- /** Tailwind / CSS classes merged onto the description via `cn()`. */
76
- className?: string;
77
- }
78
-
79
- const AlertDescription = React.forwardRef<HTMLParagraphElement, AlertDescriptionProps>(
80
- ({ className, ...props }, ref) => (
81
- <div ref={ref} className={cn("text-sm [&_p]:leading-relaxed", className)} {...props} />
82
- ),
83
- );
84
- AlertDescription.displayName = "AlertDescription";
85
-
86
- export { Alert, AlertDescription, AlertTitle };
@@ -1,92 +0,0 @@
1
- import { cn } from "../../lib/utils";
2
-
3
- export interface AnimatedBackgroundOrb {
4
- /** CSS color for the radial gradient (hex / rgb / hsl / token). */
5
- color: string;
6
- /**
7
- * Diameter in pixels.
8
- * @default 500
9
- */
10
- size?: number;
11
- /**
12
- * Opacity 0–1. Lower values let more of the page bg bleed through.
13
- * @default 0.2
14
- */
15
- opacity?: number;
16
- /**
17
- * Gaussian blur radius in pixels. Larger values produce softer orbs.
18
- * @default 100
19
- */
20
- blur?: number;
21
- /** Tailwind position classes, e.g. `"-top-32 -right-32"` or `"left-1/2 top-1/3"`. */
22
- position?: string;
23
- /**
24
- * CSS `animation` shorthand. Pair with the `orb-float-1` / `orb-float-2`
25
- * keyframes shipped in canvas's tokens, or your own.
26
- */
27
- animation?: string;
28
- }
29
-
30
- export interface AnimatedBackgroundProps {
31
- /**
32
- * Orbs to render. Defaults to a 3-orb indigo/purple/cyan composition
33
- * that mirrors the auth-screen backdrop. Pass an empty array `[]` to
34
- * render no orbs (useful for testing).
35
- */
36
- orbs?: AnimatedBackgroundOrb[];
37
- /**
38
- * Tailwind / CSS classes merged onto the root container via `cn()`.
39
- * Defaults to `pointer-events-none fixed inset-0 overflow-hidden`.
40
- */
41
- className?: string;
42
- }
43
-
44
- const DEFAULT_ORBS: AnimatedBackgroundOrb[] = [
45
- {
46
- color: "#6366f1",
47
- size: 500,
48
- opacity: 0.2,
49
- blur: 120,
50
- position: "-top-32 -right-32",
51
- animation: "orb-float-1 8s ease-in-out infinite",
52
- },
53
- {
54
- color: "#8b5cf6",
55
- size: 400,
56
- opacity: 0.15,
57
- blur: 100,
58
- position: "-bottom-32 -left-32",
59
- animation: "orb-float-2 10s ease-in-out infinite",
60
- },
61
- {
62
- color: "#06b6d4",
63
- size: 300,
64
- opacity: 0.1,
65
- blur: 80,
66
- position: "left-1/2 top-1/3 -translate-x-1/2",
67
- animation: "orb-float-1 12s ease-in-out 2s infinite",
68
- },
69
- ];
70
-
71
- export function AnimatedBackground({ orbs = DEFAULT_ORBS, className }: AnimatedBackgroundProps) {
72
- return (
73
- <div className={cn("pointer-events-none fixed inset-0 overflow-hidden", className)}>
74
- {orbs.map((orb, i) => (
75
- <div
76
- key={i}
77
- className={cn("absolute rounded-full", orb.position)}
78
- style={{
79
- width: orb.size ?? 500,
80
- height: orb.size ?? 500,
81
- opacity: orb.opacity ?? 0.2,
82
- filter: `blur(${orb.blur ?? 100}px)`,
83
- background: `radial-gradient(circle, ${orb.color} 0%, transparent 70%)`,
84
- animation: orb.animation,
85
- }}
86
- />
87
- ))}
88
- </div>
89
- );
90
- }
91
-
92
- AnimatedBackground.displayName = "AnimatedBackground";
@@ -1,95 +0,0 @@
1
- "use client";
2
-
3
- import * as React from "react";
4
-
5
- import { cn } from "../../lib/utils";
6
- import { Card, CardContent, CardHeader } from "./card";
7
-
8
- export interface AuthShellProps {
9
- /**
10
- * Brand lockup rendered above the card. Compose a `ClientBrand` (or
11
- * your own component) and pass it in. Slot is left empty to keep this
12
- * shell unaware of OAuth2 / Hydra specifics.
13
- */
14
- brand?: React.ReactNode;
15
- /** Card title. Pairs with `subtitle`. */
16
- title?: React.ReactNode;
17
- /** Optional one-line subtitle shown beneath `title`. */
18
- subtitle?: React.ReactNode;
19
- /** Card body content. */
20
- children?: React.ReactNode;
21
- /**
22
- * Optional footer line beneath the card (legal / privacy / support).
23
- */
24
- footer?: React.ReactNode;
25
- /**
26
- * Card width preset. `default` = 408px (single-column flows like login,
27
- * recovery), `wide` = 460px (multi-step or denser flows like consent).
28
- * @default "default"
29
- */
30
- width?: "default" | "wide";
31
- /**
32
- * Allow children to break the card frame (useful for full-bleed flow
33
- * pickers or media). When `false` the card content gets the standard
34
- * `p-6` padding.
35
- * @default false
36
- */
37
- flush?: boolean;
38
- /** Tailwind / CSS classes merged onto the outer wrapper via `cn()`. */
39
- className?: string;
40
- }
41
-
42
- /**
43
- * Centered single-card layout for auth flows.
44
- *
45
- * Renders, top to bottom:
46
- * 1. Brand slot (`brand`) above the card
47
- * 2. Card with optional centred title + subtitle, then `children`
48
- * 3. Optional footer line beneath the card
49
- *
50
- * Layout is intentionally narrow and prescriptive: this is the
51
- * agreed-upon shape for every Olympus auth screen (login, register,
52
- * recovery, reset, verify, OTP, consent, logout, status). For app shells
53
- * with sidebars or multi-pane layouts, compose your own flex shell
54
- * directly (see `Sidebar` + `SidebarInset`).
55
- */
56
- const AuthShell = React.forwardRef<HTMLDivElement, AuthShellProps>(
57
- (
58
- { brand, title, subtitle, children, footer, width = "default", flush = false, className },
59
- ref,
60
- ) => (
61
- <div
62
- ref={ref}
63
- className={cn(
64
- "relative z-10 flex min-h-screen flex-col items-center justify-center px-6 py-12",
65
- className,
66
- )}
67
- >
68
- {brand && <div className="mb-7">{brand}</div>}
69
-
70
- <Card
71
- className={cn(
72
- "w-full shadow-lg shadow-black/5 dark:shadow-black/30",
73
- width === "wide" ? "max-w-[460px]" : "max-w-[408px]",
74
- )}
75
- >
76
- {title && (
77
- <CardHeader className={cn("text-center", subtitle ? "pb-3" : "pb-4")}>
78
- <h1 className="text-xl font-semibold tracking-tight">{title}</h1>
79
- {subtitle && <p className="mt-1.5 text-sm text-muted-foreground">{subtitle}</p>}
80
- </CardHeader>
81
- )}
82
- <CardContent className={cn(flush ? "p-0" : "pt-0", !title && !flush && "pt-6")}>
83
- {children}
84
- </CardContent>
85
- </Card>
86
-
87
- {footer && (
88
- <div className="mt-6 max-w-[460px] text-center text-xs text-muted-foreground">{footer}</div>
89
- )}
90
- </div>
91
- ),
92
- );
93
- AuthShell.displayName = "AuthShell";
94
-
95
- export { AuthShell };
@@ -1,48 +0,0 @@
1
- import * as React from "react";
2
-
3
- import { cn } from "../../lib/utils";
4
-
5
- export interface BrandLockupProps extends React.HTMLAttributes<HTMLDivElement> {
6
- /**
7
- * Logo node rendered to the left of the wordmark — typically `<Logo />`
8
- * for the canvas Olympus ring, or any other brand mark of the consumer's
9
- * choosing. Required so canvas stays brand-agnostic.
10
- */
11
- logo: React.ReactNode;
12
- /** Wordmark next to the logo (e.g. "Athena", "Hera"). */
13
- productName: string;
14
- /** Optional secondary line under the wordmark. */
15
- subtitle?: string;
16
- /** Visual size — sm = sidebar collapsed, md = sidebar expanded, lg = hero. */
17
- size?: "sm" | "md" | "lg";
18
- /** When true, renders only the logo without the wordmark or subtitle. */
19
- collapsed?: boolean;
20
- }
21
-
22
- const SIZE: Record<NonNullable<BrandLockupProps["size"]>, { name: string; sub: string }> = {
23
- sm: { name: "text-[13px]", sub: "text-[10px]" },
24
- md: { name: "text-sm", sub: "text-[11px]" },
25
- lg: { name: "text-2xl", sub: "text-xs" },
26
- };
27
-
28
- export const BrandLockup = React.forwardRef<HTMLDivElement, BrandLockupProps>(
29
- ({ logo, productName, subtitle, size = "md", collapsed = false, className, ...props }, ref) => {
30
- const sz = SIZE[size];
31
- return (
32
- <div ref={ref} className={cn("flex items-center gap-2.5", className)} {...props}>
33
- <span className="shrink-0">{logo}</span>
34
- {!collapsed && (
35
- <div className="flex flex-col leading-tight">
36
- <span className={cn(sz.name, "font-semibold tracking-tight text-foreground")}>
37
- {productName}
38
- </span>
39
- {subtitle && (
40
- <span className={cn(sz.sub, "font-mono text-muted-foreground")}>{subtitle}</span>
41
- )}
42
- </div>
43
- )}
44
- </div>
45
- );
46
- },
47
- );
48
- BrandLockup.displayName = "BrandLockup";