@sqlrooms/ui 0.0.0 → 0.0.1-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +2 -0
- package/package.json +3 -5
- package/.turbo/turbo-build.log +0 -8
- package/.turbo/turbo-lint.log +0 -10
- package/dist/tsconfig.tsbuildinfo +0 -1
- package/eslint.config.js +0 -4
- package/src/components/accordion.tsx +0 -55
- package/src/components/alert.tsx +0 -59
- package/src/components/badge.tsx +0 -34
- package/src/components/breadcrumb.tsx +0 -115
- package/src/components/button.tsx +0 -55
- package/src/components/card.tsx +0 -76
- package/src/components/checkbox.tsx +0 -28
- package/src/components/dialog.tsx +0 -120
- package/src/components/dropdown-menu.tsx +0 -199
- package/src/components/editable-text.tsx +0 -199
- package/src/components/error-boundary.tsx +0 -48
- package/src/components/error-pane.tsx +0 -81
- package/src/components/form.tsx +0 -176
- package/src/components/input.tsx +0 -22
- package/src/components/label.tsx +0 -24
- package/src/components/popover.tsx +0 -31
- package/src/components/progress-modal.tsx +0 -33
- package/src/components/progress.tsx +0 -26
- package/src/components/resizable.tsx +0 -43
- package/src/components/select.tsx +0 -157
- package/src/components/skeleton-pane.tsx +0 -45
- package/src/components/skeleton.tsx +0 -12
- package/src/components/spinner-pane.tsx +0 -44
- package/src/components/spinner.tsx +0 -16
- package/src/components/switch.tsx +0 -27
- package/src/components/table.tsx +0 -136
- package/src/components/tabs.tsx +0 -53
- package/src/components/textarea.tsx +0 -21
- package/src/components/toast.tsx +0 -127
- package/src/components/toaster.tsx +0 -33
- package/src/components/tooltip.tsx +0 -29
- package/src/hooks/use-toast.ts +0 -191
- package/src/hooks/useDisclosure.ts +0 -26
- package/src/index.ts +0 -35
- package/src/lib/utils.ts +0 -6
- package/src/tailwind-preset.css +0 -68
- package/src/tailwind-preset.ts +0 -55
- package/tsconfig.json +0 -12
|
@@ -1,199 +0,0 @@
|
|
|
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
|
-
};
|
|
@@ -1,199 +0,0 @@
|
|
|
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
|
-
};
|
|
@@ -1,48 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,81 +0,0 @@
|
|
|
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};
|
package/src/components/form.tsx
DELETED
|
@@ -1,176 +0,0 @@
|
|
|
1
|
-
import * as React from 'react';
|
|
2
|
-
import * as LabelPrimitive from '@radix-ui/react-label';
|
|
3
|
-
import {Slot} from '@radix-ui/react-slot';
|
|
4
|
-
import {
|
|
5
|
-
Controller,
|
|
6
|
-
ControllerProps,
|
|
7
|
-
FieldPath,
|
|
8
|
-
FieldValues,
|
|
9
|
-
FormProvider,
|
|
10
|
-
useFormContext,
|
|
11
|
-
} from 'react-hook-form';
|
|
12
|
-
|
|
13
|
-
import {cn} from '../lib/utils';
|
|
14
|
-
import {Label} from './label';
|
|
15
|
-
|
|
16
|
-
const Form = FormProvider;
|
|
17
|
-
|
|
18
|
-
type FormFieldContextValue<
|
|
19
|
-
TFieldValues extends FieldValues = FieldValues,
|
|
20
|
-
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
|
|
21
|
-
> = {
|
|
22
|
-
name: TName;
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
const FormFieldContext = React.createContext<FormFieldContextValue>(
|
|
26
|
-
{} as FormFieldContextValue,
|
|
27
|
-
);
|
|
28
|
-
|
|
29
|
-
const FormField = <
|
|
30
|
-
TFieldValues extends FieldValues = FieldValues,
|
|
31
|
-
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
|
|
32
|
-
>({
|
|
33
|
-
...props
|
|
34
|
-
}: ControllerProps<TFieldValues, TName>) => {
|
|
35
|
-
return (
|
|
36
|
-
<FormFieldContext.Provider value={{name: props.name}}>
|
|
37
|
-
<Controller {...props} />
|
|
38
|
-
</FormFieldContext.Provider>
|
|
39
|
-
);
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
const useFormField = () => {
|
|
43
|
-
const fieldContext = React.useContext(FormFieldContext);
|
|
44
|
-
const itemContext = React.useContext(FormItemContext);
|
|
45
|
-
const {getFieldState, formState} = useFormContext();
|
|
46
|
-
|
|
47
|
-
const fieldState = getFieldState(fieldContext.name, formState);
|
|
48
|
-
|
|
49
|
-
if (!fieldContext) {
|
|
50
|
-
throw new Error('useFormField should be used within <FormField>');
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const {id} = itemContext;
|
|
54
|
-
|
|
55
|
-
return {
|
|
56
|
-
id,
|
|
57
|
-
name: fieldContext.name,
|
|
58
|
-
formItemId: `${id}-form-item`,
|
|
59
|
-
formDescriptionId: `${id}-form-item-description`,
|
|
60
|
-
formMessageId: `${id}-form-item-message`,
|
|
61
|
-
...fieldState,
|
|
62
|
-
};
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
type FormItemContextValue = {
|
|
66
|
-
id: string;
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
const FormItemContext = React.createContext<FormItemContextValue>(
|
|
70
|
-
{} as FormItemContextValue,
|
|
71
|
-
);
|
|
72
|
-
|
|
73
|
-
const FormItem = React.forwardRef<
|
|
74
|
-
HTMLDivElement,
|
|
75
|
-
React.HTMLAttributes<HTMLDivElement>
|
|
76
|
-
>(({className, ...props}, ref) => {
|
|
77
|
-
const id = React.useId();
|
|
78
|
-
|
|
79
|
-
return (
|
|
80
|
-
<FormItemContext.Provider value={{id}}>
|
|
81
|
-
<div ref={ref} className={cn('space-y-2', className)} {...props} />
|
|
82
|
-
</FormItemContext.Provider>
|
|
83
|
-
);
|
|
84
|
-
});
|
|
85
|
-
FormItem.displayName = 'FormItem';
|
|
86
|
-
|
|
87
|
-
const FormLabel = React.forwardRef<
|
|
88
|
-
React.ElementRef<typeof LabelPrimitive.Root>,
|
|
89
|
-
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>
|
|
90
|
-
>(({className, ...props}, ref) => {
|
|
91
|
-
const {error, formItemId} = useFormField();
|
|
92
|
-
|
|
93
|
-
return (
|
|
94
|
-
<Label
|
|
95
|
-
ref={ref}
|
|
96
|
-
className={cn(error && 'text-destructive', className)}
|
|
97
|
-
htmlFor={formItemId}
|
|
98
|
-
{...props}
|
|
99
|
-
/>
|
|
100
|
-
);
|
|
101
|
-
});
|
|
102
|
-
FormLabel.displayName = 'FormLabel';
|
|
103
|
-
|
|
104
|
-
const FormControl = React.forwardRef<
|
|
105
|
-
React.ElementRef<typeof Slot>,
|
|
106
|
-
React.ComponentPropsWithoutRef<typeof Slot>
|
|
107
|
-
>(({...props}, ref) => {
|
|
108
|
-
const {error, formItemId, formDescriptionId, formMessageId} = useFormField();
|
|
109
|
-
|
|
110
|
-
return (
|
|
111
|
-
<Slot
|
|
112
|
-
ref={ref}
|
|
113
|
-
id={formItemId}
|
|
114
|
-
aria-describedby={
|
|
115
|
-
!error
|
|
116
|
-
? `${formDescriptionId}`
|
|
117
|
-
: `${formDescriptionId} ${formMessageId}`
|
|
118
|
-
}
|
|
119
|
-
aria-invalid={!!error}
|
|
120
|
-
{...props}
|
|
121
|
-
/>
|
|
122
|
-
);
|
|
123
|
-
});
|
|
124
|
-
FormControl.displayName = 'FormControl';
|
|
125
|
-
|
|
126
|
-
const FormDescription = React.forwardRef<
|
|
127
|
-
HTMLParagraphElement,
|
|
128
|
-
React.HTMLAttributes<HTMLParagraphElement>
|
|
129
|
-
>(({className, ...props}, ref) => {
|
|
130
|
-
const {formDescriptionId} = useFormField();
|
|
131
|
-
|
|
132
|
-
return (
|
|
133
|
-
<p
|
|
134
|
-
ref={ref}
|
|
135
|
-
id={formDescriptionId}
|
|
136
|
-
className={cn('text-[0.8rem] text-muted-foreground', className)}
|
|
137
|
-
{...props}
|
|
138
|
-
/>
|
|
139
|
-
);
|
|
140
|
-
});
|
|
141
|
-
FormDescription.displayName = 'FormDescription';
|
|
142
|
-
|
|
143
|
-
const FormMessage = React.forwardRef<
|
|
144
|
-
HTMLParagraphElement,
|
|
145
|
-
React.HTMLAttributes<HTMLParagraphElement>
|
|
146
|
-
>(({className, children, ...props}, ref) => {
|
|
147
|
-
const {error, formMessageId} = useFormField();
|
|
148
|
-
const body = error ? String(error?.message) : children;
|
|
149
|
-
|
|
150
|
-
if (!body) {
|
|
151
|
-
return null;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
return (
|
|
155
|
-
<p
|
|
156
|
-
ref={ref}
|
|
157
|
-
id={formMessageId}
|
|
158
|
-
className={cn('text-[0.8rem] font-medium text-destructive', className)}
|
|
159
|
-
{...props}
|
|
160
|
-
>
|
|
161
|
-
{body}
|
|
162
|
-
</p>
|
|
163
|
-
);
|
|
164
|
-
});
|
|
165
|
-
FormMessage.displayName = 'FormMessage';
|
|
166
|
-
|
|
167
|
-
export {
|
|
168
|
-
useFormField,
|
|
169
|
-
Form,
|
|
170
|
-
FormItem,
|
|
171
|
-
FormLabel,
|
|
172
|
-
FormControl,
|
|
173
|
-
FormDescription,
|
|
174
|
-
FormMessage,
|
|
175
|
-
FormField,
|
|
176
|
-
};
|