@sqlrooms/ui 0.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 (164) hide show
  1. package/.turbo/turbo-build.log +8 -0
  2. package/.turbo/turbo-lint.log +10 -0
  3. package/CHANGELOG.md +8 -0
  4. package/LICENSE.md +9 -0
  5. package/components.json +21 -0
  6. package/dist/components/DefaultErrorBoundary.d.ts +2 -0
  7. package/dist/components/DefaultErrorBoundary.d.ts.map +1 -0
  8. package/dist/components/DefaultErrorBoundary.js +1 -0
  9. package/dist/components/ProgressModal.d.ts +10 -0
  10. package/dist/components/ProgressModal.d.ts.map +1 -0
  11. package/dist/components/ProgressModal.js +7 -0
  12. package/dist/components/accordion.d.ts +8 -0
  13. package/dist/components/accordion.d.ts.map +1 -0
  14. package/dist/components/accordion.js +13 -0
  15. package/dist/components/alert.d.ts +9 -0
  16. package/dist/components/alert.d.ts.map +1 -0
  17. package/dist/components/alert.js +22 -0
  18. package/dist/components/badge.d.ts +10 -0
  19. package/dist/components/badge.d.ts.map +1 -0
  20. package/dist/components/badge.js +20 -0
  21. package/dist/components/breadcrumb.d.ts +20 -0
  22. package/dist/components/breadcrumb.d.ts.map +1 -0
  23. package/dist/components/breadcrumb.js +23 -0
  24. package/dist/components/button.d.ts +12 -0
  25. package/dist/components/button.d.ts.map +1 -0
  26. package/dist/components/button.js +33 -0
  27. package/dist/components/card.d.ts +9 -0
  28. package/dist/components/card.d.ts.map +1 -0
  29. package/dist/components/card.js +16 -0
  30. package/dist/components/checkbox.d.ts +5 -0
  31. package/dist/components/checkbox.d.ts.map +1 -0
  32. package/dist/components/checkbox.js +8 -0
  33. package/dist/components/dialog.d.ts +20 -0
  34. package/dist/components/dialog.d.ts.map +1 -0
  35. package/dist/components/dialog.js +22 -0
  36. package/dist/components/dropdown-menu.d.ts +28 -0
  37. package/dist/components/dropdown-menu.d.ts.map +1 -0
  38. package/dist/components/dropdown-menu.js +35 -0
  39. package/dist/components/editable-text.d.ts +53 -0
  40. package/dist/components/editable-text.d.ts.map +1 -0
  41. package/dist/components/editable-text.js +87 -0
  42. package/dist/components/error-boundary.d.ts +19 -0
  43. package/dist/components/error-boundary.d.ts.map +1 -0
  44. package/dist/components/error-boundary.js +26 -0
  45. package/dist/components/error-pane.d.ts +13 -0
  46. package/dist/components/error-pane.d.ts.map +1 -0
  47. package/dist/components/error-pane.js +12 -0
  48. package/dist/components/form.d.ts +24 -0
  49. package/dist/components/form.d.ts.map +1 -0
  50. package/dist/components/form.js +62 -0
  51. package/dist/components/index.d.ts +2 -0
  52. package/dist/components/index.d.ts.map +1 -0
  53. package/dist/components/index.js +1 -0
  54. package/dist/components/input.d.ts +4 -0
  55. package/dist/components/input.d.ts.map +1 -0
  56. package/dist/components/input.js +8 -0
  57. package/dist/components/label.d.ts +6 -0
  58. package/dist/components/label.d.ts.map +1 -0
  59. package/dist/components/label.js +9 -0
  60. package/dist/components/popover.d.ts +8 -0
  61. package/dist/components/popover.d.ts.map +1 -0
  62. package/dist/components/popover.js +10 -0
  63. package/dist/components/progress-modal.d.ts +10 -0
  64. package/dist/components/progress-modal.d.ts.map +1 -0
  65. package/dist/components/progress-modal.js +8 -0
  66. package/dist/components/progress.d.ts +5 -0
  67. package/dist/components/progress.d.ts.map +1 -0
  68. package/dist/components/progress.js +7 -0
  69. package/dist/components/resizable.d.ts +24 -0
  70. package/dist/components/resizable.d.ts.map +1 -0
  71. package/dist/components/resizable.js +8 -0
  72. package/dist/components/select.d.ts +14 -0
  73. package/dist/components/select.d.ts.map +1 -0
  74. package/dist/components/select.js +26 -0
  75. package/dist/components/skeleton-pane.d.ts +10 -0
  76. package/dist/components/skeleton-pane.d.ts.map +1 -0
  77. package/dist/components/skeleton-pane.js +17 -0
  78. package/dist/components/skeleton.d.ts +3 -0
  79. package/dist/components/skeleton.d.ts.map +1 -0
  80. package/dist/components/skeleton.js +6 -0
  81. package/dist/components/spinner-pane.d.ts +10 -0
  82. package/dist/components/spinner-pane.d.ts.map +1 -0
  83. package/dist/components/spinner-pane.js +17 -0
  84. package/dist/components/spinner.d.ts +8 -0
  85. package/dist/components/spinner.d.ts.map +1 -0
  86. package/dist/components/spinner.js +9 -0
  87. package/dist/components/switch.d.ts +5 -0
  88. package/dist/components/switch.d.ts.map +1 -0
  89. package/dist/components/switch.js +7 -0
  90. package/dist/components/table.d.ts +20 -0
  91. package/dist/components/table.d.ts.map +1 -0
  92. package/dist/components/table.js +20 -0
  93. package/dist/components/tabs.d.ts +8 -0
  94. package/dist/components/tabs.d.ts.map +1 -0
  95. package/dist/components/tabs.js +12 -0
  96. package/dist/components/textarea.d.ts +4 -0
  97. package/dist/components/textarea.d.ts.map +1 -0
  98. package/dist/components/textarea.js +8 -0
  99. package/dist/components/toast.d.ts +16 -0
  100. package/dist/components/toast.d.ts.map +1 -0
  101. package/dist/components/toast.js +33 -0
  102. package/dist/components/toaster.d.ts +2 -0
  103. package/dist/components/toaster.d.ts.map +1 -0
  104. package/dist/components/toaster.js +9 -0
  105. package/dist/components/tooltip.d.ts +8 -0
  106. package/dist/components/tooltip.d.ts.map +1 -0
  107. package/dist/components/tooltip.js +10 -0
  108. package/dist/hooks/use-toast.d.ts +45 -0
  109. package/dist/hooks/use-toast.d.ts.map +1 -0
  110. package/dist/hooks/use-toast.js +128 -0
  111. package/dist/hooks/useDisclosure.d.ts +9 -0
  112. package/dist/hooks/useDisclosure.d.ts.map +1 -0
  113. package/dist/hooks/useDisclosure.js +14 -0
  114. package/dist/index.d.ts +36 -0
  115. package/dist/index.d.ts.map +1 -0
  116. package/dist/index.js +35 -0
  117. package/dist/lib/utils.d.ts +3 -0
  118. package/dist/lib/utils.d.ts.map +1 -0
  119. package/dist/lib/utils.js +5 -0
  120. package/dist/tailwind-preset.css +68 -0
  121. package/dist/tailwind-preset.d.ts +5 -0
  122. package/dist/tailwind-preset.d.ts.map +1 -0
  123. package/dist/tailwind-preset.js +50 -0
  124. package/dist/tsconfig.tsbuildinfo +1 -0
  125. package/eslint.config.js +4 -0
  126. package/package.json +59 -0
  127. package/src/components/accordion.tsx +55 -0
  128. package/src/components/alert.tsx +59 -0
  129. package/src/components/badge.tsx +34 -0
  130. package/src/components/breadcrumb.tsx +115 -0
  131. package/src/components/button.tsx +55 -0
  132. package/src/components/card.tsx +76 -0
  133. package/src/components/checkbox.tsx +28 -0
  134. package/src/components/dialog.tsx +120 -0
  135. package/src/components/dropdown-menu.tsx +199 -0
  136. package/src/components/editable-text.tsx +199 -0
  137. package/src/components/error-boundary.tsx +48 -0
  138. package/src/components/error-pane.tsx +81 -0
  139. package/src/components/form.tsx +176 -0
  140. package/src/components/input.tsx +22 -0
  141. package/src/components/label.tsx +24 -0
  142. package/src/components/popover.tsx +31 -0
  143. package/src/components/progress-modal.tsx +33 -0
  144. package/src/components/progress.tsx +26 -0
  145. package/src/components/resizable.tsx +43 -0
  146. package/src/components/select.tsx +157 -0
  147. package/src/components/skeleton-pane.tsx +45 -0
  148. package/src/components/skeleton.tsx +12 -0
  149. package/src/components/spinner-pane.tsx +44 -0
  150. package/src/components/spinner.tsx +16 -0
  151. package/src/components/switch.tsx +27 -0
  152. package/src/components/table.tsx +136 -0
  153. package/src/components/tabs.tsx +53 -0
  154. package/src/components/textarea.tsx +21 -0
  155. package/src/components/toast.tsx +127 -0
  156. package/src/components/toaster.tsx +33 -0
  157. package/src/components/tooltip.tsx +29 -0
  158. package/src/hooks/use-toast.ts +191 -0
  159. package/src/hooks/useDisclosure.ts +26 -0
  160. package/src/index.ts +35 -0
  161. package/src/lib/utils.ts +6 -0
  162. package/src/tailwind-preset.css +68 -0
  163. package/src/tailwind-preset.ts +55 -0
  164. package/tsconfig.json +12 -0
