@olympusoss/canvas 2.20.1 → 4.0.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 (208) hide show
  1. package/package.json +41 -177
  2. package/src/cn.ts +3 -0
  3. package/src/index.ts +12 -603
  4. package/src/theme.ts +41 -0
  5. package/src/tokens.ts +11 -0
  6. package/styles/base.css +17 -0
  7. package/styles/canvas.css +69 -52
  8. package/styles/components/alert.css +66 -0
  9. package/styles/components/app-shell.css +46 -0
  10. package/styles/components/avatar.css +15 -0
  11. package/styles/components/badge.css +83 -0
  12. package/styles/components/breadcrumb.css +35 -0
  13. package/styles/components/button-group.css +23 -0
  14. package/styles/components/button.css +107 -0
  15. package/styles/components/calendar.css +73 -0
  16. package/styles/components/card.css +58 -0
  17. package/styles/components/checkbox.css +55 -0
  18. package/styles/components/code-block.css +18 -0
  19. package/styles/components/combobox.css +75 -0
  20. package/styles/components/command.css +94 -0
  21. package/styles/components/data-table.css +142 -0
  22. package/styles/components/dialog.css +72 -0
  23. package/styles/components/dropdown.css +54 -0
  24. package/styles/components/empty-state.css +17 -0
  25. package/styles/components/field.css +27 -0
  26. package/styles/components/filter-panel.css +58 -0
  27. package/styles/components/form.css +27 -0
  28. package/styles/components/icon.css +8 -0
  29. package/styles/components/input-group.css +45 -0
  30. package/styles/components/input.css +56 -0
  31. package/styles/components/kbd.css +15 -0
  32. package/styles/components/page-header.css +52 -0
  33. package/styles/components/pagination.css +48 -0
  34. package/styles/components/popover.css +14 -0
  35. package/styles/components/radio.css +28 -0
  36. package/styles/components/row-menu.css +69 -0
  37. package/styles/components/section-card.css +49 -0
  38. package/styles/components/select.css +57 -0
  39. package/styles/components/separator.css +32 -0
  40. package/styles/components/sheet.css +70 -0
  41. package/styles/components/sidebar.css +146 -0
  42. package/styles/components/skeleton.css +32 -0
  43. package/styles/components/spinner.css +26 -0
  44. package/styles/components/stat-card.css +71 -0
  45. package/styles/components/stepper.css +63 -0
  46. package/styles/components/switch.css +45 -0
  47. package/styles/components/tabs.css +40 -0
  48. package/styles/components/textarea.css +31 -0
  49. package/styles/components/toast.css +95 -0
  50. package/styles/components/tooltip.css +53 -0
  51. package/styles/components/topbar.css +24 -0
  52. package/styles/components/typography.css +105 -0
  53. package/styles/patterns/backdrops.css +35 -0
  54. package/styles/patterns/density.css +66 -0
  55. package/styles/patterns/focus.css +38 -0
  56. package/styles/patterns/glass.css +85 -0
  57. package/styles/patterns/high-contrast.css +70 -0
  58. package/styles/patterns/reduced-motion.css +12 -0
  59. package/styles/patterns/scrollbar.css +10 -0
  60. package/styles/reset.css +89 -0
  61. package/styles/tokens/colors.css +106 -0
  62. package/styles/tokens/motion.css +33 -0
  63. package/styles/tokens/radius.css +10 -0
  64. package/styles/tokens/shadows.css +35 -0
  65. package/styles/tokens/spacing.css +19 -0
  66. package/styles/tokens/typography.css +6 -0
  67. package/styles/tokens/z-index.css +12 -0
  68. package/tsconfig.json +20 -21
  69. package/README.md +0 -60
  70. package/src/components/atoms/README.md +0 -11
  71. package/src/components/atoms/aspect-ratio.tsx +0 -32
  72. package/src/components/atoms/avatar.tsx +0 -98
  73. package/src/components/atoms/badge.tsx +0 -44
  74. package/src/components/atoms/brand-mark.tsx +0 -74
  75. package/src/components/atoms/button.tsx +0 -105
  76. package/src/components/atoms/checkbox.tsx +0 -63
  77. package/src/components/atoms/flex-box.tsx +0 -105
  78. package/src/components/atoms/icon.tsx +0 -34
  79. package/src/components/atoms/input.tsx +0 -92
  80. package/src/components/atoms/label.tsx +0 -41
  81. package/src/components/atoms/logo.tsx +0 -89
  82. package/src/components/atoms/progress.tsx +0 -55
  83. package/src/components/atoms/radio-group.tsx +0 -122
  84. package/src/components/atoms/scroll-area.tsx +0 -106
  85. package/src/components/atoms/section.tsx +0 -48
  86. package/src/components/atoms/separator.tsx +0 -45
  87. package/src/components/atoms/skeleton.tsx +0 -17
  88. package/src/components/atoms/slider.tsx +0 -93
  89. package/src/components/atoms/spinner.tsx +0 -47
  90. package/src/components/atoms/switch.tsx +0 -60
  91. package/src/components/atoms/textarea.tsx +0 -78
  92. package/src/components/atoms/toggle.tsx +0 -80
  93. package/src/components/charts/activity-heatmap.tsx +0 -186
  94. package/src/components/charts/axes.tsx +0 -21
  95. package/src/components/charts/chart-container.tsx +0 -254
  96. package/src/components/charts/chart-legend.tsx +0 -67
  97. package/src/components/charts/chart-tooltip.tsx +0 -161
  98. package/src/components/charts/chart-types.tsx +0 -49
  99. package/src/components/charts/containers.tsx +0 -11
  100. package/src/components/charts/data.tsx +0 -16
  101. package/src/components/charts/details.tsx +0 -25
  102. package/src/components/charts/dot-pulse.tsx +0 -61
  103. package/src/components/charts/gauge.tsx +0 -106
  104. package/src/components/charts/grids.tsx +0 -8
  105. package/src/components/charts/index.ts +0 -62
  106. package/src/components/charts/labeled-bar-list.tsx +0 -85
  107. package/src/components/charts/metric-breakdown.tsx +0 -316
  108. package/src/components/charts/references.tsx +0 -8
  109. package/src/components/charts/service-health-list.tsx +0 -85
  110. package/src/components/charts/sparkline-area.tsx +0 -80
  111. package/src/components/charts/sparkline.tsx +0 -52
  112. package/src/components/charts/stacked-bar.tsx +0 -104
  113. package/src/components/charts/text.tsx +0 -10
  114. package/src/components/charts/world-heat-map-inner.tsx +0 -317
  115. package/src/components/charts/world-heat-map.tsx +0 -184
  116. package/src/components/molecules/README.md +0 -12
  117. package/src/components/molecules/action-bar.tsx +0 -73
  118. package/src/components/molecules/activity-item.tsx +0 -74
  119. package/src/components/molecules/alert.tsx +0 -86
  120. package/src/components/molecules/animated-background.tsx +0 -92
  121. package/src/components/molecules/auth-shell.tsx +0 -95
  122. package/src/components/molecules/brand-lockup.tsx +0 -48
  123. package/src/components/molecules/breadcrumb.tsx +0 -157
  124. package/src/components/molecules/button-group.tsx +0 -104
  125. package/src/components/molecules/calendar.tsx +0 -217
  126. package/src/components/molecules/card.tsx +0 -102
  127. package/src/components/molecules/client-brand.tsx +0 -95
  128. package/src/components/molecules/code-block.tsx +0 -86
  129. package/src/components/molecules/countdown-button.tsx +0 -92
  130. package/src/components/molecules/empty-state.tsx +0 -56
  131. package/src/components/molecules/error-state.tsx +0 -42
  132. package/src/components/molecules/field-display.tsx +0 -35
  133. package/src/components/molecules/input-otp.tsx +0 -74
  134. package/src/components/molecules/launcher-card.tsx +0 -152
  135. package/src/components/molecules/loading-state.tsx +0 -36
  136. package/src/components/molecules/notification-item.tsx +0 -67
  137. package/src/components/molecules/notification-list.tsx +0 -45
  138. package/src/components/molecules/number-badge.tsx +0 -53
  139. package/src/components/molecules/or-separator.tsx +0 -38
  140. package/src/components/molecules/page-header.tsx +0 -88
  141. package/src/components/molecules/page-tabs.tsx +0 -94
  142. package/src/components/molecules/pagination.tsx +0 -150
  143. package/src/components/molecules/password-input.tsx +0 -83
  144. package/src/components/molecules/password-strength-meter.tsx +0 -104
  145. package/src/components/molecules/phone-input.tsx +0 -200
  146. package/src/components/molecules/search-bar.tsx +0 -64
  147. package/src/components/molecules/secret-field.tsx +0 -158
  148. package/src/components/molecules/section-card.tsx +0 -91
  149. package/src/components/molecules/social-buttons.tsx +0 -165
  150. package/src/components/molecules/stat-card.tsx +0 -100
  151. package/src/components/molecules/status-badge.tsx +0 -42
  152. package/src/components/molecules/stepper.tsx +0 -96
  153. package/src/components/molecules/table.tsx +0 -157
  154. package/src/components/molecules/terminal.tsx +0 -74
  155. package/src/components/molecules/toggle-group.tsx +0 -145
  156. package/src/components/molecules/tooltip.tsx +0 -155
  157. package/src/components/molecules/user-avatar-chip.tsx +0 -71
  158. package/src/components/organisms/README.md +0 -14
  159. package/src/components/organisms/accordion.tsx +0 -154
  160. package/src/components/organisms/alert-dialog.tsx +0 -277
  161. package/src/components/organisms/carousel.tsx +0 -244
  162. package/src/components/organisms/collapsible.tsx +0 -69
  163. package/src/components/organisms/command.tsx +0 -144
  164. package/src/components/organisms/context-menu.tsx +0 -339
  165. package/src/components/organisms/dashboard-grid.tsx +0 -369
  166. package/src/components/organisms/data-table.tsx +0 -330
  167. package/src/components/organisms/dialog.tsx +0 -312
  168. package/src/components/organisms/drawer.tsx +0 -123
  169. package/src/components/organisms/dropdown-menu.tsx +0 -440
  170. package/src/components/organisms/editors/code-editor.tsx +0 -144
  171. package/src/components/organisms/editors/index.ts +0 -4
  172. package/src/components/organisms/editors/markdown-editor.tsx +0 -153
  173. package/src/components/organisms/editors/markdown-renderer.ts +0 -27
  174. package/src/components/organisms/editors/prose-canvas-classes.ts +0 -45
  175. package/src/components/organisms/editors/rich-text-editor.tsx +0 -126
  176. package/src/components/organisms/editors/toolbar/md-toolbar.tsx +0 -129
  177. package/src/components/organisms/editors/toolbar/rte-toolbar.tsx +0 -211
  178. package/src/components/organisms/editors/toolbar/toolbar-shell.tsx +0 -45
  179. package/src/components/organisms/editors/use-codemirror-theme.ts +0 -61
  180. package/src/components/organisms/error-boundary.tsx +0 -61
  181. package/src/components/organisms/form.tsx +0 -174
  182. package/src/components/organisms/hover-card.tsx +0 -115
  183. package/src/components/organisms/menubar.tsx +0 -498
  184. package/src/components/organisms/navbar.tsx +0 -104
  185. package/src/components/organisms/navigation-menu.tsx +0 -235
  186. package/src/components/organisms/popover.tsx +0 -149
  187. package/src/components/organisms/resizable.tsx +0 -58
  188. package/src/components/organisms/schema-form.tsx +0 -232
  189. package/src/components/organisms/select.tsx +0 -309
  190. package/src/components/organisms/sheet.tsx +0 -265
  191. package/src/components/organisms/sidebar.tsx +0 -1040
  192. package/src/components/organisms/sonner.tsx +0 -96
  193. package/src/components/organisms/tabs.tsx +0 -133
  194. package/src/components/organisms/theme-provider.tsx +0 -101
  195. package/src/hooks/use-mobile.tsx +0 -19
  196. package/src/lib/portal-container.tsx +0 -35
  197. package/src/lib/utils.ts +0 -6
  198. package/src/native.ts +0 -23
  199. package/src/tokens/colors.ts +0 -91
  200. package/src/tokens/index.ts +0 -3
  201. package/src/tokens/spacing.ts +0 -55
  202. package/src/tokens/typography.ts +0 -27
  203. package/styles/dashboard-grid.css +0 -47
  204. package/styles/fonts/Roboto-VariableFont_wdth_wght.ttf +0 -0
  205. package/styles/glass.css +0 -171
  206. package/styles/leaflet.css +0 -13
  207. package/styles/tokens.css +0 -317
  208. package/tailwind.config.ts +0 -70
