create-ej-app 1.0.0 → 2.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.
- package/cli.js +21 -3
- package/package.json +1 -1
- package/template/{.env.example → advanced/.env.example} +4 -0
- package/template/{components.json → advanced/components.json} +1 -1
- package/template/{package.json → advanced/package.json} +11 -0
- package/template/{prisma → advanced/prisma}/schema.prisma +18 -1
- package/template/{src → advanced/src}/app/(app)/tasks/[id]/actions.ts +46 -4
- package/template/{src → advanced/src}/app/(app)/tasks/[id]/render.tsx +136 -25
- package/template/{src → advanced/src}/app/(app)/tasks/actions.ts +0 -6
- package/template/{src → advanced/src}/app/(app)/tasks/page.tsx +0 -3
- package/template/{src → advanced/src}/app/(app)/tasks/render.tsx +0 -10
- package/template/advanced/src/app/api/upload/route.ts +45 -0
- package/template/{src → advanced/src}/app/layout.tsx +1 -1
- package/template/{src → advanced/src}/env.mjs +10 -2
- package/template/advanced/src/styles/editor.css +61 -0
- package/template/{tailwind.config.ts → advanced/tailwind.config.ts} +1 -1
- package/template/base/.env.example +16 -0
- package/template/base/.eslintrc.cjs +24 -0
- package/template/base/components.json +16 -0
- package/template/base/next.config.mjs +18 -0
- package/template/base/package.json +79 -0
- package/template/base/postcss.config.cjs +6 -0
- package/template/base/prettier.config.ts +11 -0
- package/template/base/prisma/schema.prisma +115 -0
- package/template/base/public/next.svg +1 -0
- package/template/base/src/app/(app)/dashboard/page.tsx +19 -0
- package/template/base/src/app/(app)/layout.tsx +90 -0
- package/template/base/src/app/(app)/settings/actions.ts +18 -0
- package/template/base/src/app/(app)/settings/loading.tsx +28 -0
- package/template/base/src/app/(app)/settings/page.tsx +28 -0
- package/template/base/src/app/(app)/settings/render.tsx +113 -0
- package/template/base/src/app/(app)/tasks/[id]/actions.ts +149 -0
- package/template/base/src/app/(app)/tasks/[id]/page.tsx +25 -0
- package/template/base/src/app/(app)/tasks/[id]/render.tsx +413 -0
- package/template/base/src/app/(app)/tasks/actions.ts +69 -0
- package/template/base/src/app/(app)/tasks/page.tsx +53 -0
- package/template/base/src/app/(app)/tasks/render.tsx +107 -0
- package/template/base/src/app/(app)/users/[id]/actions.ts +83 -0
- package/template/base/src/app/(app)/users/[id]/page.tsx +32 -0
- package/template/base/src/app/(app)/users/[id]/render.tsx +240 -0
- package/template/base/src/app/(app)/users/actions.ts +55 -0
- package/template/base/src/app/(app)/users/page.tsx +49 -0
- package/template/base/src/app/(app)/users/render.tsx +80 -0
- package/template/base/src/app/(auth)/layout.tsx +20 -0
- package/template/base/src/app/(auth)/login/page.tsx +49 -0
- package/template/base/src/app/(auth)/login/user-auth-form.tsx +152 -0
- package/template/base/src/app/(marketing)/layout.tsx +42 -0
- package/template/base/src/app/(marketing)/page.tsx +96 -0
- package/template/base/src/app/api/auth/[...nextauth]/route.ts +7 -0
- package/template/base/src/app/api/upload/route.ts +45 -0
- package/template/base/src/app/favicon.ico +0 -0
- package/template/base/src/app/layout.tsx +32 -0
- package/template/base/src/components/app-nav-command.tsx +160 -0
- package/template/base/src/components/dark-mode-toggle.tsx +43 -0
- package/template/base/src/components/data-table/data-table-column-header.tsx +91 -0
- package/template/base/src/components/data-table/data-table-faceted-filter.tsx +149 -0
- package/template/base/src/components/data-table/data-table-loading.tsx +77 -0
- package/template/base/src/components/data-table/data-table-pagination.tsx +94 -0
- package/template/base/src/components/data-table/data-table-toolbar.tsx +78 -0
- package/template/base/src/components/data-table/data-table-types.ts +17 -0
- package/template/base/src/components/data-table/data-table-view-options.tsx +59 -0
- package/template/base/src/components/data-table/data-table.tsx +301 -0
- package/template/base/src/components/data-table/simple-data-table.tsx +109 -0
- package/template/base/src/components/empty-placeholder.tsx +78 -0
- package/template/base/src/components/heading.tsx +17 -0
- package/template/base/src/components/icons.tsx +83 -0
- package/template/base/src/components/main-nav.tsx +64 -0
- package/template/base/src/components/mobile-nav.tsx +50 -0
- package/template/base/src/components/shell.tsx +13 -0
- package/template/base/src/components/side-nav.tsx +51 -0
- package/template/base/src/components/site-footer.tsx +31 -0
- package/template/base/src/components/system-info.tsx +36 -0
- package/template/base/src/components/theme-provider.tsx +13 -0
- package/template/base/src/components/ui/alert-dialog.tsx +145 -0
- package/template/base/src/components/ui/avatar.tsx +50 -0
- package/template/base/src/components/ui/badge.tsx +36 -0
- package/template/base/src/components/ui/button.tsx +56 -0
- package/template/base/src/components/ui/card.tsx +79 -0
- package/template/base/src/components/ui/checkbox.tsx +30 -0
- package/template/base/src/components/ui/command.tsx +155 -0
- package/template/base/src/components/ui/dialog.tsx +123 -0
- package/template/base/src/components/ui/dropdown-menu.tsx +200 -0
- package/template/base/src/components/ui/form.tsx +176 -0
- package/template/base/src/components/ui/input.tsx +25 -0
- package/template/base/src/components/ui/label.tsx +26 -0
- package/template/base/src/components/ui/popover.tsx +31 -0
- package/template/base/src/components/ui/select.tsx +121 -0
- package/template/base/src/components/ui/separator.tsx +31 -0
- package/template/base/src/components/ui/skeleton.tsx +15 -0
- package/template/base/src/components/ui/table.tsx +114 -0
- package/template/base/src/components/ui/textarea.tsx +24 -0
- package/template/base/src/components/ui/toast.tsx +127 -0
- package/template/base/src/components/ui/toaster.tsx +35 -0
- package/template/base/src/components/ui/use-toast.ts +189 -0
- package/template/base/src/components/user-account-nav.tsx +60 -0
- package/template/base/src/components/user-avatar.tsx +25 -0
- package/template/base/src/env.mjs +75 -0
- package/template/base/src/hooks/use-debounce.ts +17 -0
- package/template/base/src/hooks/use-lock-body.tsx +12 -0
- package/template/base/src/lib/auth.ts +79 -0
- package/template/base/src/lib/cn.ts +4 -0
- package/template/base/src/lib/db.ts +20 -0
- package/template/base/src/lib/delay.ts +2 -0
- package/template/base/src/lib/formatDate.ts +11 -0
- package/template/base/src/lib/generateLabel.ts +5 -0
- package/template/base/src/lib/siteConfig.ts +4 -0
- package/template/base/src/styles/editor.css +61 -0
- package/template/base/src/styles/globals.css +76 -0
- package/template/base/src/types/UnwrapPromise.ts +1 -0
- package/template/base/tailwind.config.ts +79 -0
- package/template/base/tsconfig.json +46 -0
- /package/template/{.eslintrc.cjs → advanced/.eslintrc.cjs} +0 -0
- /package/template/{next.config.mjs → advanced/next.config.mjs} +0 -0
- /package/template/{postcss.config.cjs → advanced/postcss.config.cjs} +0 -0
- /package/template/{prettier.config.ts → advanced/prettier.config.ts} +0 -0
- /package/template/{public → advanced/public}/next.svg +0 -0
- /package/template/{src → advanced/src}/app/(app)/dashboard/page.tsx +0 -0
- /package/template/{src → advanced/src}/app/(app)/layout.tsx +0 -0
- /package/template/{src → advanced/src}/app/(app)/roles/[id]/actions.ts +0 -0
- /package/template/{src → advanced/src}/app/(app)/roles/[id]/page.tsx +0 -0
- /package/template/{src → advanced/src}/app/(app)/roles/[id]/render.tsx +0 -0
- /package/template/{src → advanced/src}/app/(app)/roles/actions.ts +0 -0
- /package/template/{src → advanced/src}/app/(app)/roles/page.tsx +0 -0
- /package/template/{src → advanced/src}/app/(app)/roles/render.tsx +0 -0
- /package/template/{src → advanced/src}/app/(app)/settings/actions.ts +0 -0
- /package/template/{src → advanced/src}/app/(app)/settings/loading.tsx +0 -0
- /package/template/{src → advanced/src}/app/(app)/settings/page.tsx +0 -0
- /package/template/{src → advanced/src}/app/(app)/settings/render.tsx +0 -0
- /package/template/{src → advanced/src}/app/(app)/tasks/[id]/page.tsx +0 -0
- /package/template/{src → advanced/src}/app/(app)/users/[id]/actions.ts +0 -0
- /package/template/{src → advanced/src}/app/(app)/users/[id]/page.tsx +0 -0
- /package/template/{src → advanced/src}/app/(app)/users/[id]/render.tsx +0 -0
- /package/template/{src → advanced/src}/app/(app)/users/actions.ts +0 -0
- /package/template/{src → advanced/src}/app/(app)/users/page.tsx +0 -0
- /package/template/{src → advanced/src}/app/(app)/users/render.tsx +0 -0
- /package/template/{src → advanced/src}/app/(auth)/layout.tsx +0 -0
- /package/template/{src → advanced/src}/app/(auth)/login/page.tsx +0 -0
- /package/template/{src → advanced/src}/app/(auth)/login/user-auth-form.tsx +0 -0
- /package/template/{src → advanced/src}/app/(marketing)/layout.tsx +0 -0
- /package/template/{src → advanced/src}/app/(marketing)/page.tsx +0 -0
- /package/template/{src → advanced/src}/app/api/auth/[...nextauth]/route.ts +0 -0
- /package/template/{src → advanced/src}/app/favicon.ico +0 -0
- /package/template/{src → advanced/src}/components/app-nav-command.tsx +0 -0
- /package/template/{src → advanced/src}/components/dark-mode-toggle.tsx +0 -0
- /package/template/{src → advanced/src}/components/data-table/data-table-column-header.tsx +0 -0
- /package/template/{src → advanced/src}/components/data-table/data-table-faceted-filter.tsx +0 -0
- /package/template/{src → advanced/src}/components/data-table/data-table-loading.tsx +0 -0
- /package/template/{src → advanced/src}/components/data-table/data-table-pagination.tsx +0 -0
- /package/template/{src → advanced/src}/components/data-table/data-table-toolbar.tsx +0 -0
- /package/template/{src → advanced/src}/components/data-table/data-table-types.ts +0 -0
- /package/template/{src → advanced/src}/components/data-table/data-table-view-options.tsx +0 -0
- /package/template/{src → advanced/src}/components/data-table/data-table.tsx +0 -0
- /package/template/{src → advanced/src}/components/data-table/simple-data-table.tsx +0 -0
- /package/template/{src → advanced/src}/components/empty-placeholder.tsx +0 -0
- /package/template/{src → advanced/src}/components/heading.tsx +0 -0
- /package/template/{src → advanced/src}/components/icons.tsx +0 -0
- /package/template/{src → advanced/src}/components/main-nav.tsx +0 -0
- /package/template/{src → advanced/src}/components/mobile-nav.tsx +0 -0
- /package/template/{src → advanced/src}/components/shell.tsx +0 -0
- /package/template/{src → advanced/src}/components/side-nav.tsx +0 -0
- /package/template/{src → advanced/src}/components/site-footer.tsx +0 -0
- /package/template/{src → advanced/src}/components/system-info.tsx +0 -0
- /package/template/{src → advanced/src}/components/theme-provider.tsx +0 -0
- /package/template/{src → advanced/src}/components/ui/alert-dialog.tsx +0 -0
- /package/template/{src → advanced/src}/components/ui/avatar.tsx +0 -0
- /package/template/{src → advanced/src}/components/ui/badge.tsx +0 -0
- /package/template/{src → advanced/src}/components/ui/button.tsx +0 -0
- /package/template/{src → advanced/src}/components/ui/card.tsx +0 -0
- /package/template/{src → advanced/src}/components/ui/checkbox.tsx +0 -0
- /package/template/{src → advanced/src}/components/ui/command.tsx +0 -0
- /package/template/{src → advanced/src}/components/ui/dialog.tsx +0 -0
- /package/template/{src → advanced/src}/components/ui/dropdown-menu.tsx +0 -0
- /package/template/{src → advanced/src}/components/ui/form.tsx +0 -0
- /package/template/{src → advanced/src}/components/ui/input.tsx +0 -0
- /package/template/{src → advanced/src}/components/ui/label.tsx +0 -0
- /package/template/{src → advanced/src}/components/ui/popover.tsx +0 -0
- /package/template/{src → advanced/src}/components/ui/select.tsx +0 -0
- /package/template/{src → advanced/src}/components/ui/separator.tsx +0 -0
- /package/template/{src → advanced/src}/components/ui/skeleton.tsx +0 -0
- /package/template/{src → advanced/src}/components/ui/table.tsx +0 -0
- /package/template/{src → advanced/src}/components/ui/textarea.tsx +0 -0
- /package/template/{src → advanced/src}/components/ui/toast.tsx +0 -0
- /package/template/{src → advanced/src}/components/ui/toaster.tsx +0 -0
- /package/template/{src → advanced/src}/components/ui/use-toast.ts +0 -0
- /package/template/{src → advanced/src}/components/user-account-nav.tsx +0 -0
- /package/template/{src → advanced/src}/components/user-avatar.tsx +0 -0
- /package/template/{src → advanced/src}/hooks/use-debounce.ts +0 -0
- /package/template/{src → advanced/src}/hooks/use-lock-body.tsx +0 -0
- /package/template/{src → advanced/src}/lib/auth.ts +0 -0
- /package/template/{src → advanced/src}/lib/cn.ts +0 -0
- /package/template/{src → advanced/src}/lib/db.ts +0 -0
- /package/template/{src → advanced/src}/lib/delay.ts +0 -0
- /package/template/{src → advanced/src}/lib/formatDate.ts +0 -0
- /package/template/{src → advanced/src}/lib/generateLabel.ts +0 -0
- /package/template/{src → advanced/src}/lib/siteConfig.ts +0 -0
- /package/template/{src/app → advanced/src/styles}/globals.css +0 -0
- /package/template/{src → advanced/src}/types/UnwrapPromise.ts +0 -0
- /package/template/{tsconfig.json → advanced/tsconfig.json} +0 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import * as PopoverPrimitive from '@radix-ui/react-popover'
|
|
4
|
+
import * as React from 'react'
|
|
5
|
+
|
|
6
|
+
import { cn } from '@/lib/cn'
|
|
7
|
+
|
|
8
|
+
const Popover = PopoverPrimitive.Root
|
|
9
|
+
|
|
10
|
+
const PopoverTrigger = PopoverPrimitive.Trigger
|
|
11
|
+
|
|
12
|
+
const PopoverContent = React.forwardRef<
|
|
13
|
+
React.ElementRef<typeof PopoverPrimitive.Content>,
|
|
14
|
+
React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>
|
|
15
|
+
>(({ className, align = 'center', sideOffset = 4, ...props }, ref) => (
|
|
16
|
+
<PopoverPrimitive.Portal>
|
|
17
|
+
<PopoverPrimitive.Content
|
|
18
|
+
ref={ref}
|
|
19
|
+
align={align}
|
|
20
|
+
sideOffset={sideOffset}
|
|
21
|
+
className={cn(
|
|
22
|
+
'z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none 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',
|
|
23
|
+
className
|
|
24
|
+
)}
|
|
25
|
+
{...props}
|
|
26
|
+
/>
|
|
27
|
+
</PopoverPrimitive.Portal>
|
|
28
|
+
))
|
|
29
|
+
PopoverContent.displayName = PopoverPrimitive.Content.displayName
|
|
30
|
+
|
|
31
|
+
export { Popover, PopoverContent, PopoverTrigger }
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import * as SelectPrimitive from '@radix-ui/react-select'
|
|
4
|
+
import { Check, ChevronDown } from 'lucide-react'
|
|
5
|
+
import * as React from 'react'
|
|
6
|
+
|
|
7
|
+
import { cn } from '@/lib/cn'
|
|
8
|
+
|
|
9
|
+
const Select = SelectPrimitive.Root
|
|
10
|
+
|
|
11
|
+
const SelectGroup = SelectPrimitive.Group
|
|
12
|
+
|
|
13
|
+
const SelectValue = SelectPrimitive.Value
|
|
14
|
+
|
|
15
|
+
const SelectTrigger = React.forwardRef<
|
|
16
|
+
React.ElementRef<typeof SelectPrimitive.Trigger>,
|
|
17
|
+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
|
|
18
|
+
>(({ className, children, ...props }, ref) => (
|
|
19
|
+
<SelectPrimitive.Trigger
|
|
20
|
+
ref={ref}
|
|
21
|
+
className={cn(
|
|
22
|
+
'flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
|
|
23
|
+
className
|
|
24
|
+
)}
|
|
25
|
+
{...props}
|
|
26
|
+
>
|
|
27
|
+
{children}
|
|
28
|
+
<SelectPrimitive.Icon asChild>
|
|
29
|
+
<ChevronDown className="h-4 w-4 opacity-50" />
|
|
30
|
+
</SelectPrimitive.Icon>
|
|
31
|
+
</SelectPrimitive.Trigger>
|
|
32
|
+
))
|
|
33
|
+
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName
|
|
34
|
+
|
|
35
|
+
const SelectContent = React.forwardRef<
|
|
36
|
+
React.ElementRef<typeof SelectPrimitive.Content>,
|
|
37
|
+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
|
|
38
|
+
>(({ className, children, position = 'popper', ...props }, ref) => (
|
|
39
|
+
<SelectPrimitive.Portal>
|
|
40
|
+
<SelectPrimitive.Content
|
|
41
|
+
ref={ref}
|
|
42
|
+
className={cn(
|
|
43
|
+
'relative z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md 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',
|
|
44
|
+
position === 'popper' &&
|
|
45
|
+
'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1',
|
|
46
|
+
className
|
|
47
|
+
)}
|
|
48
|
+
position={position}
|
|
49
|
+
{...props}
|
|
50
|
+
>
|
|
51
|
+
<SelectPrimitive.Viewport
|
|
52
|
+
className={cn(
|
|
53
|
+
'p-1',
|
|
54
|
+
position === 'popper' &&
|
|
55
|
+
'h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]'
|
|
56
|
+
)}
|
|
57
|
+
>
|
|
58
|
+
{children}
|
|
59
|
+
</SelectPrimitive.Viewport>
|
|
60
|
+
</SelectPrimitive.Content>
|
|
61
|
+
</SelectPrimitive.Portal>
|
|
62
|
+
))
|
|
63
|
+
SelectContent.displayName = SelectPrimitive.Content.displayName
|
|
64
|
+
|
|
65
|
+
const SelectLabel = React.forwardRef<
|
|
66
|
+
React.ElementRef<typeof SelectPrimitive.Label>,
|
|
67
|
+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
|
|
68
|
+
>(({ className, ...props }, ref) => (
|
|
69
|
+
<SelectPrimitive.Label
|
|
70
|
+
ref={ref}
|
|
71
|
+
className={cn('py-1.5 pl-8 pr-2 text-sm font-semibold', className)}
|
|
72
|
+
{...props}
|
|
73
|
+
/>
|
|
74
|
+
))
|
|
75
|
+
SelectLabel.displayName = SelectPrimitive.Label.displayName
|
|
76
|
+
|
|
77
|
+
const SelectItem = React.forwardRef<
|
|
78
|
+
React.ElementRef<typeof SelectPrimitive.Item>,
|
|
79
|
+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
|
|
80
|
+
>(({ className, children, ...props }, ref) => (
|
|
81
|
+
<SelectPrimitive.Item
|
|
82
|
+
ref={ref}
|
|
83
|
+
className={cn(
|
|
84
|
+
'relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
|
85
|
+
className
|
|
86
|
+
)}
|
|
87
|
+
{...props}
|
|
88
|
+
>
|
|
89
|
+
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
|
90
|
+
<SelectPrimitive.ItemIndicator>
|
|
91
|
+
<Check className="h-4 w-4" />
|
|
92
|
+
</SelectPrimitive.ItemIndicator>
|
|
93
|
+
</span>
|
|
94
|
+
|
|
95
|
+
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
|
|
96
|
+
</SelectPrimitive.Item>
|
|
97
|
+
))
|
|
98
|
+
SelectItem.displayName = SelectPrimitive.Item.displayName
|
|
99
|
+
|
|
100
|
+
const SelectSeparator = React.forwardRef<
|
|
101
|
+
React.ElementRef<typeof SelectPrimitive.Separator>,
|
|
102
|
+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
|
|
103
|
+
>(({ className, ...props }, ref) => (
|
|
104
|
+
<SelectPrimitive.Separator
|
|
105
|
+
ref={ref}
|
|
106
|
+
className={cn('-mx-1 my-1 h-px bg-muted', className)}
|
|
107
|
+
{...props}
|
|
108
|
+
/>
|
|
109
|
+
))
|
|
110
|
+
SelectSeparator.displayName = SelectPrimitive.Separator.displayName
|
|
111
|
+
|
|
112
|
+
export {
|
|
113
|
+
Select,
|
|
114
|
+
SelectContent,
|
|
115
|
+
SelectGroup,
|
|
116
|
+
SelectItem,
|
|
117
|
+
SelectLabel,
|
|
118
|
+
SelectSeparator,
|
|
119
|
+
SelectTrigger,
|
|
120
|
+
SelectValue
|
|
121
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import * as SeparatorPrimitive from '@radix-ui/react-separator'
|
|
4
|
+
import * as React from 'react'
|
|
5
|
+
|
|
6
|
+
import { cn } from '@/lib/cn'
|
|
7
|
+
|
|
8
|
+
const Separator = React.forwardRef<
|
|
9
|
+
React.ElementRef<typeof SeparatorPrimitive.Root>,
|
|
10
|
+
React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root>
|
|
11
|
+
>(
|
|
12
|
+
(
|
|
13
|
+
{ className, orientation = 'horizontal', decorative = true, ...props },
|
|
14
|
+
ref
|
|
15
|
+
) => (
|
|
16
|
+
<SeparatorPrimitive.Root
|
|
17
|
+
ref={ref}
|
|
18
|
+
decorative={decorative}
|
|
19
|
+
orientation={orientation}
|
|
20
|
+
className={cn(
|
|
21
|
+
'shrink-0 bg-border',
|
|
22
|
+
orientation === 'horizontal' ? 'h-[1px] w-full' : 'h-full w-[1px]',
|
|
23
|
+
className
|
|
24
|
+
)}
|
|
25
|
+
{...props}
|
|
26
|
+
/>
|
|
27
|
+
)
|
|
28
|
+
)
|
|
29
|
+
Separator.displayName = SeparatorPrimitive.Root.displayName
|
|
30
|
+
|
|
31
|
+
export { Separator }
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { cn } from '@/lib/cn'
|
|
2
|
+
|
|
3
|
+
function Skeleton({
|
|
4
|
+
className,
|
|
5
|
+
...props
|
|
6
|
+
}: React.HTMLAttributes<HTMLDivElement>) {
|
|
7
|
+
return (
|
|
8
|
+
<div
|
|
9
|
+
className={cn('animate-pulse rounded-md bg-muted', className)}
|
|
10
|
+
{...props}
|
|
11
|
+
/>
|
|
12
|
+
)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export { Skeleton }
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
|
|
3
|
+
import { cn } from '@/lib/cn'
|
|
4
|
+
|
|
5
|
+
const Table = React.forwardRef<
|
|
6
|
+
HTMLTableElement,
|
|
7
|
+
React.HTMLAttributes<HTMLTableElement>
|
|
8
|
+
>(({ className, ...props }, ref) => (
|
|
9
|
+
<div className="w-full overflow-auto">
|
|
10
|
+
<table
|
|
11
|
+
ref={ref}
|
|
12
|
+
className={cn('w-full caption-bottom text-sm', className)}
|
|
13
|
+
{...props}
|
|
14
|
+
/>
|
|
15
|
+
</div>
|
|
16
|
+
))
|
|
17
|
+
Table.displayName = 'Table'
|
|
18
|
+
|
|
19
|
+
const TableHeader = React.forwardRef<
|
|
20
|
+
HTMLTableSectionElement,
|
|
21
|
+
React.HTMLAttributes<HTMLTableSectionElement>
|
|
22
|
+
>(({ className, ...props }, ref) => (
|
|
23
|
+
<thead ref={ref} className={cn('[&_tr]:border-b', className)} {...props} />
|
|
24
|
+
))
|
|
25
|
+
TableHeader.displayName = 'TableHeader'
|
|
26
|
+
|
|
27
|
+
const TableBody = React.forwardRef<
|
|
28
|
+
HTMLTableSectionElement,
|
|
29
|
+
React.HTMLAttributes<HTMLTableSectionElement>
|
|
30
|
+
>(({ className, ...props }, ref) => (
|
|
31
|
+
<tbody
|
|
32
|
+
ref={ref}
|
|
33
|
+
className={cn('[&_tr:last-child]:border-0', className)}
|
|
34
|
+
{...props}
|
|
35
|
+
/>
|
|
36
|
+
))
|
|
37
|
+
TableBody.displayName = 'TableBody'
|
|
38
|
+
|
|
39
|
+
const TableFooter = React.forwardRef<
|
|
40
|
+
HTMLTableSectionElement,
|
|
41
|
+
React.HTMLAttributes<HTMLTableSectionElement>
|
|
42
|
+
>(({ className, ...props }, ref) => (
|
|
43
|
+
<tfoot
|
|
44
|
+
ref={ref}
|
|
45
|
+
className={cn('bg-primary font-medium text-primary-foreground', className)}
|
|
46
|
+
{...props}
|
|
47
|
+
/>
|
|
48
|
+
))
|
|
49
|
+
TableFooter.displayName = 'TableFooter'
|
|
50
|
+
|
|
51
|
+
const TableRow = React.forwardRef<
|
|
52
|
+
HTMLTableRowElement,
|
|
53
|
+
React.HTMLAttributes<HTMLTableRowElement>
|
|
54
|
+
>(({ className, ...props }, ref) => (
|
|
55
|
+
<tr
|
|
56
|
+
ref={ref}
|
|
57
|
+
className={cn(
|
|
58
|
+
'border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted',
|
|
59
|
+
className
|
|
60
|
+
)}
|
|
61
|
+
{...props}
|
|
62
|
+
/>
|
|
63
|
+
))
|
|
64
|
+
TableRow.displayName = 'TableRow'
|
|
65
|
+
|
|
66
|
+
const TableHead = React.forwardRef<
|
|
67
|
+
HTMLTableCellElement,
|
|
68
|
+
React.ThHTMLAttributes<HTMLTableCellElement>
|
|
69
|
+
>(({ className, ...props }, ref) => (
|
|
70
|
+
<th
|
|
71
|
+
ref={ref}
|
|
72
|
+
className={cn(
|
|
73
|
+
'h-12 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0',
|
|
74
|
+
className
|
|
75
|
+
)}
|
|
76
|
+
{...props}
|
|
77
|
+
/>
|
|
78
|
+
))
|
|
79
|
+
TableHead.displayName = 'TableHead'
|
|
80
|
+
|
|
81
|
+
const TableCell = React.forwardRef<
|
|
82
|
+
HTMLTableCellElement,
|
|
83
|
+
React.TdHTMLAttributes<HTMLTableCellElement>
|
|
84
|
+
>(({ className, ...props }, ref) => (
|
|
85
|
+
<td
|
|
86
|
+
ref={ref}
|
|
87
|
+
className={cn('p-4 align-middle [&:has([role=checkbox])]:pr-0', className)}
|
|
88
|
+
{...props}
|
|
89
|
+
/>
|
|
90
|
+
))
|
|
91
|
+
TableCell.displayName = 'TableCell'
|
|
92
|
+
|
|
93
|
+
const TableCaption = React.forwardRef<
|
|
94
|
+
HTMLTableCaptionElement,
|
|
95
|
+
React.HTMLAttributes<HTMLTableCaptionElement>
|
|
96
|
+
>(({ className, ...props }, ref) => (
|
|
97
|
+
<caption
|
|
98
|
+
ref={ref}
|
|
99
|
+
className={cn('mt-4 text-sm text-muted-foreground', className)}
|
|
100
|
+
{...props}
|
|
101
|
+
/>
|
|
102
|
+
))
|
|
103
|
+
TableCaption.displayName = 'TableCaption'
|
|
104
|
+
|
|
105
|
+
export {
|
|
106
|
+
Table,
|
|
107
|
+
TableBody,
|
|
108
|
+
TableCaption,
|
|
109
|
+
TableCell,
|
|
110
|
+
TableFooter,
|
|
111
|
+
TableHead,
|
|
112
|
+
TableHeader,
|
|
113
|
+
TableRow
|
|
114
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
|
|
3
|
+
import { cn } from '@/lib/cn'
|
|
4
|
+
|
|
5
|
+
export interface TextareaProps
|
|
6
|
+
extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {}
|
|
7
|
+
|
|
8
|
+
const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
|
|
9
|
+
({ className, ...props }, ref) => {
|
|
10
|
+
return (
|
|
11
|
+
<textarea
|
|
12
|
+
className={cn(
|
|
13
|
+
'flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-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',
|
|
14
|
+
className
|
|
15
|
+
)}
|
|
16
|
+
ref={ref}
|
|
17
|
+
{...props}
|
|
18
|
+
/>
|
|
19
|
+
)
|
|
20
|
+
}
|
|
21
|
+
)
|
|
22
|
+
Textarea.displayName = 'Textarea'
|
|
23
|
+
|
|
24
|
+
export { Textarea }
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import * as ToastPrimitives from '@radix-ui/react-toast'
|
|
2
|
+
import { cva, type VariantProps } from 'class-variance-authority'
|
|
3
|
+
import { X } from 'lucide-react'
|
|
4
|
+
import * as React from 'react'
|
|
5
|
+
|
|
6
|
+
import { cn } from '@/lib/cn'
|
|
7
|
+
|
|
8
|
+
const ToastProvider = ToastPrimitives.Provider
|
|
9
|
+
|
|
10
|
+
const ToastViewport = React.forwardRef<
|
|
11
|
+
React.ElementRef<typeof ToastPrimitives.Viewport>,
|
|
12
|
+
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Viewport>
|
|
13
|
+
>(({ className, ...props }, ref) => (
|
|
14
|
+
<ToastPrimitives.Viewport
|
|
15
|
+
ref={ref}
|
|
16
|
+
className={cn(
|
|
17
|
+
'fixed top-0 z-[100] flex max-h-screen w-full flex-col-reverse p-4 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[420px]',
|
|
18
|
+
className
|
|
19
|
+
)}
|
|
20
|
+
{...props}
|
|
21
|
+
/>
|
|
22
|
+
))
|
|
23
|
+
ToastViewport.displayName = ToastPrimitives.Viewport.displayName
|
|
24
|
+
|
|
25
|
+
const toastVariants = cva(
|
|
26
|
+
'group pointer-events-auto relative flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border p-6 pr-8 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full',
|
|
27
|
+
{
|
|
28
|
+
variants: {
|
|
29
|
+
variant: {
|
|
30
|
+
default: 'border bg-background text-foreground',
|
|
31
|
+
destructive:
|
|
32
|
+
'destructive group border-destructive bg-destructive text-destructive-foreground'
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
defaultVariants: {
|
|
36
|
+
variant: 'default'
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
const Toast = React.forwardRef<
|
|
42
|
+
React.ElementRef<typeof ToastPrimitives.Root>,
|
|
43
|
+
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Root> &
|
|
44
|
+
VariantProps<typeof toastVariants>
|
|
45
|
+
>(({ className, variant, ...props }, ref) => {
|
|
46
|
+
return (
|
|
47
|
+
<ToastPrimitives.Root
|
|
48
|
+
ref={ref}
|
|
49
|
+
className={cn(toastVariants({ variant }), className)}
|
|
50
|
+
{...props}
|
|
51
|
+
/>
|
|
52
|
+
)
|
|
53
|
+
})
|
|
54
|
+
Toast.displayName = ToastPrimitives.Root.displayName
|
|
55
|
+
|
|
56
|
+
const ToastAction = React.forwardRef<
|
|
57
|
+
React.ElementRef<typeof ToastPrimitives.Action>,
|
|
58
|
+
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Action>
|
|
59
|
+
>(({ className, ...props }, ref) => (
|
|
60
|
+
<ToastPrimitives.Action
|
|
61
|
+
ref={ref}
|
|
62
|
+
className={cn(
|
|
63
|
+
'inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium ring-offset-background transition-colors hover:bg-secondary focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 group-[.destructive]:border-muted/40 group-[.destructive]:hover:border-destructive/30 group-[.destructive]:hover:bg-destructive group-[.destructive]:hover:text-destructive-foreground group-[.destructive]:focus:ring-destructive',
|
|
64
|
+
className
|
|
65
|
+
)}
|
|
66
|
+
{...props}
|
|
67
|
+
/>
|
|
68
|
+
))
|
|
69
|
+
ToastAction.displayName = ToastPrimitives.Action.displayName
|
|
70
|
+
|
|
71
|
+
const ToastClose = React.forwardRef<
|
|
72
|
+
React.ElementRef<typeof ToastPrimitives.Close>,
|
|
73
|
+
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Close>
|
|
74
|
+
>(({ className, ...props }, ref) => (
|
|
75
|
+
<ToastPrimitives.Close
|
|
76
|
+
ref={ref}
|
|
77
|
+
className={cn(
|
|
78
|
+
'absolute right-2 top-2 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-2 group-hover:opacity-100 group-[.destructive]:text-red-300 group-[.destructive]:hover:text-red-50 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600',
|
|
79
|
+
className
|
|
80
|
+
)}
|
|
81
|
+
toast-close=""
|
|
82
|
+
{...props}
|
|
83
|
+
>
|
|
84
|
+
<X className="h-4 w-4" />
|
|
85
|
+
</ToastPrimitives.Close>
|
|
86
|
+
))
|
|
87
|
+
ToastClose.displayName = ToastPrimitives.Close.displayName
|
|
88
|
+
|
|
89
|
+
const ToastTitle = React.forwardRef<
|
|
90
|
+
React.ElementRef<typeof ToastPrimitives.Title>,
|
|
91
|
+
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Title>
|
|
92
|
+
>(({ className, ...props }, ref) => (
|
|
93
|
+
<ToastPrimitives.Title
|
|
94
|
+
ref={ref}
|
|
95
|
+
className={cn('text-sm font-semibold', className)}
|
|
96
|
+
{...props}
|
|
97
|
+
/>
|
|
98
|
+
))
|
|
99
|
+
ToastTitle.displayName = ToastPrimitives.Title.displayName
|
|
100
|
+
|
|
101
|
+
const ToastDescription = React.forwardRef<
|
|
102
|
+
React.ElementRef<typeof ToastPrimitives.Description>,
|
|
103
|
+
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Description>
|
|
104
|
+
>(({ className, ...props }, ref) => (
|
|
105
|
+
<ToastPrimitives.Description
|
|
106
|
+
ref={ref}
|
|
107
|
+
className={cn('text-sm opacity-90', className)}
|
|
108
|
+
{...props}
|
|
109
|
+
/>
|
|
110
|
+
))
|
|
111
|
+
ToastDescription.displayName = ToastPrimitives.Description.displayName
|
|
112
|
+
|
|
113
|
+
type ToastProps = React.ComponentPropsWithoutRef<typeof Toast>
|
|
114
|
+
|
|
115
|
+
type ToastActionElement = React.ReactElement<typeof ToastAction>
|
|
116
|
+
|
|
117
|
+
export {
|
|
118
|
+
Toast,
|
|
119
|
+
ToastAction,
|
|
120
|
+
ToastClose,
|
|
121
|
+
ToastDescription,
|
|
122
|
+
ToastProvider,
|
|
123
|
+
ToastTitle,
|
|
124
|
+
ToastViewport,
|
|
125
|
+
type ToastActionElement,
|
|
126
|
+
type ToastProps
|
|
127
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
Toast,
|
|
5
|
+
ToastClose,
|
|
6
|
+
ToastDescription,
|
|
7
|
+
ToastProvider,
|
|
8
|
+
ToastTitle,
|
|
9
|
+
ToastViewport
|
|
10
|
+
} from '@/components/ui/toast'
|
|
11
|
+
import { useToast } from '@/components/ui/use-toast'
|
|
12
|
+
|
|
13
|
+
export function Toaster() {
|
|
14
|
+
const { toasts } = useToast()
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<ToastProvider>
|
|
18
|
+
{toasts.map(function ({ id, title, description, action, ...props }) {
|
|
19
|
+
return (
|
|
20
|
+
<Toast key={id} {...props}>
|
|
21
|
+
<div className="grid gap-1">
|
|
22
|
+
{title && <ToastTitle>{title}</ToastTitle>}
|
|
23
|
+
{description && (
|
|
24
|
+
<ToastDescription>{description}</ToastDescription>
|
|
25
|
+
)}
|
|
26
|
+
</div>
|
|
27
|
+
{action}
|
|
28
|
+
<ToastClose />
|
|
29
|
+
</Toast>
|
|
30
|
+
)
|
|
31
|
+
})}
|
|
32
|
+
<ToastViewport />
|
|
33
|
+
</ToastProvider>
|
|
34
|
+
)
|
|
35
|
+
}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
// Inspired by react-hot-toast library
|
|
2
|
+
import * as React from 'react'
|
|
3
|
+
|
|
4
|
+
import type { ToastActionElement, ToastProps } from '@/components/ui/toast'
|
|
5
|
+
|
|
6
|
+
const TOAST_LIMIT = 1
|
|
7
|
+
const TOAST_REMOVE_DELAY = 1000000
|
|
8
|
+
|
|
9
|
+
type ToasterToast = ToastProps & {
|
|
10
|
+
id: string
|
|
11
|
+
title?: React.ReactNode
|
|
12
|
+
description?: React.ReactNode
|
|
13
|
+
action?: ToastActionElement
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const actionTypes = {
|
|
17
|
+
ADD_TOAST: 'ADD_TOAST',
|
|
18
|
+
UPDATE_TOAST: 'UPDATE_TOAST',
|
|
19
|
+
DISMISS_TOAST: 'DISMISS_TOAST',
|
|
20
|
+
REMOVE_TOAST: 'REMOVE_TOAST'
|
|
21
|
+
} as const
|
|
22
|
+
|
|
23
|
+
let count = 0
|
|
24
|
+
|
|
25
|
+
function genId() {
|
|
26
|
+
count = (count + 1) % Number.MAX_VALUE
|
|
27
|
+
return count.toString()
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
type ActionType = typeof actionTypes
|
|
31
|
+
|
|
32
|
+
type Action =
|
|
33
|
+
| {
|
|
34
|
+
type: ActionType['ADD_TOAST']
|
|
35
|
+
toast: ToasterToast
|
|
36
|
+
}
|
|
37
|
+
| {
|
|
38
|
+
type: ActionType['UPDATE_TOAST']
|
|
39
|
+
toast: Partial<ToasterToast>
|
|
40
|
+
}
|
|
41
|
+
| {
|
|
42
|
+
type: ActionType['DISMISS_TOAST']
|
|
43
|
+
toastId?: ToasterToast['id']
|
|
44
|
+
}
|
|
45
|
+
| {
|
|
46
|
+
type: ActionType['REMOVE_TOAST']
|
|
47
|
+
toastId?: ToasterToast['id']
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
interface State {
|
|
51
|
+
toasts: ToasterToast[]
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>()
|
|
55
|
+
|
|
56
|
+
const addToRemoveQueue = (toastId: string) => {
|
|
57
|
+
if (toastTimeouts.has(toastId)) {
|
|
58
|
+
return
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const timeout = setTimeout(() => {
|
|
62
|
+
toastTimeouts.delete(toastId)
|
|
63
|
+
dispatch({
|
|
64
|
+
type: 'REMOVE_TOAST',
|
|
65
|
+
toastId: toastId
|
|
66
|
+
})
|
|
67
|
+
}, TOAST_REMOVE_DELAY)
|
|
68
|
+
|
|
69
|
+
toastTimeouts.set(toastId, timeout)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export const reducer = (state: State, action: Action): State => {
|
|
73
|
+
switch (action.type) {
|
|
74
|
+
case 'ADD_TOAST':
|
|
75
|
+
return {
|
|
76
|
+
...state,
|
|
77
|
+
toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
case 'UPDATE_TOAST':
|
|
81
|
+
return {
|
|
82
|
+
...state,
|
|
83
|
+
toasts: state.toasts.map(t =>
|
|
84
|
+
t.id === action.toast.id ? { ...t, ...action.toast } : t
|
|
85
|
+
)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
case 'DISMISS_TOAST': {
|
|
89
|
+
const { toastId } = action
|
|
90
|
+
|
|
91
|
+
// ! Side effects ! - This could be extracted into a dismissToast() action,
|
|
92
|
+
// but I'll keep it here for simplicity
|
|
93
|
+
if (toastId) {
|
|
94
|
+
addToRemoveQueue(toastId)
|
|
95
|
+
} else {
|
|
96
|
+
state.toasts.forEach(toast => {
|
|
97
|
+
addToRemoveQueue(toast.id)
|
|
98
|
+
})
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
...state,
|
|
103
|
+
toasts: state.toasts.map(t =>
|
|
104
|
+
t.id === toastId || toastId === undefined
|
|
105
|
+
? {
|
|
106
|
+
...t,
|
|
107
|
+
open: false
|
|
108
|
+
}
|
|
109
|
+
: t
|
|
110
|
+
)
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
case 'REMOVE_TOAST':
|
|
114
|
+
if (action.toastId === undefined) {
|
|
115
|
+
return {
|
|
116
|
+
...state,
|
|
117
|
+
toasts: []
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return {
|
|
121
|
+
...state,
|
|
122
|
+
toasts: state.toasts.filter(t => t.id !== action.toastId)
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const listeners: Array<(state: State) => void> = []
|
|
128
|
+
|
|
129
|
+
let memoryState: State = { toasts: [] }
|
|
130
|
+
|
|
131
|
+
function dispatch(action: Action) {
|
|
132
|
+
memoryState = reducer(memoryState, action)
|
|
133
|
+
listeners.forEach(listener => {
|
|
134
|
+
listener(memoryState)
|
|
135
|
+
})
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
type Toast = Omit<ToasterToast, 'id'>
|
|
139
|
+
|
|
140
|
+
function toast({ ...props }: Toast) {
|
|
141
|
+
const id = genId()
|
|
142
|
+
|
|
143
|
+
const update = (props: ToasterToast) =>
|
|
144
|
+
dispatch({
|
|
145
|
+
type: 'UPDATE_TOAST',
|
|
146
|
+
toast: { ...props, id }
|
|
147
|
+
})
|
|
148
|
+
const dismiss = () => dispatch({ type: 'DISMISS_TOAST', toastId: id })
|
|
149
|
+
|
|
150
|
+
dispatch({
|
|
151
|
+
type: 'ADD_TOAST',
|
|
152
|
+
toast: {
|
|
153
|
+
...props,
|
|
154
|
+
id,
|
|
155
|
+
open: true,
|
|
156
|
+
onOpenChange: open => {
|
|
157
|
+
if (!open) dismiss()
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
return {
|
|
163
|
+
id: id,
|
|
164
|
+
dismiss,
|
|
165
|
+
update
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function useToast() {
|
|
170
|
+
const [state, setState] = React.useState<State>(memoryState)
|
|
171
|
+
|
|
172
|
+
React.useEffect(() => {
|
|
173
|
+
listeners.push(setState)
|
|
174
|
+
return () => {
|
|
175
|
+
const index = listeners.indexOf(setState)
|
|
176
|
+
if (index > -1) {
|
|
177
|
+
listeners.splice(index, 1)
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}, [state])
|
|
181
|
+
|
|
182
|
+
return {
|
|
183
|
+
...state,
|
|
184
|
+
toast,
|
|
185
|
+
dismiss: (toastId?: string) => dispatch({ type: 'DISMISS_TOAST', toastId })
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export { toast, useToast }
|