beads-kanban-ui 0.1.0 → 0.1.1

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 (154) hide show
  1. package/README.md +16 -222
  2. package/package.json +18 -55
  3. package/.designs/beads-kanban-ui-bj0.md +0 -73
  4. package/.designs/beads-kanban-ui-qxq.md +0 -144
  5. package/.designs/epic-support.md +0 -282
  6. package/.env.local.example +0 -2
  7. package/.eslintrc.json +0 -3
  8. package/.gitattributes +0 -3
  9. package/.github/workflows/release.yml +0 -123
  10. package/.history/README_20260121193710.md +0 -227
  11. package/.history/README_20260121193918.md +0 -227
  12. package/.history/README_20260121193921.md +0 -227
  13. package/.history/README_20260121193933.md +0 -227
  14. package/.history/README_20260121193934.md +0 -227
  15. package/.history/README_20260121193944.md +0 -227
  16. package/.history/README_20260121193953.md +0 -227
  17. package/.history/src/app/page_20260121133429.tsx +0 -134
  18. package/.history/src/app/page_20260121133928.tsx +0 -134
  19. package/.history/src/app/page_20260121144850.tsx +0 -138
  20. package/.history/src/app/page_20260121144854.tsx +0 -138
  21. package/.history/src/app/page_20260121144858.tsx +0 -138
  22. package/.history/src/app/page_20260121144902.tsx +0 -138
  23. package/.history/src/app/page_20260121144906.tsx +0 -138
  24. package/.history/src/app/page_20260121144911.tsx +0 -138
  25. package/.history/src/app/page_20260121144928.tsx +0 -138
  26. package/.playwright-mcp/.playwright-mcp/morphing-dialog-wheel-scroll-fix.png +0 -0
  27. package/.playwright-mcp/beams-test.png +0 -0
  28. package/.playwright-mcp/card-verification.png +0 -0
  29. package/.playwright-mcp/design-doc-dialog-fix-verification.png +0 -0
  30. package/.playwright-mcp/dialog-width-test.png +0 -0
  31. package/.playwright-mcp/homepage.png +0 -0
  32. package/.playwright-mcp/morphing-dialog-expanded.png +0 -0
  33. package/.playwright-mcp/morphing-dialog-fixes-final.png +0 -0
  34. package/.playwright-mcp/morphing-dialog-open.png +0 -0
  35. package/.playwright-mcp/page-2026-01-21T14-08-31-529Z.png +0 -0
  36. package/.playwright-mcp/page-2026-01-21T14-09-23-431Z.png +0 -0
  37. package/.playwright-mcp/page-2026-01-21T14-10-28-773Z.png +0 -0
  38. package/.playwright-mcp/page-2026-01-21T14-10-47-432Z.png +0 -0
  39. package/.playwright-mcp/page-2026-01-21T14-11-12-350Z.png +0 -0
  40. package/.playwright-mcp/screenshot-after-click.png +0 -0
  41. package/.playwright-mcp/screenshot-after-dialog-click.png +0 -0
  42. package/.playwright-mcp/sheet-restored-after-dialog-close.png +0 -0
  43. package/.playwright-mcp/test-1-sheet-open-with-overlay.png +0 -0
  44. package/.playwright-mcp/test-2-morphing-dialog-with-overlay.png +0 -0
  45. package/.playwright-mcp/test-3-sheet-open-dark-overlay.png +0 -0
  46. package/.playwright-mcp/test-4-morphing-dialog-with-dark-overlay.png +0 -0
  47. package/.playwright-mcp/test-5-morphing-dialog-scrolled.png +0 -0
  48. package/.playwright-mcp/test-6-sheet-restored-after-dialog-close.png +0 -0
  49. package/.playwright-mcp/wheel-scroll-fixed.png +0 -0
  50. package/Screenshots/bead-detail.png +0 -0
  51. package/Screenshots/dashboard.png +0 -0
  52. package/Screenshots/kanban-board.png +0 -0
  53. package/components.json +0 -27
  54. package/logo/logo.svg +0 -1
  55. package/next.config.js +0 -9
  56. package/npm/README.md +0 -37
  57. package/npm/package.json +0 -20
  58. package/postcss.config.js +0 -6
  59. package/public/logo.svg +0 -1
  60. package/restart.sh +0 -5
  61. package/server/Cargo.lock +0 -1685
  62. package/server/Cargo.toml +0 -24
  63. package/server/src/db.rs +0 -570
  64. package/server/src/main.rs +0 -141
  65. package/server/src/routes/beads.rs +0 -413
  66. package/server/src/routes/cli.rs +0 -150
  67. package/server/src/routes/fs.rs +0 -360
  68. package/server/src/routes/git.rs +0 -169
  69. package/server/src/routes/mod.rs +0 -107
  70. package/server/src/routes/projects.rs +0 -177
  71. package/server/src/routes/watch.rs +0 -211
  72. package/src/app/globals.css +0 -101
  73. package/src/app/layout.tsx +0 -36
  74. package/src/app/page.tsx +0 -348
  75. package/src/app/project/kanban-board.tsx +0 -356
  76. package/src/app/project/page.tsx +0 -18
  77. package/src/app/settings/page.tsx +0 -224
  78. package/src/components/Beams.css +0 -5
  79. package/src/components/Beams.jsx +0 -307
  80. package/src/components/Galaxy.css +0 -5
  81. package/src/components/Galaxy.jsx +0 -333
  82. package/src/components/activity-timeline.tsx +0 -172
  83. package/src/components/add-project-dialog.tsx +0 -219
  84. package/src/components/bead-card.tsx +0 -196
  85. package/src/components/bead-detail.tsx +0 -306
  86. package/src/components/color-picker.tsx +0 -101
  87. package/src/components/comment-input.tsx +0 -155
  88. package/src/components/comment-list.tsx +0 -147
  89. package/src/components/dependency-badge.tsx +0 -106
  90. package/src/components/design-doc-dialog.tsx +0 -58
  91. package/src/components/design-doc-preview.tsx +0 -97
  92. package/src/components/design-doc-viewer.tsx +0 -199
  93. package/src/components/editable-project-name.tsx +0 -178
  94. package/src/components/epic-card.tsx +0 -263
  95. package/src/components/folder-browser.tsx +0 -273
  96. package/src/components/footer.tsx +0 -27
  97. package/src/components/kanban/default.tsx +0 -184
  98. package/src/components/kanban-column.tsx +0 -167
  99. package/src/components/project-card.tsx +0 -191
  100. package/src/components/quick-filter-bar.tsx +0 -279
  101. package/src/components/scan-directory-dialog.tsx +0 -368
  102. package/src/components/status-donut.tsx +0 -197
  103. package/src/components/subtask-list.tsx +0 -128
  104. package/src/components/tag-picker.tsx +0 -252
  105. package/src/components/ui/.gitkeep +0 -0
  106. package/src/components/ui/alert-dialog.tsx +0 -141
  107. package/src/components/ui/avatar.tsx +0 -67
  108. package/src/components/ui/badge.tsx +0 -230
  109. package/src/components/ui/button.tsx +0 -433
  110. package/src/components/ui/card/index.tsx +0 -24
  111. package/src/components/ui/card/roiui-card.module.css +0 -197
  112. package/src/components/ui/card/roiui-card.tsx +0 -154
  113. package/src/components/ui/card/shadcn-card.tsx +0 -76
  114. package/src/components/ui/chart.tsx +0 -369
  115. package/src/components/ui/dialog.tsx +0 -122
  116. package/src/components/ui/dropdown-menu.tsx +0 -201
  117. package/src/components/ui/input.tsx +0 -22
  118. package/src/components/ui/kanban.tsx +0 -522
  119. package/src/components/ui/morphing-dialog.tsx +0 -457
  120. package/src/components/ui/popover.tsx +0 -33
  121. package/src/components/ui/progress.tsx +0 -28
  122. package/src/components/ui/scroll-area.tsx +0 -48
  123. package/src/components/ui/select.tsx +0 -159
  124. package/src/components/ui/separator.tsx +0 -31
  125. package/src/components/ui/sheet.tsx +0 -142
  126. package/src/components/ui/skeleton.tsx +0 -15
  127. package/src/components/ui/toast.tsx +0 -129
  128. package/src/components/ui/toaster.tsx +0 -35
  129. package/src/components/ui/tooltip.tsx +0 -30
  130. package/src/hooks/.gitkeep +0 -0
  131. package/src/hooks/use-bead-filters.ts +0 -261
  132. package/src/hooks/use-beads.ts +0 -162
  133. package/src/hooks/use-branch-statuses.ts +0 -161
  134. package/src/hooks/use-epics.ts +0 -173
  135. package/src/hooks/use-file-watcher.ts +0 -111
  136. package/src/hooks/use-keyboard-navigation.ts +0 -282
  137. package/src/hooks/use-project.ts +0 -61
  138. package/src/hooks/use-projects.ts +0 -93
  139. package/src/hooks/use-toast.ts +0 -194
  140. package/src/hooks/useClickOutside.tsx +0 -26
  141. package/src/lib/.gitkeep +0 -0
  142. package/src/lib/api.ts +0 -186
  143. package/src/lib/beads-parser.ts +0 -252
  144. package/src/lib/cli.ts +0 -193
  145. package/src/lib/db.ts +0 -145
  146. package/src/lib/design-doc.ts +0 -74
  147. package/src/lib/epic-parser.ts +0 -242
  148. package/src/lib/git.ts +0 -102
  149. package/src/lib/utils.ts +0 -12
  150. package/src/types/index.ts +0 -107
  151. package/tailwind.config.ts +0 -85
  152. package/tsconfig.json +0 -26
  153. /package/{npm/bin → bin}/cli.js +0 -0
  154. /package/{npm/scripts → scripts}/postinstall.js +0 -0