@@ -0,0 +1,120 @@
1
+ import * as React from 'react';
2
+ import * as DialogPrimitive from '@radix-ui/react-dialog';
3
+ import {X} from 'lucide-react';
4
+
5
+ import {cn} from '../lib/utils';
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
+ 'fixed inset-0 z-50 bg-black/80 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>
33
+ >(({className, children, ...props}, ref) => (
34
+ <DialogPortal>
35
+ <DialogOverlay />
36
+ <DialogPrimitive.Content
37
+ ref={ref}
38
+ className={cn(
39
+ 'fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg 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',
40
+ className,
41
+ )}
42
+ {...props}
43
+ >
44
+ {children}
45
+ <DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
46
+ <X className="h-4 w-4" />
47
+ <span className="sr-only">Close</span>
48
+ </DialogPrimitive.Close>
49
+ </DialogPrimitive.Content>
50
+ </DialogPortal>
51
+ ));
52
+ DialogContent.displayName = DialogPrimitive.Content.displayName;
53
+
54
+ const DialogHeader = ({
55
+ className,
56
+ ...props
57
+ }: React.HTMLAttributes<HTMLDivElement>) => (
58
+ <div
59
+ className={cn(
60
+ 'flex flex-col space-y-1.5 text-center sm:text-left',
61
+ className,
62
+ )}
63
+ {...props}
64
+ />
65
+ );
66
+ DialogHeader.displayName = 'DialogHeader';
67
+
68
+ const DialogFooter = ({
69
+ className,
70
+ ...props
71
+ }: React.HTMLAttributes<HTMLDivElement>) => (
72
+ <div
73
+ className={cn(
74
+ 'flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2',
75
+ className,
76
+ )}
77
+ {...props}
78
+ />
79
+ );
80
+ DialogFooter.displayName = 'DialogFooter';
81
+
82
+ const DialogTitle = React.forwardRef<
83
+ React.ElementRef<typeof DialogPrimitive.Title>,
84
+ React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
85
+ >(({className, ...props}, ref) => (
86
+ <DialogPrimitive.Title
87
+ ref={ref}
88
+ className={cn(
89
+ 'text-lg font-semibold leading-none tracking-tight',
90
+ className,
91
+ )}
92
+ {...props}
93
+ />
94
+ ));
95
+ DialogTitle.displayName = DialogPrimitive.Title.displayName;
96
+
97
+ const DialogDescription = React.forwardRef<
98
+ React.ElementRef<typeof DialogPrimitive.Description>,
99
+ React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
100
+ >(({className, ...props}, ref) => (
101
+ <DialogPrimitive.Description
102
+ ref={ref}
103
+ className={cn('text-sm text-muted-foreground', className)}
104
+ {...props}
105
+ />
106
+ ));
107
+ DialogDescription.displayName = DialogPrimitive.Description.displayName;
108
+
109
+ export {
110
+ Dialog,
111
+ DialogPortal,
112
+ DialogOverlay,
113
+ DialogTrigger,
114
+ DialogClose,
115
+ DialogContent,
116
+ DialogHeader,
117
+ DialogFooter,
118
+ DialogTitle,
119
+ DialogDescription,
120
+ };
@@ -0,0 +1,199 @@
1
+ import * as React from 'react';
2
+ import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';
3
+ import {Check, ChevronRight, Circle} from 'lucide-react';
4
+
5
+ import {cn} from '../lib/utils';
6
+
7
+ const DropdownMenu = DropdownMenuPrimitive.Root;
8
+
9
+ const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
10
+
11
+ const DropdownMenuGroup = DropdownMenuPrimitive.Group;
12
+
13
+ const DropdownMenuPortal = DropdownMenuPrimitive.Portal;
14
+
15
+ const DropdownMenuSub = DropdownMenuPrimitive.Sub;
16
+
17
+ const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
18
+
19
+ const DropdownMenuSubTrigger = React.forwardRef<
20
+ React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,
21
+ React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {
22
+ inset?: boolean;
23
+ }
24
+ >(({className, inset, children, ...props}, ref) => (
25
+ <DropdownMenuPrimitive.SubTrigger
26
+ ref={ref}
27
+ className={cn(
28
+ 'flex cursor-default gap-2 select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
29
+ inset && 'pl-8',
30
+ className,
31
+ )}
32
+ {...props}
33
+ >
34
+ {children}
35
+ <ChevronRight className="ml-auto" />
36
+ </DropdownMenuPrimitive.SubTrigger>
37
+ ));
38
+ DropdownMenuSubTrigger.displayName =
39
+ DropdownMenuPrimitive.SubTrigger.displayName;
40
+
41
+ const DropdownMenuSubContent = React.forwardRef<
42
+ React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,
43
+ React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent>
44
+ >(({className, ...props}, ref) => (
45
+ <DropdownMenuPrimitive.SubContent
46
+ ref={ref}
47
+ className={cn(
48
+ 'z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg 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-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
49
+ className,
50
+ )}
51
+ {...props}
52
+ />
53
+ ));
54
+ DropdownMenuSubContent.displayName =
55
+ DropdownMenuPrimitive.SubContent.displayName;
56
+
57
+ const DropdownMenuContent = React.forwardRef<
58
+ React.ElementRef<typeof DropdownMenuPrimitive.Content>,
59
+ React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>
60
+ >(({className, sideOffset = 4, ...props}, ref) => (
61
+ <DropdownMenuPrimitive.Portal>
62
+ <DropdownMenuPrimitive.Content
63
+ ref={ref}
64
+ sideOffset={sideOffset}
65
+ className={cn(
66
+ 'z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md',
67
+ '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-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
68
+ className,
69
+ )}
70
+ {...props}
71
+ />
72
+ </DropdownMenuPrimitive.Portal>
73
+ ));
74
+ DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;
75
+
76
+ const DropdownMenuItem = React.forwardRef<
77
+ React.ElementRef<typeof DropdownMenuPrimitive.Item>,
78
+ React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {
79
+ inset?: boolean;
80
+ }
81
+ >(({className, inset, ...props}, ref) => (
82
+ <DropdownMenuPrimitive.Item
83
+ ref={ref}
84
+ className={cn(
85
+ 'relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&>svg]:size-4 [&>svg]:shrink-0',
86
+ inset && 'pl-8',
87
+ className,
88
+ )}
89
+ {...props}
90
+ />
91
+ ));
92
+ DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;
93
+
94
+ const DropdownMenuCheckboxItem = React.forwardRef<
95
+ React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,
96
+ React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem>
97
+ >(({className, children, checked, ...props}, ref) => (
98
+ <DropdownMenuPrimitive.CheckboxItem
99
+ ref={ref}
100
+ className={cn(
101
+ 'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
102
+ className,
103
+ )}
104
+ checked={checked}
105
+ {...props}
106
+ >
107
+ <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
108
+ <DropdownMenuPrimitive.ItemIndicator>
109
+ <Check className="h-4 w-4" />
110
+ </DropdownMenuPrimitive.ItemIndicator>
111
+ </span>
112
+ {children}
113
+ </DropdownMenuPrimitive.CheckboxItem>
114
+ ));
115
+ DropdownMenuCheckboxItem.displayName =
116
+ DropdownMenuPrimitive.CheckboxItem.displayName;
117
+
118
+ const DropdownMenuRadioItem = React.forwardRef<
119
+ React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,
120
+ React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem>
121
+ >(({className, children, ...props}, ref) => (
122
+ <DropdownMenuPrimitive.RadioItem
123
+ ref={ref}
124
+ className={cn(
125
+ 'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
126
+ className,
127
+ )}
128
+ {...props}
129
+ >
130
+ <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
131
+ <DropdownMenuPrimitive.ItemIndicator>
132
+ <Circle className="h-2 w-2 fill-current" />
133
+ </DropdownMenuPrimitive.ItemIndicator>
134
+ </span>
135
+ {children}
136
+ </DropdownMenuPrimitive.RadioItem>
137
+ ));
138
+ DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName;
139
+
140
+ const DropdownMenuLabel = React.forwardRef<
141
+ React.ElementRef<typeof DropdownMenuPrimitive.Label>,
142
+ React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & {
143
+ inset?: boolean;
144
+ }
145
+ >(({className, inset, ...props}, ref) => (
146
+ <DropdownMenuPrimitive.Label
147
+ ref={ref}
148
+ className={cn(
149
+ 'px-2 py-1.5 text-sm font-semibold',
150
+ inset && 'pl-8',
151
+ className,
152
+ )}
153
+ {...props}
154
+ />
155
+ ));
156
+ DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName;
157
+
158
+ const DropdownMenuSeparator = React.forwardRef<
159
+ React.ElementRef<typeof DropdownMenuPrimitive.Separator>,
160
+ React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>
161
+ >(({className, ...props}, ref) => (
162
+ <DropdownMenuPrimitive.Separator
163
+ ref={ref}
164
+ className={cn('-mx-1 my-1 h-px bg-muted', className)}
165
+ {...props}
166
+ />
167
+ ));
168
+ DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;
169
+
170
+ const DropdownMenuShortcut = ({
171
+ className,
172
+ ...props
173
+ }: React.HTMLAttributes<HTMLSpanElement>) => {
174
+ return (
175
+ <span
176
+ className={cn('ml-auto text-xs tracking-widest opacity-60', className)}
177
+ {...props}
178
+ />
179
+ );
180
+ };
181
+ DropdownMenuShortcut.displayName = 'DropdownMenuShortcut';
182
+
183
+ export {
184
+ DropdownMenu,
185
+ DropdownMenuTrigger,
186
+ DropdownMenuContent,
187
+ DropdownMenuItem,
188
+ DropdownMenuCheckboxItem,
189
+ DropdownMenuRadioItem,
190
+ DropdownMenuLabel,
191
+ DropdownMenuSeparator,
192
+ DropdownMenuShortcut,
193
+ DropdownMenuGroup,
194
+ DropdownMenuPortal,
195
+ DropdownMenuSub,
196
+ DropdownMenuSubContent,
197
+ DropdownMenuSubTrigger,
198
+ DropdownMenuRadioGroup,
199
+ };
@@ -0,0 +1,199 @@
1
+ import {ChangeEvent, FC, useCallback, useEffect, useRef, useState} from 'react';
2
+
3
+ import {Input} from './input';
4
+ import {cn} from '../lib/utils';
5
+
6
+ /**
7
+ * Component that allows the user to edit a string.
8
+ *
9
+ * The editing mode can be controlled (the mode is managed by the parent component)
10
+ * or uncontrolled (managed by the component itself).
11
+ *
12
+ * Controlled mode example:
13
+ * ```
14
+ * const [text, setText] = useState('');
15
+ * const [isEditing, setEditing] = useState(false);
16
+ * ...
17
+ * <EditableText
18
+ * value={text}
19
+ * onChange={setText}
20
+ * isEditing={isEditing}
21
+ * onEditingChange={setEditing}
22
+ * />
23
+ * ```
24
+ *
25
+ * Uncontrolled mode example:
26
+ * ```
27
+ * const [text, setText] = useState('');
28
+ * ...
29
+ * <EditableText
30
+ * value={text}
31
+ * onChange={setText}
32
+ * defaultEditing={false}
33
+ * />
34
+ * ```
35
+ */
36
+
37
+ type Props = {
38
+ className?: string;
39
+ isReadOnly?: boolean;
40
+ value: string;
41
+ minWidth?: number;
42
+ maxWidth?: number;
43
+ placeholder?: string;
44
+ onChange: (text: string) => void;
45
+
46
+ /**
47
+ * The editing state when it is initially rendered. Use when you do not need to control its editing state
48
+ * in the parent component.
49
+ **/
50
+ defaultEditing?: boolean;
51
+
52
+ /**
53
+ * The controlled editing state of the component. Must be used in conjunction with onEditingChange.
54
+ **/
55
+ isEditing?: boolean;
56
+ onEditingChange?: (isEditing: boolean) => void;
57
+ };
58
+
59
+ const EDITING_PAD = 12;
60
+
61
+ export const EditableText: FC<Props> = ({
62
+ className,
63
+ isReadOnly = false,
64
+ defaultEditing = false,
65
+ minWidth = 100,
66
+ maxWidth = 500,
67
+ isEditing,
68
+ placeholder,
69
+ value,
70
+ onChange,
71
+ onEditingChange,
72
+ }) => {
73
+ const [isInternalEditing, setInternalIsEditing] = useState(defaultEditing);
74
+ const inputRef = useRef<HTMLInputElement>(null);
75
+ const [internalValue, setInternalValue] = useState(value);
76
+ const internalValueRef = useRef(internalValue);
77
+ internalValueRef.current = internalValue;
78
+
79
+ const [inputWidth, setInputWidth] = useState(minWidth);
80
+ const spanRef = useRef<HTMLDivElement>(null);
81
+
82
+ useEffect(() => {
83
+ // Update input width based on the invisible span width
84
+ setInputWidth(spanRef.current?.offsetWidth ?? 0);
85
+ }, [internalValue]);
86
+
87
+ const handleSetValue = useCallback(
88
+ (e: ChangeEvent<HTMLInputElement>) => {
89
+ if (isReadOnly || !isInternalEditing) {
90
+ return;
91
+ }
92
+ return setInternalValue(e.target.value);
93
+ },
94
+ [isInternalEditing, isReadOnly],
95
+ );
96
+
97
+ const handleSetEditing = useCallback(
98
+ (nextIsEditing: boolean) => {
99
+ if (isReadOnly) {
100
+ return;
101
+ }
102
+ setInternalIsEditing(nextIsEditing);
103
+ onEditingChange?.(nextIsEditing);
104
+ },
105
+ [isReadOnly, onEditingChange],
106
+ );
107
+
108
+ const handleBlur = useCallback(() => {
109
+ handleSetEditing(false);
110
+ onChange(internalValueRef.current?.trim());
111
+ }, [handleSetEditing, onChange]);
112
+
113
+ const handleClick = useCallback(() => {
114
+ if (!isInternalEditing) {
115
+ handleSetEditing(true);
116
+ }
117
+ }, [isInternalEditing, handleSetEditing]);
118
+
119
+ useEffect(() => {
120
+ if (value !== internalValueRef.current) {
121
+ setInternalValue(value);
122
+ }
123
+ }, [value]);
124
+ useEffect(() => {
125
+ if (isEditing !== undefined && isEditing !== isInternalEditing) {
126
+ setInternalIsEditing(Boolean(isEditing));
127
+ if (isEditing) {
128
+ setTimeout(() => {
129
+ // When enabling editing from a dropdown menu, there will be a blur event when the dropdown closes,
130
+ // so we need to wait a bit before making sure the input is focused and selected
131
+ inputRef.current?.select();
132
+ inputRef.current?.focus();
133
+ }, 200);
134
+ }
135
+ }
136
+ }, [isInternalEditing, handleSetEditing, isEditing]);
137
+
138
+ // Add keydown event listener to handle enter key
139
+ useEffect(() => {
140
+ const handleKeyDown = (e: KeyboardEvent) => {
141
+ switch (e.key) {
142
+ case 'Escape':
143
+ // Reset the internal value to the original value
144
+ setInternalValue(value);
145
+ internalValueRef.current = value;
146
+ handleSetEditing(false);
147
+ inputRef.current?.blur();
148
+ break;
149
+ case 'Enter':
150
+ handleSetEditing(false);
151
+ onChange(internalValueRef.current.trim());
152
+ inputRef.current?.blur();
153
+ break;
154
+ }
155
+ };
156
+ if (isInternalEditing) {
157
+ document.addEventListener('keydown', handleKeyDown);
158
+ }
159
+ return () => {
160
+ document.removeEventListener('keydown', handleKeyDown);
161
+ };
162
+ }, [isInternalEditing, onChange, handleSetEditing, value]);
163
+
164
+ return (
165
+ <>
166
+ {/* Hidden span to measure the input width, so that the we can make the input grow to fit the text */}
167
+ <span
168
+ ref={spanRef}
169
+ className={cn(
170
+ className,
171
+ 'white-space-pre pointer-events-none invisible absolute left-0 top-0 px-1',
172
+ )}
173
+ style={{minWidth, maxWidth}}
174
+ >
175
+ {internalValue}
176
+ </span>
177
+ <Input
178
+ ref={inputRef}
179
+ className={cn(
180
+ 'disabled:opacity-1 rounded-sm border-transparent px-1 py-0 focus:border-blue-500 focus:outline-none focus:ring-blue-500 disabled:cursor-text',
181
+ {'select-none bg-transparent': !isInternalEditing},
182
+ className,
183
+ )}
184
+ style={{
185
+ width: inputWidth + EDITING_PAD, // add padding to avoid jittering when editing
186
+ caretColor: isInternalEditing ? undefined : 'transparent',
187
+ }}
188
+ value={internalValue}
189
+ onChange={handleSetValue}
190
+ onBlur={handleBlur}
191
+ disabled={isReadOnly}
192
+ onClick={handleClick}
193
+ placeholder={
194
+ !isInternalEditing ? (placeholder ?? 'Click to edit') : undefined
195
+ }
196
+ />
197
+ </>
198
+ );
199
+ };
@@ -0,0 +1,48 @@
1
+ import {Component, ErrorInfo, ReactNode} from 'react';
2
+ import {ErrorPane} from './error-pane';
3
+
4
+ interface Props {
5
+ children?: ReactNode;
6
+ onRetry?: () => void;
7
+ fallback?: ReactNode;
8
+ }
9
+
10
+ interface State {
11
+ hasError: boolean;
12
+ error: Error | null;
13
+ }
14
+
15
+ export class ErrorBoundary extends Component<Props, State> {
16
+ public state: State = {
17
+ hasError: false,
18
+ error: null,
19
+ };
20
+
21
+ public static getDerivedStateFromError(error: Error): State {
22
+ return {hasError: true, error};
23
+ }
24
+
25
+ public componentDidCatch(error: Error, errorInfo: ErrorInfo) {
26
+ console.error('Uncaught error:', error, errorInfo);
27
+ }
28
+
29
+ private handleRetry = () => {
30
+ if (this.props.onRetry) {
31
+ this.props.onRetry();
32
+ }
33
+ };
34
+
35
+ public render() {
36
+ if (this.state.hasError) {
37
+ return (
38
+ <ErrorPane
39
+ error={this.state.error}
40
+ onRetry={this.props.onRetry ? this.handleRetry : undefined}
41
+ actions={!!this.props.onRetry}
42
+ />
43
+ );
44
+ }
45
+
46
+ return this.props.children;
47
+ }
48
+ }
@@ -0,0 +1,81 @@
1
+ import {Alert, AlertDescription, AlertTitle} from './alert';
2
+ import {RotateCcwIcon, TriangleAlertIcon} from 'lucide-react';
3
+ import * as React from 'react';
4
+ import {cn} from '../lib/utils';
5
+ import {Button} from './button';
6
+
7
+ interface ErrorPaneProps extends React.HTMLAttributes<HTMLDivElement> {
8
+ embed?: boolean;
9
+ error?: string | Error | unknown;
10
+ title?: string;
11
+ text?: string;
12
+ onRetry?: () => void;
13
+ onGoToStart?: () => void;
14
+ actions?: boolean;
15
+ }
16
+
17
+ const ErrorPane = React.forwardRef<HTMLDivElement, ErrorPaneProps>(
18
+ (
19
+ {
20
+ className,
21
+ embed,
22
+ title = 'Something went wrong',
23
+ text = `We are sorry, but something unexpected happened. We were notified
24
+ and will be working on resolving the issue as soon as possible.`,
25
+ onRetry,
26
+ actions = false,
27
+ onGoToStart,
28
+ ...props
29
+ },
30
+ ref,
31
+ ) => {
32
+ return (
33
+ <div
34
+ ref={ref}
35
+ className={cn('flex justify-center', className)}
36
+ {...props}
37
+ >
38
+ <Alert
39
+ variant="destructive"
40
+ className={cn(
41
+ 'flex min-h-[200px] max-w-[450px] flex-col items-center justify-center rounded-lg bg-gray-700 px-6 py-6 text-center',
42
+ !embed && 'min-w-[350px]',
43
+ )}
44
+ >
45
+ <div className="mb-4">
46
+ <TriangleAlertIcon className="h-8 w-8 text-destructive" />
47
+ </div>
48
+ <AlertTitle className="mb-1 text-xl">{title}</AlertTitle>
49
+ <AlertDescription className="mt-3 max-w-sm px-2">
50
+ <p className="mb-5 text-left">{text}</p>
51
+ {actions && (
52
+ <div className="mt-6 mb-3">
53
+ <div className="flex justify-center gap-2">
54
+ {onRetry && (
55
+ <Button
56
+ size="sm"
57
+ onClick={onRetry}
58
+ className="inline-flex items-center"
59
+ >
60
+ <RotateCcwIcon className="mr-2 h-4 w-4" />
61
+ Retry
62
+ </Button>
63
+ )}
64
+ {!embed && onGoToStart && (
65
+ <Button size="sm" onClick={onGoToStart}>
66
+ Go to start page
67
+ </Button>
68
+ )}
69
+ </div>
70
+ </div>
71
+ )}
72
+ </AlertDescription>
73
+ </Alert>
74
+ </div>
75
+ );
76
+ },
77
+ );
78
+
79
+ ErrorPane.displayName = 'ErrorPane';
80
+
81
+ export {ErrorPane};