@xyhp915/slack-base-ui 0.0.1 → 0.0.3
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/README.md +220 -4
- package/agents/slack-base-ui/SKILL.md +137 -0
- package/agents/slack-base-ui/checklists/style-review.md +56 -0
- package/agents/slack-base-ui/templates/consumer-setup.md +109 -0
- package/agents/slack-base-ui/templates/slack-theme.css +152 -0
- package/libs/Dialog.d.ts +73 -0
- package/libs/Dialog.d.ts.map +1 -1
- package/libs/Popover.d.ts +69 -0
- package/libs/Popover.d.ts.map +1 -1
- package/libs/index.d.ts +4 -4
- package/libs/index.d.ts.map +1 -1
- package/libs/index.js +2885 -2718
- package/package.json +1 -1
- package/src/App.css +7 -0
- package/src/App.tsx +18 -0
- package/src/assets/react.svg +1 -0
- package/src/components/AlertDialog.tsx +185 -0
- package/src/components/AutoComplete.tsx +311 -0
- package/src/components/Avatar.tsx +70 -0
- package/src/components/Badge.tsx +48 -0
- package/src/components/Button.tsx +53 -0
- package/src/components/Checkbox.tsx +109 -0
- package/src/components/ContextMenu.tsx +393 -0
- package/src/components/Dialog.tsx +371 -0
- package/src/components/Form.tsx +409 -0
- package/src/components/IconButton.tsx +49 -0
- package/src/components/Input.tsx +56 -0
- package/src/components/Loading.tsx +123 -0
- package/src/components/Menu.tsx +368 -0
- package/src/components/Popover.tsx +367 -0
- package/src/components/Progress.tsx +89 -0
- package/src/components/Radio.tsx +137 -0
- package/src/components/Select.tsx +177 -0
- package/src/components/Switch.tsx +116 -0
- package/src/components/Tabs.tsx +128 -0
- package/src/components/Toast.tsx +149 -0
- package/src/components/Tooltip.tsx +46 -0
- package/src/components/index.ts +186 -0
- package/src/context/ThemeContext.tsx +53 -0
- package/src/context/useTheme.ts +11 -0
- package/src/examples/slack-clone/SlackApp.tsx +94 -0
- package/src/examples/slack-clone/components/ChannelHeader.tsx +34 -0
- package/src/examples/slack-clone/components/Composer.tsx +42 -0
- package/src/examples/slack-clone/components/Message.tsx +97 -0
- package/src/examples/slack-clone/components/UserProfile.tsx +78 -0
- package/src/examples/slack-clone/layout/Layout.tsx +27 -0
- package/src/examples/slack-clone/layout/Sidebar.tsx +67 -0
- package/src/examples/slack-clone/layout/SidebarItem.tsx +57 -0
- package/src/examples/slack-clone/layout/TopBar.tsx +30 -0
- package/src/index.css +240 -0
- package/src/main.tsx +22 -0
- package/src/pages/ComponentShowcase.tsx +1964 -0
- package/src/pages/Dashboard.tsx +87 -0
- package/src/pages/QuickStartDemo.tsx +262 -0
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { Menu as BaseMenu } from '@base-ui/react'
|
|
3
|
+
import clsx from 'clsx'
|
|
4
|
+
import { Check, ChevronRight } from 'lucide-react'
|
|
5
|
+
|
|
6
|
+
export interface MenuProps {
|
|
7
|
+
children: React.ReactNode
|
|
8
|
+
open?: boolean
|
|
9
|
+
defaultOpen?: boolean
|
|
10
|
+
onOpenChange?: (open: boolean) => void
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface MenuTriggerProps {
|
|
14
|
+
children?: React.ReactNode
|
|
15
|
+
className?: string
|
|
16
|
+
render?: React.ReactElement
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface MenuContentProps {
|
|
20
|
+
children: React.ReactNode
|
|
21
|
+
className?: string
|
|
22
|
+
side?: 'top' | 'right' | 'bottom' | 'left'
|
|
23
|
+
align?: 'start' | 'center' | 'end'
|
|
24
|
+
sideOffset?: number
|
|
25
|
+
alignOffset?: number
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface MenuItemProps {
|
|
29
|
+
children: React.ReactNode
|
|
30
|
+
className?: string
|
|
31
|
+
disabled?: boolean
|
|
32
|
+
onSelect?: () => void
|
|
33
|
+
destructive?: boolean
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface MenuCheckboxItemProps {
|
|
37
|
+
children: React.ReactNode
|
|
38
|
+
className?: string
|
|
39
|
+
checked?: boolean
|
|
40
|
+
onCheckedChange?: (checked: boolean) => void
|
|
41
|
+
disabled?: boolean
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface MenuRadioGroupProps {
|
|
45
|
+
children: React.ReactNode
|
|
46
|
+
value?: string
|
|
47
|
+
onValueChange?: (value: string) => void
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface MenuRadioItemProps {
|
|
51
|
+
children: React.ReactNode
|
|
52
|
+
value: string
|
|
53
|
+
className?: string
|
|
54
|
+
disabled?: boolean
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface MenuLabelProps {
|
|
58
|
+
children: React.ReactNode
|
|
59
|
+
className?: string
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface MenuSeparatorProps {
|
|
63
|
+
className?: string
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface MenuSubProps {
|
|
67
|
+
children: React.ReactNode
|
|
68
|
+
open?: boolean
|
|
69
|
+
defaultOpen?: boolean
|
|
70
|
+
onOpenChange?: (open: boolean) => void
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface MenuSubTriggerProps {
|
|
74
|
+
children: React.ReactNode
|
|
75
|
+
className?: string
|
|
76
|
+
disabled?: boolean
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export interface MenuSubContentProps {
|
|
80
|
+
children: React.ReactNode
|
|
81
|
+
className?: string
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export const Menu: React.FC<MenuProps> = ({
|
|
85
|
+
children,
|
|
86
|
+
open,
|
|
87
|
+
defaultOpen,
|
|
88
|
+
onOpenChange,
|
|
89
|
+
}) => {
|
|
90
|
+
return (
|
|
91
|
+
<BaseMenu.Root
|
|
92
|
+
open={open}
|
|
93
|
+
defaultOpen={defaultOpen}
|
|
94
|
+
onOpenChange={onOpenChange}
|
|
95
|
+
>
|
|
96
|
+
{children}
|
|
97
|
+
</BaseMenu.Root>
|
|
98
|
+
)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export const MenuTrigger = React.forwardRef<HTMLButtonElement, MenuTriggerProps>(
|
|
102
|
+
({ children, className, render }, ref) => {
|
|
103
|
+
if (render) {
|
|
104
|
+
return (
|
|
105
|
+
<span className="contents">
|
|
106
|
+
<BaseMenu.Trigger
|
|
107
|
+
ref={ref}
|
|
108
|
+
className={clsx('outline-none', className)}
|
|
109
|
+
render={render}
|
|
110
|
+
/>
|
|
111
|
+
</span>
|
|
112
|
+
)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return (
|
|
116
|
+
<span className="contents">
|
|
117
|
+
<BaseMenu.Trigger
|
|
118
|
+
ref={ref}
|
|
119
|
+
className={clsx('outline-none', className)}
|
|
120
|
+
>
|
|
121
|
+
{children}
|
|
122
|
+
</BaseMenu.Trigger>
|
|
123
|
+
</span>
|
|
124
|
+
)
|
|
125
|
+
}
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
MenuTrigger.displayName = 'MenuTrigger'
|
|
129
|
+
|
|
130
|
+
export const MenuContent = React.forwardRef<HTMLDivElement, MenuContentProps>(
|
|
131
|
+
(
|
|
132
|
+
{
|
|
133
|
+
children,
|
|
134
|
+
className,
|
|
135
|
+
side = 'bottom',
|
|
136
|
+
align = 'start',
|
|
137
|
+
sideOffset = 4,
|
|
138
|
+
alignOffset = 0,
|
|
139
|
+
},
|
|
140
|
+
ref
|
|
141
|
+
) => {
|
|
142
|
+
return (
|
|
143
|
+
<BaseMenu.Portal>
|
|
144
|
+
<BaseMenu.Positioner
|
|
145
|
+
side={side}
|
|
146
|
+
align={align}
|
|
147
|
+
sideOffset={sideOffset}
|
|
148
|
+
alignOffset={alignOffset}
|
|
149
|
+
>
|
|
150
|
+
<BaseMenu.Popup
|
|
151
|
+
ref={ref}
|
|
152
|
+
className={clsx(
|
|
153
|
+
'z-50 min-w-45 max-w-80 rounded-md border border-(--border-light) bg-(--bg-primary) shadow-lg',
|
|
154
|
+
'py-1',
|
|
155
|
+
className
|
|
156
|
+
)}
|
|
157
|
+
>
|
|
158
|
+
{children}
|
|
159
|
+
<BaseMenu.Arrow className="fill-(--bg-primary) stroke-(--border-light)" />
|
|
160
|
+
</BaseMenu.Popup>
|
|
161
|
+
</BaseMenu.Positioner>
|
|
162
|
+
</BaseMenu.Portal>
|
|
163
|
+
)
|
|
164
|
+
}
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
MenuContent.displayName = 'MenuContent'
|
|
168
|
+
|
|
169
|
+
export const MenuItem = React.forwardRef<HTMLDivElement, MenuItemProps>(
|
|
170
|
+
({ children, className, disabled, onSelect, destructive }, ref) => {
|
|
171
|
+
return (
|
|
172
|
+
<BaseMenu.Item
|
|
173
|
+
ref={ref}
|
|
174
|
+
className={clsx(
|
|
175
|
+
'relative flex items-center gap-2 px-3 py-1.5 text-[15px] outline-none cursor-pointer select-none',
|
|
176
|
+
'text-(--text-primary) hover:bg-(--bg-hover)',
|
|
177
|
+
'data-highlighted:bg-(--bg-hover)',
|
|
178
|
+
'data-disabled:opacity-50 data-disabled:pointer-events-none',
|
|
179
|
+
destructive && 'text-(--danger) hover:bg-red-50 dark:hover:bg-red-950/20',
|
|
180
|
+
className
|
|
181
|
+
)}
|
|
182
|
+
disabled={disabled}
|
|
183
|
+
onSelect={onSelect}
|
|
184
|
+
>
|
|
185
|
+
{children}
|
|
186
|
+
</BaseMenu.Item>
|
|
187
|
+
)
|
|
188
|
+
}
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
MenuItem.displayName = 'MenuItem'
|
|
192
|
+
|
|
193
|
+
export const MenuCheckboxItem = React.forwardRef<
|
|
194
|
+
HTMLDivElement,
|
|
195
|
+
MenuCheckboxItemProps
|
|
196
|
+
>(({ children, className, checked, onCheckedChange, disabled }, ref) => {
|
|
197
|
+
return (
|
|
198
|
+
<BaseMenu.CheckboxItem
|
|
199
|
+
ref={ref}
|
|
200
|
+
className={clsx(
|
|
201
|
+
'relative flex items-center gap-2 px-3 py-1.5 pl-8 text-[15px] outline-none cursor-pointer select-none',
|
|
202
|
+
'text-(--text-primary) hover:bg-(--bg-hover)',
|
|
203
|
+
'data-highlighted:bg-(--bg-hover)',
|
|
204
|
+
'data-disabled:opacity-50 data-disabled:pointer-events-none',
|
|
205
|
+
className
|
|
206
|
+
)}
|
|
207
|
+
checked={checked}
|
|
208
|
+
onCheckedChange={onCheckedChange}
|
|
209
|
+
disabled={disabled}
|
|
210
|
+
>
|
|
211
|
+
<BaseMenu.CheckboxItemIndicator className="absolute left-2 flex items-center justify-center">
|
|
212
|
+
<Check className="w-4 h-4" />
|
|
213
|
+
</BaseMenu.CheckboxItemIndicator>
|
|
214
|
+
{children}
|
|
215
|
+
</BaseMenu.CheckboxItem>
|
|
216
|
+
)
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
MenuCheckboxItem.displayName = 'MenuCheckboxItem'
|
|
220
|
+
|
|
221
|
+
export const MenuRadioGroup: React.FC<MenuRadioGroupProps> = ({
|
|
222
|
+
children,
|
|
223
|
+
value,
|
|
224
|
+
onValueChange,
|
|
225
|
+
}) => {
|
|
226
|
+
return (
|
|
227
|
+
<BaseMenu.RadioGroup value={value} onValueChange={onValueChange}>
|
|
228
|
+
{children}
|
|
229
|
+
</BaseMenu.RadioGroup>
|
|
230
|
+
)
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export const MenuRadioItem = React.forwardRef<HTMLDivElement, MenuRadioItemProps>(
|
|
234
|
+
({ children, value, className, disabled }, ref) => {
|
|
235
|
+
return (
|
|
236
|
+
<BaseMenu.RadioItem
|
|
237
|
+
ref={ref}
|
|
238
|
+
value={value}
|
|
239
|
+
className={clsx(
|
|
240
|
+
'relative flex items-center gap-2 px-3 py-1.5 pl-8 text-[15px] outline-none cursor-pointer select-none',
|
|
241
|
+
'text-(--text-primary) hover:bg-(--bg-hover)',
|
|
242
|
+
'data-highlighted:bg-(--bg-hover)',
|
|
243
|
+
'data-disabled:opacity-50 data-disabled:pointer-events-none',
|
|
244
|
+
className
|
|
245
|
+
)}
|
|
246
|
+
disabled={disabled}
|
|
247
|
+
>
|
|
248
|
+
<BaseMenu.RadioItemIndicator className="absolute left-2 flex items-center justify-center">
|
|
249
|
+
<Check className="w-4 h-4" />
|
|
250
|
+
</BaseMenu.RadioItemIndicator>
|
|
251
|
+
{children}
|
|
252
|
+
</BaseMenu.RadioItem>
|
|
253
|
+
)
|
|
254
|
+
}
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
MenuRadioItem.displayName = 'MenuRadioItem'
|
|
258
|
+
|
|
259
|
+
export const MenuLabel: React.FC<MenuLabelProps> = ({ children, className }) => {
|
|
260
|
+
return (
|
|
261
|
+
<div
|
|
262
|
+
className={clsx(
|
|
263
|
+
'px-3 py-2 text-xs font-semibold text-(--text-muted) uppercase tracking-wider',
|
|
264
|
+
className
|
|
265
|
+
)}
|
|
266
|
+
>
|
|
267
|
+
{children}
|
|
268
|
+
</div>
|
|
269
|
+
)
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
export const MenuSeparator: React.FC<MenuSeparatorProps> = ({ className }) => {
|
|
273
|
+
return (
|
|
274
|
+
<BaseMenu.Separator
|
|
275
|
+
className={clsx('my-1 h-px bg-(--border-light)', className)}
|
|
276
|
+
/>
|
|
277
|
+
)
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
export const MenuSub: React.FC<MenuSubProps> = ({
|
|
281
|
+
children,
|
|
282
|
+
open,
|
|
283
|
+
defaultOpen,
|
|
284
|
+
onOpenChange,
|
|
285
|
+
}) => {
|
|
286
|
+
return (
|
|
287
|
+
<BaseMenu.Root
|
|
288
|
+
open={open}
|
|
289
|
+
defaultOpen={defaultOpen}
|
|
290
|
+
onOpenChange={onOpenChange}
|
|
291
|
+
>
|
|
292
|
+
{children}
|
|
293
|
+
</BaseMenu.Root>
|
|
294
|
+
)
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
export const MenuSubTrigger = React.forwardRef<HTMLButtonElement, MenuSubTriggerProps>(
|
|
298
|
+
({ children, className, disabled }, ref) => {
|
|
299
|
+
return (
|
|
300
|
+
<BaseMenu.Trigger
|
|
301
|
+
ref={ref}
|
|
302
|
+
className={clsx(
|
|
303
|
+
'relative flex items-center justify-between gap-2 px-3 py-2 text-[15px] outline-none cursor-pointer select-none',
|
|
304
|
+
'text-(--text-primary) hover:bg-(--bg-hover)',
|
|
305
|
+
'data-highlighted:bg-(--bg-hover)',
|
|
306
|
+
'data-disabled:opacity-50 data-disabled:pointer-events-none w-full',
|
|
307
|
+
className
|
|
308
|
+
)}
|
|
309
|
+
disabled={disabled}
|
|
310
|
+
>
|
|
311
|
+
{children}
|
|
312
|
+
<ChevronRight className="w-4 h-4 ml-auto" />
|
|
313
|
+
</BaseMenu.Trigger>
|
|
314
|
+
)
|
|
315
|
+
}
|
|
316
|
+
)
|
|
317
|
+
|
|
318
|
+
MenuSubTrigger.displayName = 'MenuSubTrigger'
|
|
319
|
+
|
|
320
|
+
export const MenuSubContent = React.forwardRef<HTMLDivElement, MenuSubContentProps>(
|
|
321
|
+
({ children, className }, ref) => {
|
|
322
|
+
return (
|
|
323
|
+
<BaseMenu.Portal>
|
|
324
|
+
<BaseMenu.Positioner side="right" align="start" sideOffset={8}>
|
|
325
|
+
<BaseMenu.Popup
|
|
326
|
+
ref={ref}
|
|
327
|
+
className={clsx(
|
|
328
|
+
'z-50 min-w-45 max-w-80 rounded-md border border-(--border-light) bg-(--bg-primary) shadow-lg',
|
|
329
|
+
'py-1',
|
|
330
|
+
className
|
|
331
|
+
)}
|
|
332
|
+
>
|
|
333
|
+
{children}
|
|
334
|
+
</BaseMenu.Popup>
|
|
335
|
+
</BaseMenu.Positioner>
|
|
336
|
+
</BaseMenu.Portal>
|
|
337
|
+
)
|
|
338
|
+
}
|
|
339
|
+
)
|
|
340
|
+
|
|
341
|
+
MenuSubContent.displayName = 'MenuSubContent'
|
|
342
|
+
|
|
343
|
+
// Convenience component for menu items with icons
|
|
344
|
+
export const MenuItemWithIcon: React.FC<{
|
|
345
|
+
icon?: React.ReactNode
|
|
346
|
+
children: React.ReactNode
|
|
347
|
+
shortcut?: string
|
|
348
|
+
className?: string
|
|
349
|
+
disabled?: boolean
|
|
350
|
+
onSelect?: () => void
|
|
351
|
+
destructive?: boolean
|
|
352
|
+
}> = ({ icon, children, shortcut, className, disabled, onSelect, destructive }) => {
|
|
353
|
+
return (
|
|
354
|
+
<MenuItem
|
|
355
|
+
className={className}
|
|
356
|
+
disabled={disabled}
|
|
357
|
+
onSelect={onSelect}
|
|
358
|
+
destructive={destructive}
|
|
359
|
+
>
|
|
360
|
+
{icon && <span className="shrink-0">{icon}</span>}
|
|
361
|
+
<span className="flex-1">{children}</span>
|
|
362
|
+
{shortcut && (
|
|
363
|
+
<span className="ml-auto text-xs text-(--text-muted)">{shortcut}</span>
|
|
364
|
+
)}
|
|
365
|
+
</MenuItem>
|
|
366
|
+
)
|
|
367
|
+
}
|
|
368
|
+
|