@@ -1,252 +0,0 @@
1
- "use client";
2
-
3
- import * as React from "react";
4
- import { Plus, Check, X } from "lucide-react";
5
- import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
6
- import { Button } from "@/components/ui/button";
7
- import { Badge } from "@/components/ui/badge";
8
- import { Input } from "@/components/ui/input";
9
- import { ColorPicker } from "@/components/color-picker";
10
- import { cn } from "@/lib/utils";
11
- import type { Tag } from "@/lib/db";
12
- import {
13
- getTags,
14
- createTag,
15
- addTagToProject,
16
- removeTagFromProject,
17
- } from "@/lib/db";
18
-
19
- interface TagPickerProps {
20
- projectId: string;
21
- projectTags: Tag[];
22
- onTagsChange: (tags: Tag[]) => void;
23
- className?: string;
24
- }
25
-
26
- export function TagPicker({
27
- projectId,
28
- projectTags,
29
- onTagsChange,
30
- className,
31
- }: TagPickerProps) {
32
- const [isOpen, setIsOpen] = React.useState(false);
33
- const [allTags, setAllTags] = React.useState<Tag[]>([]);
34
- const [isCreating, setIsCreating] = React.useState(false);
35
- const [newTagName, setNewTagName] = React.useState("");
36
- const [newTagColor, setNewTagColor] = React.useState("#3b82f6");
37
- const [isLoading, setIsLoading] = React.useState(false);
38
-
39
- // Load all tags when popover opens
40
- React.useEffect(() => {
41
- if (isOpen) {
42
- loadTags();
43
- }
44
- }, [isOpen]);
45
-
46
- const loadTags = async () => {
47
- try {
48
- const tags = await getTags();
49
- setAllTags(tags);
50
- } catch (error) {
51
- console.error("Failed to load tags:", error);
52
- }
53
- };
54
-
55
- const isTagSelected = (tagId: string) => {
56
- return projectTags.some((t) => t.id === tagId);
57
- };
58
-
59
- const handleToggleTag = async (tag: Tag) => {
60
- setIsLoading(true);
61
- try {
62
- if (isTagSelected(tag.id)) {
63
- await removeTagFromProject(projectId, tag.id);
64
- onTagsChange(projectTags.filter((t) => t.id !== tag.id));
65
- } else {
66
- await addTagToProject(projectId, tag.id);
67
- onTagsChange([...projectTags, tag]);
68
- }
69
- } catch (error) {
70
- console.error("Failed to toggle tag:", error);
71
- } finally {
72
- setIsLoading(false);
73
- }
74
- };
75
-
76
- const handleCreateTag = async () => {
77
- if (!newTagName.trim()) return;
78
-
79
- setIsLoading(true);
80
- try {
81
- const newTag = await createTag({
82
- name: newTagName.trim(),
83
- color: newTagColor,
84
- });
85
- setAllTags([...allTags, newTag]);
86
- // Auto-add to project
87
- await addTagToProject(projectId, newTag.id);
88
- onTagsChange([...projectTags, newTag]);
89
- // Reset form
90
- setNewTagName("");
91
- setNewTagColor("#3b82f6");
92
- setIsCreating(false);
93
- } catch (error) {
94
- console.error("Failed to create tag:", error);
95
- } finally {
96
- setIsLoading(false);
97
- }
98
- };
99
-
100
- const handleCancelCreate = () => {
101
- setNewTagName("");
102
- setNewTagColor("#3b82f6");
103
- setIsCreating(false);
104
- };
105
-
106
- return (
107
- <Popover open={isOpen} onOpenChange={setIsOpen}>
108
- <PopoverTrigger asChild>
109
- <Button
110
- variant="ghost"
111
- size="sm"
112
- className={cn(
113
- "h-6 w-6 p-0 rounded-full",
114
- "hover:bg-zinc-100 dark:hover:bg-zinc-700",
115
- className
116
- )}
117
- onClick={(e) => {
118
- // Only stop propagation to prevent Link navigation
119
- // Do NOT call e.preventDefault() - let Radix handle the click
120
- e.stopPropagation();
121
- }}
122
- >
123
- <Plus className="h-4 w-4 text-zinc-400" aria-hidden="true" />
124
- <span className="sr-only">Add tag</span>
125
- </Button>
126
- </PopoverTrigger>
127
- <PopoverContent
128
- className="w-64 p-2"
129
- align="start"
130
- onClick={(e) => e.stopPropagation()}
131
- >
132
- <div className="space-y-2">
133
- {/* Existing tags list */}
134
- {allTags.length > 0 && (
135
- <div className="space-y-1">
136
- {allTags.map((tag) => (
137
- <button
138
- key={tag.id}
139
- className={cn(
140
- "flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-left text-sm transition-colors",
141
- "hover:bg-zinc-100 dark:hover:bg-zinc-700",
142
- isLoading && "opacity-50 pointer-events-none"
143
- )}
144
- onClick={() => handleToggleTag(tag)}
145
- disabled={isLoading}
146
- type="button"
147
- aria-pressed={isTagSelected(tag.id)}
148
- >
149
- <div
150
- className="h-3 w-3 rounded-full shrink-0"
151
- style={{ backgroundColor: tag.color }}
152
- />
153
- <span className="flex-1 truncate">{tag.name}</span>
154
- {isTagSelected(tag.id) && (
155
- <Check className="h-4 w-4 text-green-600 shrink-0" aria-hidden="true" />
156
- )}
157
- </button>
158
- ))}
159
- </div>
160
- )}
161
-
162
- {/* Divider */}
163
- {allTags.length > 0 && <div className="border-t border-zinc-200 dark:border-zinc-700" />}
164
-
165
- {/* Create new tag section */}
166
- {isCreating ? (
167
- <div className="space-y-2 p-1">
168
- <div className="flex items-center gap-2">
169
- <ColorPicker
170
- value={newTagColor}
171
- onChange={setNewTagColor}
172
- />
173
- <Input
174
- value={newTagName}
175
- onChange={(e) => setNewTagName(e.target.value)}
176
- placeholder="Tag name"
177
- aria-label="Tag name"
178
- className="h-8 text-sm"
179
- autoFocus
180
- onKeyDown={(e) => {
181
- if (e.key === "Enter") {
182
- e.preventDefault();
183
- handleCreateTag();
184
- } else if (e.key === "Escape") {
185
- handleCancelCreate();
186
- }
187
- }}
188
- />
189
- </div>
190
- <div className="flex items-center gap-1">
191
- <Button
192
- size="sm"
193
- className="h-7 flex-1"
194
- onClick={handleCreateTag}
195
- disabled={!newTagName.trim() || isLoading}
196
- >
197
- Create
198
- </Button>
199
- <Button
200
- size="sm"
201
- variant="ghost"
202
- className="h-7"
203
- onClick={handleCancelCreate}
204
- aria-label="Cancel"
205
- >
206
- <X className="h-4 w-4" aria-hidden="true" />
207
- </Button>
208
- </div>
209
- </div>
210
- ) : (
211
- <button
212
- className="flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-left text-sm text-zinc-600 dark:text-zinc-400 transition-colors hover:bg-zinc-100 dark:hover:bg-zinc-700"
213
- onClick={() => setIsCreating(true)}
214
- type="button"
215
- >
216
- <Plus className="h-4 w-4" aria-hidden="true" />
217
- <span>Create new tag</span>
218
- </button>
219
- )}
220
- </div>
221
- </PopoverContent>
222
- </Popover>
223
- );
224
- }
225
-
226
- interface TagBadgeListProps {
227
- tags: Tag[];
228
- className?: string;
229
- }
230
-
231
- export function TagBadgeList({ tags, className }: TagBadgeListProps) {
232
- if (tags.length === 0) return null;
233
-
234
- return (
235
- <div className={cn("flex flex-wrap gap-1.5", className)}>
236
- {tags.map((tag) => (
237
- <Badge
238
- key={tag.id}
239
- variant="secondary"
240
- className="text-xs"
241
- style={{
242
- backgroundColor: `${tag.color}20`,
243
- color: tag.color,
244
- borderColor: tag.color,
245
- }}
246
- >
247
- {tag.name}
248
- </Badge>
249
- ))}
250
- </div>
251
- );
252
- }
File without changes
@@ -1,141 +0,0 @@
1
- "use client"
2
-
3
- import * as React from "react"
4
- import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog"
5
-
6
- import { cn } from "@/lib/utils"
7
- import { buttonVariants } from "@/components/ui/button"
8
-
9
- const AlertDialog = AlertDialogPrimitive.Root
10
-
11
- const AlertDialogTrigger = AlertDialogPrimitive.Trigger
12
-
13
- const AlertDialogPortal = AlertDialogPrimitive.Portal
14
-
15
- const AlertDialogOverlay = React.forwardRef<
16
- React.ElementRef<typeof AlertDialogPrimitive.Overlay>,
17
- React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Overlay>
18
- >(({ className, ...props }, ref) => (
19
- <AlertDialogPrimitive.Overlay
20
- className={cn(
21
- "fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
22
- className
23
- )}
24
- {...props}
25
- ref={ref}
26
- />
27
- ))
28
- AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName
29
-
30
- const AlertDialogContent = React.forwardRef<
31
- React.ElementRef<typeof AlertDialogPrimitive.Content>,
32
- React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Content>
33
- >(({ className, ...props }, ref) => (
34
- <AlertDialogPortal>
35
- <AlertDialogOverlay />
36
- <AlertDialogPrimitive.Content
37
- ref={ref}
38
- className={cn(
39
- "fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border border-zinc-800 bg-zinc-900 p-6 shadow-lg duration-200 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-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
40
- className
41
- )}
42
- {...props}
43
- />
44
- </AlertDialogPortal>
45
- ))
46
- AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName
47
-
48
- const AlertDialogHeader = ({
49
- className,
50
- ...props
51
- }: React.HTMLAttributes<HTMLDivElement>) => (
52
- <div
53
- className={cn(
54
- "flex flex-col space-y-2 text-center sm:text-left",
55
- className
56
- )}
57
- {...props}
58
- />
59
- )
60
- AlertDialogHeader.displayName = "AlertDialogHeader"
61
-
62
- const AlertDialogFooter = ({
63
- className,
64
- ...props
65
- }: React.HTMLAttributes<HTMLDivElement>) => (
66
- <div
67
- className={cn(
68
- "flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
69
- className
70
- )}
71
- {...props}
72
- />
73
- )
74
- AlertDialogFooter.displayName = "AlertDialogFooter"
75
-
76
- const AlertDialogTitle = React.forwardRef<
77
- React.ElementRef<typeof AlertDialogPrimitive.Title>,
78
- React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Title>
79
- >(({ className, ...props }, ref) => (
80
- <AlertDialogPrimitive.Title
81
- ref={ref}
82
- className={cn("text-lg font-semibold text-zinc-100", className)}
83
- {...props}
84
- />
85
- ))
86
- AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName
87
-
88
- const AlertDialogDescription = React.forwardRef<
89
- React.ElementRef<typeof AlertDialogPrimitive.Description>,
90
- React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Description>
91
- >(({ className, ...props }, ref) => (
92
- <AlertDialogPrimitive.Description
93
- ref={ref}
94
- className={cn("text-sm text-zinc-400", className)}
95
- {...props}
96
- />
97
- ))
98
- AlertDialogDescription.displayName =
99
- AlertDialogPrimitive.Description.displayName
100
-
101
- const AlertDialogAction = React.forwardRef<
102
- React.ElementRef<typeof AlertDialogPrimitive.Action>,
103
- React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Action>
104
- >(({ className, ...props }, ref) => (
105
- <AlertDialogPrimitive.Action
106
- ref={ref}
107
- className={cn(buttonVariants(), className)}
108
- {...props}
109
- />
110
- ))
111
- AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName
112
-
113
- const AlertDialogCancel = React.forwardRef<
114
- React.ElementRef<typeof AlertDialogPrimitive.Cancel>,
115
- React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Cancel>
116
- >(({ className, ...props }, ref) => (
117
- <AlertDialogPrimitive.Cancel
118
- ref={ref}
119
- className={cn(
120
- buttonVariants({ variant: "outline" }),
121
- "mt-2 sm:mt-0",
122
- className
123
- )}
124
- {...props}
125
- />
126
- ))
127
- AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName
128
-
129
- export {
130
- AlertDialog,
131
- AlertDialogPortal,
132
- AlertDialogOverlay,
133
- AlertDialogTrigger,
134
- AlertDialogContent,
135
- AlertDialogHeader,
136
- AlertDialogFooter,
137
- AlertDialogTitle,
138
- AlertDialogDescription,
139
- AlertDialogAction,
140
- AlertDialogCancel,
141
- }
@@ -1,67 +0,0 @@
1
- 'use client';
2
-
3
- import * as React from 'react';
4
- import { cn } from '@/lib/utils';
5
- import { cva, VariantProps } from 'class-variance-authority';
6
- import { Avatar as AvatarPrimitive } from 'radix-ui';
7
-
8
- const avatarStatusVariants = cva('flex items-center rounded-full size-2 border-2 border-background', {
9
- variants: {
10
- variant: {
11
- online: 'bg-green-600',
12
- offline: 'bg-zinc-400 dark:bg-zinc-500',
13
- busy: 'bg-yellow-600',
14
- away: 'bg-blue-600',
15
- },
16
- },
17
- defaultVariants: {
18
- variant: 'online',
19
- },
20
- });
21
-
22
- function Avatar({ className, ...props }: React.ComponentProps<typeof AvatarPrimitive.Root>) {
23
- return (
24
- <AvatarPrimitive.Root data-slot="avatar" className={cn('relative flex shrink-0 size-10', className)} {...props} />
25
- );
26
- }
27
-
28
- function AvatarImage({ className, ...props }: React.ComponentProps<typeof AvatarPrimitive.Image>) {
29
- return (
30
- <div className={cn('relative overflow-hidden rounded-full', className)}>
31
- <AvatarPrimitive.Image data-slot="avatar-image" className={cn('aspect-square h-full w-full')} {...props} />
32
- </div>
33
- );
34
- }
35
-
36
- function AvatarFallback({ className, ...props }: React.ComponentProps<typeof AvatarPrimitive.Fallback>) {
37
- return (
38
- <AvatarPrimitive.Fallback
39
- data-slot="avatar-fallback"
40
- className={cn(
41
- 'flex h-full w-full items-center justify-center rounded-full border border-border bg-accent text-accent-foreground text-xs',
42
- className,
43
- )}
44
- {...props}
45
- />
46
- );
47
- }
48
-
49
- function AvatarIndicator({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
50
- return (
51
- <div
52
- data-slot="avatar-indicator"
53
- className={cn('absolute flex size-6 items-center justify-center', className)}
54
- {...props}
55
- />
56
- );
57
- }
58
-
59
- function AvatarStatus({
60
- className,
61
- variant,
62
- ...props
63
- }: React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof avatarStatusVariants>) {
64
- return <div data-slot="avatar-status" className={cn(avatarStatusVariants({ variant }), className)} {...props} />;
65
- }
66
-
67
- export { Avatar, AvatarFallback, AvatarImage, AvatarIndicator, AvatarStatus, avatarStatusVariants };
@@ -1,230 +0,0 @@
1
- import * as React from 'react';
2
- import { cn } from '@/lib/utils';
3
- import { cva, type VariantProps } from 'class-variance-authority';
4
- import { Slot as SlotPrimitive } from 'radix-ui';
5
-
6
- export interface BadgeProps extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof badgeVariants> {
7
- asChild?: boolean;
8
- dotClassName?: string;
9
- disabled?: boolean;
10
- }
11
-
12
- export interface BadgeButtonProps
13
- extends React.ButtonHTMLAttributes<HTMLDivElement>,
14
- VariantProps<typeof badgeButtonVariants> {
15
- asChild?: boolean;
16
- }
17
-
18
- export type BadgeDotProps = React.HTMLAttributes<HTMLSpanElement>;
19
-
20
- const badgeVariants = cva(
21
- 'inline-flex items-center whitespace-nowrap justify-center border border-transparent font-medium focus:outline-hidden focus:ring-2 focus:ring-ring focus:ring-offset-2 [&_svg]:-ms-px [&_svg]:shrink-0',
22
- {
23
- variants: {
24
- variant: {
25
- primary: 'bg-primary text-primary-foreground',
26
- secondary: 'bg-secondary text-secondary-foreground',
27
- success:
28
- 'bg-[var(--color-success-accent,var(--color-green-500))] text-[var(--color-success-foreground,var(--color-white))]',
29
- warning:
30
- 'bg-[var(--color-warning-accent,var(--color-yellow-500))] text-[var(--color-warning-foreground,var(--color-white))]',
31
- info: 'bg-[var(--color-info-accent,var(--color-violet-500))] text-[var(--color-info-foreground,var(--color-white))]',
32
- outline: 'bg-transparent border border-border text-secondary-foreground',
33
- destructive: 'bg-destructive text-destructive-foreground',
34
- },
35
- appearance: {
36
- default: '',
37
- light: '',
38
- outline: '',
39
- ghost: 'border-transparent bg-transparent',
40
- },
41
- disabled: {
42
- true: 'opacity-50 pointer-events-none',
43
- },
44
- size: {
45
- lg: 'rounded-md px-[0.5rem] h-7 min-w-7 gap-1.5 text-xs [&_svg]:size-3.5',
46
- md: 'rounded-md px-[0.45rem] h-6 min-w-6 gap-1.5 text-xs [&_svg]:size-3.5 ',
47
- sm: 'rounded-sm px-[0.325rem] h-5 min-w-5 gap-1 text-[0.6875rem] leading-[0.75rem] [&_svg]:size-3',
48
- xs: 'rounded-sm px-[0.25rem] h-4 min-w-4 gap-1 text-[0.625rem] leading-[0.5rem] [&_svg]:size-3',
49
- },
50
- shape: {
51
- default: '',
52
- circle: 'rounded-full',
53
- },
54
- },
55
- compoundVariants: [
56
- /* Light */
57
- {
58
- variant: 'primary',
59
- appearance: 'light',
60
- className:
61
- 'text-[var(--color-primary-accent,var(--color-blue-700))] bg-[var(--color-primary-soft,var(--color-blue-50))] dark:bg-[var(--color-primary-soft,var(--color-blue-950))] dark:text-[var(--color-primary-soft,var(--color-blue-600))]',
62
- },
63
- {
64
- variant: 'secondary',
65
- appearance: 'light',
66
- className: 'bg-secondary dark:bg-secondary/50 text-secondary-foreground',
67
- },
68
- {
69
- variant: 'success',
70
- appearance: 'light',
71
- className:
72
- 'text-[var(--color-success-accent,var(--color-green-800))] bg-[var(--color-success-soft,var(--color-green-100))] dark:bg-[var(--color-success-soft,var(--color-green-950))] dark:text-[var(--color-success-soft,var(--color-green-600))]',
73
- },
74
- {
75
- variant: 'warning',
76
- appearance: 'light',
77
- className:
78
- 'text-[var(--color-warning-accent,var(--color-yellow-700))] bg-[var(--color-warning-soft,var(--color-yellow-100))] dark:bg-[var(--color-warning-soft,var(--color-yellow-950))] dark:text-[var(--color-warning-soft,var(--color-yellow-600))]',
79
- },
80
- {
81
- variant: 'info',
82
- appearance: 'light',
83
- className:
84
- 'text-[var(--color-info-accent,var(--color-violet-700))] bg-[var(--color-info-soft,var(--color-violet-100))] dark:bg-[var(--color-info-soft,var(--color-violet-950))] dark:text-[var(--color-info-soft,var(--color-violet-400))]',
85
- },
86
- {
87
- variant: 'destructive',
88
- appearance: 'light',
89
- className:
90
- 'text-[var(--color-destructive-accent,var(--color-red-700))] bg-[var(--color-destructive-soft,var(--color-red-50))] dark:bg-[var(--color-destructive-soft,var(--color-red-950))] dark:text-[var(--color-destructive-soft,var(--color-red-600))]',
91
- },
92
- /* Outline */
93
- {
94
- variant: 'primary',
95
- appearance: 'outline',
96
- className:
97
- 'text-[var(--color-primary-accent,var(--color-blue-700))] border-[var(--color-primary-soft,var(--color-blue-100))] bg-[var(--color-primary-soft,var(--color-blue-50))] dark:bg-[var(--color-primary-soft,var(--color-blue-950))] dark:border-[var(--color-primary-soft,var(--color-blue-900))] dark:text-[var(--color-primary-soft,var(--color-blue-600))]',
98
- },
99
- {
100
- variant: 'success',
101
- appearance: 'outline',
102
- className:
103
- 'text-[var(--color-success-accent,var(--color-green-700))] border-[var(--color-success-soft,var(--color-green-200))] bg-[var(--color-success-soft,var(--color-green-50))] dark:bg-[var(--color-success-soft,var(--color-green-950))] dark:border-[var(--color-success-soft,var(--color-green-900))] dark:text-[var(--color-success-soft,var(--color-green-600))]',
104
- },
105
- {
106
- variant: 'warning',
107
- appearance: 'outline',
108
- className:
109
- 'text-[var(--color-warning-accent,var(--color-yellow-700))] border-[var(--color-warning-soft,var(--color-yellow-200))] bg-[var(--color-warning-soft,var(--color-yellow-50))] dark:bg-[var(--color-warning-soft,var(--color-yellow-950))] dark:border-[var(--color-warning-soft,var(--color-yellow-900))] dark:text-[var(--color-warning-soft,var(--color-yellow-600))]',
110
- },
111
- {
112
- variant: 'info',
113
- appearance: 'outline',
114
- className:
115
- 'text-[var(--color-info-accent,var(--color-violet-700))] border-[var(--color-info-soft,var(--color-violet-100))] bg-[var(--color-info-soft,var(--color-violet-50))] dark:bg-[var(--color-info-soft,var(--color-violet-950))] dark:border-[var(--color-info-soft,var(--color-violet-900))] dark:text-[var(--color-info-soft,var(--color-violet-400))]',
116
- },
117
- {
118
- variant: 'destructive',
119
- appearance: 'outline',
120
- className:
121
- 'text-[var(--color-destructive-accent,var(--color-red-700))] border-[var(--color-destructive-soft,var(--color-red-100))] bg-[var(--color-destructive-soft,var(--color-red-50))] dark:bg-[var(--color-destructive-soft,var(--color-red-950))] dark:border-[var(--color-destructive-soft,var(--color-red-900))] dark:text-[var(--color-destructive-soft,var(--color-red-600))]',
122
- },
123
- /* Ghost */
124
- {
125
- variant: 'primary',
126
- appearance: 'ghost',
127
- className: 'text-primary',
128
- },
129
- {
130
- variant: 'secondary',
131
- appearance: 'ghost',
132
- className: 'text-secondary-foreground',
133
- },
134
- {
135
- variant: 'success',
136
- appearance: 'ghost',
137
- className: 'text-[var(--color-success-accent,var(--color-green-500))]',
138
- },
139
- {
140
- variant: 'warning',
141
- appearance: 'ghost',
142
- className: 'text-[var(--color-warning-accent,var(--color-yellow-500))]',
143
- },
144
- {
145
- variant: 'info',
146
- appearance: 'ghost',
147
- className: 'text-[var(--color-info-accent,var(--color-violet-500))]',
148
- },
149
- {
150
- variant: 'destructive',
151
- appearance: 'ghost',
152
- className: 'text-destructive',
153
- },
154
-
155
- { size: 'lg', appearance: 'ghost', className: 'px-0' },
156
- { size: 'md', appearance: 'ghost', className: 'px-0' },
157
- { size: 'sm', appearance: 'ghost', className: 'px-0' },
158
- { size: 'xs', appearance: 'ghost', className: 'px-0' },
159
- ],
160
- defaultVariants: {
161
- variant: 'primary',
162
- appearance: 'default',
163
- size: 'md',
164
- },
165
- },
166
- );
167
-
168
- const badgeButtonVariants = cva(
169
- 'cursor-pointer transition-all inline-flex items-center justify-center leading-none size-3.5 [&>svg]:opacity-100! [&>svg]:size-3.5! p-0 rounded-md -me-0.5 opacity-60 hover:opacity-100',
170
- {
171
- variants: {
172
- variant: {
173
- default: '',
174
- },
175
- },
176
- defaultVariants: {
177
- variant: 'default',
178
- },
179
- },
180
- );
181
-
182
- function Badge({
183
- className,
184
- variant,
185
- size,
186
- appearance,
187
- shape,
188
- asChild = false,
189
- disabled,
190
- ...props
191
- }: React.ComponentProps<'span'> & VariantProps<typeof badgeVariants> & { asChild?: boolean }) {
192
- const Comp = asChild ? SlotPrimitive.Slot : 'span';
193
-
194
- return (
195
- <Comp
196
- data-slot="badge"
197
- className={cn(badgeVariants({ variant, size, appearance, shape, disabled }), className)}
198
- {...props}
199
- />
200
- );
201
- }
202
-
203
- function BadgeButton({
204
- className,
205
- variant,
206
- asChild = false,
207
- ...props
208
- }: React.ComponentProps<'button'> & VariantProps<typeof badgeButtonVariants> & { asChild?: boolean }) {
209
- const Comp = asChild ? SlotPrimitive.Slot : 'span';
210
- return (
211
- <Comp
212
- data-slot="badge-button"
213
- className={cn(badgeButtonVariants({ variant, className }))}
214
- role="button"
215
- {...props}
216
- />
217
- );
218
- }
219
-
220
- function BadgeDot({ className, ...props }: React.ComponentProps<'span'>) {
221
- return (
222
- <span
223
- data-slot="badge-dot"
224
- className={cn('size-1.5 rounded-full bg-[currentColor] opacity-75', className)}
225
- {...props}
226
- />
227
- );
228
- }
229
-
230
- export { Badge, BadgeButton, BadgeDot, badgeVariants };