@gv-tech/ui-native 2.19.0 → 2.21.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/dist/index.d.ts +627 -107
- package/dist/ui-native.cjs +2 -0
- package/dist/ui-native.mjs +2036 -1428
- package/package.json +14 -11
- package/src/aspect-ratio.tsx +9 -5
- package/src/breadcrumb.tsx +126 -4
- package/src/calendar.tsx +5 -7
- package/src/carousel.tsx +28 -8
- package/src/chart.tsx +30 -8
- package/src/command.tsx +48 -8
- package/src/context-menu.tsx +213 -5
- package/src/drawer.tsx +132 -6
- package/src/dropdown-menu.tsx +217 -5
- package/src/hooks/use-toast.ts +186 -0
- package/src/hover-card.tsx +26 -6
- package/src/index.ts +114 -11
- package/src/menubar.tsx +253 -5
- package/src/navigation-menu.tsx +175 -5
- package/src/pagination.tsx +107 -4
- package/src/resizable.tsx +9 -19
- package/src/scroll-area.tsx +24 -7
- package/src/slider.tsx +38 -5
- package/src/sonner.tsx +5 -7
- package/src/toaster.tsx +25 -6
package/src/drawer.tsx
CHANGED
|
@@ -1,9 +1,135 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type {
|
|
2
|
+
DrawerBaseProps,
|
|
3
|
+
DrawerCloseBaseProps,
|
|
4
|
+
DrawerContentBaseProps,
|
|
5
|
+
DrawerDescriptionBaseProps,
|
|
6
|
+
DrawerFooterBaseProps,
|
|
7
|
+
DrawerHeaderBaseProps,
|
|
8
|
+
DrawerTitleBaseProps,
|
|
9
|
+
DrawerTriggerBaseProps,
|
|
10
|
+
} from '@gv-tech/ui-core';
|
|
11
|
+
import * as DialogPrimitive from '@rn-primitives/dialog';
|
|
12
|
+
import * as React from 'react';
|
|
13
|
+
import { StyleSheet, View, type ViewStyle } from 'react-native';
|
|
14
|
+
import Animated, { FadeIn, FadeOut, SlideInDown, SlideOutDown } from 'react-native-reanimated';
|
|
15
|
+
import { wrapTextChildren } from './lib/render-native';
|
|
16
|
+
import { cn } from './lib/utils';
|
|
2
17
|
|
|
3
|
-
export const Drawer = () => {
|
|
18
|
+
export const Drawer: React.FC<DrawerBaseProps> = ({ children }) => {
|
|
19
|
+
return <DialogPrimitive.Root>{children}</DialogPrimitive.Root>;
|
|
20
|
+
};
|
|
21
|
+
Drawer.displayName = 'Drawer';
|
|
22
|
+
|
|
23
|
+
export const DrawerTrigger = React.forwardRef<
|
|
24
|
+
React.ElementRef<typeof DialogPrimitive.Trigger>,
|
|
25
|
+
DrawerTriggerBaseProps & { className?: string }
|
|
26
|
+
>(({ children, className, ...props }, ref) => {
|
|
4
27
|
return (
|
|
5
|
-
<
|
|
6
|
-
|
|
7
|
-
</
|
|
28
|
+
<DialogPrimitive.Trigger ref={ref} className={className} {...props}>
|
|
29
|
+
{wrapTextChildren(children)}
|
|
30
|
+
</DialogPrimitive.Trigger>
|
|
8
31
|
);
|
|
9
|
-
};
|
|
32
|
+
});
|
|
33
|
+
DrawerTrigger.displayName = 'DrawerTrigger';
|
|
34
|
+
|
|
35
|
+
export const DrawerPortal = DialogPrimitive.Portal;
|
|
36
|
+
|
|
37
|
+
export type DrawerOverlayProps = React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>;
|
|
38
|
+
export type DrawerOverlayRef = React.ElementRef<typeof DialogPrimitive.Overlay>;
|
|
39
|
+
|
|
40
|
+
export const DrawerOverlay: React.ForwardRefExoticComponent<
|
|
41
|
+
DrawerOverlayProps & React.RefAttributes<DrawerOverlayRef>
|
|
42
|
+
> = React.forwardRef<DrawerOverlayRef, DrawerOverlayProps>(({ className, ...props }, ref) => {
|
|
43
|
+
return (
|
|
44
|
+
<DialogPrimitive.Overlay style={StyleSheet.absoluteFill} asChild ref={ref} {...props}>
|
|
45
|
+
<Animated.View
|
|
46
|
+
entering={FadeIn.duration(150)}
|
|
47
|
+
exiting={FadeOut.duration(150)}
|
|
48
|
+
className={cn('z-50 bg-black/80', className)}
|
|
49
|
+
/>
|
|
50
|
+
</DialogPrimitive.Overlay>
|
|
51
|
+
);
|
|
52
|
+
});
|
|
53
|
+
DrawerOverlay.displayName = 'DrawerOverlay';
|
|
54
|
+
|
|
55
|
+
export const DrawerContent = React.forwardRef<
|
|
56
|
+
React.ElementRef<typeof DialogPrimitive.Content>,
|
|
57
|
+
DrawerContentBaseProps & {
|
|
58
|
+
portalHost?: string;
|
|
59
|
+
overlayClassName?: string;
|
|
60
|
+
overlayStyle?: ViewStyle;
|
|
61
|
+
}
|
|
62
|
+
>(({ className, children, portalHost, overlayClassName, overlayStyle, ...props }, ref) => {
|
|
63
|
+
return (
|
|
64
|
+
<DrawerPortal hostName={portalHost}>
|
|
65
|
+
<DrawerOverlay className={overlayClassName} style={overlayStyle} />
|
|
66
|
+
<DialogPrimitive.Content ref={ref} asChild {...props}>
|
|
67
|
+
<Animated.View
|
|
68
|
+
entering={SlideInDown.duration(200)}
|
|
69
|
+
exiting={SlideOutDown.duration(200)}
|
|
70
|
+
className={cn(
|
|
71
|
+
'border-border bg-background fixed inset-x-0 bottom-0 z-50 flex h-auto flex-col rounded-t-xl border p-6 pb-10 shadow-lg',
|
|
72
|
+
className,
|
|
73
|
+
)}
|
|
74
|
+
>
|
|
75
|
+
<View className="bg-muted mx-auto mb-4 h-1.5 w-12 rounded-full" />
|
|
76
|
+
{children}
|
|
77
|
+
</Animated.View>
|
|
78
|
+
</DialogPrimitive.Content>
|
|
79
|
+
</DrawerPortal>
|
|
80
|
+
);
|
|
81
|
+
});
|
|
82
|
+
DrawerContent.displayName = 'DrawerContent';
|
|
83
|
+
|
|
84
|
+
export const DrawerHeader = ({ className, children, ...props }: DrawerHeaderBaseProps) => (
|
|
85
|
+
<View className={cn('flex flex-col gap-1.5 text-center sm:text-left', className)} {...props}>
|
|
86
|
+
{wrapTextChildren(children)}
|
|
87
|
+
</View>
|
|
88
|
+
);
|
|
89
|
+
DrawerHeader.displayName = 'DrawerHeader';
|
|
90
|
+
|
|
91
|
+
export const DrawerFooter = ({ className, children, ...props }: DrawerFooterBaseProps) => (
|
|
92
|
+
<View className={cn('mt-auto flex flex-col gap-2', className)} {...props}>
|
|
93
|
+
{wrapTextChildren(children)}
|
|
94
|
+
</View>
|
|
95
|
+
);
|
|
96
|
+
DrawerFooter.displayName = 'DrawerFooter';
|
|
97
|
+
|
|
98
|
+
export const DrawerClose = React.forwardRef<
|
|
99
|
+
React.ElementRef<typeof DialogPrimitive.Close>,
|
|
100
|
+
DrawerCloseBaseProps & { className?: string }
|
|
101
|
+
>(({ children, className, ...props }, ref) => {
|
|
102
|
+
return (
|
|
103
|
+
<DialogPrimitive.Close ref={ref} className={className} {...props}>
|
|
104
|
+
{wrapTextChildren(children)}
|
|
105
|
+
</DialogPrimitive.Close>
|
|
106
|
+
);
|
|
107
|
+
});
|
|
108
|
+
DrawerClose.displayName = 'DrawerClose';
|
|
109
|
+
|
|
110
|
+
export const DrawerTitle = React.forwardRef<React.ElementRef<typeof DialogPrimitive.Title>, DrawerTitleBaseProps>(
|
|
111
|
+
({ className, children, ...props }, ref) => {
|
|
112
|
+
return (
|
|
113
|
+
<DialogPrimitive.Title
|
|
114
|
+
ref={ref}
|
|
115
|
+
className={cn('text-foreground text-lg leading-none font-semibold tracking-tight', className)}
|
|
116
|
+
{...props}
|
|
117
|
+
>
|
|
118
|
+
{wrapTextChildren(children)}
|
|
119
|
+
</DialogPrimitive.Title>
|
|
120
|
+
);
|
|
121
|
+
},
|
|
122
|
+
);
|
|
123
|
+
DrawerTitle.displayName = 'DrawerTitle';
|
|
124
|
+
|
|
125
|
+
export const DrawerDescription = React.forwardRef<
|
|
126
|
+
React.ElementRef<typeof DialogPrimitive.Description>,
|
|
127
|
+
DrawerDescriptionBaseProps
|
|
128
|
+
>(({ className, children, ...props }, ref) => {
|
|
129
|
+
return (
|
|
130
|
+
<DialogPrimitive.Description ref={ref} className={cn('text-muted-foreground text-sm', className)} {...props}>
|
|
131
|
+
{wrapTextChildren(children)}
|
|
132
|
+
</DialogPrimitive.Description>
|
|
133
|
+
);
|
|
134
|
+
});
|
|
135
|
+
DrawerDescription.displayName = 'DrawerDescription';
|
package/src/dropdown-menu.tsx
CHANGED
|
@@ -1,9 +1,221 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type {
|
|
2
|
+
DropdownMenuCheckboxItemBaseProps,
|
|
3
|
+
DropdownMenuContentBaseProps,
|
|
4
|
+
DropdownMenuItemBaseProps,
|
|
5
|
+
DropdownMenuLabelBaseProps,
|
|
6
|
+
DropdownMenuRadioItemBaseProps,
|
|
7
|
+
DropdownMenuSeparatorBaseProps,
|
|
8
|
+
DropdownMenuShortcutBaseProps,
|
|
9
|
+
DropdownMenuSubContentBaseProps,
|
|
10
|
+
DropdownMenuSubTriggerBaseProps,
|
|
11
|
+
} from '@gv-tech/ui-core';
|
|
12
|
+
import * as DropdownMenuPrimitive from '@rn-primitives/dropdown-menu';
|
|
13
|
+
import { Check, ChevronRight, Circle } from 'lucide-react-native';
|
|
14
|
+
import * as React from 'react';
|
|
15
|
+
import { Platform, StyleSheet, View } from 'react-native';
|
|
16
|
+
import Animated, { FadeIn, FadeOut } from 'react-native-reanimated';
|
|
17
|
+
import { wrapTextChildren } from './lib/render-native';
|
|
18
|
+
import { cn } from './lib/utils';
|
|
19
|
+
import { Text } from './text';
|
|
2
20
|
|
|
3
|
-
export const DropdownMenu =
|
|
21
|
+
export const DropdownMenu = DropdownMenuPrimitive.Root;
|
|
22
|
+
|
|
23
|
+
export const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
|
|
24
|
+
|
|
25
|
+
export const DropdownMenuPortal = DropdownMenuPrimitive.Portal;
|
|
26
|
+
|
|
27
|
+
export const DropdownMenuGroup = DropdownMenuPrimitive.Group;
|
|
28
|
+
|
|
29
|
+
export const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
|
|
30
|
+
|
|
31
|
+
export const DropdownMenuSub = DropdownMenuPrimitive.Sub;
|
|
32
|
+
|
|
33
|
+
const DropdownMenuOverlay = React.forwardRef<
|
|
34
|
+
React.ElementRef<typeof DropdownMenuPrimitive.Overlay>,
|
|
35
|
+
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Overlay>
|
|
36
|
+
>(({ className, ...props }, ref) => {
|
|
37
|
+
return (
|
|
38
|
+
<DropdownMenuPrimitive.Overlay
|
|
39
|
+
style={Platform.OS !== 'web' ? StyleSheet.absoluteFill : undefined}
|
|
40
|
+
ref={ref}
|
|
41
|
+
{...props}
|
|
42
|
+
>
|
|
43
|
+
<Animated.View
|
|
44
|
+
entering={FadeIn.duration(100)}
|
|
45
|
+
exiting={FadeOut.duration(100)}
|
|
46
|
+
className={cn('absolute inset-0 z-50 bg-black/30', className)}
|
|
47
|
+
/>
|
|
48
|
+
</DropdownMenuPrimitive.Overlay>
|
|
49
|
+
);
|
|
50
|
+
});
|
|
51
|
+
DropdownMenuOverlay.displayName = 'DropdownMenuOverlay';
|
|
52
|
+
|
|
53
|
+
export const DropdownMenuContent = React.forwardRef<
|
|
54
|
+
React.ElementRef<typeof DropdownMenuPrimitive.Content>,
|
|
55
|
+
DropdownMenuContentBaseProps
|
|
56
|
+
>(({ className, children, side, ...props }, ref) => {
|
|
57
|
+
const nativeSide = side === 'left' || side === 'right' ? 'bottom' : side;
|
|
4
58
|
return (
|
|
5
|
-
<
|
|
6
|
-
<
|
|
7
|
-
|
|
59
|
+
<DropdownMenuPortal>
|
|
60
|
+
<DropdownMenuOverlay />
|
|
61
|
+
<DropdownMenuPrimitive.Content
|
|
62
|
+
ref={ref}
|
|
63
|
+
side={nativeSide}
|
|
64
|
+
className={cn(
|
|
65
|
+
'bg-popover border-border z-50 min-w-[8rem] overflow-hidden rounded-md border p-1 shadow-md',
|
|
66
|
+
className,
|
|
67
|
+
)}
|
|
68
|
+
{...props}
|
|
69
|
+
>
|
|
70
|
+
{children}
|
|
71
|
+
</DropdownMenuPrimitive.Content>
|
|
72
|
+
</DropdownMenuPortal>
|
|
73
|
+
);
|
|
74
|
+
});
|
|
75
|
+
DropdownMenuContent.displayName = 'DropdownMenuContent';
|
|
76
|
+
|
|
77
|
+
export const DropdownMenuItem = React.forwardRef<
|
|
78
|
+
React.ElementRef<typeof DropdownMenuPrimitive.Item>,
|
|
79
|
+
DropdownMenuItemBaseProps
|
|
80
|
+
>(({ className, children, inset, ...props }, ref) => {
|
|
81
|
+
return (
|
|
82
|
+
<DropdownMenuPrimitive.Item
|
|
83
|
+
ref={ref}
|
|
84
|
+
className={cn(
|
|
85
|
+
'focus:bg-accent focus:text-accent-foreground active:bg-accent active:text-accent-foreground relative flex flex-row items-center rounded-sm px-2 py-1.5 text-sm outline-none',
|
|
86
|
+
inset && 'pl-8',
|
|
87
|
+
className,
|
|
88
|
+
)}
|
|
89
|
+
{...props}
|
|
90
|
+
>
|
|
91
|
+
{wrapTextChildren(children, Text)}
|
|
92
|
+
</DropdownMenuPrimitive.Item>
|
|
93
|
+
);
|
|
94
|
+
});
|
|
95
|
+
DropdownMenuItem.displayName = 'DropdownMenuItem';
|
|
96
|
+
|
|
97
|
+
export const DropdownMenuCheckboxItem = React.forwardRef<
|
|
98
|
+
React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,
|
|
99
|
+
DropdownMenuCheckboxItemBaseProps
|
|
100
|
+
>(({ className, children, checked, onCheckedChange, ...props }, ref) => {
|
|
101
|
+
return (
|
|
102
|
+
<DropdownMenuPrimitive.CheckboxItem
|
|
103
|
+
ref={ref}
|
|
104
|
+
checked={!!checked}
|
|
105
|
+
onCheckedChange={onCheckedChange || (() => {})}
|
|
106
|
+
className={cn(
|
|
107
|
+
'focus:bg-accent focus:text-accent-foreground active:bg-accent active:text-accent-foreground relative flex flex-row items-center rounded-sm py-1.5 pr-2 pl-8 text-sm outline-none',
|
|
108
|
+
className,
|
|
109
|
+
)}
|
|
110
|
+
{...props}
|
|
111
|
+
>
|
|
112
|
+
<View className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
|
113
|
+
<DropdownMenuPrimitive.ItemIndicator>
|
|
114
|
+
<Check size={14} className="text-foreground" />
|
|
115
|
+
</DropdownMenuPrimitive.ItemIndicator>
|
|
116
|
+
</View>
|
|
117
|
+
{wrapTextChildren(children, Text)}
|
|
118
|
+
</DropdownMenuPrimitive.CheckboxItem>
|
|
119
|
+
);
|
|
120
|
+
});
|
|
121
|
+
DropdownMenuCheckboxItem.displayName = 'DropdownMenuCheckboxItem';
|
|
122
|
+
|
|
123
|
+
export const DropdownMenuRadioItem = React.forwardRef<
|
|
124
|
+
React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,
|
|
125
|
+
DropdownMenuRadioItemBaseProps
|
|
126
|
+
>(({ className, children, value, ...props }, ref) => {
|
|
127
|
+
return (
|
|
128
|
+
<DropdownMenuPrimitive.RadioItem
|
|
129
|
+
ref={ref}
|
|
130
|
+
value={value}
|
|
131
|
+
className={cn(
|
|
132
|
+
'focus:bg-accent focus:text-accent-foreground active:bg-accent active:text-accent-foreground relative flex flex-row items-center rounded-sm py-1.5 pr-2 pl-8 text-sm outline-none',
|
|
133
|
+
className,
|
|
134
|
+
)}
|
|
135
|
+
{...props}
|
|
136
|
+
>
|
|
137
|
+
<View className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
|
138
|
+
<DropdownMenuPrimitive.ItemIndicator>
|
|
139
|
+
<Circle size={8} className="text-foreground fill-current" />
|
|
140
|
+
</DropdownMenuPrimitive.ItemIndicator>
|
|
141
|
+
</View>
|
|
142
|
+
{wrapTextChildren(children, Text)}
|
|
143
|
+
</DropdownMenuPrimitive.RadioItem>
|
|
144
|
+
);
|
|
145
|
+
});
|
|
146
|
+
DropdownMenuRadioItem.displayName = 'DropdownMenuRadioItem';
|
|
147
|
+
|
|
148
|
+
export const DropdownMenuLabel = React.forwardRef<
|
|
149
|
+
React.ElementRef<typeof DropdownMenuPrimitive.Label>,
|
|
150
|
+
DropdownMenuLabelBaseProps
|
|
151
|
+
>(({ className, children, inset, ...props }, ref) => {
|
|
152
|
+
return (
|
|
153
|
+
<DropdownMenuPrimitive.Label
|
|
154
|
+
ref={ref}
|
|
155
|
+
className={cn('text-foreground px-2 py-1.5 text-sm font-semibold', inset && 'pl-8', className)}
|
|
156
|
+
{...props}
|
|
157
|
+
>
|
|
158
|
+
{wrapTextChildren(children, Text)}
|
|
159
|
+
</DropdownMenuPrimitive.Label>
|
|
160
|
+
);
|
|
161
|
+
});
|
|
162
|
+
DropdownMenuLabel.displayName = 'DropdownMenuLabel';
|
|
163
|
+
|
|
164
|
+
export const DropdownMenuSeparator = React.forwardRef<
|
|
165
|
+
React.ElementRef<typeof DropdownMenuPrimitive.Separator>,
|
|
166
|
+
DropdownMenuSeparatorBaseProps
|
|
167
|
+
>(({ className, ...props }, ref) => {
|
|
168
|
+
return (
|
|
169
|
+
<DropdownMenuPrimitive.Separator ref={ref} className={cn('bg-border -mx-1 my-1 h-px', className)} {...props} />
|
|
170
|
+
);
|
|
171
|
+
});
|
|
172
|
+
DropdownMenuSeparator.displayName = 'DropdownMenuSeparator';
|
|
173
|
+
|
|
174
|
+
export const DropdownMenuShortcut = ({ className, children, ...props }: DropdownMenuShortcutBaseProps) => {
|
|
175
|
+
return (
|
|
176
|
+
<Text className={cn('text-muted-foreground ml-auto text-xs tracking-widest', className)} {...props}>
|
|
177
|
+
{children}
|
|
178
|
+
</Text>
|
|
8
179
|
);
|
|
9
180
|
};
|
|
181
|
+
DropdownMenuShortcut.displayName = 'DropdownMenuShortcut';
|
|
182
|
+
|
|
183
|
+
export const DropdownMenuSubTrigger = React.forwardRef<
|
|
184
|
+
React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,
|
|
185
|
+
DropdownMenuSubTriggerBaseProps
|
|
186
|
+
>(({ className, children, inset, ...props }, ref) => {
|
|
187
|
+
return (
|
|
188
|
+
<DropdownMenuPrimitive.SubTrigger
|
|
189
|
+
ref={ref}
|
|
190
|
+
className={cn(
|
|
191
|
+
'focus:bg-accent focus:text-accent-foreground active:bg-accent active:text-accent-foreground flex flex-row items-center rounded-sm px-2 py-1.5 text-sm outline-none',
|
|
192
|
+
inset && 'pl-8',
|
|
193
|
+
className,
|
|
194
|
+
)}
|
|
195
|
+
{...props}
|
|
196
|
+
>
|
|
197
|
+
<View className="flex flex-row items-center gap-1.5">{wrapTextChildren(children, Text)}</View>
|
|
198
|
+
<ChevronRight size={14} className="text-foreground ml-auto" />
|
|
199
|
+
</DropdownMenuPrimitive.SubTrigger>
|
|
200
|
+
);
|
|
201
|
+
});
|
|
202
|
+
DropdownMenuSubTrigger.displayName = 'DropdownMenuSubTrigger';
|
|
203
|
+
|
|
204
|
+
export const DropdownMenuSubContent = React.forwardRef<
|
|
205
|
+
React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,
|
|
206
|
+
DropdownMenuSubContentBaseProps
|
|
207
|
+
>(({ className, children, ...props }, ref) => {
|
|
208
|
+
return (
|
|
209
|
+
<DropdownMenuPrimitive.SubContent
|
|
210
|
+
ref={ref}
|
|
211
|
+
className={cn(
|
|
212
|
+
'bg-popover border-border z-50 min-w-[8rem] overflow-hidden rounded-md border p-1 shadow-md',
|
|
213
|
+
className,
|
|
214
|
+
)}
|
|
215
|
+
{...props}
|
|
216
|
+
>
|
|
217
|
+
{children}
|
|
218
|
+
</DropdownMenuPrimitive.SubContent>
|
|
219
|
+
);
|
|
220
|
+
});
|
|
221
|
+
DropdownMenuSubContent.displayName = 'DropdownMenuSubContent';
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import type { ToastProps } from '../toast';
|
|
3
|
+
|
|
4
|
+
const TOAST_LIMIT = 5;
|
|
5
|
+
const TOAST_REMOVE_DELAY = 1000000;
|
|
6
|
+
|
|
7
|
+
type ToasterToast = ToastProps & {
|
|
8
|
+
id: string;
|
|
9
|
+
title?: React.ReactNode;
|
|
10
|
+
description?: React.ReactNode;
|
|
11
|
+
action?: React.ReactNode;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const actionTypes = {
|
|
15
|
+
ADD_TOAST: 'ADD_TOAST',
|
|
16
|
+
UPDATE_TOAST: 'UPDATE_TOAST',
|
|
17
|
+
DISMISS_TOAST: 'DISMISS_TOAST',
|
|
18
|
+
REMOVE_TOAST: 'REMOVE_TOAST',
|
|
19
|
+
} as const;
|
|
20
|
+
|
|
21
|
+
let count = 0;
|
|
22
|
+
|
|
23
|
+
function genId() {
|
|
24
|
+
count = (count + 1) % Number.MAX_SAFE_INTEGER;
|
|
25
|
+
return count.toString();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
type ActionType = typeof actionTypes;
|
|
29
|
+
|
|
30
|
+
type Action =
|
|
31
|
+
| {
|
|
32
|
+
type: ActionType['ADD_TOAST'];
|
|
33
|
+
toast: ToasterToast;
|
|
34
|
+
}
|
|
35
|
+
| {
|
|
36
|
+
type: ActionType['UPDATE_TOAST'];
|
|
37
|
+
toast: Partial<ToasterToast>;
|
|
38
|
+
}
|
|
39
|
+
| {
|
|
40
|
+
type: ActionType['DISMISS_TOAST'];
|
|
41
|
+
toastId?: ToasterToast['id'];
|
|
42
|
+
}
|
|
43
|
+
| {
|
|
44
|
+
type: ActionType['REMOVE_TOAST'];
|
|
45
|
+
toastId?: ToasterToast['id'];
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
interface State {
|
|
49
|
+
toasts: ToasterToast[];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>();
|
|
53
|
+
|
|
54
|
+
const addToRemoveQueue = (toastId: string) => {
|
|
55
|
+
if (toastTimeouts.has(toastId)) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const timeout = setTimeout(() => {
|
|
60
|
+
toastTimeouts.delete(toastId);
|
|
61
|
+
dispatch({
|
|
62
|
+
type: 'REMOVE_TOAST',
|
|
63
|
+
toastId: toastId,
|
|
64
|
+
});
|
|
65
|
+
}, TOAST_REMOVE_DELAY);
|
|
66
|
+
|
|
67
|
+
toastTimeouts.set(toastId, timeout);
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export const reducer = (state: State, action: Action): State => {
|
|
71
|
+
switch (action.type) {
|
|
72
|
+
case actionTypes.ADD_TOAST:
|
|
73
|
+
return {
|
|
74
|
+
...state,
|
|
75
|
+
toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
case actionTypes.UPDATE_TOAST:
|
|
79
|
+
return {
|
|
80
|
+
...state,
|
|
81
|
+
toasts: state.toasts.map((t) => (t.id === action.toast.id ? { ...t, ...action.toast } : t)),
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
case actionTypes.DISMISS_TOAST: {
|
|
85
|
+
const { toastId } = action;
|
|
86
|
+
|
|
87
|
+
if (toastId) {
|
|
88
|
+
addToRemoveQueue(toastId);
|
|
89
|
+
} else {
|
|
90
|
+
state.toasts.forEach((toast) => {
|
|
91
|
+
addToRemoveQueue(toast.id);
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
...state,
|
|
97
|
+
toasts: state.toasts.map((t) =>
|
|
98
|
+
t.id === toastId || toastId === undefined
|
|
99
|
+
? {
|
|
100
|
+
...t,
|
|
101
|
+
open: false,
|
|
102
|
+
}
|
|
103
|
+
: t,
|
|
104
|
+
),
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
case actionTypes.REMOVE_TOAST:
|
|
108
|
+
if (action.toastId === undefined) {
|
|
109
|
+
return {
|
|
110
|
+
...state,
|
|
111
|
+
toasts: [],
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
return {
|
|
115
|
+
...state,
|
|
116
|
+
toasts: state.toasts.filter((t) => t.id !== action.toastId),
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const listeners: Array<(state: State) => void> = [];
|
|
122
|
+
|
|
123
|
+
let memoryState: State = { toasts: [] };
|
|
124
|
+
|
|
125
|
+
function dispatch(action: Action) {
|
|
126
|
+
memoryState = reducer(memoryState, action);
|
|
127
|
+
listeners.forEach((listener) => {
|
|
128
|
+
listener(memoryState);
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
type Toast = Omit<ToasterToast, 'id'>;
|
|
133
|
+
|
|
134
|
+
function toast({ ...props }: Toast) {
|
|
135
|
+
const id = genId();
|
|
136
|
+
|
|
137
|
+
const update = (props: ToasterToast) =>
|
|
138
|
+
dispatch({
|
|
139
|
+
type: 'UPDATE_TOAST',
|
|
140
|
+
toast: { ...props, id },
|
|
141
|
+
});
|
|
142
|
+
const dismiss = () => dispatch({ type: 'DISMISS_TOAST', toastId: id });
|
|
143
|
+
|
|
144
|
+
dispatch({
|
|
145
|
+
type: 'ADD_TOAST',
|
|
146
|
+
toast: {
|
|
147
|
+
...props,
|
|
148
|
+
id,
|
|
149
|
+
open: true,
|
|
150
|
+
onOpenChange: (open) => {
|
|
151
|
+
if (!open) {
|
|
152
|
+
dismiss();
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
return {
|
|
159
|
+
id: id,
|
|
160
|
+
dismiss,
|
|
161
|
+
update,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function useToast() {
|
|
166
|
+
const [state, setState] = React.useState<State>(memoryState);
|
|
167
|
+
|
|
168
|
+
React.useEffect(() => {
|
|
169
|
+
listeners.push(setState);
|
|
170
|
+
return () => {
|
|
171
|
+
const index = listeners.indexOf(setState);
|
|
172
|
+
if (index > -1) {
|
|
173
|
+
listeners.splice(index, 1);
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
}, []);
|
|
177
|
+
|
|
178
|
+
return {
|
|
179
|
+
...state,
|
|
180
|
+
toast,
|
|
181
|
+
dismiss: (toastId?: string) => dispatch({ type: 'DISMISS_TOAST', toastId }),
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export { toast, useToast };
|
|
186
|
+
export type { ToasterToast };
|
package/src/hover-card.tsx
CHANGED
|
@@ -1,9 +1,29 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { HoverCardContentBaseProps } from '@gv-tech/ui-core';
|
|
2
|
+
import * as HoverCardPrimitive from '@rn-primitives/hover-card';
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { cn } from './lib/utils';
|
|
2
5
|
|
|
3
|
-
export const HoverCard =
|
|
6
|
+
export const HoverCard = HoverCardPrimitive.Root;
|
|
7
|
+
|
|
8
|
+
export const HoverCardTrigger = HoverCardPrimitive.Trigger;
|
|
9
|
+
|
|
10
|
+
export const HoverCardContent = React.forwardRef<
|
|
11
|
+
React.ElementRef<typeof HoverCardPrimitive.Content>,
|
|
12
|
+
HoverCardContentBaseProps
|
|
13
|
+
>(({ className, children, ...props }, ref) => {
|
|
4
14
|
return (
|
|
5
|
-
<
|
|
6
|
-
<
|
|
7
|
-
|
|
15
|
+
<HoverCardPrimitive.Portal>
|
|
16
|
+
<HoverCardPrimitive.Content
|
|
17
|
+
ref={ref}
|
|
18
|
+
className={cn(
|
|
19
|
+
'bg-popover text-popover-foreground border-border z-50 w-64 rounded-md border p-4 shadow-md',
|
|
20
|
+
className,
|
|
21
|
+
)}
|
|
22
|
+
{...props}
|
|
23
|
+
>
|
|
24
|
+
{children}
|
|
25
|
+
</HoverCardPrimitive.Content>
|
|
26
|
+
</HoverCardPrimitive.Portal>
|
|
8
27
|
);
|
|
9
|
-
};
|
|
28
|
+
});
|
|
29
|
+
HoverCardContent.displayName = HoverCardPrimitive.Content?.displayName || 'HoverCardContent';
|