@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.
Files changed (86) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/dist/hooks/use-below-breakpoint.d.ts +2 -0
  3. package/dist/hooks/use-below-breakpoint.js +17 -0
  4. package/dist/hooks/use-confirm-delete.d.ts +10 -0
  5. package/dist/hooks/use-confirm-delete.js +35 -0
  6. package/dist/hooks/use-toast.d.ts +7 -0
  7. package/dist/hooks/use-toast.js +21 -0
  8. package/dist/index.d.ts +11 -1
  9. package/dist/index.js +23 -2
  10. package/dist/ui/components/bottom-sheet.d.ts +15 -0
  11. package/dist/ui/components/bottom-sheet.js +192 -0
  12. package/dist/ui/components/card.d.ts +5 -0
  13. package/dist/ui/components/card.js +74 -0
  14. package/dist/ui/components/checkbox.d.ts +1 -1
  15. package/dist/ui/components/checkbox.js +2 -2
  16. package/dist/ui/components/confirm-dialog.d.ts +13 -0
  17. package/dist/ui/components/confirm-dialog.js +113 -0
  18. package/dist/ui/components/dialog.d.ts +15 -0
  19. package/dist/ui/components/dialog.js +171 -0
  20. package/dist/ui/components/input.d.ts +1 -0
  21. package/dist/ui/components/input.js +21 -0
  22. package/dist/ui/components/label.d.ts +1 -0
  23. package/dist/ui/components/label.js +18 -0
  24. package/dist/ui/components/separator.d.ts +5 -0
  25. package/dist/ui/components/separator.js +22 -0
  26. package/dist/ui/components/toast.d.ts +8 -0
  27. package/dist/ui/components/toast.js +39 -0
  28. package/dist/ui/globals.css +14 -2
  29. package/package.json +2 -2
  30. package/src/hooks/use-below-breakpoint.ts +21 -0
  31. package/src/hooks/use-confirm-delete.ts +43 -0
  32. package/src/hooks/use-toast.ts +29 -0
  33. package/src/index.ts +22 -1
  34. package/src/ui/components/animated-count.stories.tsx +1 -1
  35. package/src/ui/components/ascii.stories.tsx +1 -1
  36. package/src/ui/components/badge.stories.tsx +1 -1
  37. package/src/ui/components/blend-mode.stories.tsx +1 -1
  38. package/src/ui/components/blink.stories.tsx +1 -1
  39. package/src/ui/components/bottom-sheet.stories.tsx +43 -0
  40. package/src/ui/components/bottom-sheet.tsx +227 -0
  41. package/src/ui/components/button.stories.tsx +1 -1
  42. package/src/ui/components/card.stories.tsx +63 -0
  43. package/src/ui/components/card.tsx +85 -0
  44. package/src/ui/components/checkbox.stories.tsx +1 -1
  45. package/src/ui/components/checkbox.tsx +1 -1
  46. package/src/ui/components/command-block.stories.tsx +1 -1
  47. package/src/ui/components/confirm-dialog.stories.tsx +91 -0
  48. package/src/ui/components/confirm-dialog.tsx +130 -0
  49. package/src/ui/components/dialog.stories.tsx +169 -0
  50. package/src/ui/components/dialog.tsx +177 -0
  51. package/src/ui/components/dropdown-menu.stories.tsx +1 -1
  52. package/src/ui/components/fit-text/index.stories.tsx +1 -1
  53. package/src/ui/components/forms.stories.tsx +173 -0
  54. package/src/ui/components/graphs/index.stories.tsx +1 -1
  55. package/src/ui/components/hover-bg.stories.tsx +1 -1
  56. package/src/ui/components/image-distortion.stories.tsx +1 -1
  57. package/src/ui/components/input.stories.tsx +39 -0
  58. package/src/ui/components/input.tsx +20 -0
  59. package/src/ui/components/label.stories.tsx +26 -0
  60. package/src/ui/components/label.tsx +16 -0
  61. package/src/ui/components/list-item.stories.tsx +1 -1
  62. package/src/ui/components/poster.stories.tsx +1 -1
  63. package/src/ui/components/progress.stories.tsx +1 -1
  64. package/src/ui/components/scramble.stories.tsx +1 -1
  65. package/src/ui/components/segmented.stories.tsx +1 -1
  66. package/src/ui/components/select.stories.tsx +1 -1
  67. package/src/ui/components/separator.stories.tsx +33 -0
  68. package/src/ui/components/separator.tsx +24 -0
  69. package/src/ui/components/spinner.stories.tsx +1 -1
  70. package/src/ui/components/stats.stories.tsx +1 -1
  71. package/src/ui/components/switch.stories.tsx +1 -1
  72. package/src/ui/components/tabs.stories.tsx +1 -1
  73. package/src/ui/components/terminal-demo.stories.tsx +1 -1
  74. package/src/ui/components/theme-toggle.stories.tsx +1 -1
  75. package/src/ui/components/tier-card.stories.tsx +1 -1
  76. package/src/ui/components/toast.stories.tsx +55 -0
  77. package/src/ui/components/toast.tsx +49 -0
  78. package/src/ui/components/tv.stories.tsx +1 -1
  79. package/src/ui/components/watchlist.stories.tsx +1 -1
  80. package/src/ui/globals.css +14 -2
  81. package/dist/ui/components/modal/index.d.ts +0 -8
  82. package/dist/ui/components/modal/index.js +0 -35
  83. package/dist/ui/components/modal/modal.css +0 -36
  84. package/src/ui/components/modal/index.stories.tsx +0 -46
  85. package/src/ui/components/modal/index.tsx +0 -48
  86. package/src/ui/components/modal/modal.css +0 -36
