@xemahq/ui-kernel 0.1.12 → 0.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 (195) hide show
  1. package/dist/index.d.ts +1 -0
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +1 -0
  4. package/dist/index.js.map +1 -1
  5. package/dist/lib/biome-host/host-bridge.d.ts +2 -0
  6. package/dist/lib/biome-host/host-bridge.d.ts.map +1 -1
  7. package/dist/lib/biome-host/host-bridge.js.map +1 -1
  8. package/dist/lib/capabilities/capability-provider.d.ts +15 -0
  9. package/dist/lib/capabilities/capability-provider.d.ts.map +1 -0
  10. package/dist/lib/capabilities/capability-provider.js +36 -0
  11. package/dist/lib/capabilities/capability-provider.js.map +1 -0
  12. package/dist/lib/capabilities/index.d.ts +4 -0
  13. package/dist/lib/capabilities/index.d.ts.map +1 -0
  14. package/dist/lib/capabilities/index.js +20 -0
  15. package/dist/lib/capabilities/index.js.map +1 -0
  16. package/dist/lib/capabilities/types.d.ts +18 -0
  17. package/dist/lib/capabilities/types.d.ts.map +1 -0
  18. package/dist/lib/capabilities/types.js +3 -0
  19. package/dist/lib/capabilities/types.js.map +1 -0
  20. package/dist/lib/capabilities/use-capability.d.ts +18 -0
  21. package/dist/lib/capabilities/use-capability.d.ts.map +1 -0
  22. package/dist/lib/capabilities/use-capability.js +21 -0
  23. package/dist/lib/capabilities/use-capability.js.map +1 -0
  24. package/dist/ui/chrome/AsyncBoundary.d.ts +22 -0
  25. package/dist/ui/chrome/AsyncBoundary.d.ts.map +1 -0
  26. package/dist/ui/chrome/AsyncBoundary.js +23 -0
  27. package/dist/ui/chrome/AsyncBoundary.js.map +1 -0
  28. package/dist/ui/chrome/EmptyState.d.ts +34 -0
  29. package/dist/ui/chrome/EmptyState.d.ts.map +1 -0
  30. package/dist/ui/chrome/EmptyState.js +27 -0
  31. package/dist/ui/chrome/EmptyState.js.map +1 -0
  32. package/dist/ui/chrome/ErrorCard.d.ts +11 -0
  33. package/dist/ui/chrome/ErrorCard.d.ts.map +1 -0
  34. package/dist/ui/chrome/ErrorCard.js +21 -0
  35. package/dist/ui/chrome/ErrorCard.js.map +1 -0
  36. package/dist/ui/chrome/LoadingState.d.ts +10 -0
  37. package/dist/ui/chrome/LoadingState.d.ts.map +1 -0
  38. package/dist/ui/chrome/LoadingState.js +17 -0
  39. package/dist/ui/chrome/LoadingState.js.map +1 -0
  40. package/dist/ui/chrome/PageHeader.d.ts +20 -0
  41. package/dist/ui/chrome/PageHeader.d.ts.map +1 -0
  42. package/dist/ui/chrome/PageHeader.js +26 -0
  43. package/dist/ui/chrome/PageHeader.js.map +1 -0
  44. package/dist/ui/chrome/StateCard.d.ts +24 -0
  45. package/dist/ui/chrome/StateCard.d.ts.map +1 -0
  46. package/dist/ui/chrome/StateCard.js +17 -0
  47. package/dist/ui/chrome/StateCard.js.map +1 -0
  48. package/dist/ui/cn.d.ts +3 -0
  49. package/dist/ui/cn.d.ts.map +1 -0
  50. package/dist/ui/cn.js +18 -0
  51. package/dist/ui/cn.js.map +1 -0
  52. package/dist/ui/index.d.ts +33 -0
  53. package/dist/ui/index.d.ts.map +1 -0
  54. package/dist/ui/index.js +61 -0
  55. package/dist/ui/index.js.map +1 -0
  56. package/dist/ui/primitives/alert-dialog.d.ts +21 -0
  57. package/dist/ui/primitives/alert-dialog.d.ts.map +1 -0
  58. package/dist/ui/primitives/alert-dialog.js +72 -0
  59. package/dist/ui/primitives/alert-dialog.js.map +1 -0
  60. package/dist/ui/primitives/badge.d.ts +10 -0
  61. package/dist/ui/primitives/badge.d.ts.map +1 -0
  62. package/dist/ui/primitives/badge.js +60 -0
  63. package/dist/ui/primitives/badge.js.map +1 -0
  64. package/dist/ui/primitives/button.d.ts +12 -0
  65. package/dist/ui/primitives/button.d.ts.map +1 -0
  66. package/dist/ui/primitives/button.js +71 -0
  67. package/dist/ui/primitives/button.js.map +1 -0
  68. package/dist/ui/primitives/card.d.ts +9 -0
  69. package/dist/ui/primitives/card.d.ts.map +1 -0
  70. package/dist/ui/primitives/card.js +58 -0
  71. package/dist/ui/primitives/card.js.map +1 -0
  72. package/dist/ui/primitives/checkbox.d.ts +5 -0
  73. package/dist/ui/primitives/checkbox.d.ts.map +1 -0
  74. package/dist/ui/primitives/checkbox.js +45 -0
  75. package/dist/ui/primitives/checkbox.js.map +1 -0
  76. package/dist/ui/primitives/collapsible.d.ts +6 -0
  77. package/dist/ui/primitives/collapsible.d.ts.map +1 -0
  78. package/dist/ui/primitives/collapsible.js +44 -0
  79. package/dist/ui/primitives/collapsible.js.map +1 -0
  80. package/dist/ui/primitives/dialog.d.ts +22 -0
  81. package/dist/ui/primitives/dialog.d.ts.map +1 -0
  82. package/dist/ui/primitives/dialog.js +68 -0
  83. package/dist/ui/primitives/dialog.js.map +1 -0
  84. package/dist/ui/primitives/dropdown-menu.d.ts +28 -0
  85. package/dist/ui/primitives/dropdown-menu.d.ts.map +1 -0
  86. package/dist/ui/primitives/dropdown-menu.js +83 -0
  87. package/dist/ui/primitives/dropdown-menu.js.map +1 -0
  88. package/dist/ui/primitives/input.d.ts +4 -0
  89. package/dist/ui/primitives/input.d.ts.map +1 -0
  90. package/dist/ui/primitives/input.js +45 -0
  91. package/dist/ui/primitives/input.js.map +1 -0
  92. package/dist/ui/primitives/label.d.ts +6 -0
  93. package/dist/ui/primitives/label.d.ts.map +1 -0
  94. package/dist/ui/primitives/label.js +46 -0
  95. package/dist/ui/primitives/label.js.map +1 -0
  96. package/dist/ui/primitives/overflow-tabs.d.ts +18 -0
  97. package/dist/ui/primitives/overflow-tabs.d.ts.map +1 -0
  98. package/dist/ui/primitives/overflow-tabs.js +84 -0
  99. package/dist/ui/primitives/overflow-tabs.js.map +1 -0
  100. package/dist/ui/primitives/popover.d.ts +9 -0
  101. package/dist/ui/primitives/popover.d.ts.map +1 -0
  102. package/dist/ui/primitives/popover.js +48 -0
  103. package/dist/ui/primitives/popover.js.map +1 -0
  104. package/dist/ui/primitives/radio-group.d.ts +6 -0
  105. package/dist/ui/primitives/radio-group.d.ts.map +1 -0
  106. package/dist/ui/primitives/radio-group.js +52 -0
  107. package/dist/ui/primitives/radio-group.js.map +1 -0
  108. package/dist/ui/primitives/resizable.d.ts +12 -0
  109. package/dist/ui/primitives/resizable.d.ts.map +1 -0
  110. package/dist/ui/primitives/resizable.js +18 -0
  111. package/dist/ui/primitives/resizable.js.map +1 -0
  112. package/dist/ui/primitives/scroll-area.d.ts +6 -0
  113. package/dist/ui/primitives/scroll-area.d.ts.map +1 -0
  114. package/dist/ui/primitives/scroll-area.js +47 -0
  115. package/dist/ui/primitives/scroll-area.js.map +1 -0
  116. package/dist/ui/primitives/select.d.ts +14 -0
  117. package/dist/ui/primitives/select.d.ts.map +1 -0
  118. package/dist/ui/primitives/select.js +71 -0
  119. package/dist/ui/primitives/select.js.map +1 -0
  120. package/dist/ui/primitives/separator.d.ts +5 -0
  121. package/dist/ui/primitives/separator.d.ts.map +1 -0
  122. package/dist/ui/primitives/separator.js +44 -0
  123. package/dist/ui/primitives/separator.js.map +1 -0
  124. package/dist/ui/primitives/sheet.d.ts +26 -0
  125. package/dist/ui/primitives/sheet.d.ts.map +1 -0
  126. package/dist/ui/primitives/sheet.js +82 -0
  127. package/dist/ui/primitives/sheet.js.map +1 -0
  128. package/dist/ui/primitives/skeleton.d.ts +13 -0
  129. package/dist/ui/primitives/skeleton.d.ts.map +1 -0
  130. package/dist/ui/primitives/skeleton.js +29 -0
  131. package/dist/ui/primitives/skeleton.js.map +1 -0
  132. package/dist/ui/primitives/switch.d.ts +5 -0
  133. package/dist/ui/primitives/switch.d.ts.map +1 -0
  134. package/dist/ui/primitives/switch.js +44 -0
  135. package/dist/ui/primitives/switch.js.map +1 -0
  136. package/dist/ui/primitives/table.d.ts +11 -0
  137. package/dist/ui/primitives/table.d.ts.map +1 -0
  138. package/dist/ui/primitives/table.js +64 -0
  139. package/dist/ui/primitives/table.js.map +1 -0
  140. package/dist/ui/primitives/tabs.d.ts +8 -0
  141. package/dist/ui/primitives/tabs.d.ts.map +1 -0
  142. package/dist/ui/primitives/tabs.js +52 -0
  143. package/dist/ui/primitives/tabs.js.map +1 -0
  144. package/dist/ui/primitives/tag-multi-select.d.ts +19 -0
  145. package/dist/ui/primitives/tag-multi-select.d.ts.map +1 -0
  146. package/dist/ui/primitives/tag-multi-select.js +92 -0
  147. package/dist/ui/primitives/tag-multi-select.js.map +1 -0
  148. package/dist/ui/primitives/textarea.d.ts +5 -0
  149. package/dist/ui/primitives/textarea.d.ts.map +1 -0
  150. package/dist/ui/primitives/textarea.js +45 -0
  151. package/dist/ui/primitives/textarea.js.map +1 -0
  152. package/dist/ui/primitives/tooltip.d.ts +8 -0
  153. package/dist/ui/primitives/tooltip.d.ts.map +1 -0
  154. package/dist/ui/primitives/tooltip.js +50 -0
  155. package/dist/ui/primitives/tooltip.js.map +1 -0
  156. package/package.json +24 -1
  157. package/src/index.ts +1 -0
  158. package/src/lib/biome-host/host-bridge.ts +10 -0
  159. package/src/lib/capabilities/capability-provider.tsx +95 -0
  160. package/src/lib/capabilities/index.ts +16 -0
  161. package/src/lib/capabilities/types.ts +69 -0
  162. package/src/lib/capabilities/use-capability.ts +72 -0
  163. package/src/ui/chrome/AsyncBoundary.tsx +66 -0
  164. package/src/ui/chrome/EmptyState.tsx +184 -0
  165. package/src/ui/chrome/ErrorCard.tsx +68 -0
  166. package/src/ui/chrome/LoadingState.tsx +61 -0
  167. package/src/ui/chrome/PageHeader.tsx +137 -0
  168. package/src/ui/chrome/StateCard.tsx +150 -0
  169. package/src/ui/cn.ts +32 -0
  170. package/src/ui/index.ts +53 -0
  171. package/src/ui/primitives/alert-dialog.tsx +104 -0
  172. package/src/ui/primitives/badge.tsx +32 -0
  173. package/src/ui/primitives/button.tsx +47 -0
  174. package/src/ui/primitives/card.tsx +43 -0
  175. package/src/ui/primitives/checkbox.tsx +26 -0
  176. package/src/ui/primitives/collapsible.tsx +9 -0
  177. package/src/ui/primitives/dialog.tsx +103 -0
  178. package/src/ui/primitives/dropdown-menu.tsx +179 -0
  179. package/src/ui/primitives/input.tsx +22 -0
  180. package/src/ui/primitives/label.tsx +17 -0
  181. package/src/ui/primitives/overflow-tabs.tsx +281 -0
  182. package/src/ui/primitives/popover.tsx +33 -0
  183. package/src/ui/primitives/radio-group.tsx +36 -0
  184. package/src/ui/primitives/resizable.tsx +67 -0
  185. package/src/ui/primitives/scroll-area.tsx +38 -0
  186. package/src/ui/primitives/select.tsx +143 -0
  187. package/src/ui/primitives/separator.tsx +20 -0
  188. package/src/ui/primitives/sheet.tsx +107 -0
  189. package/src/ui/primitives/skeleton.tsx +99 -0
  190. package/src/ui/primitives/switch.tsx +27 -0
  191. package/src/ui/primitives/table.tsx +72 -0
  192. package/src/ui/primitives/tabs.tsx +53 -0
  193. package/src/ui/primitives/tag-multi-select.tsx +241 -0
  194. package/src/ui/primitives/textarea.tsx +21 -0
  195. package/src/ui/primitives/tooltip.tsx +30 -0
