@sundaysf/cli-v2 1.0.3 → 1.0.6
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/bin/index.js +1 -1
- package/dist/bin/index.js.map +1 -1
- package/dist/templates/backend/.sundaysrc +7 -0
- package/dist/templates/backend/eslint.config.js.map +1 -0
- package/dist/templates/backend/gitignore +52 -0
- package/dist/templates/backend-db-sql/.sundaysrc +7 -0
- package/dist/templates/backend-db-sql/eslint.config.js.map +1 -0
- package/dist/templates/backend-db-sql/gitignore +65 -0
- package/dist/templates/backend-embedded-db-sql/.sundaysrc +7 -0
- package/dist/templates/backend-embedded-db-sql/eslint.config.js.map +1 -0
- package/dist/templates/backend-embedded-db-sql/gitignore +64 -0
- package/dist/templates/frontend-nextjs/.sundaysrc +7 -0
- package/dist/templates/frontend-nextjs/app/globals.css +125 -0
- package/dist/templates/frontend-nextjs/app/layout.tsx +45 -0
- package/dist/templates/frontend-nextjs/app/page.tsx +5 -0
- package/dist/templates/frontend-nextjs/components/project-generator.tsx +558 -0
- package/dist/templates/frontend-nextjs/components/theme-provider.tsx +11 -0
- package/dist/templates/frontend-nextjs/components/ui/accordion.tsx +66 -0
- package/dist/templates/frontend-nextjs/components/ui/alert-dialog.tsx +157 -0
- package/dist/templates/frontend-nextjs/components/ui/alert.tsx +66 -0
- package/dist/templates/frontend-nextjs/components/ui/aspect-ratio.tsx +11 -0
- package/dist/templates/frontend-nextjs/components/ui/avatar.tsx +53 -0
- package/dist/templates/frontend-nextjs/components/ui/badge.tsx +46 -0
- package/dist/templates/frontend-nextjs/components/ui/breadcrumb.tsx +109 -0
- package/dist/templates/frontend-nextjs/components/ui/button-group.tsx +83 -0
- package/dist/templates/frontend-nextjs/components/ui/button.tsx +60 -0
- package/dist/templates/frontend-nextjs/components/ui/calendar.tsx +213 -0
- package/dist/templates/frontend-nextjs/components/ui/card.tsx +92 -0
- package/dist/templates/frontend-nextjs/components/ui/carousel.tsx +241 -0
- package/dist/templates/frontend-nextjs/components/ui/chart.tsx +353 -0
- package/dist/templates/frontend-nextjs/components/ui/checkbox.tsx +32 -0
- package/dist/templates/frontend-nextjs/components/ui/collapsible.tsx +33 -0
- package/dist/templates/frontend-nextjs/components/ui/command.tsx +184 -0
- package/dist/templates/frontend-nextjs/components/ui/context-menu.tsx +252 -0
- package/dist/templates/frontend-nextjs/components/ui/dialog.tsx +143 -0
- package/dist/templates/frontend-nextjs/components/ui/drawer.tsx +135 -0
- package/dist/templates/frontend-nextjs/components/ui/dropdown-menu.tsx +257 -0
- package/dist/templates/frontend-nextjs/components/ui/empty.tsx +104 -0
- package/dist/templates/frontend-nextjs/components/ui/field.tsx +244 -0
- package/dist/templates/frontend-nextjs/components/ui/form.tsx +167 -0
- package/dist/templates/frontend-nextjs/components/ui/hover-card.tsx +44 -0
- package/dist/templates/frontend-nextjs/components/ui/input-group.tsx +169 -0
- package/dist/templates/frontend-nextjs/components/ui/input-otp.tsx +77 -0
- package/dist/templates/frontend-nextjs/components/ui/input.tsx +21 -0
- package/dist/templates/frontend-nextjs/components/ui/item.tsx +193 -0
- package/dist/templates/frontend-nextjs/components/ui/kbd.tsx +28 -0
- package/dist/templates/frontend-nextjs/components/ui/label.tsx +24 -0
- package/dist/templates/frontend-nextjs/components/ui/menubar.tsx +276 -0
- package/dist/templates/frontend-nextjs/components/ui/navigation-menu.tsx +166 -0
- package/dist/templates/frontend-nextjs/components/ui/pagination.tsx +127 -0
- package/dist/templates/frontend-nextjs/components/ui/popover.tsx +48 -0
- package/dist/templates/frontend-nextjs/components/ui/progress.tsx +31 -0
- package/dist/templates/frontend-nextjs/components/ui/radio-group.tsx +45 -0
- package/dist/templates/frontend-nextjs/components/ui/resizable.tsx +56 -0
- package/dist/templates/frontend-nextjs/components/ui/scroll-area.tsx +58 -0
- package/dist/templates/frontend-nextjs/components/ui/select.tsx +185 -0
- package/dist/templates/frontend-nextjs/components/ui/separator.tsx +28 -0
- package/dist/templates/frontend-nextjs/components/ui/sheet.tsx +139 -0
- package/dist/templates/frontend-nextjs/components/ui/sidebar.tsx +726 -0
- package/dist/templates/frontend-nextjs/components/ui/skeleton.tsx +13 -0
- package/dist/templates/frontend-nextjs/components/ui/slider.tsx +63 -0
- package/dist/templates/frontend-nextjs/components/ui/sonner.tsx +25 -0
- package/dist/templates/frontend-nextjs/components/ui/spinner.tsx +16 -0
- package/dist/templates/frontend-nextjs/components/ui/switch.tsx +31 -0
- package/dist/templates/frontend-nextjs/components/ui/table.tsx +116 -0
- package/dist/templates/frontend-nextjs/components/ui/tabs.tsx +66 -0
- package/dist/templates/frontend-nextjs/components/ui/textarea.tsx +18 -0
- package/dist/templates/frontend-nextjs/components/ui/toast.tsx +129 -0
- package/dist/templates/frontend-nextjs/components/ui/toaster.tsx +35 -0
- package/dist/templates/frontend-nextjs/components/ui/toggle-group.tsx +73 -0
- package/dist/templates/frontend-nextjs/components/ui/toggle.tsx +47 -0
- package/dist/templates/frontend-nextjs/components/ui/tooltip.tsx +61 -0
- package/dist/templates/frontend-nextjs/components/ui/use-mobile.tsx +19 -0
- package/dist/templates/frontend-nextjs/components/ui/use-toast.ts +191 -0
- package/dist/templates/frontend-nextjs/components.json +21 -0
- package/dist/templates/frontend-nextjs/gitignore +17 -0
- package/dist/templates/frontend-nextjs/hooks/use-mobile.ts +19 -0
- package/dist/templates/frontend-nextjs/hooks/use-toast.ts +191 -0
- package/dist/templates/frontend-nextjs/lib/utils.ts +6 -0
- package/dist/templates/frontend-nextjs/next.config.mjs +11 -0
- package/dist/templates/frontend-nextjs/postcss.config.mjs +8 -0
- package/dist/templates/frontend-nextjs/public/apple-icon.png +0 -0
- package/dist/templates/frontend-nextjs/public/icon-dark-32x32.png +0 -0
- package/dist/templates/frontend-nextjs/public/icon-light-32x32.png +0 -0
- package/dist/templates/frontend-nextjs/public/icon.svg +26 -0
- package/dist/templates/frontend-nextjs/public/placeholder-logo.png +0 -0
- package/dist/templates/frontend-nextjs/public/placeholder-logo.svg +1 -0
- package/dist/templates/frontend-nextjs/public/placeholder-user.jpg +0 -0
- package/dist/templates/frontend-nextjs/public/placeholder.jpg +0 -0
- package/dist/templates/frontend-nextjs/public/placeholder.svg +1 -0
- package/dist/templates/frontend-nextjs/styles/globals.css +125 -0
- package/dist/templates/frontend-nextjs/tsconfig.json +27 -0
- package/package.json +1 -1
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import * as React from 'react'
|
|
4
|
+
import * as TooltipPrimitive from '@radix-ui/react-tooltip'
|
|
5
|
+
|
|
6
|
+
import { cn } from '@/lib/utils'
|
|
7
|
+
|
|
8
|
+
function TooltipProvider({
|
|
9
|
+
delayDuration = 0,
|
|
10
|
+
...props
|
|
11
|
+
}: React.ComponentProps<typeof TooltipPrimitive.Provider>) {
|
|
12
|
+
return (
|
|
13
|
+
<TooltipPrimitive.Provider
|
|
14
|
+
data-slot="tooltip-provider"
|
|
15
|
+
delayDuration={delayDuration}
|
|
16
|
+
{...props}
|
|
17
|
+
/>
|
|
18
|
+
)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function Tooltip({
|
|
22
|
+
...props
|
|
23
|
+
}: React.ComponentProps<typeof TooltipPrimitive.Root>) {
|
|
24
|
+
return (
|
|
25
|
+
<TooltipProvider>
|
|
26
|
+
<TooltipPrimitive.Root data-slot="tooltip" {...props} />
|
|
27
|
+
</TooltipProvider>
|
|
28
|
+
)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function TooltipTrigger({
|
|
32
|
+
...props
|
|
33
|
+
}: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {
|
|
34
|
+
return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function TooltipContent({
|
|
38
|
+
className,
|
|
39
|
+
sideOffset = 0,
|
|
40
|
+
children,
|
|
41
|
+
...props
|
|
42
|
+
}: React.ComponentProps<typeof TooltipPrimitive.Content>) {
|
|
43
|
+
return (
|
|
44
|
+
<TooltipPrimitive.Portal>
|
|
45
|
+
<TooltipPrimitive.Content
|
|
46
|
+
data-slot="tooltip-content"
|
|
47
|
+
sideOffset={sideOffset}
|
|
48
|
+
className={cn(
|
|
49
|
+
'bg-foreground text-background animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-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 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md px-3 py-1.5 text-xs text-balance',
|
|
50
|
+
className,
|
|
51
|
+
)}
|
|
52
|
+
{...props}
|
|
53
|
+
>
|
|
54
|
+
{children}
|
|
55
|
+
<TooltipPrimitive.Arrow className="bg-foreground fill-foreground z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]" />
|
|
56
|
+
</TooltipPrimitive.Content>
|
|
57
|
+
</TooltipPrimitive.Portal>
|
|
58
|
+
)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
|
|
3
|
+
const MOBILE_BREAKPOINT = 768
|
|
4
|
+
|
|
5
|
+
export function useIsMobile() {
|
|
6
|
+
const [isMobile, setIsMobile] = React.useState<boolean | undefined>(undefined)
|
|
7
|
+
|
|
8
|
+
React.useEffect(() => {
|
|
9
|
+
const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`)
|
|
10
|
+
const onChange = () => {
|
|
11
|
+
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
|
|
12
|
+
}
|
|
13
|
+
mql.addEventListener('change', onChange)
|
|
14
|
+
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
|
|
15
|
+
return () => mql.removeEventListener('change', onChange)
|
|
16
|
+
}, [])
|
|
17
|
+
|
|
18
|
+
return !!isMobile
|
|
19
|
+
}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
// Inspired by react-hot-toast library
|
|
4
|
+
import * as React from 'react'
|
|
5
|
+
|
|
6
|
+
import type { ToastActionElement, ToastProps } from '@/components/ui/toast'
|
|
7
|
+
|
|
8
|
+
const TOAST_LIMIT = 1
|
|
9
|
+
const TOAST_REMOVE_DELAY = 1000000
|
|
10
|
+
|
|
11
|
+
type ToasterToast = ToastProps & {
|
|
12
|
+
id: string
|
|
13
|
+
title?: React.ReactNode
|
|
14
|
+
description?: React.ReactNode
|
|
15
|
+
action?: ToastActionElement
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const actionTypes = {
|
|
19
|
+
ADD_TOAST: 'ADD_TOAST',
|
|
20
|
+
UPDATE_TOAST: 'UPDATE_TOAST',
|
|
21
|
+
DISMISS_TOAST: 'DISMISS_TOAST',
|
|
22
|
+
REMOVE_TOAST: 'REMOVE_TOAST',
|
|
23
|
+
} as const
|
|
24
|
+
|
|
25
|
+
let count = 0
|
|
26
|
+
|
|
27
|
+
function genId() {
|
|
28
|
+
count = (count + 1) % Number.MAX_SAFE_INTEGER
|
|
29
|
+
return count.toString()
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
type ActionType = typeof actionTypes
|
|
33
|
+
|
|
34
|
+
type Action =
|
|
35
|
+
| {
|
|
36
|
+
type: ActionType['ADD_TOAST']
|
|
37
|
+
toast: ToasterToast
|
|
38
|
+
}
|
|
39
|
+
| {
|
|
40
|
+
type: ActionType['UPDATE_TOAST']
|
|
41
|
+
toast: Partial<ToasterToast>
|
|
42
|
+
}
|
|
43
|
+
| {
|
|
44
|
+
type: ActionType['DISMISS_TOAST']
|
|
45
|
+
toastId?: ToasterToast['id']
|
|
46
|
+
}
|
|
47
|
+
| {
|
|
48
|
+
type: ActionType['REMOVE_TOAST']
|
|
49
|
+
toastId?: ToasterToast['id']
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
interface State {
|
|
53
|
+
toasts: ToasterToast[]
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>()
|
|
57
|
+
|
|
58
|
+
const addToRemoveQueue = (toastId: string) => {
|
|
59
|
+
if (toastTimeouts.has(toastId)) {
|
|
60
|
+
return
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const timeout = setTimeout(() => {
|
|
64
|
+
toastTimeouts.delete(toastId)
|
|
65
|
+
dispatch({
|
|
66
|
+
type: 'REMOVE_TOAST',
|
|
67
|
+
toastId: toastId,
|
|
68
|
+
})
|
|
69
|
+
}, TOAST_REMOVE_DELAY)
|
|
70
|
+
|
|
71
|
+
toastTimeouts.set(toastId, timeout)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export const reducer = (state: State, action: Action): State => {
|
|
75
|
+
switch (action.type) {
|
|
76
|
+
case 'ADD_TOAST':
|
|
77
|
+
return {
|
|
78
|
+
...state,
|
|
79
|
+
toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
case 'UPDATE_TOAST':
|
|
83
|
+
return {
|
|
84
|
+
...state,
|
|
85
|
+
toasts: state.toasts.map((t) =>
|
|
86
|
+
t.id === action.toast.id ? { ...t, ...action.toast } : t,
|
|
87
|
+
),
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
case 'DISMISS_TOAST': {
|
|
91
|
+
const { toastId } = action
|
|
92
|
+
|
|
93
|
+
// ! Side effects ! - This could be extracted into a dismissToast() action,
|
|
94
|
+
// but I'll keep it here for simplicity
|
|
95
|
+
if (toastId) {
|
|
96
|
+
addToRemoveQueue(toastId)
|
|
97
|
+
} else {
|
|
98
|
+
state.toasts.forEach((toast) => {
|
|
99
|
+
addToRemoveQueue(toast.id)
|
|
100
|
+
})
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
...state,
|
|
105
|
+
toasts: state.toasts.map((t) =>
|
|
106
|
+
t.id === toastId || toastId === undefined
|
|
107
|
+
? {
|
|
108
|
+
...t,
|
|
109
|
+
open: false,
|
|
110
|
+
}
|
|
111
|
+
: t,
|
|
112
|
+
),
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
case 'REMOVE_TOAST':
|
|
116
|
+
if (action.toastId === undefined) {
|
|
117
|
+
return {
|
|
118
|
+
...state,
|
|
119
|
+
toasts: [],
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return {
|
|
123
|
+
...state,
|
|
124
|
+
toasts: state.toasts.filter((t) => t.id !== action.toastId),
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const listeners: Array<(state: State) => void> = []
|
|
130
|
+
|
|
131
|
+
let memoryState: State = { toasts: [] }
|
|
132
|
+
|
|
133
|
+
function dispatch(action: Action) {
|
|
134
|
+
memoryState = reducer(memoryState, action)
|
|
135
|
+
listeners.forEach((listener) => {
|
|
136
|
+
listener(memoryState)
|
|
137
|
+
})
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
type Toast = Omit<ToasterToast, 'id'>
|
|
141
|
+
|
|
142
|
+
function toast({ ...props }: Toast) {
|
|
143
|
+
const id = genId()
|
|
144
|
+
|
|
145
|
+
const update = (props: ToasterToast) =>
|
|
146
|
+
dispatch({
|
|
147
|
+
type: 'UPDATE_TOAST',
|
|
148
|
+
toast: { ...props, id },
|
|
149
|
+
})
|
|
150
|
+
const dismiss = () => dispatch({ type: 'DISMISS_TOAST', toastId: id })
|
|
151
|
+
|
|
152
|
+
dispatch({
|
|
153
|
+
type: 'ADD_TOAST',
|
|
154
|
+
toast: {
|
|
155
|
+
...props,
|
|
156
|
+
id,
|
|
157
|
+
open: true,
|
|
158
|
+
onOpenChange: (open) => {
|
|
159
|
+
if (!open) dismiss()
|
|
160
|
+
},
|
|
161
|
+
},
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
return {
|
|
165
|
+
id: id,
|
|
166
|
+
dismiss,
|
|
167
|
+
update,
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function useToast() {
|
|
172
|
+
const [state, setState] = React.useState<State>(memoryState)
|
|
173
|
+
|
|
174
|
+
React.useEffect(() => {
|
|
175
|
+
listeners.push(setState)
|
|
176
|
+
return () => {
|
|
177
|
+
const index = listeners.indexOf(setState)
|
|
178
|
+
if (index > -1) {
|
|
179
|
+
listeners.splice(index, 1)
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}, [state])
|
|
183
|
+
|
|
184
|
+
return {
|
|
185
|
+
...state,
|
|
186
|
+
toast,
|
|
187
|
+
dismiss: (toastId?: string) => dispatch({ type: 'DISMISS_TOAST', toastId }),
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export { useToast, toast }
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://ui.shadcn.com/schema.json",
|
|
3
|
+
"style": "new-york",
|
|
4
|
+
"rsc": true,
|
|
5
|
+
"tsx": true,
|
|
6
|
+
"tailwind": {
|
|
7
|
+
"config": "",
|
|
8
|
+
"css": "app/globals.css",
|
|
9
|
+
"baseColor": "neutral",
|
|
10
|
+
"cssVariables": true,
|
|
11
|
+
"prefix": ""
|
|
12
|
+
},
|
|
13
|
+
"aliases": {
|
|
14
|
+
"components": "@/components",
|
|
15
|
+
"utils": "@/lib/utils",
|
|
16
|
+
"ui": "@/components/ui",
|
|
17
|
+
"lib": "@/lib",
|
|
18
|
+
"hooks": "@/hooks"
|
|
19
|
+
},
|
|
20
|
+
"iconLibrary": "lucide"
|
|
21
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# v0 sandbox internal files
|
|
2
|
+
__v0_runtime_loader.js
|
|
3
|
+
__v0_devtools.tsx
|
|
4
|
+
__v0_jsx-dev-runtime.ts
|
|
5
|
+
.npmrc
|
|
6
|
+
.snowflake/
|
|
7
|
+
.v0-trash/
|
|
8
|
+
.vercel/
|
|
9
|
+
next.user-config.*
|
|
10
|
+
|
|
11
|
+
# Environment variables
|
|
12
|
+
.env*.local
|
|
13
|
+
|
|
14
|
+
# Common ignores
|
|
15
|
+
node_modules/
|
|
16
|
+
.next/
|
|
17
|
+
.DS_Store
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
|
|
3
|
+
const MOBILE_BREAKPOINT = 768
|
|
4
|
+
|
|
5
|
+
export function useIsMobile() {
|
|
6
|
+
const [isMobile, setIsMobile] = React.useState<boolean | undefined>(undefined)
|
|
7
|
+
|
|
8
|
+
React.useEffect(() => {
|
|
9
|
+
const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`)
|
|
10
|
+
const onChange = () => {
|
|
11
|
+
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
|
|
12
|
+
}
|
|
13
|
+
mql.addEventListener('change', onChange)
|
|
14
|
+
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
|
|
15
|
+
return () => mql.removeEventListener('change', onChange)
|
|
16
|
+
}, [])
|
|
17
|
+
|
|
18
|
+
return !!isMobile
|
|
19
|
+
}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
// Inspired by react-hot-toast library
|
|
4
|
+
import * as React from 'react'
|
|
5
|
+
|
|
6
|
+
import type { ToastActionElement, ToastProps } from '@/components/ui/toast'
|
|
7
|
+
|
|
8
|
+
const TOAST_LIMIT = 1
|
|
9
|
+
const TOAST_REMOVE_DELAY = 1000000
|
|
10
|
+
|
|
11
|
+
type ToasterToast = ToastProps & {
|
|
12
|
+
id: string
|
|
13
|
+
title?: React.ReactNode
|
|
14
|
+
description?: React.ReactNode
|
|
15
|
+
action?: ToastActionElement
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const actionTypes = {
|
|
19
|
+
ADD_TOAST: 'ADD_TOAST',
|
|
20
|
+
UPDATE_TOAST: 'UPDATE_TOAST',
|
|
21
|
+
DISMISS_TOAST: 'DISMISS_TOAST',
|
|
22
|
+
REMOVE_TOAST: 'REMOVE_TOAST',
|
|
23
|
+
} as const
|
|
24
|
+
|
|
25
|
+
let count = 0
|
|
26
|
+
|
|
27
|
+
function genId() {
|
|
28
|
+
count = (count + 1) % Number.MAX_SAFE_INTEGER
|
|
29
|
+
return count.toString()
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
type ActionType = typeof actionTypes
|
|
33
|
+
|
|
34
|
+
type Action =
|
|
35
|
+
| {
|
|
36
|
+
type: ActionType['ADD_TOAST']
|
|
37
|
+
toast: ToasterToast
|
|
38
|
+
}
|
|
39
|
+
| {
|
|
40
|
+
type: ActionType['UPDATE_TOAST']
|
|
41
|
+
toast: Partial<ToasterToast>
|
|
42
|
+
}
|
|
43
|
+
| {
|
|
44
|
+
type: ActionType['DISMISS_TOAST']
|
|
45
|
+
toastId?: ToasterToast['id']
|
|
46
|
+
}
|
|
47
|
+
| {
|
|
48
|
+
type: ActionType['REMOVE_TOAST']
|
|
49
|
+
toastId?: ToasterToast['id']
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
interface State {
|
|
53
|
+
toasts: ToasterToast[]
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>()
|
|
57
|
+
|
|
58
|
+
const addToRemoveQueue = (toastId: string) => {
|
|
59
|
+
if (toastTimeouts.has(toastId)) {
|
|
60
|
+
return
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const timeout = setTimeout(() => {
|
|
64
|
+
toastTimeouts.delete(toastId)
|
|
65
|
+
dispatch({
|
|
66
|
+
type: 'REMOVE_TOAST',
|
|
67
|
+
toastId: toastId,
|
|
68
|
+
})
|
|
69
|
+
}, TOAST_REMOVE_DELAY)
|
|
70
|
+
|
|
71
|
+
toastTimeouts.set(toastId, timeout)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export const reducer = (state: State, action: Action): State => {
|
|
75
|
+
switch (action.type) {
|
|
76
|
+
case 'ADD_TOAST':
|
|
77
|
+
return {
|
|
78
|
+
...state,
|
|
79
|
+
toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
case 'UPDATE_TOAST':
|
|
83
|
+
return {
|
|
84
|
+
...state,
|
|
85
|
+
toasts: state.toasts.map((t) =>
|
|
86
|
+
t.id === action.toast.id ? { ...t, ...action.toast } : t,
|
|
87
|
+
),
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
case 'DISMISS_TOAST': {
|
|
91
|
+
const { toastId } = action
|
|
92
|
+
|
|
93
|
+
// ! Side effects ! - This could be extracted into a dismissToast() action,
|
|
94
|
+
// but I'll keep it here for simplicity
|
|
95
|
+
if (toastId) {
|
|
96
|
+
addToRemoveQueue(toastId)
|
|
97
|
+
} else {
|
|
98
|
+
state.toasts.forEach((toast) => {
|
|
99
|
+
addToRemoveQueue(toast.id)
|
|
100
|
+
})
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
...state,
|
|
105
|
+
toasts: state.toasts.map((t) =>
|
|
106
|
+
t.id === toastId || toastId === undefined
|
|
107
|
+
? {
|
|
108
|
+
...t,
|
|
109
|
+
open: false,
|
|
110
|
+
}
|
|
111
|
+
: t,
|
|
112
|
+
),
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
case 'REMOVE_TOAST':
|
|
116
|
+
if (action.toastId === undefined) {
|
|
117
|
+
return {
|
|
118
|
+
...state,
|
|
119
|
+
toasts: [],
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return {
|
|
123
|
+
...state,
|
|
124
|
+
toasts: state.toasts.filter((t) => t.id !== action.toastId),
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const listeners: Array<(state: State) => void> = []
|
|
130
|
+
|
|
131
|
+
let memoryState: State = { toasts: [] }
|
|
132
|
+
|
|
133
|
+
function dispatch(action: Action) {
|
|
134
|
+
memoryState = reducer(memoryState, action)
|
|
135
|
+
listeners.forEach((listener) => {
|
|
136
|
+
listener(memoryState)
|
|
137
|
+
})
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
type Toast = Omit<ToasterToast, 'id'>
|
|
141
|
+
|
|
142
|
+
function toast({ ...props }: Toast) {
|
|
143
|
+
const id = genId()
|
|
144
|
+
|
|
145
|
+
const update = (props: ToasterToast) =>
|
|
146
|
+
dispatch({
|
|
147
|
+
type: 'UPDATE_TOAST',
|
|
148
|
+
toast: { ...props, id },
|
|
149
|
+
})
|
|
150
|
+
const dismiss = () => dispatch({ type: 'DISMISS_TOAST', toastId: id })
|
|
151
|
+
|
|
152
|
+
dispatch({
|
|
153
|
+
type: 'ADD_TOAST',
|
|
154
|
+
toast: {
|
|
155
|
+
...props,
|
|
156
|
+
id,
|
|
157
|
+
open: true,
|
|
158
|
+
onOpenChange: (open) => {
|
|
159
|
+
if (!open) dismiss()
|
|
160
|
+
},
|
|
161
|
+
},
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
return {
|
|
165
|
+
id: id,
|
|
166
|
+
dismiss,
|
|
167
|
+
update,
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function useToast() {
|
|
172
|
+
const [state, setState] = React.useState<State>(memoryState)
|
|
173
|
+
|
|
174
|
+
React.useEffect(() => {
|
|
175
|
+
listeners.push(setState)
|
|
176
|
+
return () => {
|
|
177
|
+
const index = listeners.indexOf(setState)
|
|
178
|
+
if (index > -1) {
|
|
179
|
+
listeners.splice(index, 1)
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}, [state])
|
|
183
|
+
|
|
184
|
+
return {
|
|
185
|
+
...state,
|
|
186
|
+
toast,
|
|
187
|
+
dismiss: (toastId?: string) => dispatch({ type: 'DISMISS_TOAST', toastId }),
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export { useToast, toast }
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
<svg width="180" height="180" viewBox="0 0 180 180" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<style>
|
|
3
|
+
@media (prefers-color-scheme: light) {
|
|
4
|
+
.background { fill: black; }
|
|
5
|
+
.foreground { fill: white; }
|
|
6
|
+
}
|
|
7
|
+
@media (prefers-color-scheme: dark) {
|
|
8
|
+
.background { fill: white; }
|
|
9
|
+
.foreground { fill: black; }
|
|
10
|
+
}
|
|
11
|
+
</style>
|
|
12
|
+
<g clip-path="url(#clip0_7960_43945)">
|
|
13
|
+
<rect class="background" width="180" height="180" rx="37" />
|
|
14
|
+
<g style="transform: scale(95%); transform-origin: center">
|
|
15
|
+
<path class="foreground"
|
|
16
|
+
d="M101.141 53H136.632C151.023 53 162.689 64.6662 162.689 79.0573V112.904H148.112V79.0573C148.112 78.7105 148.098 78.3662 148.072 78.0251L112.581 112.898C112.701 112.902 112.821 112.904 112.941 112.904H148.112V126.672H112.941C98.5504 126.672 86.5638 114.891 86.5638 100.5V66.7434H101.141V100.5C101.141 101.15 101.191 101.792 101.289 102.422L137.56 66.7816C137.255 66.7563 136.945 66.7434 136.632 66.7434H101.141V53Z" />
|
|
17
|
+
<path class="foreground"
|
|
18
|
+
d="M65.2926 124.136L14 66.7372H34.6355L64.7495 100.436V66.7372H80.1365V118.47C80.1365 126.278 70.4953 129.958 65.2926 124.136Z" />
|
|
19
|
+
</g>
|
|
20
|
+
</g>
|
|
21
|
+
<defs>
|
|
22
|
+
<clipPath id="clip0_7960_43945">
|
|
23
|
+
<rect width="180" height="180" fill="white" />
|
|
24
|
+
</clipPath>
|
|
25
|
+
</defs>
|
|
26
|
+
</svg>
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="215" height="48" fill="none"><path fill="#000" d="M57.588 9.6h6L73.828 38h-5.2l-2.36-6.88h-11.36L52.548 38h-5.2l10.24-28.4Zm7.16 17.16-4.16-12.16-4.16 12.16h8.32Zm23.694-2.24c-.186-1.307-.706-2.32-1.56-3.04-.853-.72-1.866-1.08-3.04-1.08-1.68 0-2.986.613-3.92 1.84-.906 1.227-1.36 2.947-1.36 5.16s.454 3.933 1.36 5.16c.934 1.227 2.24 1.84 3.92 1.84 1.254 0 2.307-.373 3.16-1.12.854-.773 1.387-1.867 1.6-3.28l5.12.24c-.186 1.68-.733 3.147-1.64 4.4-.906 1.227-2.08 2.173-3.52 2.84-1.413.667-2.986 1-4.72 1-2.08 0-3.906-.453-5.48-1.36-1.546-.907-2.76-2.2-3.64-3.88-.853-1.68-1.28-3.627-1.28-5.84 0-2.24.427-4.187 1.28-5.84.88-1.68 2.094-2.973 3.64-3.88 1.574-.907 3.4-1.36 5.48-1.36 1.68 0 3.227.32 4.64.96 1.414.64 2.56 1.56 3.44 2.76.907 1.2 1.454 2.6 1.64 4.2l-5.12.28Zm11.486-7.72.12 3.4c.534-1.227 1.307-2.173 2.32-2.84 1.04-.693 2.267-1.04 3.68-1.04 1.494 0 2.76.387 3.8 1.16 1.067.747 1.827 1.813 2.28 3.2.507-1.44 1.294-2.52 2.36-3.24 1.094-.747 2.414-1.12 3.96-1.12 1.414 0 2.64.307 3.68.92s1.84 1.52 2.4 2.72c.56 1.2.84 2.667.84 4.4V38h-4.96V25.92c0-1.813-.293-3.187-.88-4.12-.56-.96-1.413-1.44-2.56-1.44-.906 0-1.68.213-2.32.64-.64.427-1.133 1.053-1.48 1.88-.32.827-.48 1.84-.48 3.04V38h-4.56V25.92c0-1.2-.133-2.213-.4-3.04-.24-.827-.626-1.453-1.16-1.88-.506-.427-1.133-.64-1.88-.64-.906 0-1.68.227-2.32.68-.64.427-1.133 1.053-1.48 1.88-.32.827-.48 1.827-.48 3V38h-4.96V16.8h4.48Zm26.723 10.6c0-2.24.427-4.187 1.28-5.84.854-1.68 2.067-2.973 3.64-3.88 1.574-.907 3.4-1.36 5.48-1.36 1.84 0 3.494.413 4.96 1.24 1.467.827 2.64 2.08 3.52 3.76.88 1.653 1.347 3.693 1.4 6.12v1.32h-15.08c.107 1.813.614 3.227 1.52 4.24.907.987 2.134 1.48 3.68 1.48.987 0 1.88-.253 2.68-.76a4.803 4.803 0 0 0 1.84-2.2l5.08.36c-.64 2.027-1.84 3.64-3.6 4.84-1.733 1.173-3.733 1.76-6 1.76-2.08 0-3.906-.453-5.48-1.36-1.573-.907-2.786-2.2-3.64-3.88-.853-1.68-1.28-3.627-1.28-5.84Zm15.16-2.04c-.213-1.733-.76-3.013-1.64-3.84-.853-.827-1.893-1.24-3.12-1.24-1.44 0-2.6.453-3.48 1.36-.88.88-1.44 2.12-1.68 3.72h9.92ZM163.139 9.6V38h-5.04V9.6h5.04Zm8.322 7.2.24 5.88-.64-.36c.32-2.053 1.094-3.56 2.32-4.52 1.254-.987 2.787-1.48 4.6-1.48 2.32 0 4.107.733 5.36 2.2 1.254 1.44 1.88 3.387 1.88 5.84V38h-4.96V25.92c0-1.253-.12-2.28-.36-3.08-.24-.8-.64-1.413-1.2-1.84-.533-.427-1.253-.64-2.16-.64-1.44 0-2.573.48-3.4 1.44-.8.933-1.2 2.307-1.2 4.12V38h-4.96V16.8h4.48Zm30.003 7.72c-.186-1.307-.706-2.32-1.56-3.04-.853-.72-1.866-1.08-3.04-1.08-1.68 0-2.986.613-3.92 1.84-.906 1.227-1.36 2.947-1.36 5.16s.454 3.933 1.36 5.16c.934 1.227 2.24 1.84 3.92 1.84 1.254 0 2.307-.373 3.16-1.12.854-.773 1.387-1.867 1.6-3.28l5.12.24c-.186 1.68-.733 3.147-1.64 4.4-.906 1.227-2.08 2.173-3.52 2.84-1.413.667-2.986 1-4.72 1-2.08 0-3.906-.453-5.48-1.36-1.546-.907-2.76-2.2-3.64-3.88-.853-1.68-1.28-3.627-1.28-5.84 0-2.24.427-4.187 1.28-5.84.88-1.68 2.094-2.973 3.64-3.88 1.574-.907 3.4-1.36 5.48-1.36 1.68 0 3.227.32 4.64.96 1.414.64 2.56 1.56 3.44 2.76.907 1.2 1.454 2.6 1.64 4.2l-5.12.28Zm11.443 8.16V38h-5.6v-5.32h5.6Z"/><path fill="#171717" fill-rule="evenodd" d="m7.839 40.783 16.03-28.054L20 6 0 40.783h7.839Zm8.214 0H40L27.99 19.894l-4.02 7.032 3.976 6.914H20.02l-3.967 6.943Z" clip-rule="evenodd"/></svg>
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="1200" fill="none"><rect width="1200" height="1200" fill="#EAEAEA" rx="3"/><g opacity=".5"><g opacity=".5"><path fill="#FAFAFA" d="M600.709 736.5c-75.454 0-136.621-61.167-136.621-136.62 0-75.454 61.167-136.621 136.621-136.621 75.453 0 136.62 61.167 136.62 136.621 0 75.453-61.167 136.62-136.62 136.62Z"/><path stroke="#C9C9C9" stroke-width="2.418" d="M600.709 736.5c-75.454 0-136.621-61.167-136.621-136.62 0-75.454 61.167-136.621 136.621-136.621 75.453 0 136.62 61.167 136.62 136.621 0 75.453-61.167 136.62-136.62 136.62Z"/></g><path stroke="url(#a)" stroke-width="2.418" d="M0-1.209h553.581" transform="scale(1 -1) rotate(45 1163.11 91.165)"/><path stroke="url(#b)" stroke-width="2.418" d="M404.846 598.671h391.726"/><path stroke="url(#c)" stroke-width="2.418" d="M599.5 795.742V404.017"/><path stroke="url(#d)" stroke-width="2.418" d="m795.717 796.597-391.441-391.44"/><path fill="#fff" d="M600.709 656.704c-31.384 0-56.825-25.441-56.825-56.824 0-31.384 25.441-56.825 56.825-56.825 31.383 0 56.824 25.441 56.824 56.825 0 31.383-25.441 56.824-56.824 56.824Z"/><g clip-path="url(#e)"><path fill="#666" fill-rule="evenodd" d="M616.426 586.58h-31.434v16.176l3.553-3.554.531-.531h9.068l.074-.074 8.463-8.463h2.565l7.18 7.181V586.58Zm-15.715 14.654 3.698 3.699 1.283 1.282-2.565 2.565-1.282-1.283-5.2-5.199h-6.066l-5.514 5.514-.073.073v2.876a2.418 2.418 0 0 0 2.418 2.418h26.598a2.418 2.418 0 0 0 2.418-2.418v-8.317l-8.463-8.463-7.181 7.181-.071.072Zm-19.347 5.442v4.085a6.045 6.045 0 0 0 6.046 6.045h26.598a6.044 6.044 0 0 0 6.045-6.045v-7.108l1.356-1.355-1.282-1.283-.074-.073v-17.989h-38.689v23.43l-.146.146.146.147Z" clip-rule="evenodd"/></g><path stroke="#C9C9C9" stroke-width="2.418" d="M600.709 656.704c-31.384 0-56.825-25.441-56.825-56.824 0-31.384 25.441-56.825 56.825-56.825 31.383 0 56.824 25.441 56.824 56.825 0 31.383-25.441 56.824-56.824 56.824Z"/></g><defs><linearGradient id="a" x1="554.061" x2="-.48" y1=".083" y2=".087" gradientUnits="userSpaceOnUse"><stop stop-color="#C9C9C9" stop-opacity="0"/><stop offset=".208" stop-color="#C9C9C9"/><stop offset=".792" stop-color="#C9C9C9"/><stop offset="1" stop-color="#C9C9C9" stop-opacity="0"/></linearGradient><linearGradient id="b" x1="796.912" x2="404.507" y1="599.963" y2="599.965" gradientUnits="userSpaceOnUse"><stop stop-color="#C9C9C9" stop-opacity="0"/><stop offset=".208" stop-color="#C9C9C9"/><stop offset=".792" stop-color="#C9C9C9"/><stop offset="1" stop-color="#C9C9C9" stop-opacity="0"/></linearGradient><linearGradient id="c" x1="600.792" x2="600.794" y1="403.677" y2="796.082" gradientUnits="userSpaceOnUse"><stop stop-color="#C9C9C9" stop-opacity="0"/><stop offset=".208" stop-color="#C9C9C9"/><stop offset=".792" stop-color="#C9C9C9"/><stop offset="1" stop-color="#C9C9C9" stop-opacity="0"/></linearGradient><linearGradient id="d" x1="404.85" x2="796.972" y1="403.903" y2="796.02" gradientUnits="userSpaceOnUse"><stop stop-color="#C9C9C9" stop-opacity="0"/><stop offset=".208" stop-color="#C9C9C9"/><stop offset=".792" stop-color="#C9C9C9"/><stop offset="1" stop-color="#C9C9C9" stop-opacity="0"/></linearGradient><clipPath id="e"><path fill="#fff" d="M581.364 580.535h38.689v38.689h-38.689z"/></clipPath></defs></svg>
|