@@ -1,217 +0,0 @@
1
- "use client";
2
-
3
- import { ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon } from "lucide-react";
4
- import * as React from "react";
5
- import { type DayButton, DayPicker, getDefaultClassNames } from "react-day-picker";
6
-
7
- import { cn } from "../../lib/utils";
8
- import { Button, buttonVariants } from "../atoms/button";
9
-
10
- export type CalendarProps = React.ComponentProps<typeof DayPicker> & {
11
- /**
12
- * Variant applied to the prev/next month nav buttons. Maps to the
13
- * `Button` atom's variants.
14
- * @default "ghost"
15
- */
16
- buttonVariant?: React.ComponentProps<typeof Button>["variant"];
17
- /**
18
- * Show days from the previous/next month as faded entries to fill the
19
- * grid.
20
- * @default true
21
- */
22
- showOutsideDays?: boolean;
23
- /**
24
- * How the month/year heading renders. `label` is plain text, the
25
- * dropdown variants render selectable controls.
26
- * @default "label"
27
- */
28
- captionLayout?: "label" | "dropdown" | "dropdown-months" | "dropdown-years";
29
- className?: string;
30
- };
31
-
32
- function Calendar({
33
- className,
34
- classNames,
35
- showOutsideDays = true,
36
- captionLayout = "label",
37
- buttonVariant = "ghost",
38
- formatters,
39
- components,
40
- ...props
41
- }: CalendarProps) {
42
- const defaultClassNames = getDefaultClassNames();
43
-
44
- return (
45
- <DayPicker
46
- showOutsideDays={showOutsideDays}
47
- data-slot="calendar"
48
- className={cn(
49
- "bg-background group/calendar p-4 [--cell-size:2rem] [[data-slot=card-content]_&]:bg-transparent [[data-slot=popover-content]_&]:bg-transparent",
50
- String.raw`rtl:**:[.rdp-button\_next>svg]:rotate-180`,
51
- String.raw`rtl:**:[.rdp-button\_previous>svg]:rotate-180`,
52
- className,
53
- )}
54
- captionLayout={captionLayout}
55
- formatters={{
56
- formatMonthDropdown: (date) => date.toLocaleString("default", { month: "short" }),
57
- ...formatters,
58
- }}
59
- classNames={{
60
- root: cn("w-fit", defaultClassNames.root),
61
- months: cn("relative flex flex-col gap-4 md:flex-row", defaultClassNames.months),
62
- month: cn("flex w-full flex-col gap-3", defaultClassNames.month),
63
- nav: cn(
64
- "absolute inset-x-0 top-0 flex w-full items-center justify-between gap-1",
65
- defaultClassNames.nav,
66
- ),
67
- button_previous: cn(
68
- buttonVariants({ variant: buttonVariant }),
69
- "h-[var(--cell-size)] w-[var(--cell-size)] select-none p-0 aria-disabled:opacity-50",
70
- defaultClassNames.button_previous,
71
- ),
72
- button_next: cn(
73
- buttonVariants({ variant: buttonVariant }),
74
- "h-[var(--cell-size)] w-[var(--cell-size)] select-none p-0 aria-disabled:opacity-50",
75
- defaultClassNames.button_next,
76
- ),
77
- month_caption: cn(
78
- "flex h-[var(--cell-size)] w-full items-center justify-center px-[var(--cell-size)]",
79
- defaultClassNames.month_caption,
80
- ),
81
- dropdowns: cn(
82
- "flex h-[var(--cell-size)] w-full items-center justify-center gap-1.5 text-sm font-medium",
83
- defaultClassNames.dropdowns,
84
- ),
85
- dropdown_root: cn(
86
- "has-focus:border-ring border-input shadow-xs has-focus:ring-ring/50 has-focus:ring-[3px] relative rounded-md border",
87
- defaultClassNames.dropdown_root,
88
- ),
89
- dropdown: cn("bg-popover absolute inset-0 opacity-0", defaultClassNames.dropdown),
90
- caption_label: cn(
91
- "select-none font-semibold tracking-tight text-foreground",
92
- captionLayout === "label"
93
- ? "text-sm"
94
- : "[&>svg]:text-muted-foreground flex h-8 items-center gap-1 rounded-md pl-2 pr-1 text-sm [&>svg]:size-3.5",
95
- defaultClassNames.caption_label,
96
- ),
97
- table: "w-full border-collapse",
98
- weekdays: cn("flex border-b border-border pb-1.5", defaultClassNames.weekdays),
99
- weekday: cn(
100
- "text-muted-foreground flex-1 select-none rounded-md text-[0.7rem] font-medium uppercase tracking-wider",
101
- defaultClassNames.weekday,
102
- ),
103
- week: cn("mt-1.5 flex w-full", defaultClassNames.week),
104
- week_number_header: cn(
105
- "w-[var(--cell-size)] select-none",
106
- defaultClassNames.week_number_header,
107
- ),
108
- week_number: cn(
109
- "text-muted-foreground select-none text-[0.8rem]",
110
- defaultClassNames.week_number,
111
- ),
112
- day: cn(
113
- "group/day relative aspect-square h-full w-full select-none p-0 text-center [&:first-child[data-selected=true]_button]:rounded-l-md [&:last-child[data-selected=true]_button]:rounded-r-md",
114
- defaultClassNames.day,
115
- ),
116
- range_start: cn("bg-brand/10 rounded-l-md", defaultClassNames.range_start),
117
- range_middle: cn("bg-brand/10 rounded-none", defaultClassNames.range_middle),
118
- range_end: cn("bg-brand/10 rounded-r-md", defaultClassNames.range_end),
119
- today: cn(
120
- "text-brand font-semibold data-[selected=true]:rounded-none",
121
- defaultClassNames.today,
122
- ),
123
- outside: cn(
124
- "text-muted-foreground/50 aria-selected:text-muted-foreground",
125
- defaultClassNames.outside,
126
- ),
127
- disabled: cn("text-muted-foreground opacity-40", defaultClassNames.disabled),
128
- hidden: cn("invisible", defaultClassNames.hidden),
129
- ...classNames,
130
- }}
131
- components={{
132
- Root: ({ className, rootRef, ...props }) => {
133
- return <div data-slot="calendar" ref={rootRef} className={cn(className)} {...props} />;
134
- },
135
- Chevron: ({ className, orientation, ...props }) => {
136
- if (orientation === "left") {
137
- return <ChevronLeftIcon className={cn("size-4", className)} {...props} />;
138
- }
139
-
140
- if (orientation === "right") {
141
- return <ChevronRightIcon className={cn("size-4", className)} {...props} />;
142
- }
143
-
144
- return <ChevronDownIcon className={cn("size-4", className)} {...props} />;
145
- },
146
- DayButton: CalendarDayButton,
147
- WeekNumber: ({ children, ...props }) => {
148
- return (
149
- <td {...props}>
150
- <div className="flex size-[var(--cell-size)] items-center justify-center text-center">
151
- {children}
152
- </div>
153
- </td>
154
- );
155
- },
156
- ...components,
157
- }}
158
- {...props}
159
- />
160
- );
161
- }
162
-
163
- function CalendarDayButton({
164
- className,
165
- day,
166
- modifiers,
167
- ...props
168
- }: React.ComponentProps<typeof DayButton>) {
169
- const defaultClassNames = getDefaultClassNames();
170
-
171
- const ref = React.useRef<HTMLButtonElement>(null);
172
- React.useEffect(() => {
173
- if (modifiers.focused) ref.current?.focus();
174
- }, [modifiers.focused]);
175
-
176
- return (
177
- <Button
178
- ref={ref}
179
- variant="ghost"
180
- size="icon"
181
- data-day={day.date.toLocaleDateString()}
182
- data-selected-single={
183
- modifiers.selected &&
184
- !modifiers.range_start &&
185
- !modifiers.range_end &&
186
- !modifiers.range_middle
187
- }
188
- data-range-start={modifiers.range_start}
189
- data-range-end={modifiers.range_end}
190
- data-range-middle={modifiers.range_middle}
191
- className={cn(
192
- // base size + typography (small inner inset so adjacent dates breathe)
193
- "m-0.5 flex aspect-square h-auto w-[calc(100%-0.25rem)] min-w-[calc(var(--cell-size)-0.25rem)] items-center justify-center rounded-md text-sm font-normal leading-none transition-colors",
194
- // hover (when not selected)
195
- "hover:bg-accent hover:text-accent-foreground",
196
- // today: keep brand text colour from row classNames; add subtle ring
197
- "group-[[data-today=true]]/day:ring-1 group-[[data-today=true]]/day:ring-brand/40",
198
- // single selection: brand fill, white text, full rounding
199
- "data-[selected-single=true]:bg-brand data-[selected-single=true]:text-brand-foreground data-[selected-single=true]:hover:bg-brand/90 data-[selected-single=true]:hover:text-brand-foreground",
200
- // range endpoints: brand fill, only outer corners rounded
201
- "data-[range-start=true]:bg-brand data-[range-start=true]:text-brand-foreground data-[range-start=true]:hover:bg-brand/90 data-[range-start=true]:hover:text-brand-foreground data-[range-start=true]:rounded-r-none",
202
- "data-[range-end=true]:bg-brand data-[range-end=true]:text-brand-foreground data-[range-end=true]:hover:bg-brand/90 data-[range-end=true]:hover:text-brand-foreground data-[range-end=true]:rounded-l-none",
203
- // range middle: foreground stays default, no rounding
204
- "data-[range-middle=true]:bg-transparent data-[range-middle=true]:text-foreground data-[range-middle=true]:rounded-none",
205
- // focus ring (for keyboard nav)
206
- "group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:ring-[3px] group-data-[focused=true]/day:ring-brand/40 group-data-[focused=true]/day:border-brand",
207
- // modifier label (e.g. holidays) sub-line
208
- "[&>span]:text-xs [&>span]:opacity-70",
209
- defaultClassNames.day,
210
- className,
211
- )}
212
- {...props}
213
- />
214
- );
215
- }
216
-
217
- export { Calendar, CalendarDayButton };
@@ -1,102 +0,0 @@
1
- import * as React from "react";
2
-
3
- import { cn } from "../../lib/utils";
4
-
5
- export interface CardProps extends React.HTMLAttributes<HTMLDivElement> {
6
- /** `<CardHeader>`, `<CardContent>`, `<CardFooter>` (or any custom layout). */
7
- children?: React.ReactNode;
8
- /**
9
- * Tailwind / CSS classes merged onto the card via `cn()`. Default
10
- * styling: `rounded-xl` (12px), `border`, `bg-card`, `shadow`.
11
- */
12
- className?: string;
13
- }
14
-
15
- const Card = React.forwardRef<HTMLDivElement, CardProps>(({ className, ...props }, ref) => (
16
- <div
17
- ref={ref}
18
- data-slot="card"
19
- className={cn("rounded-xl border border-border bg-card text-card-foreground shadow", className)}
20
- {...props}
21
- />
22
- ));
23
- Card.displayName = "Card";
24
-
25
- export interface CardHeaderProps extends React.HTMLAttributes<HTMLDivElement> {
26
- /** Typically `<CardTitle>` + `<CardDescription>`. */
27
- children?: React.ReactNode;
28
- /** Tailwind / CSS classes merged onto the header via `cn()`. Default: `p-6 space-y-1.5`. */
29
- className?: string;
30
- }
31
-
32
- const CardHeader = React.forwardRef<HTMLDivElement, CardHeaderProps>(
33
- ({ className, ...props }, ref) => (
34
- <div ref={ref} className={cn("flex flex-col space-y-1.5 p-6", className)} {...props} />
35
- ),
36
- );
37
- CardHeader.displayName = "CardHeader";
38
-
39
- export interface CardTitleProps extends React.HTMLAttributes<HTMLDivElement> {
40
- /** The card heading. */
41
- children?: React.ReactNode;
42
- /** Tailwind / CSS classes merged onto the title via `cn()`. */
43
- className?: string;
44
- }
45
-
46
- const CardTitle = React.forwardRef<HTMLDivElement, CardTitleProps>(
47
- ({ className, ...props }, ref) => (
48
- <div
49
- ref={ref}
50
- className={cn("font-semibold leading-none tracking-tight", className)}
51
- {...props}
52
- />
53
- ),
54
- );
55
- CardTitle.displayName = "CardTitle";
56
-
57
- export interface CardDescriptionProps extends React.HTMLAttributes<HTMLDivElement> {
58
- /** Subtitle / supporting copy below the title. */
59
- children?: React.ReactNode;
60
- /** Tailwind / CSS classes merged onto the description via `cn()`. */
61
- className?: string;
62
- }
63
-
64
- const CardDescription = React.forwardRef<HTMLDivElement, CardDescriptionProps>(
65
- ({ className, ...props }, ref) => (
66
- <div ref={ref} className={cn("text-sm text-muted-foreground", className)} {...props} />
67
- ),
68
- );
69
- CardDescription.displayName = "CardDescription";
70
-
71
- export interface CardContentProps extends React.HTMLAttributes<HTMLDivElement> {
72
- /** Card body. */
73
- children?: React.ReactNode;
74
- /** Tailwind / CSS classes merged onto the content via `cn()`. Default: `p-6 pt-0`. */
75
- className?: string;
76
- }
77
-
78
- const CardContent = React.forwardRef<HTMLDivElement, CardContentProps>(
79
- ({ className, ...props }, ref) => (
80
- <div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
81
- ),
82
- );
83
- CardContent.displayName = "CardContent";
84
-
85
- export interface CardFooterProps extends React.HTMLAttributes<HTMLDivElement> {
86
- /** Card actions / metadata row. */
87
- children?: React.ReactNode;
88
- /**
89
- * Tailwind / CSS classes merged onto the footer via `cn()`. Default:
90
- * `flex items-center p-6 pt-0`.
91
- */
92
- className?: string;
93
- }
94
-
95
- const CardFooter = React.forwardRef<HTMLDivElement, CardFooterProps>(
96
- ({ className, ...props }, ref) => (
97
- <div ref={ref} className={cn("flex items-center p-6 pt-0", className)} {...props} />
98
- ),
99
- );
100
- CardFooter.displayName = "CardFooter";
101
-
102
- export { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle };
@@ -1,95 +0,0 @@
1
- "use client";
2
-
3
- import * as React from "react";
4
-
5
- import { cn } from "../../lib/utils";
6
-
7
- export interface ClientBrandProps {
8
- /** OAuth2 client display name (e.g. "Questrade", "Athena"). */
9
- clientName: string;
10
- /**
11
- * Optional logo URI. Renders as an `<img>` next to the wordmark when
12
- * present. When absent, a tinted monogram tile is rendered using the
13
- * first letter of `clientName`.
14
- */
15
- clientLogoUri?: string;
16
- /**
17
- * Pixel size of the logo/monogram tile.
18
- * @default 36
19
- */
20
- size?: number;
21
- /**
22
- * Show the wordmark next to the logo.
23
- * @default true
24
- */
25
- showWordmark?: boolean;
26
- /**
27
- * Background colour applied to the monogram fallback tile. Honours
28
- * Tailwind classes or a raw CSS colour.
29
- * @default "bg-foreground text-background"
30
- */
31
- monogramClassName?: string;
32
- /** Tailwind / CSS classes merged onto the root via `cn()`. */
33
- className?: string;
34
- }
35
-
36
- /**
37
- * OAuth2 client lockup. Renders the requesting application's logo + name
38
- * so users see "you're signing in to [App]", not the identity-provider
39
- * platform underneath.
40
- *
41
- * Provide `clientLogoUri` to render the client's logo image. Falls back to
42
- * a monogram tile when no URI is supplied or the image fails to load.
43
- *
44
- * Designed for the brand slot of `AuthShell` / sign-in pages. Stays a thin
45
- * presentation primitive: no client-registry lookups, no fetches. Pass the
46
- * shaped data in from your auth backend.
47
- */
48
- const ClientBrand = React.forwardRef<HTMLDivElement, ClientBrandProps>(
49
- (
50
- { clientName, clientLogoUri, size = 36, showWordmark = true, monogramClassName, className },
51
- ref,
52
- ) => {
53
- const [imgFailed, setImgFailed] = React.useState(false);
54
- const showImage = !!clientLogoUri && !imgFailed;
55
- const initial = (clientName?.trim()?.[0] || "?").toUpperCase();
56
-
57
- return (
58
- <div
59
- ref={ref}
60
- role="img"
61
- aria-label={`Signing in to ${clientName}`}
62
- className={cn("flex items-center gap-2.5", className)}
63
- >
64
- {showImage ? (
65
- <img
66
- src={clientLogoUri}
67
- alt=""
68
- width={size}
69
- height={size}
70
- className="shrink-0 rounded-lg object-contain"
71
- style={{ width: size, height: size }}
72
- onError={() => setImgFailed(true)}
73
- />
74
- ) : (
75
- <div
76
- className={cn(
77
- "flex shrink-0 items-center justify-center rounded-lg font-semibold",
78
- monogramClassName || "bg-foreground text-background",
79
- )}
80
- style={{ width: size, height: size, fontSize: Math.round(size * 0.5) }}
81
- aria-hidden
82
- >
83
- {initial}
84
- </div>
85
- )}
86
- {showWordmark && (
87
- <span className="text-lg font-semibold tracking-tight text-foreground">{clientName}</span>
88
- )}
89
- </div>
90
- );
91
- },
92
- );
93
- ClientBrand.displayName = "ClientBrand";
94
-
95
- export { ClientBrand };
@@ -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 };