@@ -0,0 +1,150 @@
1
+ /**
2
+ * Unified state display card for empty, error-simple, and error-detailed states.
3
+ * Replaces ErrorCard, ErrorDisplay, and EmptyState components.
4
+ * Supports collapsible error details and structured field extraction.
5
+ */
6
+ import { ChevronDown, ChevronRight } from 'lucide-react';
7
+ import React, { useState } from 'react';
8
+
9
+
10
+ import { cn } from '../cn';
11
+
12
+ interface FieldEntry {
13
+ label: string;
14
+ value: string | React.ReactNode;
15
+ }
16
+
17
+ interface StateCardProps {
18
+ /** State variant: 'empty' | 'error-simple' | 'error-detailed' */
19
+ variant: 'empty' | 'error-simple' | 'error-detailed';
20
+ /** Icon React element */
21
+ icon: React.ReactNode;
22
+ /** Primary title */
23
+ title: string;
24
+ /** Description or error message */
25
+ description?: string;
26
+ /** Optional action button */
27
+ action?: {
28
+ label: string;
29
+ onClick: () => void;
30
+ };
31
+ /** Optional error details (for error-detailed variant) */
32
+ errorDetails?: {
33
+ fields?: FieldEntry[];
34
+ stackTrace?: string;
35
+ requestId?: string;
36
+ };
37
+ /** Optional CSS class */
38
+ className?: string;
39
+ }
40
+
41
+ /**
42
+ * Renders a state card (empty/error) with consistent appearance.
43
+ * Supports collapsible error details for debugging.
44
+ */
45
+ export const StateCard: React.FC<StateCardProps> = ({
46
+ variant,
47
+ icon,
48
+ title,
49
+ description,
50
+ action,
51
+ errorDetails,
52
+ className = '',
53
+ }) => {
54
+ const [detailsExpanded, setDetailsExpanded] = useState(false);
55
+
56
+ const isError = variant.startsWith('error');
57
+ const isDetailed = variant === 'error-detailed';
58
+
59
+ return (
60
+ <div className={cn(
61
+ 'flex flex-col items-center justify-center py-14 px-6 rounded-xl border-2',
62
+ isError ? 'border-destructive/20 bg-destructive/[0.03]' : 'border-dashed border-border/50 bg-paper-elev/60',
63
+ className,
64
+ )}>
65
+ <div className={cn(
66
+ 'h-14 w-14 rounded-2xl flex items-center justify-center mb-5',
67
+ isError ? 'bg-destructive/10' : 'bg-primary/10',
68
+ )}>
69
+ <span className={isError ? 'text-destructive' : 'text-primary'}>{icon}</span>
70
+ </div>
71
+
72
+ <h2 className="text-subtitle font-semibold text-ink mb-2 text-center">
73
+ {title}
74
+ </h2>
75
+
76
+ {description && (
77
+ <p className="text-body-1 text-ink-3 max-w-md text-center mb-4 leading-relaxed">
78
+ {description}
79
+ </p>
80
+ )}
81
+
82
+ {/* Error details section */}
83
+ {isDetailed && errorDetails && (
84
+ <div className="mt-4 w-full max-w-md">
85
+ <button
86
+ type="button"
87
+ onClick={() => setDetailsExpanded(!detailsExpanded)}
88
+ className="text-body-1 font-medium text-destructive hover:text-destructive flex items-center gap-1.5 mb-2"
89
+ >
90
+ {detailsExpanded ? <ChevronDown className="h-4 w-4" /> : <ChevronRight className="h-4 w-4" />}
91
+ Error Details
92
+ </button>
93
+
94
+ {detailsExpanded && (
95
+ <div className="p-4 bg-card border border-destructive/20 rounded-xl space-y-3">
96
+ {/* Structured fields */}
97
+ {errorDetails.fields && errorDetails.fields.length > 0 && (
98
+ <div className="space-y-1.5">
99
+ {errorDetails.fields.map((field, idx) => (
100
+ <div key={idx} className="text-body-1">
101
+ <span className="font-semibold text-destructive">{field.label}</span>
102
+ <span className="text-ink/70">: {field.value}</span>
103
+ </div>
104
+ ))}
105
+ </div>
106
+ )}
107
+
108
+ {/* Stack trace */}
109
+ {errorDetails.stackTrace && (
110
+ <details className="text-body-1">
111
+ <summary className="font-medium text-destructive cursor-pointer mb-1">
112
+ Stack Trace
113
+ </summary>
114
+ <pre className="bg-destructive/5 p-3 rounded-lg overflow-auto max-h-32 text-destructive whitespace-pre-wrap break-words text-body-1 font-mono">
115
+ {errorDetails.stackTrace}
116
+ </pre>
117
+ </details>
118
+ )}
119
+
120
+ {/* Request ID */}
121
+ {errorDetails.requestId && (
122
+ <div className="text-body-1 pt-2 border-t border-destructive/15">
123
+ <span className="font-medium text-ink-3">Request ID</span>
124
+ <br />
125
+ <code className="text-body-1 text-ink/70 font-mono break-all">{errorDetails.requestId}</code>
126
+ </div>
127
+ )}
128
+ </div>
129
+ )}
130
+ </div>
131
+ )}
132
+
133
+ {/* Action button */}
134
+ {action && (
135
+ <button
136
+ type="button"
137
+ onClick={action.onClick}
138
+ className={cn(
139
+ 'mt-6 px-5 py-2.5 rounded-lg font-medium transition-colors h-10',
140
+ isError
141
+ ? 'bg-destructive text-destructive-foreground hover:bg-destructive/90'
142
+ : 'bg-primary text-primary-foreground hover:bg-primary/90',
143
+ )}
144
+ >
145
+ {action.label}
146
+ </button>
147
+ )}
148
+ </div>
149
+ );
150
+ };
package/src/ui/cn.ts ADDED
@@ -0,0 +1,32 @@
1
+ import { clsx, type ClassValue } from 'clsx';
2
+ import { extendTailwindMerge } from 'tailwind-merge';
3
+
4
+ // The Xema design system defines a custom typography scale in the host
5
+ // Tailwind config (`fontSize` keys: caption / body-1 / body-2 / subtitle /
6
+ // title / display). tailwind-merge doesn't know these keys, so it
7
+ // misclassifies `text-body-1`, `text-caption`, … as text-*color* utilities
8
+ // and silently strips a real `text-*` colour when both land on one element
9
+ // via cn(). Registering them in the `font-size` group keeps conflict
10
+ // resolution correct.
11
+ //
12
+ // This is the SAME config the host shell ships in `@/lib/utils`. The shared
13
+ // primitives moved into this package MUST merge classes identically, so the
14
+ // config is duplicated here (one trivial block) rather than imported from the
15
+ // host — this package stays host-framework-agnostic.
16
+ const twMerge = extendTailwindMerge({
17
+ extend: {
18
+ classGroups: {
19
+ 'font-size': [
20
+ { text: ['caption', 'body-1', 'body-2', 'subtitle', 'title', 'display'] },
21
+ ],
22
+ },
23
+ },
24
+ });
25
+
26
+ /**
27
+ * Tailwind-aware class merge used by every shared UI primitive. Identical in
28
+ * behaviour to the host shell's `cn` (including the custom typography scale).
29
+ */
30
+ export function cn(...inputs: ClassValue[]): string {
31
+ return twMerge(clsx(inputs));
32
+ }
@@ -0,0 +1,53 @@
1
+ // ═══════════════════════════════════════════════════════════════════════════
2
+ // @xemahq/ui-kernel/ui — shared UI surface for biomes
3
+ //
4
+ // Canonical shadcn primitives + Xema chrome, owned by the kernel so biomes
5
+ // stop deep-importing the host shell's `@/components/ui/*` and re-implementing
6
+ // chrome (ErrorCard / EmptyState / StateCard / PageHeader). Host-framework-
7
+ // agnostic: React + radix + cva + lucide only, no Next/Vite/React-Router.
8
+ //
9
+ // The class-merge helper (`cn`) carries the Xema typography scale so merging
10
+ // matches the host shell exactly.
11
+ // ═══════════════════════════════════════════════════════════════════════════
12
+
13
+ export { cn } from './cn';
14
+
15
+ // ── shadcn primitives ──
16
+ export * from './primitives/button';
17
+ export * from './primitives/badge';
18
+ export * from './primitives/input';
19
+ export * from './primitives/label';
20
+ export * from './primitives/select';
21
+ export * from './primitives/dialog';
22
+ export * from './primitives/card';
23
+ export * from './primitives/textarea';
24
+ export * from './primitives/tabs';
25
+ export * from './primitives/dropdown-menu';
26
+ export * from './primitives/skeleton';
27
+ export * from './primitives/checkbox';
28
+ export * from './primitives/table';
29
+ export * from './primitives/tooltip';
30
+ export * from './primitives/switch';
31
+ export * from './primitives/separator';
32
+ export * from './primitives/scroll-area';
33
+ export * from './primitives/alert-dialog';
34
+ export * from './primitives/popover';
35
+ export * from './primitives/collapsible';
36
+ export * from './primitives/resizable';
37
+ export * from './primitives/radio-group';
38
+ export * from './primitives/overflow-tabs';
39
+ export * from './primitives/tag-multi-select';
40
+ export * from './primitives/sheet';
41
+
42
+ // ── Xema chrome ──
43
+ export { default as ErrorCard, type ErrorMessageFormatter } from './chrome/ErrorCard';
44
+ export {
45
+ default as EmptyState,
46
+ type EmptyStateVariant,
47
+ type EmptyStateSize,
48
+ type EmptyStateLinkComponent,
49
+ } from './chrome/EmptyState';
50
+ export { StateCard } from './chrome/StateCard';
51
+ export { default as PageHeader, PageToolbar } from './chrome/PageHeader';
52
+ export { default as LoadingState, type LoadingStateVariant } from './chrome/LoadingState';
53
+ export { default as AsyncBoundary } from './chrome/AsyncBoundary';
@@ -0,0 +1,104 @@
1
+ import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog";
2
+ import * as React from "react";
3
+
4
+ import { buttonVariants } from './button';
5
+ import { cn } from '../cn';
6
+
7
+ const AlertDialog = AlertDialogPrimitive.Root;
8
+
9
+ const AlertDialogTrigger = AlertDialogPrimitive.Trigger;
10
+
11
+ const AlertDialogPortal = AlertDialogPrimitive.Portal;
12
+
13
+ const AlertDialogOverlay = React.forwardRef<
14
+ React.ElementRef<typeof AlertDialogPrimitive.Overlay>,
15
+ React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Overlay>
16
+ >(({ className, ...props }, ref) => (
17
+ <AlertDialogPrimitive.Overlay
18
+ className={cn(
19
+ "modal-scrim fixed inset-0 z-50 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
20
+ className,
21
+ )}
22
+ {...props}
23
+ ref={ref}
24
+ />
25
+ ));
26
+ AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName;
27
+
28
+ const AlertDialogContent = React.forwardRef<
29
+ React.ElementRef<typeof AlertDialogPrimitive.Content>,
30
+ React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Content>
31
+ >(({ className, ...props }, ref) => (
32
+ <AlertDialogPortal>
33
+ <AlertDialogOverlay />
34
+ <AlertDialogPrimitive.Content
35
+ ref={ref}
36
+ className={cn(
37
+ "modal-surface fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 p-6 duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
38
+ className,
39
+ )}
40
+ {...props}
41
+ />
42
+ </AlertDialogPortal>
43
+ ));
44
+ AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName;
45
+
46
+ const AlertDialogHeader = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
47
+ <div className={cn("flex flex-col space-y-2 text-center sm:text-left", className)} {...props} />
48
+ );
49
+ AlertDialogHeader.displayName = "AlertDialogHeader";
50
+
51
+ const AlertDialogFooter = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
52
+ <div className={cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", className)} {...props} />
53
+ );
54
+ AlertDialogFooter.displayName = "AlertDialogFooter";
55
+
56
+ const AlertDialogTitle = React.forwardRef<
57
+ React.ElementRef<typeof AlertDialogPrimitive.Title>,
58
+ React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Title>
59
+ >(({ className, ...props }, ref) => (
60
+ <AlertDialogPrimitive.Title ref={ref} className={cn("text-subtitle font-semibold", className)} {...props} />
61
+ ));
62
+ AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName;
63
+
64
+ const AlertDialogDescription = React.forwardRef<
65
+ React.ElementRef<typeof AlertDialogPrimitive.Description>,
66
+ React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Description>
67
+ >(({ className, ...props }, ref) => (
68
+ <AlertDialogPrimitive.Description ref={ref} className={cn("text-body-1 text-ink-3", className)} {...props} />
69
+ ));
70
+ AlertDialogDescription.displayName = AlertDialogPrimitive.Description.displayName;
71
+
72
+ const AlertDialogAction = React.forwardRef<
73
+ React.ElementRef<typeof AlertDialogPrimitive.Action>,
74
+ React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Action>
75
+ >(({ className, ...props }, ref) => (
76
+ <AlertDialogPrimitive.Action ref={ref} className={cn(buttonVariants(), className)} {...props} />
77
+ ));
78
+ AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName;
79
+
80
+ const AlertDialogCancel = React.forwardRef<
81
+ React.ElementRef<typeof AlertDialogPrimitive.Cancel>,
82
+ React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Cancel>
83
+ >(({ className, ...props }, ref) => (
84
+ <AlertDialogPrimitive.Cancel
85
+ ref={ref}
86
+ className={cn(buttonVariants({ variant: "outline" }), "mt-2 sm:mt-0", className)}
87
+ {...props}
88
+ />
89
+ ));
90
+ AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName;
91
+
92
+ export {
93
+ AlertDialog,
94
+ AlertDialogPortal,
95
+ AlertDialogOverlay,
96
+ AlertDialogTrigger,
97
+ AlertDialogContent,
98
+ AlertDialogHeader,
99
+ AlertDialogFooter,
100
+ AlertDialogTitle,
101
+ AlertDialogDescription,
102
+ AlertDialogAction,
103
+ AlertDialogCancel,
104
+ };
@@ -0,0 +1,32 @@
1
+ import { cva, type VariantProps } from "class-variance-authority";
2
+ import * as React from "react";
3
+
4
+ import { cn } from '../cn';
5
+
6
+ const badgeVariants = cva(
7
+ "inline-flex items-center rounded-full border px-2.5 py-0.5 text-body-1 font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
8
+ {
9
+ variants: {
10
+ variant: {
11
+ default: "border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
12
+ secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
13
+ destructive: "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
14
+ outline: "border-rule bg-paper text-ink",
15
+ },
16
+ },
17
+ defaultVariants: {
18
+ variant: "default",
19
+ },
20
+ },
21
+ );
22
+
23
+ export interface BadgeProps extends React.HTMLAttributes<HTMLSpanElement>, VariantProps<typeof badgeVariants> {}
24
+
25
+ const Badge = React.forwardRef<HTMLSpanElement, BadgeProps>(
26
+ ({ className, variant, ...props }, ref) => {
27
+ return <span ref={ref} className={cn(badgeVariants({ variant }), className)} {...props} />;
28
+ },
29
+ );
30
+ Badge.displayName = "Badge";
31
+
32
+ export { Badge, badgeVariants };
@@ -0,0 +1,47 @@
1
+ import { Slot } from "@radix-ui/react-slot";
2
+ import { cva, type VariantProps } from "class-variance-authority";
3
+ import * as React from "react";
4
+
5
+ import { cn } from '../cn';
6
+
7
+ const buttonVariants = cva(
8
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-body-1 font-medium ring-offset-background transition-[background-color,color,border-color,box-shadow] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
9
+ {
10
+ variants: {
11
+ variant: {
12
+ default: "bg-primary text-primary-foreground hover:bg-primary/90",
13
+ destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
14
+ outline: "border border-input bg-background text-foreground hover:bg-accent hover:text-accent-foreground",
15
+ secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
16
+ ghost: "text-ink-2 hover:bg-paper-elev hover:text-ink",
17
+ link: "text-primary underline-offset-4 hover:underline",
18
+ },
19
+ size: {
20
+ default: "h-10 px-4 py-2",
21
+ sm: "h-9 rounded-md px-3",
22
+ lg: "h-11 rounded-md px-8",
23
+ icon: "h-10 w-10",
24
+ },
25
+ },
26
+ defaultVariants: {
27
+ variant: "default",
28
+ size: "default",
29
+ },
30
+ },
31
+ );
32
+
33
+ export interface ButtonProps
34
+ extends React.ButtonHTMLAttributes<HTMLButtonElement>,
35
+ VariantProps<typeof buttonVariants> {
36
+ asChild?: boolean;
37
+ }
38
+
39
+ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
40
+ ({ className, variant, size, asChild = false, ...props }, ref) => {
41
+ const Comp = asChild ? Slot : "button";
42
+ return <Comp className={cn(buttonVariants({ variant, size, className }))} ref={ref} {...props} />;
43
+ },
44
+ );
45
+ Button.displayName = "Button";
46
+
47
+ export { Button, buttonVariants };
@@ -0,0 +1,43 @@
1
+ import * as React from "react";
2
+
3
+ import { cn } from '../cn';
4
+
5
+ const Card = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(({ className, ...props }, ref) => (
6
+ <div ref={ref} className={cn("rounded-lg border bg-card text-card-foreground shadow-sm", className)} {...props} />
7
+ ));
8
+ Card.displayName = "Card";
9
+
10
+ const CardHeader = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
11
+ ({ className, ...props }, ref) => (
12
+ <div ref={ref} className={cn("flex flex-col space-y-1.5 p-6", className)} {...props} />
13
+ ),
14
+ );
15
+ CardHeader.displayName = "CardHeader";
16
+
17
+ const CardTitle = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLHeadingElement>>(
18
+ ({ className, children, ...props }, ref) => (
19
+ <h3 ref={ref} className={cn("text-display font-semibold leading-none tracking-tight", className)} {...props}>{children}</h3>
20
+ ),
21
+ );
22
+ CardTitle.displayName = "CardTitle";
23
+
24
+ const CardDescription = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLParagraphElement>>(
25
+ ({ className, ...props }, ref) => (
26
+ <p ref={ref} className={cn("text-body-1 text-ink-3", className)} {...props} />
27
+ ),
28
+ );
29
+ CardDescription.displayName = "CardDescription";
30
+
31
+ const CardContent = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
32
+ ({ className, ...props }, ref) => <div ref={ref} className={cn("p-6 pt-0", className)} {...props} />,
33
+ );
34
+ CardContent.displayName = "CardContent";
35
+
36
+ const CardFooter = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
37
+ ({ className, ...props }, ref) => (
38
+ <div ref={ref} className={cn("flex items-center p-6 pt-0", className)} {...props} />
39
+ ),
40
+ );
41
+ CardFooter.displayName = "CardFooter";
42
+
43
+ export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent };
@@ -0,0 +1,26 @@
1
+ import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
2
+ import { Check } from "lucide-react";
3
+ import * as React from "react";
4
+
5
+ import { cn } from '../cn';
6
+
7
+ const Checkbox = React.forwardRef<
8
+ React.ElementRef<typeof CheckboxPrimitive.Root>,
9
+ React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root>
10
+ >(({ className, ...props }, ref) => (
11
+ <CheckboxPrimitive.Root
12
+ ref={ref}
13
+ className={cn(
14
+ "peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
15
+ className,
16
+ )}
17
+ {...props}
18
+ >
19
+ <CheckboxPrimitive.Indicator className={cn("flex items-center justify-center text-current")}>
20
+ <Check className="h-4 w-4" />
21
+ </CheckboxPrimitive.Indicator>
22
+ </CheckboxPrimitive.Root>
23
+ ));
24
+ Checkbox.displayName = CheckboxPrimitive.Root.displayName;
25
+
26
+ export { Checkbox };
@@ -0,0 +1,9 @@
1
+ import * as CollapsiblePrimitive from "@radix-ui/react-collapsible";
2
+
3
+ const Collapsible = CollapsiblePrimitive.Root;
4
+
5
+ const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger;
6
+
7
+ const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent;
8
+
9
+ export { Collapsible, CollapsibleTrigger, CollapsibleContent };
@@ -0,0 +1,103 @@
1
+ import * as DialogPrimitive from "@radix-ui/react-dialog";
2
+ import { X } from "lucide-react";
3
+ import * as React from "react";
4
+
5
+ import { cn } from '../cn';
6
+
7
+ const Dialog = DialogPrimitive.Root;
8
+
9
+ const DialogTrigger = DialogPrimitive.Trigger;
10
+
11
+ const DialogPortal = DialogPrimitive.Portal;
12
+
13
+ const DialogClose = DialogPrimitive.Close;
14
+
15
+ const DialogOverlay = React.forwardRef<
16
+ React.ElementRef<typeof DialogPrimitive.Overlay>,
17
+ React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
18
+ >(({ className, ...props }, ref) => (
19
+ <DialogPrimitive.Overlay
20
+ ref={ref}
21
+ className={cn(
22
+ "modal-scrim fixed inset-0 z-50 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
23
+ className,
24
+ )}
25
+ {...props}
26
+ />
27
+ ));
28
+ DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
29
+
30
+ const DialogContent = React.forwardRef<
31
+ React.ElementRef<typeof DialogPrimitive.Content>,
32
+ React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> & { 'data-hide-close'?: boolean }
33
+ >(({ className, children, 'data-hide-close': hideClose, 'aria-describedby': ariaDescribedBy, ...props }, ref) => (
34
+ <DialogPortal>
35
+ <DialogOverlay />
36
+ <DialogPrimitive.Content
37
+ ref={ref}
38
+ // Pulling `aria-describedby` out of props and re-passing it (even
39
+ // when undefined) silences Radix's "Missing Description or
40
+ // aria-describedby={undefined}" warning for dialogs that
41
+ // intentionally have no description. Dialogs that DO include a
42
+ // <DialogDescription> still set this through the spread.
43
+ aria-describedby={ariaDescribedBy}
44
+ className={cn(
45
+ "modal-surface fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg max-h-[calc(100dvh-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 overflow-y-auto p-6 duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
46
+ className,
47
+ )}
48
+ {...props}
49
+ >
50
+ {children}
51
+ {!hideClose && (
52
+ <DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm text-ink-3 ring-offset-background transition-colors hover:text-ink focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none">
53
+ <X className="h-4 w-4" />
54
+ <span className="sr-only">Close</span>
55
+ </DialogPrimitive.Close>
56
+ )}
57
+ </DialogPrimitive.Content>
58
+ </DialogPortal>
59
+ ));
60
+ DialogContent.displayName = DialogPrimitive.Content.displayName;
61
+
62
+ const DialogHeader = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
63
+ <div className={cn("flex flex-col space-y-1.5 text-center sm:text-left", className)} {...props} />
64
+ );
65
+ DialogHeader.displayName = "DialogHeader";
66
+
67
+ const DialogFooter = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
68
+ <div className={cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", className)} {...props} />
69
+ );
70
+ DialogFooter.displayName = "DialogFooter";
71
+
72
+ const DialogTitle = React.forwardRef<
73
+ React.ElementRef<typeof DialogPrimitive.Title>,
74
+ React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
75
+ >(({ className, ...props }, ref) => (
76
+ <DialogPrimitive.Title
77
+ ref={ref}
78
+ className={cn("text-subtitle font-semibold leading-none tracking-tight", className)}
79
+ {...props}
80
+ />
81
+ ));
82
+ DialogTitle.displayName = DialogPrimitive.Title.displayName;
83
+
84
+ const DialogDescription = React.forwardRef<
85
+ React.ElementRef<typeof DialogPrimitive.Description>,
86
+ React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
87
+ >(({ className, ...props }, ref) => (
88
+ <DialogPrimitive.Description ref={ref} className={cn("text-body-1 text-ink-3", className)} {...props} />
89
+ ));
90
+ DialogDescription.displayName = DialogPrimitive.Description.displayName;
91
+
92
+ export {
93
+ Dialog,
94
+ DialogPortal,
95
+ DialogOverlay,
96
+ DialogClose,
97
+ DialogTrigger,
98
+ DialogContent,
99
+ DialogHeader,
100
+ DialogFooter,
101
+ DialogTitle,
102
+ DialogDescription,
103
+ };