@nous-research/ui 0.16.0 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +39 -0
- package/dist/hooks/use-below-breakpoint.d.ts +2 -0
- package/dist/hooks/use-below-breakpoint.js +17 -0
- package/dist/hooks/use-confirm-delete.d.ts +10 -0
- package/dist/hooks/use-confirm-delete.js +35 -0
- package/dist/hooks/use-toast.d.ts +7 -0
- package/dist/hooks/use-toast.js +21 -0
- package/dist/index.d.ts +11 -1
- package/dist/index.js +23 -2
- package/dist/ui/components/bottom-sheet.d.ts +15 -0
- package/dist/ui/components/bottom-sheet.js +192 -0
- package/dist/ui/components/card.d.ts +5 -0
- package/dist/ui/components/card.js +74 -0
- package/dist/ui/components/checkbox.d.ts +1 -1
- package/dist/ui/components/checkbox.js +2 -2
- package/dist/ui/components/confirm-dialog.d.ts +13 -0
- package/dist/ui/components/confirm-dialog.js +113 -0
- package/dist/ui/components/dialog.d.ts +15 -0
- package/dist/ui/components/dialog.js +171 -0
- package/dist/ui/components/input.d.ts +1 -0
- package/dist/ui/components/input.js +21 -0
- package/dist/ui/components/label.d.ts +1 -0
- package/dist/ui/components/label.js +18 -0
- package/dist/ui/components/separator.d.ts +5 -0
- package/dist/ui/components/separator.js +22 -0
- package/dist/ui/components/toast.d.ts +8 -0
- package/dist/ui/components/toast.js +39 -0
- package/dist/ui/globals.css +14 -2
- package/package.json +2 -2
- package/src/hooks/use-below-breakpoint.ts +21 -0
- package/src/hooks/use-confirm-delete.ts +43 -0
- package/src/hooks/use-toast.ts +29 -0
- package/src/index.ts +22 -1
- package/src/ui/components/animated-count.stories.tsx +1 -1
- package/src/ui/components/ascii.stories.tsx +1 -1
- package/src/ui/components/badge.stories.tsx +1 -1
- package/src/ui/components/blend-mode.stories.tsx +1 -1
- package/src/ui/components/blink.stories.tsx +1 -1
- package/src/ui/components/bottom-sheet.stories.tsx +43 -0
- package/src/ui/components/bottom-sheet.tsx +227 -0
- package/src/ui/components/button.stories.tsx +1 -1
- package/src/ui/components/card.stories.tsx +63 -0
- package/src/ui/components/card.tsx +85 -0
- package/src/ui/components/checkbox.stories.tsx +1 -1
- package/src/ui/components/checkbox.tsx +1 -1
- package/src/ui/components/command-block.stories.tsx +1 -1
- package/src/ui/components/confirm-dialog.stories.tsx +91 -0
- package/src/ui/components/confirm-dialog.tsx +130 -0
- package/src/ui/components/dialog.stories.tsx +169 -0
- package/src/ui/components/dialog.tsx +177 -0
- package/src/ui/components/dropdown-menu.stories.tsx +1 -1
- package/src/ui/components/fit-text/index.stories.tsx +1 -1
- package/src/ui/components/forms.stories.tsx +173 -0
- package/src/ui/components/graphs/index.stories.tsx +1 -1
- package/src/ui/components/hover-bg.stories.tsx +1 -1
- package/src/ui/components/image-distortion.stories.tsx +1 -1
- package/src/ui/components/input.stories.tsx +39 -0
- package/src/ui/components/input.tsx +20 -0
- package/src/ui/components/label.stories.tsx +26 -0
- package/src/ui/components/label.tsx +16 -0
- package/src/ui/components/list-item.stories.tsx +1 -1
- package/src/ui/components/poster.stories.tsx +1 -1
- package/src/ui/components/progress.stories.tsx +1 -1
- package/src/ui/components/scramble.stories.tsx +1 -1
- package/src/ui/components/segmented.stories.tsx +1 -1
- package/src/ui/components/select.stories.tsx +1 -1
- package/src/ui/components/separator.stories.tsx +33 -0
- package/src/ui/components/separator.tsx +24 -0
- package/src/ui/components/spinner.stories.tsx +1 -1
- package/src/ui/components/stats.stories.tsx +1 -1
- package/src/ui/components/switch.stories.tsx +1 -1
- package/src/ui/components/tabs.stories.tsx +1 -1
- package/src/ui/components/terminal-demo.stories.tsx +1 -1
- package/src/ui/components/theme-toggle.stories.tsx +1 -1
- package/src/ui/components/tier-card.stories.tsx +1 -1
- package/src/ui/components/toast.stories.tsx +55 -0
- package/src/ui/components/toast.tsx +49 -0
- package/src/ui/components/tv.stories.tsx +1 -1
- package/src/ui/components/watchlist.stories.tsx +1 -1
- package/src/ui/globals.css +14 -2
- package/dist/ui/components/modal/index.d.ts +0 -8
- package/dist/ui/components/modal/index.js +0 -35
- package/dist/ui/components/modal/modal.css +0 -36
- package/src/ui/components/modal/index.stories.tsx +0 -46
- package/src/ui/components/modal/index.tsx +0 -48
- package/src/ui/components/modal/modal.css +0 -36
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
2
|
+
import { useState } from 'react'
|
|
3
|
+
|
|
4
|
+
import { Button } from './button'
|
|
5
|
+
import { ConfirmDialog } from './confirm-dialog'
|
|
6
|
+
|
|
7
|
+
const meta: Meta<typeof ConfirmDialog> = {
|
|
8
|
+
component: ConfirmDialog,
|
|
9
|
+
title: 'Components/Overlays/ConfirmDialog'
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export default meta
|
|
13
|
+
|
|
14
|
+
type Story = StoryObj<typeof ConfirmDialog>
|
|
15
|
+
|
|
16
|
+
export const Default: Story = {
|
|
17
|
+
render: () => {
|
|
18
|
+
function Demo() {
|
|
19
|
+
const [open, setOpen] = useState(false)
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<>
|
|
23
|
+
<Button onClick={() => setOpen(true)}>Open dialog</Button>
|
|
24
|
+
|
|
25
|
+
<ConfirmDialog
|
|
26
|
+
description="This action cannot be undone."
|
|
27
|
+
onCancel={() => setOpen(false)}
|
|
28
|
+
onConfirm={() => setOpen(false)}
|
|
29
|
+
open={open}
|
|
30
|
+
title="Are you sure?"
|
|
31
|
+
/>
|
|
32
|
+
</>
|
|
33
|
+
)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return <Demo />
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export const Destructive: Story = {
|
|
41
|
+
render: () => {
|
|
42
|
+
function Demo() {
|
|
43
|
+
const [open, setOpen] = useState(false)
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<>
|
|
47
|
+
<Button destructive onClick={() => setOpen(true)}>
|
|
48
|
+
Delete item
|
|
49
|
+
</Button>
|
|
50
|
+
|
|
51
|
+
<ConfirmDialog
|
|
52
|
+
confirmLabel="Delete"
|
|
53
|
+
description="This will permanently delete the item. This action cannot be undone."
|
|
54
|
+
destructive
|
|
55
|
+
onCancel={() => setOpen(false)}
|
|
56
|
+
onConfirm={() => setOpen(false)}
|
|
57
|
+
open={open}
|
|
58
|
+
title="Delete item?"
|
|
59
|
+
/>
|
|
60
|
+
</>
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return <Demo />
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export const Loading: Story = {
|
|
69
|
+
render: () => {
|
|
70
|
+
function Demo() {
|
|
71
|
+
const [open, setOpen] = useState(false)
|
|
72
|
+
|
|
73
|
+
return (
|
|
74
|
+
<>
|
|
75
|
+
<Button onClick={() => setOpen(true)}>With loading state</Button>
|
|
76
|
+
|
|
77
|
+
<ConfirmDialog
|
|
78
|
+
description="Simulating a loading state."
|
|
79
|
+
loading
|
|
80
|
+
onCancel={() => setOpen(false)}
|
|
81
|
+
onConfirm={() => {}}
|
|
82
|
+
open={open}
|
|
83
|
+
title="Processing…"
|
|
84
|
+
/>
|
|
85
|
+
</>
|
|
86
|
+
)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return <Demo />
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useRef } from 'react'
|
|
4
|
+
import { AlertDialog as AlertDialogPrimitive } from 'radix-ui'
|
|
5
|
+
|
|
6
|
+
import { cn } from '../../utils'
|
|
7
|
+
import { Button } from './button'
|
|
8
|
+
|
|
9
|
+
function WarningTriangle({ className }: { className?: string }) {
|
|
10
|
+
return (
|
|
11
|
+
<svg
|
|
12
|
+
aria-hidden
|
|
13
|
+
className={className}
|
|
14
|
+
fill="none"
|
|
15
|
+
stroke="currentColor"
|
|
16
|
+
strokeLinecap="round"
|
|
17
|
+
strokeLinejoin="round"
|
|
18
|
+
strokeWidth={2}
|
|
19
|
+
viewBox="0 0 24 24"
|
|
20
|
+
>
|
|
21
|
+
<path d="m10.29 3.86-8.16 14a2 2 0 0 0 1.73 3h16.28a2 2 0 0 0 1.73-3l-8.16-14a2 2 0 0 0-3.46 0z" />
|
|
22
|
+
<line x1="12" x2="12" y1="9" y2="13" />
|
|
23
|
+
<line x1="12" x2="12.01" y1="17" y2="17" />
|
|
24
|
+
</svg>
|
|
25
|
+
)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function ConfirmDialog({
|
|
29
|
+
cancelLabel = 'Cancel',
|
|
30
|
+
confirmLabel = 'Confirm',
|
|
31
|
+
description,
|
|
32
|
+
destructive = false,
|
|
33
|
+
loading = false,
|
|
34
|
+
onCancel,
|
|
35
|
+
onConfirm,
|
|
36
|
+
open,
|
|
37
|
+
title
|
|
38
|
+
}: ConfirmDialogProps) {
|
|
39
|
+
const confirmedRef = useRef(false)
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<AlertDialogPrimitive.Root
|
|
43
|
+
onOpenChange={v => {
|
|
44
|
+
if (!v && !confirmedRef.current) onCancel()
|
|
45
|
+
confirmedRef.current = false
|
|
46
|
+
}}
|
|
47
|
+
open={open}
|
|
48
|
+
>
|
|
49
|
+
<AlertDialogPrimitive.Portal>
|
|
50
|
+
<AlertDialogPrimitive.Overlay
|
|
51
|
+
className={cn(
|
|
52
|
+
'fixed inset-0 z-50',
|
|
53
|
+
'bg-black/60 backdrop-blur-sm',
|
|
54
|
+
'data-[state=open]:animate-in data-[state=open]:fade-in-0',
|
|
55
|
+
'data-[state=closed]:animate-out data-[state=closed]:fade-out-0'
|
|
56
|
+
)}
|
|
57
|
+
/>
|
|
58
|
+
|
|
59
|
+
<AlertDialogPrimitive.Content
|
|
60
|
+
className={cn(
|
|
61
|
+
'fixed top-1/2 left-1/2 z-50 -translate-x-1/2 -translate-y-1/2',
|
|
62
|
+
'w-[calc(100%-2rem)] max-w-md',
|
|
63
|
+
'border border-midground/15 bg-background-base text-foreground-base shadow-lg outline-none',
|
|
64
|
+
'data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95',
|
|
65
|
+
'data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95',
|
|
66
|
+
'duration-150'
|
|
67
|
+
)}
|
|
68
|
+
>
|
|
69
|
+
<div className="flex items-start gap-3 p-4 border-b border-midground/15">
|
|
70
|
+
{destructive && (
|
|
71
|
+
<div aria-hidden className="mt-0.5 shrink-0 text-destructive">
|
|
72
|
+
<WarningTriangle className="h-4 w-4" />
|
|
73
|
+
</div>
|
|
74
|
+
)}
|
|
75
|
+
|
|
76
|
+
<div className="flex-1 min-w-0 flex flex-col gap-1">
|
|
77
|
+
<AlertDialogPrimitive.Title
|
|
78
|
+
className="font-expanded text-sm font-bold tracking-[0.08em] uppercase"
|
|
79
|
+
>
|
|
80
|
+
{title}
|
|
81
|
+
</AlertDialogPrimitive.Title>
|
|
82
|
+
|
|
83
|
+
{description && (
|
|
84
|
+
<AlertDialogPrimitive.Description
|
|
85
|
+
className="font-mondwest text-xs text-midground/60 leading-relaxed"
|
|
86
|
+
>
|
|
87
|
+
{description}
|
|
88
|
+
</AlertDialogPrimitive.Description>
|
|
89
|
+
)}
|
|
90
|
+
</div>
|
|
91
|
+
</div>
|
|
92
|
+
|
|
93
|
+
<div className="flex items-center justify-end gap-2 p-3">
|
|
94
|
+
<AlertDialogPrimitive.Cancel asChild>
|
|
95
|
+
<Button disabled={loading} outlined type="button">
|
|
96
|
+
{cancelLabel}
|
|
97
|
+
</Button>
|
|
98
|
+
</AlertDialogPrimitive.Cancel>
|
|
99
|
+
|
|
100
|
+
<AlertDialogPrimitive.Action asChild>
|
|
101
|
+
<Button
|
|
102
|
+
destructive={destructive}
|
|
103
|
+
disabled={loading}
|
|
104
|
+
onClick={() => {
|
|
105
|
+
confirmedRef.current = true
|
|
106
|
+
onConfirm()
|
|
107
|
+
}}
|
|
108
|
+
type="button"
|
|
109
|
+
>
|
|
110
|
+
{loading ? '…' : confirmLabel}
|
|
111
|
+
</Button>
|
|
112
|
+
</AlertDialogPrimitive.Action>
|
|
113
|
+
</div>
|
|
114
|
+
</AlertDialogPrimitive.Content>
|
|
115
|
+
</AlertDialogPrimitive.Portal>
|
|
116
|
+
</AlertDialogPrimitive.Root>
|
|
117
|
+
)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
interface ConfirmDialogProps {
|
|
121
|
+
cancelLabel?: string
|
|
122
|
+
confirmLabel?: string
|
|
123
|
+
description?: string
|
|
124
|
+
destructive?: boolean
|
|
125
|
+
loading?: boolean
|
|
126
|
+
onCancel: () => void
|
|
127
|
+
onConfirm: () => void
|
|
128
|
+
open: boolean
|
|
129
|
+
title: string
|
|
130
|
+
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite'
|
|
2
|
+
import { useState } from 'react'
|
|
3
|
+
|
|
4
|
+
import { Button } from './button'
|
|
5
|
+
import {
|
|
6
|
+
Dialog,
|
|
7
|
+
DialogClose,
|
|
8
|
+
DialogContent,
|
|
9
|
+
DialogDescription,
|
|
10
|
+
DialogFooter,
|
|
11
|
+
DialogHeader,
|
|
12
|
+
DialogTitle,
|
|
13
|
+
DialogTrigger
|
|
14
|
+
} from './dialog'
|
|
15
|
+
import { Input } from './input'
|
|
16
|
+
import { Label } from './label'
|
|
17
|
+
|
|
18
|
+
const meta: Meta<typeof Dialog> = {
|
|
19
|
+
component: Dialog,
|
|
20
|
+
title: 'Components/Overlays/Dialog'
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export default meta
|
|
24
|
+
|
|
25
|
+
type Story = StoryObj<typeof Dialog>
|
|
26
|
+
|
|
27
|
+
export const Default: Story = {
|
|
28
|
+
render: () => (
|
|
29
|
+
<Dialog>
|
|
30
|
+
<DialogTrigger asChild>
|
|
31
|
+
<Button>Open Dialog</Button>
|
|
32
|
+
</DialogTrigger>
|
|
33
|
+
|
|
34
|
+
<DialogContent>
|
|
35
|
+
<DialogHeader>
|
|
36
|
+
<DialogTitle>Dialog Title</DialogTitle>
|
|
37
|
+
<DialogDescription>
|
|
38
|
+
A description of the dialog content and its purpose.
|
|
39
|
+
</DialogDescription>
|
|
40
|
+
</DialogHeader>
|
|
41
|
+
|
|
42
|
+
<div className="p-4">
|
|
43
|
+
<p className="font-courier text-sm text-midground/80">
|
|
44
|
+
This is a general-purpose dialog built on Radix UI primitives. It
|
|
45
|
+
handles focus trapping, ESC to close, and backdrop click
|
|
46
|
+
automatically.
|
|
47
|
+
</p>
|
|
48
|
+
</div>
|
|
49
|
+
|
|
50
|
+
<DialogFooter>
|
|
51
|
+
<DialogClose asChild>
|
|
52
|
+
<Button outlined>Close</Button>
|
|
53
|
+
</DialogClose>
|
|
54
|
+
</DialogFooter>
|
|
55
|
+
</DialogContent>
|
|
56
|
+
</Dialog>
|
|
57
|
+
)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export const Controlled: Story = {
|
|
61
|
+
render: () => {
|
|
62
|
+
function Demo() {
|
|
63
|
+
const [open, setOpen] = useState(false)
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<>
|
|
67
|
+
<Button onClick={() => setOpen(true)}>Controlled Open</Button>
|
|
68
|
+
|
|
69
|
+
<Dialog onOpenChange={setOpen} open={open}>
|
|
70
|
+
<DialogContent>
|
|
71
|
+
<DialogHeader>
|
|
72
|
+
<DialogTitle>Controlled Dialog</DialogTitle>
|
|
73
|
+
<DialogDescription>
|
|
74
|
+
This dialog is controlled via external state.
|
|
75
|
+
</DialogDescription>
|
|
76
|
+
</DialogHeader>
|
|
77
|
+
|
|
78
|
+
<div className="p-4">
|
|
79
|
+
<p className="font-courier text-sm text-midground/80">
|
|
80
|
+
Open state is managed by the parent component. Useful when
|
|
81
|
+
you need to open the dialog programmatically.
|
|
82
|
+
</p>
|
|
83
|
+
</div>
|
|
84
|
+
|
|
85
|
+
<DialogFooter>
|
|
86
|
+
<Button onClick={() => setOpen(false)} outlined>
|
|
87
|
+
Cancel
|
|
88
|
+
</Button>
|
|
89
|
+
|
|
90
|
+
<Button onClick={() => setOpen(false)}>
|
|
91
|
+
Save
|
|
92
|
+
</Button>
|
|
93
|
+
</DialogFooter>
|
|
94
|
+
</DialogContent>
|
|
95
|
+
</Dialog>
|
|
96
|
+
</>
|
|
97
|
+
)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return <Demo />
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export const WithForm: Story = {
|
|
105
|
+
render: () => (
|
|
106
|
+
<Dialog>
|
|
107
|
+
<DialogTrigger asChild>
|
|
108
|
+
<Button>Edit Profile</Button>
|
|
109
|
+
</DialogTrigger>
|
|
110
|
+
|
|
111
|
+
<DialogContent>
|
|
112
|
+
<DialogHeader>
|
|
113
|
+
<DialogTitle>Edit Profile</DialogTitle>
|
|
114
|
+
<DialogDescription>
|
|
115
|
+
Make changes to your profile. Click save when you are done.
|
|
116
|
+
</DialogDescription>
|
|
117
|
+
</DialogHeader>
|
|
118
|
+
|
|
119
|
+
<div className="flex flex-col gap-4 p-4">
|
|
120
|
+
<div className="flex flex-col gap-1.5">
|
|
121
|
+
<Label htmlFor="name">Name</Label>
|
|
122
|
+
<Input defaultValue="Hermes" id="name" />
|
|
123
|
+
</div>
|
|
124
|
+
|
|
125
|
+
<div className="flex flex-col gap-1.5">
|
|
126
|
+
<Label htmlFor="email">Email</Label>
|
|
127
|
+
<Input defaultValue="hermes@nousresearch.com" id="email" type="email" />
|
|
128
|
+
</div>
|
|
129
|
+
</div>
|
|
130
|
+
|
|
131
|
+
<DialogFooter>
|
|
132
|
+
<DialogClose asChild>
|
|
133
|
+
<Button outlined>Cancel</Button>
|
|
134
|
+
</DialogClose>
|
|
135
|
+
|
|
136
|
+
<DialogClose asChild>
|
|
137
|
+
<Button>Save Changes</Button>
|
|
138
|
+
</DialogClose>
|
|
139
|
+
</DialogFooter>
|
|
140
|
+
</DialogContent>
|
|
141
|
+
</Dialog>
|
|
142
|
+
)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export const NoCloseButton: Story = {
|
|
146
|
+
render: () => (
|
|
147
|
+
<Dialog>
|
|
148
|
+
<DialogTrigger asChild>
|
|
149
|
+
<Button>Without Close Button</Button>
|
|
150
|
+
</DialogTrigger>
|
|
151
|
+
|
|
152
|
+
<DialogContent showCloseButton={false}>
|
|
153
|
+
<DialogHeader>
|
|
154
|
+
<DialogTitle>Minimal Dialog</DialogTitle>
|
|
155
|
+
<DialogDescription>
|
|
156
|
+
This dialog hides the X close button. Users can still close it
|
|
157
|
+
by pressing ESC or clicking the backdrop.
|
|
158
|
+
</DialogDescription>
|
|
159
|
+
</DialogHeader>
|
|
160
|
+
|
|
161
|
+
<DialogFooter>
|
|
162
|
+
<DialogClose asChild>
|
|
163
|
+
<Button>Got it</Button>
|
|
164
|
+
</DialogClose>
|
|
165
|
+
</DialogFooter>
|
|
166
|
+
</DialogContent>
|
|
167
|
+
</Dialog>
|
|
168
|
+
)
|
|
169
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import * as React from 'react'
|
|
4
|
+
import { Dialog as DialogPrimitive } from 'radix-ui'
|
|
5
|
+
|
|
6
|
+
import { cn } from '../../utils'
|
|
7
|
+
|
|
8
|
+
function Dialog({ ...props }: React.ComponentProps<typeof DialogPrimitive.Root>) {
|
|
9
|
+
return <DialogPrimitive.Root data-slot="dialog" {...props} />
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function DialogTrigger({ ...props }: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
|
|
13
|
+
return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function DialogPortal({ ...props }: React.ComponentProps<typeof DialogPrimitive.Portal>) {
|
|
17
|
+
return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function DialogClose({ ...props }: React.ComponentProps<typeof DialogPrimitive.Close>) {
|
|
21
|
+
return <DialogPrimitive.Close data-slot="dialog-close" {...props} />
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function DialogOverlay({
|
|
25
|
+
className,
|
|
26
|
+
...props
|
|
27
|
+
}: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
|
|
28
|
+
return (
|
|
29
|
+
<DialogPrimitive.Overlay
|
|
30
|
+
className={cn(
|
|
31
|
+
'fixed inset-0 z-50',
|
|
32
|
+
'bg-black/60 backdrop-blur-sm',
|
|
33
|
+
'data-[state=open]:animate-in data-[state=open]:fade-in-0',
|
|
34
|
+
'data-[state=closed]:animate-out data-[state=closed]:fade-out-0',
|
|
35
|
+
className
|
|
36
|
+
)}
|
|
37
|
+
data-slot="dialog-overlay"
|
|
38
|
+
{...props}
|
|
39
|
+
/>
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function DialogContent({
|
|
44
|
+
className,
|
|
45
|
+
children,
|
|
46
|
+
showCloseButton = true,
|
|
47
|
+
...props
|
|
48
|
+
}: React.ComponentProps<typeof DialogPrimitive.Content> & {
|
|
49
|
+
showCloseButton?: boolean
|
|
50
|
+
}) {
|
|
51
|
+
return (
|
|
52
|
+
<DialogPortal>
|
|
53
|
+
<DialogOverlay />
|
|
54
|
+
|
|
55
|
+
<DialogPrimitive.Content
|
|
56
|
+
className={cn(
|
|
57
|
+
'fixed top-1/2 left-1/2 z-50 -translate-x-1/2 -translate-y-1/2',
|
|
58
|
+
'grid w-full max-w-md gap-0',
|
|
59
|
+
'border border-midground/15 bg-background-base text-foreground-base shadow-lg outline-none',
|
|
60
|
+
'data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95',
|
|
61
|
+
'data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95',
|
|
62
|
+
'duration-150',
|
|
63
|
+
className
|
|
64
|
+
)}
|
|
65
|
+
data-slot="dialog-content"
|
|
66
|
+
{...props}
|
|
67
|
+
>
|
|
68
|
+
{children}
|
|
69
|
+
|
|
70
|
+
{showCloseButton && (
|
|
71
|
+
<DialogPrimitive.Close
|
|
72
|
+
className={cn(
|
|
73
|
+
'absolute top-3 right-3',
|
|
74
|
+
'flex h-6 w-6 items-center justify-center',
|
|
75
|
+
'text-midground/50 transition-colors hover:text-midground',
|
|
76
|
+
'focus:outline-none focus-visible:ring-1 focus-visible:ring-midground/30',
|
|
77
|
+
'disabled:pointer-events-none'
|
|
78
|
+
)}
|
|
79
|
+
data-slot="dialog-close"
|
|
80
|
+
>
|
|
81
|
+
<XIcon className="h-3.5 w-3.5" />
|
|
82
|
+
<span className="sr-only">Close</span>
|
|
83
|
+
</DialogPrimitive.Close>
|
|
84
|
+
)}
|
|
85
|
+
</DialogPrimitive.Content>
|
|
86
|
+
</DialogPortal>
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function DialogHeader({ className, ...props }: React.ComponentProps<'div'>) {
|
|
91
|
+
return (
|
|
92
|
+
<div
|
|
93
|
+
className={cn(
|
|
94
|
+
'flex flex-col gap-1 p-4 border-b border-midground/15',
|
|
95
|
+
className
|
|
96
|
+
)}
|
|
97
|
+
data-slot="dialog-header"
|
|
98
|
+
{...props}
|
|
99
|
+
/>
|
|
100
|
+
)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function DialogFooter({ className, ...props }: React.ComponentProps<'div'>) {
|
|
104
|
+
return (
|
|
105
|
+
<div
|
|
106
|
+
className={cn(
|
|
107
|
+
'flex items-center justify-end gap-2 p-3',
|
|
108
|
+
className
|
|
109
|
+
)}
|
|
110
|
+
data-slot="dialog-footer"
|
|
111
|
+
{...props}
|
|
112
|
+
/>
|
|
113
|
+
)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function DialogTitle({
|
|
117
|
+
className,
|
|
118
|
+
...props
|
|
119
|
+
}: React.ComponentProps<typeof DialogPrimitive.Title>) {
|
|
120
|
+
return (
|
|
121
|
+
<DialogPrimitive.Title
|
|
122
|
+
className={cn(
|
|
123
|
+
'font-expanded text-sm font-bold tracking-[0.08em] uppercase',
|
|
124
|
+
className
|
|
125
|
+
)}
|
|
126
|
+
data-slot="dialog-title"
|
|
127
|
+
{...props}
|
|
128
|
+
/>
|
|
129
|
+
)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function DialogDescription({
|
|
133
|
+
className,
|
|
134
|
+
...props
|
|
135
|
+
}: React.ComponentProps<typeof DialogPrimitive.Description>) {
|
|
136
|
+
return (
|
|
137
|
+
<DialogPrimitive.Description
|
|
138
|
+
className={cn(
|
|
139
|
+
'font-mondwest text-xs text-midground/60 leading-relaxed',
|
|
140
|
+
className
|
|
141
|
+
)}
|
|
142
|
+
data-slot="dialog-description"
|
|
143
|
+
{...props}
|
|
144
|
+
/>
|
|
145
|
+
)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function XIcon({ className }: { className?: string }) {
|
|
149
|
+
return (
|
|
150
|
+
<svg
|
|
151
|
+
aria-hidden
|
|
152
|
+
className={className}
|
|
153
|
+
fill="none"
|
|
154
|
+
stroke="currentColor"
|
|
155
|
+
strokeLinecap="round"
|
|
156
|
+
strokeLinejoin="round"
|
|
157
|
+
strokeWidth={2}
|
|
158
|
+
viewBox="0 0 24 24"
|
|
159
|
+
>
|
|
160
|
+
<line x1="18" x2="6" y1="6" y2="18" />
|
|
161
|
+
<line x1="6" x2="18" y1="6" y2="18" />
|
|
162
|
+
</svg>
|
|
163
|
+
)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export {
|
|
167
|
+
Dialog,
|
|
168
|
+
DialogClose,
|
|
169
|
+
DialogContent,
|
|
170
|
+
DialogDescription,
|
|
171
|
+
DialogFooter,
|
|
172
|
+
DialogHeader,
|
|
173
|
+
DialogOverlay,
|
|
174
|
+
DialogPortal,
|
|
175
|
+
DialogTitle,
|
|
176
|
+
DialogTrigger
|
|
177
|
+
}
|
|
@@ -25,7 +25,7 @@ function Demo({ direction }: { direction: 'down' | 'left' | 'right' | 'up' }) {
|
|
|
25
25
|
|
|
26
26
|
const meta: Meta<typeof DropdownMenu> = {
|
|
27
27
|
component: DropdownMenu,
|
|
28
|
-
title: 'Components/DropdownMenu'
|
|
28
|
+
title: 'Components/Overlays/DropdownMenu'
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
export default meta
|