@@ -0,0 +1,173 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite'
2
+ import { useState } from 'react'
3
+
4
+ import { Button } from './button'
5
+ import { Checkbox } from './checkbox'
6
+ import { Input } from './input'
7
+ import { Label } from './label'
8
+ import { Select, SelectOption } from './select'
9
+ import { Separator } from './separator'
10
+ import { Switch } from './switch'
11
+
12
+ const meta: Meta = {
13
+ title: 'Components/Forms/All Forms'
14
+ }
15
+
16
+ export default meta
17
+
18
+ type Story = StoryObj
19
+
20
+ export const AllFormControls: Story = {
21
+ render: () => {
22
+ function FormDemo() {
23
+ const [name, setName] = useState('Hermes')
24
+ const [email, setEmail] = useState('hermes@nousresearch.com')
25
+ const [provider, setProvider] = useState('anthropic')
26
+ const [logging, setLogging] = useState(true)
27
+ const [telemetry, setTelemetry] = useState(false)
28
+ const [terms, setTerms] = useState(false)
29
+ const [newsletter, setNewsletter] = useState(true)
30
+
31
+ return (
32
+ <div className="flex w-full max-w-lg flex-col gap-6">
33
+ <div className="flex flex-col gap-1">
34
+ <h2 className="font-expanded text-sm font-bold tracking-[0.08em] uppercase">
35
+ Form Controls
36
+ </h2>
37
+
38
+ <p className="font-mondwest text-xs text-midground/60">
39
+ All form primitives from the design system.
40
+ </p>
41
+ </div>
42
+
43
+ <Separator />
44
+
45
+ <div className="flex flex-col gap-4">
46
+ <Label className="text-midground/50">Text Inputs</Label>
47
+
48
+ <div className="flex flex-col gap-1.5">
49
+ <Label htmlFor="form-name">Name</Label>
50
+ <Input
51
+ id="form-name"
52
+ onChange={e => setName(e.target.value)}
53
+ placeholder="Enter your name"
54
+ value={name}
55
+ />
56
+ </div>
57
+
58
+ <div className="flex flex-col gap-1.5">
59
+ <Label htmlFor="form-email">Email</Label>
60
+ <Input
61
+ id="form-email"
62
+ onChange={e => setEmail(e.target.value)}
63
+ placeholder="Enter your email"
64
+ type="email"
65
+ value={email}
66
+ />
67
+ </div>
68
+
69
+ <div className="flex flex-col gap-1.5">
70
+ <Label htmlFor="form-disabled">Disabled Input</Label>
71
+ <Input
72
+ disabled
73
+ id="form-disabled"
74
+ placeholder="Cannot edit"
75
+ value="Read-only value"
76
+ />
77
+ </div>
78
+ </div>
79
+
80
+ <Separator />
81
+
82
+ <div className="flex flex-col gap-4">
83
+ <Label className="text-midground/50">Select</Label>
84
+
85
+ <div className="flex flex-col gap-1.5">
86
+ <Label htmlFor="form-provider">Provider</Label>
87
+
88
+ <Select
89
+ onValueChange={setProvider}
90
+ placeholder="Choose a provider…"
91
+ value={provider}
92
+ >
93
+ <SelectOption value="openai">OpenAI</SelectOption>
94
+ <SelectOption value="anthropic">Anthropic</SelectOption>
95
+ <SelectOption value="google">Google</SelectOption>
96
+ <SelectOption value="mistral">Mistral</SelectOption>
97
+ </Select>
98
+ </div>
99
+ </div>
100
+
101
+ <Separator />
102
+
103
+ <div className="flex flex-col gap-4">
104
+ <Label className="text-midground/50">Switches</Label>
105
+
106
+ <label className="flex items-center justify-between">
107
+ <span className="text-sm">Enable logging</span>
108
+ <Switch checked={logging} onCheckedChange={setLogging} />
109
+ </label>
110
+
111
+ <label className="flex items-center justify-between">
112
+ <span className="text-sm">Send telemetry</span>
113
+ <Switch checked={telemetry} onCheckedChange={setTelemetry} />
114
+ </label>
115
+ </div>
116
+
117
+ <Separator />
118
+
119
+ <div className="flex flex-col gap-4">
120
+ <Label className="text-midground/50">Checkboxes</Label>
121
+
122
+ <div className="flex items-center gap-2.5">
123
+ <Checkbox
124
+ checked={terms}
125
+ id="form-terms"
126
+ onCheckedChange={setTerms}
127
+ />
128
+
129
+ <label className="cursor-pointer text-sm" htmlFor="form-terms">
130
+ Accept terms and conditions
131
+ </label>
132
+ </div>
133
+
134
+ <div className="flex items-center gap-2.5">
135
+ <Checkbox
136
+ checked={newsletter}
137
+ id="form-newsletter"
138
+ onCheckedChange={setNewsletter}
139
+ />
140
+
141
+ <label className="cursor-pointer text-sm" htmlFor="form-newsletter">
142
+ Subscribe to newsletter
143
+ </label>
144
+ </div>
145
+ </div>
146
+
147
+ <Separator />
148
+
149
+ <div className="flex flex-col gap-4">
150
+ <Label className="text-midground/50">Buttons</Label>
151
+
152
+ <div className="flex flex-wrap gap-2">
153
+ <Button>Primary</Button>
154
+ <Button outlined>Outlined</Button>
155
+ <Button invert>Inverted</Button>
156
+ <Button destructive>Destructive</Button>
157
+ <Button disabled>Disabled</Button>
158
+ </div>
159
+ </div>
160
+
161
+ <Separator />
162
+
163
+ <div className="flex items-center justify-end gap-2">
164
+ <Button outlined>Cancel</Button>
165
+ <Button>Save Changes</Button>
166
+ </div>
167
+ </div>
168
+ )
169
+ }
170
+
171
+ return <FormDemo />
172
+ }
173
+ }
@@ -24,7 +24,7 @@ const BAR_DATA = (() => {
24
24
 
25
25
  const meta = {
26
26
  parameters: { layout: 'padded' },
27
- title: 'Components/Graphs'
27
+ title: 'Components/Data Display/Graphs'
28
28
  } satisfies Meta
29
29
 
30
30
  export default meta
@@ -5,7 +5,7 @@ import { Typography } from './typography'
5
5
 
6
6
  const meta = {
7
7
  component: HoverBg,
8
- title: 'Components/HoverBg'
8
+ title: 'Components/Layout/HoverBg'
9
9
  } satisfies Meta<typeof HoverBg>
10
10
 
11
11
  export default meta
@@ -8,7 +8,7 @@ import { ImageDistortion } from './image-distortion'
8
8
  const meta: Meta<typeof ImageDistortion> = {
9
9
  args: { active: true, src: fillerBg.src ?? (fillerBg as unknown as string) },
10
10
  component: ImageDistortion,
11
- title: 'Components/ImageDistortion'
11
+ title: 'Components/Effects/ImageDistortion'
12
12
  }
13
13
 
14
14
  export default meta
@@ -0,0 +1,39 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite'
2
+
3
+ import { Input } from './input'
4
+ import { Label } from './label'
5
+
6
+ const meta: Meta<typeof Input> = {
7
+ component: Input,
8
+ title: 'Components/Forms/Input'
9
+ }
10
+
11
+ export default meta
12
+
13
+ type Story = StoryObj<typeof Input>
14
+
15
+ export const Playground: Story = {
16
+ render: () => <Input placeholder="Enter a value…" />
17
+ }
18
+
19
+ export const Disabled: Story = {
20
+ render: () => <Input disabled placeholder="Disabled" value="locked" />
21
+ }
22
+
23
+ export const WithLabel: Story = {
24
+ render: () => (
25
+ <div className="grid w-64 gap-1.5">
26
+ <Label htmlFor="demo-input">Model name</Label>
27
+ <Input id="demo-input" placeholder="e.g. gpt-4o" />
28
+ </div>
29
+ )
30
+ }
31
+
32
+ export const NumberInput: Story = {
33
+ render: () => (
34
+ <div className="grid w-40 gap-1.5">
35
+ <Label htmlFor="demo-number">Temperature</Label>
36
+ <Input id="demo-number" type="number" step={0.1} defaultValue={0.7} />
37
+ </div>
38
+ )
39
+ }
@@ -0,0 +1,20 @@
1
+ import { cn } from '../../utils'
2
+
3
+ export function Input({
4
+ className,
5
+ ...props
6
+ }: React.InputHTMLAttributes<HTMLInputElement>) {
7
+ return (
8
+ <input
9
+ className={cn(
10
+ 'flex h-9 w-full border border-midground/15 bg-background/40 px-3 py-1 font-courier text-sm transition-colors',
11
+ 'placeholder:text-midground/50',
12
+ 'focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-midground/30 focus-visible:border-midground/25',
13
+ 'disabled:cursor-not-allowed disabled:opacity-50',
14
+ className
15
+ )}
16
+ {...props}
17
+ />
18
+ )
19
+ }
20
+
@@ -0,0 +1,26 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite'
2
+
3
+ import { Input } from './input'
4
+ import { Label } from './label'
5
+
6
+ const meta: Meta<typeof Label> = {
7
+ component: Label,
8
+ title: 'Components/Forms/Label'
9
+ }
10
+
11
+ export default meta
12
+
13
+ type Story = StoryObj<typeof Label>
14
+
15
+ export const Playground: Story = {
16
+ render: () => <Label>Field label</Label>
17
+ }
18
+
19
+ export const WithInput: Story = {
20
+ render: () => (
21
+ <div className="grid w-64 gap-1.5">
22
+ <Label htmlFor="label-demo">API key</Label>
23
+ <Input id="label-demo" type="password" placeholder="sk-…" />
24
+ </div>
25
+ )
26
+ }
@@ -0,0 +1,16 @@
1
+ import { cn } from '../../utils'
2
+
3
+ export function Label({
4
+ className,
5
+ ...props
6
+ }: React.LabelHTMLAttributes<HTMLLabelElement>) {
7
+ return (
8
+ <label
9
+ className={cn(
10
+ 'font-mondwest text-xs tracking-[0.1em] uppercase leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70',
11
+ className
12
+ )}
13
+ {...props}
14
+ />
15
+ )
16
+ }
@@ -34,7 +34,7 @@ function Demo() {
34
34
 
35
35
  const meta: Meta<typeof ListItem> = {
36
36
  component: ListItem,
37
- title: 'Components/ListItem'
37
+ title: 'Components/Data Display/ListItem'
38
38
  }
39
39
 
40
40
  export default meta
@@ -115,7 +115,7 @@ const meta: Meta<typeof Poster> = {
115
115
  },
116
116
  layout: 'centered'
117
117
  },
118
- title: 'Components/Poster'
118
+ title: 'Components/Data Display/Poster'
119
119
  }
120
120
 
121
121
  export default meta
@@ -7,7 +7,7 @@ import { Progress } from './progress'
7
7
  const meta: Meta<typeof Progress> = {
8
8
  args: { animate: true, speed: 0.4, value: 42 },
9
9
  component: Progress,
10
- title: 'Components/Progress'
10
+ title: 'Components/Feedback/Progress'
11
11
  }
12
12
 
13
13
  export default meta
@@ -7,7 +7,7 @@ import { Small } from './typography/small'
7
7
 
8
8
  const meta: Meta<typeof Scramble> = {
9
9
  component: Scramble,
10
- title: 'Components/Scramble'
10
+ title: 'Components/Effects/Scramble'
11
11
  }
12
12
 
13
13
  export default meta
@@ -37,7 +37,7 @@ function Demo({ size }: { size?: 'md' | 'sm' }) {
37
37
 
38
38
  const meta: Meta<typeof Segmented> = {
39
39
  component: Segmented,
40
- title: 'Components/Segmented'
40
+ title: 'Components/Forms/Segmented'
41
41
  }
42
42
 
43
43
  export default meta
@@ -41,7 +41,7 @@ function Demo({
41
41
 
42
42
  const meta: Meta<typeof Select> = {
43
43
  component: Select,
44
- title: 'Components/Select'
44
+ title: 'Components/Forms/Select'
45
45
  }
46
46
 
47
47
  export default meta
@@ -0,0 +1,33 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite'
2
+
3
+ import { Separator } from './separator'
4
+ import { Small } from './typography/small'
5
+
6
+ const meta: Meta<typeof Separator> = {
7
+ component: Separator,
8
+ title: 'Components/Layout/Separator'
9
+ }
10
+
11
+ export default meta
12
+
13
+ type Story = StoryObj<typeof Separator>
14
+
15
+ export const Horizontal: Story = {
16
+ render: () => (
17
+ <div className="grid w-64 gap-3">
18
+ <Small className="opacity-60 uppercase tracking-wider">Section A</Small>
19
+ <Separator />
20
+ <Small className="opacity-60 uppercase tracking-wider">Section B</Small>
21
+ </div>
22
+ )
23
+ }
24
+
25
+ export const Vertical: Story = {
26
+ render: () => (
27
+ <div className="flex h-8 items-center gap-3">
28
+ <Small className="opacity-60 uppercase tracking-wider">Left</Small>
29
+ <Separator orientation="vertical" />
30
+ <Small className="opacity-60 uppercase tracking-wider">Right</Small>
31
+ </div>
32
+ )
33
+ }
@@ -0,0 +1,24 @@
1
+ import { cn } from '../../utils'
2
+
3
+ export function Separator({
4
+ className,
5
+ orientation = 'horizontal',
6
+ ...props
7
+ }: SeparatorProps) {
8
+ return (
9
+ <div
10
+ aria-orientation={orientation}
11
+ role="separator"
12
+ className={cn(
13
+ 'shrink-0 bg-midground/15',
14
+ orientation === 'horizontal' ? 'h-px w-full' : 'h-full w-px',
15
+ className
16
+ )}
17
+ {...props}
18
+ />
19
+ )
20
+ }
21
+
22
+ interface SeparatorProps extends React.HTMLAttributes<HTMLDivElement> {
23
+ orientation?: 'horizontal' | 'vertical'
24
+ }
@@ -26,7 +26,7 @@ const NAMES = [
26
26
 
27
27
  const meta: Meta<typeof Spinner> = {
28
28
  component: Spinner,
29
- title: 'Components/Spinner'
29
+ title: 'Components/Feedback/Spinner'
30
30
  }
31
31
 
32
32
  export default meta
@@ -4,7 +4,7 @@ import { Stats } from './stats'
4
4
 
5
5
  const meta = {
6
6
  component: Stats,
7
- title: 'Components/Stats'
7
+ title: 'Components/Feedback/Stats'
8
8
  } satisfies Meta<typeof Stats>
9
9
 
10
10
  export default meta
@@ -14,7 +14,7 @@ function Demo({ disabled }: { disabled?: boolean }) {
14
14
 
15
15
  const meta: Meta<typeof Switch> = {
16
16
  component: Switch,
17
- title: 'Components/Switch'
17
+ title: 'Components/Forms/Switch'
18
18
  }
19
19
 
20
20
  export default meta
@@ -5,7 +5,7 @@ import { Small } from './typography/small'
5
5
 
6
6
  const meta: Meta<typeof Tabs> = {
7
7
  component: Tabs,
8
- title: 'Components/Tabs'
8
+ title: 'Components/Forms/Tabs'
9
9
  }
10
10
 
11
11
  export default meta
@@ -42,7 +42,7 @@ const SEQUENCE: TerminalDemoStep[] = [
42
42
  const meta: Meta<typeof TerminalDemo> = {
43
43
  args: { label: 'Hermes', sequence: SEQUENCE },
44
44
  component: TerminalDemo,
45
- title: 'Components/TerminalDemo'
45
+ title: 'Components/Data Display/TerminalDemo'
46
46
  }
47
47
 
48
48
  export default meta
@@ -7,7 +7,7 @@ import { Small } from './typography/small'
7
7
 
8
8
  const meta: Meta<typeof ThemeToggle> = {
9
9
  component: ThemeToggle,
10
- title: 'Components/ThemeToggle'
10
+ title: 'Components/Layout/ThemeToggle'
11
11
  }
12
12
 
13
13
  export default meta
@@ -121,7 +121,7 @@ const meta = {
121
121
  },
122
122
  layout: 'fullscreen'
123
123
  },
124
- title: 'Components/TierCard'
124
+ title: 'Components/Data Display/TierCard'
125
125
  } satisfies Meta<typeof TierCard>
126
126
 
127
127
  export default meta
@@ -0,0 +1,55 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite'
2
+
3
+ import { useToast } from '../../hooks/use-toast'
4
+ import { Button } from './button'
5
+ import { Toast } from './toast'
6
+
7
+ const meta: Meta<typeof Toast> = {
8
+ component: Toast,
9
+ title: 'Components/Feedback/Toast'
10
+ }
11
+
12
+ export default meta
13
+
14
+ type Story = StoryObj<typeof Toast>
15
+
16
+ export const Success: Story = {
17
+ render: () => {
18
+ function Demo() {
19
+ const { showToast, toast } = useToast()
20
+
21
+ return (
22
+ <>
23
+ <Button onClick={() => showToast('Operation succeeded', 'success')}>
24
+ Show success toast
25
+ </Button>
26
+ <Toast toast={toast} />
27
+ </>
28
+ )
29
+ }
30
+
31
+ return <Demo />
32
+ }
33
+ }
34
+
35
+ export const Error: Story = {
36
+ render: () => {
37
+ function Demo() {
38
+ const { showToast, toast } = useToast()
39
+
40
+ return (
41
+ <>
42
+ <Button
43
+ destructive
44
+ onClick={() => showToast('Something went wrong', 'error')}
45
+ >
46
+ Show error toast
47
+ </Button>
48
+ <Toast toast={toast} />
49
+ </>
50
+ )
51
+ }
52
+
53
+ return <Demo />
54
+ }
55
+ }
@@ -0,0 +1,49 @@
1
+ 'use client'
2
+
3
+ import { useEffect, useState } from 'react'
4
+ import { createPortal } from 'react-dom'
5
+
6
+ import { cn } from '../../utils'
7
+
8
+ export function Toast({ toast }: ToastProps) {
9
+ const [visible, setVisible] = useState(false)
10
+ const [current, setCurrent] = useState(toast)
11
+
12
+ useEffect(() => {
13
+ if (toast) {
14
+ setCurrent(toast)
15
+ setVisible(true)
16
+ } else {
17
+ setVisible(false)
18
+ const timer = setTimeout(() => setCurrent(null), 200)
19
+ return () => clearTimeout(timer)
20
+ }
21
+ }, [toast])
22
+
23
+ if (!current || typeof document === 'undefined') return null
24
+
25
+ return createPortal(
26
+ <div
27
+ aria-live="polite"
28
+ className={cn(
29
+ 'fixed top-16 right-4 z-50 border px-4 py-2.5 font-courier text-xs tracking-wider uppercase backdrop-blur-sm',
30
+ current.type === 'success'
31
+ ? 'bg-success/15 text-success border-success/30'
32
+ : 'bg-destructive/15 text-destructive border-destructive/30'
33
+ )}
34
+ role="status"
35
+ style={{
36
+ animation: visible
37
+ ? 'toast-in 200ms ease-out forwards'
38
+ : 'toast-out 200ms ease-in forwards'
39
+ }}
40
+ >
41
+ {current.message}
42
+ </div>,
43
+ document.body
44
+ )
45
+ }
46
+
47
+ interface ToastProps {
48
+ toast: { message: string; type: 'error' | 'success' } | null
49
+ }
@@ -13,7 +13,7 @@ const meta = {
13
13
  }
14
14
  }
15
15
  },
16
- title: 'Components/TV'
16
+ title: 'Components/Effects/TV'
17
17
  } satisfies Meta<typeof TV>
18
18
 
19
19
  export default meta
@@ -11,7 +11,7 @@ const ADDRESSES = [
11
11
 
12
12
  const meta = {
13
13
  component: Watchlist,
14
- title: 'Components/Watchlist'
14
+ title: 'Components/Data Display/Watchlist'
15
15
  } satisfies Meta<typeof Watchlist>
16
16
 
17
17
  export default meta
@@ -1,8 +1,6 @@
1
1
  @source ".";
2
2
  @import './components/fit-text/fit-text.css' layer(components);
3
3
  @import './components/grid/grid.css' layer(components);
4
- @import './components/modal/modal.css' layer(components);
5
-
6
4
  @view-transition {
7
5
  navigation: auto;
8
6
  }
@@ -253,6 +251,20 @@
253
251
  text-underline-position: from-font;
254
252
  }
255
253
 
254
+ @keyframes toast-in {
255
+ from {
256
+ opacity: 0;
257
+ transform: translateX(1rem);
258
+ }
259
+ }
260
+
261
+ @keyframes toast-out {
262
+ to {
263
+ opacity: 0;
264
+ transform: translateX(1rem);
265
+ }
266
+ }
267
+
256
268
  @keyframes gradient-stroke {
257
269
  0% {
258
270
  background-position: 15% 15%;
@@ -1,8 +0,0 @@
1
- export declare function Modal({ children, className, id, trigger, ...props }: ModalProps): import("react").JSX.Element;
2
- interface ModalProps extends Omit<React.ComponentProps<'dialog'>, 'open'> {
3
- trigger: (controls: {
4
- close: () => void;
5
- open: () => void;
6
- }) => React.ReactNode;
7
- }
8
- export {};