@mostrom/app-shell 0.1.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 (142) hide show
  1. package/.claude/ralph-loop.local.md +9 -0
  2. package/README.md +172 -0
  3. package/bin/init.js +269 -0
  4. package/bun.lock +401 -0
  5. package/components.json +28 -0
  6. package/package.json +74 -0
  7. package/scripts/publish-npm.sh +202 -0
  8. package/src/AppShell.tsx +847 -0
  9. package/src/components/PageHeader.tsx +160 -0
  10. package/src/components/data-table/README.md +447 -0
  11. package/src/components/data-table/data-table-preferences.tsx +184 -0
  12. package/src/components/data-table/data-table-toolbar.tsx +118 -0
  13. package/src/components/data-table/data-table.tsx +37 -0
  14. package/src/components/data-table/index.ts +32 -0
  15. package/src/components/global-header/AllServicesButton.tsx +127 -0
  16. package/src/components/global-header/CategoriesButton.tsx +120 -0
  17. package/src/components/global-header/GlobalHeader.tsx +59 -0
  18. package/src/components/global-header/GlobalHeaderSearch.tsx +57 -0
  19. package/src/components/global-header/HeaderUtilities.tsx +243 -0
  20. package/src/components/global-header/ServicesMenu.tsx +246 -0
  21. package/src/components/layout/AppBreadcrumb.tsx +70 -0
  22. package/src/components/layout/AppFlashbar.tsx +95 -0
  23. package/src/components/layout/AppLayout.tsx +271 -0
  24. package/src/components/layout/AppNavigation.tsx +313 -0
  25. package/src/components/layout/AppSidebar.tsx +229 -0
  26. package/src/components/patterns/index.ts +14 -0
  27. package/src/components/patterns/p-alert-5.tsx +19 -0
  28. package/src/components/patterns/p-autocomplete-5.tsx +89 -0
  29. package/src/components/patterns/p-breadcrumb-1.tsx +28 -0
  30. package/src/components/patterns/p-button-42.tsx +37 -0
  31. package/src/components/patterns/p-button-51.tsx +14 -0
  32. package/src/components/patterns/p-button-6.tsx +5 -0
  33. package/src/components/patterns/p-calendar-1.tsx +18 -0
  34. package/src/components/patterns/p-card-1.tsx +33 -0
  35. package/src/components/patterns/p-card-2.tsx +26 -0
  36. package/src/components/patterns/p-card-5.tsx +31 -0
  37. package/src/components/patterns/p-collapsible-7.tsx +121 -0
  38. package/src/components/patterns/p-command-6.tsx +113 -0
  39. package/src/components/patterns/p-dialog-1.tsx +56 -0
  40. package/src/components/patterns/p-dropdown-menu-1.tsx +38 -0
  41. package/src/components/patterns/p-dropdown-menu-11.tsx +122 -0
  42. package/src/components/patterns/p-dropdown-menu-14.tsx +165 -0
  43. package/src/components/patterns/p-dropdown-menu-9.tsx +108 -0
  44. package/src/components/patterns/p-empty-2.tsx +34 -0
  45. package/src/components/patterns/p-file-upload-1.tsx +72 -0
  46. package/src/components/patterns/p-filters-1.tsx +666 -0
  47. package/src/components/patterns/p-frame-2.tsx +26 -0
  48. package/src/components/patterns/p-tabs-2.tsx +129 -0
  49. package/src/components/reui/alert.tsx +92 -0
  50. package/src/components/reui/autocomplete.tsx +343 -0
  51. package/src/components/reui/badge.tsx +87 -0
  52. package/src/components/reui/data-grid/data-grid-column-filter.tsx +165 -0
  53. package/src/components/reui/data-grid/data-grid-column-header.tsx +339 -0
  54. package/src/components/reui/data-grid/data-grid-column-visibility.tsx +55 -0
  55. package/src/components/reui/data-grid/data-grid-pagination.tsx +224 -0
  56. package/src/components/reui/data-grid/data-grid-table-dnd-rows.tsx +260 -0
  57. package/src/components/reui/data-grid/data-grid-table-dnd.tsx +253 -0
  58. package/src/components/reui/data-grid/data-grid-table.tsx +639 -0
  59. package/src/components/reui/data-grid/data-grid.tsx +209 -0
  60. package/src/components/reui/date-selector.tsx +1330 -0
  61. package/src/components/reui/filters.tsx +1869 -0
  62. package/src/components/reui/frame.tsx +134 -0
  63. package/src/components/reui/index.ts +17 -0
  64. package/src/components/reui/timeline.tsx +219 -0
  65. package/src/components/search/Autocomplete.tsx +183 -0
  66. package/src/components/search/AutocompleteClient.tsx +293 -0
  67. package/src/components/search/GlobalSearch.tsx +187 -0
  68. package/src/components/section-drawer/deal-drawer-content.tsx +891 -0
  69. package/src/components/section-drawer/index.ts +19 -0
  70. package/src/components/section-drawer/section-drawer.css +665 -0
  71. package/src/components/section-drawer/section-drawer.tsx +467 -0
  72. package/src/components/sectioned-list-board/README.md +78 -0
  73. package/src/components/sectioned-list-board/board-card-content.tsx +340 -0
  74. package/src/components/sectioned-list-board/date-range-filter.tsx +249 -0
  75. package/src/components/sectioned-list-board/index.ts +19 -0
  76. package/src/components/sectioned-list-board/sectioned-list-board.css +564 -0
  77. package/src/components/sectioned-list-board/sectioned-list-board.tsx +731 -0
  78. package/src/components/sectioned-list-board/sortable-card.tsx +314 -0
  79. package/src/components/sectioned-list-board/sortable-section.tsx +319 -0
  80. package/src/components/sectioned-list-board/types.ts +216 -0
  81. package/src/components/sectioned-list-table/README.md +80 -0
  82. package/src/components/sectioned-list-table/index.ts +14 -0
  83. package/src/components/sectioned-list-table/sectioned-list-table.css +534 -0
  84. package/src/components/sectioned-list-table/sectioned-list-table.tsx +740 -0
  85. package/src/components/sectioned-list-table/sortable-column-header.tsx +120 -0
  86. package/src/components/sectioned-list-table/sortable-row.tsx +420 -0
  87. package/src/components/sectioned-list-table/sortable-section.tsx +251 -0
  88. package/src/components/sectioned-list-table/table-cell-content.tsx +129 -0
  89. package/src/components/sectioned-list-table/types.ts +120 -0
  90. package/src/components/sectioned-list-table/use-column-preferences.ts +103 -0
  91. package/src/components/ui/actions-dropdown.tsx +109 -0
  92. package/src/components/ui/assignee-selector.tsx +209 -0
  93. package/src/components/ui/avatar.tsx +107 -0
  94. package/src/components/ui/breadcrumb.tsx +109 -0
  95. package/src/components/ui/button-group.tsx +83 -0
  96. package/src/components/ui/button.tsx +64 -0
  97. package/src/components/ui/calendar.tsx +220 -0
  98. package/src/components/ui/card.tsx +92 -0
  99. package/src/components/ui/chart.tsx +376 -0
  100. package/src/components/ui/checkbox.tsx +30 -0
  101. package/src/components/ui/collapsible.tsx +33 -0
  102. package/src/components/ui/command.tsx +182 -0
  103. package/src/components/ui/context-menu.tsx +250 -0
  104. package/src/components/ui/create-button-group.tsx +128 -0
  105. package/src/components/ui/dialog.tsx +156 -0
  106. package/src/components/ui/drawer.tsx +133 -0
  107. package/src/components/ui/dropdown-menu.tsx +255 -0
  108. package/src/components/ui/empty.tsx +104 -0
  109. package/src/components/ui/field.tsx +248 -0
  110. package/src/components/ui/form.tsx +165 -0
  111. package/src/components/ui/index.ts +37 -0
  112. package/src/components/ui/input-group.tsx +168 -0
  113. package/src/components/ui/input.tsx +21 -0
  114. package/src/components/ui/kbd.tsx +28 -0
  115. package/src/components/ui/label.tsx +22 -0
  116. package/src/components/ui/navigation-menu.tsx +168 -0
  117. package/src/components/ui/page-header.tsx +80 -0
  118. package/src/components/ui/popover.tsx +87 -0
  119. package/src/components/ui/scroll-area.tsx +56 -0
  120. package/src/components/ui/select.tsx +190 -0
  121. package/src/components/ui/separator.tsx +26 -0
  122. package/src/components/ui/sheet.tsx +141 -0
  123. package/src/components/ui/sidebar.tsx +726 -0
  124. package/src/components/ui/skeleton.tsx +13 -0
  125. package/src/components/ui/sonner.tsx +38 -0
  126. package/src/components/ui/switch.tsx +33 -0
  127. package/src/components/ui/tabs.tsx +91 -0
  128. package/src/components/ui/textarea.tsx +18 -0
  129. package/src/components/ui/toggle-group.tsx +83 -0
  130. package/src/components/ui/toggle.tsx +45 -0
  131. package/src/components/ui/tooltip.tsx +57 -0
  132. package/src/hooks/use-copy-to-clipboard.ts +37 -0
  133. package/src/hooks/use-file-upload.ts +415 -0
  134. package/src/hooks/use-mobile.ts +19 -0
  135. package/src/index.ts +95 -0
  136. package/src/lib/utils.ts +6 -0
  137. package/src/styles.css +1859 -0
  138. package/src/urls.ts +83 -0
  139. package/src/vite.d.ts +22 -0
  140. package/src/vite.js +241 -0
  141. package/tsconfig.base.json +18 -0
  142. package/tsconfig.json +24 -0
@@ -0,0 +1,113 @@
1
+ "use client"
2
+
3
+ import { useState } from "react"
4
+ import { Badge } from "@/components/reui/badge"
5
+
6
+ import {
7
+ Avatar,
8
+ AvatarFallback,
9
+ AvatarImage,
10
+ } from "@/components/ui/avatar"
11
+ import { Button } from "@/components/ui/button"
12
+ import {
13
+ Command,
14
+ CommandDialog,
15
+ CommandEmpty,
16
+ CommandGroup,
17
+ CommandInput,
18
+ CommandItem,
19
+ CommandList,
20
+ } from "@/components/ui/command"
21
+
22
+ const users = [
23
+ {
24
+ name: "Alex Johnson",
25
+ email: "alex@example.com",
26
+ avatar:
27
+ "https://images.unsplash.com/photo-1535713875002-d1d0cf377fde?w=96&h=96&dpr=2&q=80",
28
+ role: "Admin",
29
+ },
30
+ {
31
+ name: "Sarah Chen",
32
+ email: "sarah@example.com",
33
+ avatar:
34
+ "https://images.unsplash.com/photo-1519699047748-de8e457a634e?w=96&h=96&dpr=2&q=80",
35
+ role: "Editor",
36
+ },
37
+ {
38
+ name: "David Kim",
39
+ email: "david@example.com",
40
+ avatar:
41
+ "https://images.unsplash.com/photo-1607990281513-2c110a25bd8c?w=96&h=96&dpr=2&q=80",
42
+ role: "Viewer",
43
+ },
44
+ {
45
+ name: "Emma Wilson",
46
+ email: "emma@example.com",
47
+ avatar:
48
+ "https://images.unsplash.com/photo-1485893086445-ed75865251e0?w=96&h=96&dpr=2&q=80",
49
+ role: "Admin",
50
+ },
51
+ {
52
+ name: "Michael Rodriguez",
53
+ email: "michael@example.com",
54
+ avatar:
55
+ "https://images.unsplash.com/photo-1584308972272-9e4e7685e80f?w=96&h=96&dpr=2&q=80",
56
+ role: "Editor",
57
+ },
58
+ ]
59
+
60
+ export function Pattern() {
61
+ const [open, setOpen] = useState(false)
62
+
63
+ return (
64
+ <>
65
+ <Button onClick={() => setOpen(true)} variant="outline">
66
+ Search Users
67
+ </Button>
68
+ <CommandDialog open={open} onOpenChange={setOpen}>
69
+ <Command className="**:data-[selected=true]:bg-muted **:data-selected:bg-transparent">
70
+ <CommandInput placeholder="Search by name or email..." />
71
+ <CommandList>
72
+ <CommandEmpty>No users found.</CommandEmpty>
73
+ <CommandGroup heading="Team Members">
74
+ {users.map((user) => (
75
+ <CommandItem key={user.email} className="gap-2 py-2">
76
+ <Avatar className="size-6 shrink-0">
77
+ <AvatarImage src={user.avatar} alt={user.name} />
78
+ <AvatarFallback className="text-xs">
79
+ {user.name
80
+ .split(" ")
81
+ .map((n) => n[0])
82
+ .join("")}
83
+ </AvatarFallback>
84
+ </Avatar>
85
+ <div className="flex flex-1 flex-col">
86
+ <span className="text-sm font-medium">{user.name}</span>
87
+ <span className="text-muted-foreground text-xs">
88
+ {user.email}
89
+ </span>
90
+ </div>
91
+ <div className="ml-auto" data-slot="command-shortcut">
92
+ <Badge
93
+ variant={
94
+ user.role === "Admin"
95
+ ? "primary-light"
96
+ : user.role === "Editor"
97
+ ? "info-light"
98
+ : "success-light"
99
+ }
100
+ size="sm"
101
+ >
102
+ {user.role}
103
+ </Badge>
104
+ </div>
105
+ </CommandItem>
106
+ ))}
107
+ </CommandGroup>
108
+ </CommandList>
109
+ </Command>
110
+ </CommandDialog>
111
+ </>
112
+ )
113
+ }
@@ -0,0 +1,56 @@
1
+ import { Button } from "@/components/ui/button"
2
+ import {
3
+ Dialog,
4
+ DialogClose,
5
+ DialogContent,
6
+ DialogDescription,
7
+ DialogFooter,
8
+ DialogHeader,
9
+ DialogTitle,
10
+ DialogTrigger,
11
+ } from "@/components/ui/dialog"
12
+ import { Field, FieldGroup, FieldLabel } from "@/components/ui/field"
13
+ import { Input } from "@/components/ui/input"
14
+
15
+ export function Pattern() {
16
+ return (
17
+ <div className="flex items-center justify-center">
18
+ <Dialog>
19
+ <form>
20
+ <DialogTrigger asChild>
21
+ <Button variant="outline">Basic Dialog</Button>
22
+ </DialogTrigger>
23
+ <DialogContent>
24
+ <DialogHeader>
25
+ <DialogTitle>Edit profile</DialogTitle>
26
+ <DialogDescription>
27
+ Make changes to your profile here. Click save when you&apos;re
28
+ done. Your profile will be updated immediately.
29
+ </DialogDescription>
30
+ </DialogHeader>
31
+ <FieldGroup>
32
+ <Field>
33
+ <FieldLabel htmlFor="name-1">Name</FieldLabel>
34
+ <Input id="name-1" name="name" defaultValue="Pedro Duarte" />
35
+ </Field>
36
+ <Field>
37
+ <FieldLabel htmlFor="username-1">Username</FieldLabel>
38
+ <Input
39
+ id="username-1"
40
+ name="username"
41
+ defaultValue="@peduarte"
42
+ />
43
+ </Field>
44
+ </FieldGroup>
45
+ <DialogFooter>
46
+ <DialogClose asChild>
47
+ <Button variant="outline">Cancel</Button>
48
+ </DialogClose>
49
+ <Button type="submit">Save changes</Button>
50
+ </DialogFooter>
51
+ </DialogContent>
52
+ </form>
53
+ </Dialog>
54
+ </div>
55
+ )
56
+ }
@@ -0,0 +1,38 @@
1
+ import { Button } from "@/components/ui/button"
2
+ import {
3
+ DropdownMenu,
4
+ DropdownMenuContent,
5
+ DropdownMenuGroup,
6
+ DropdownMenuItem,
7
+ DropdownMenuLabel,
8
+ DropdownMenuSeparator,
9
+ DropdownMenuTrigger,
10
+ } from "@/components/ui/dropdown-menu"
11
+
12
+ export function Pattern() {
13
+ return (
14
+ <div className="flex items-center justify-center">
15
+ <DropdownMenu>
16
+ <DropdownMenuTrigger asChild>
17
+ <Button variant="outline" className="w-fit">
18
+ Open
19
+ </Button>
20
+ </DropdownMenuTrigger>
21
+ <DropdownMenuContent>
22
+ <DropdownMenuGroup>
23
+ <DropdownMenuLabel>My Account</DropdownMenuLabel>
24
+ <DropdownMenuItem>Profile</DropdownMenuItem>
25
+ <DropdownMenuItem>Billing</DropdownMenuItem>
26
+ <DropdownMenuItem>Settings</DropdownMenuItem>
27
+ </DropdownMenuGroup>
28
+ <DropdownMenuSeparator />
29
+ <DropdownMenuGroup>
30
+ <DropdownMenuItem>GitHub</DropdownMenuItem>
31
+ <DropdownMenuItem>Support</DropdownMenuItem>
32
+ <DropdownMenuItem disabled>API</DropdownMenuItem>
33
+ </DropdownMenuGroup>
34
+ </DropdownMenuContent>
35
+ </DropdownMenu>
36
+ </div>
37
+ )
38
+ }
@@ -0,0 +1,122 @@
1
+ import { Badge } from "@/components/reui/badge"
2
+
3
+ import {
4
+ Avatar,
5
+ AvatarFallback,
6
+ AvatarImage,
7
+ } from "@/components/ui/avatar"
8
+ import { Button } from "@/components/ui/button"
9
+ import {
10
+ DropdownMenu,
11
+ DropdownMenuContent,
12
+ DropdownMenuGroup,
13
+ DropdownMenuItem,
14
+ DropdownMenuLabel,
15
+ DropdownMenuSeparator,
16
+ DropdownMenuTrigger,
17
+ } from "@/components/ui/dropdown-menu"
18
+ import { BellIcon } from "lucide-react"
19
+
20
+ const notifications = [
21
+ {
22
+ id: "1",
23
+ user: "Sarah",
24
+ avatar:
25
+ "https://images.unsplash.com/photo-1519699047748-de8e457a634e?w=96&h=96&dpr=2&q=80",
26
+ initials: "SC",
27
+ action: "commented on",
28
+ target: "Design System v2",
29
+ time: "2 min ago",
30
+ unread: false,
31
+ },
32
+ {
33
+ id: "2",
34
+ user: "James Wilson",
35
+ avatar:
36
+ "https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=96&h=96&dpr=2&q=80",
37
+ initials: "JW",
38
+ action: "shared",
39
+ target: "Q4 Report",
40
+ time: "1 hour ago",
41
+ unread: true,
42
+ },
43
+ {
44
+ id: "3",
45
+ user: "Emily Davis",
46
+ avatar:
47
+ "https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=96&h=96&dpr=2&q=80",
48
+ initials: "ED",
49
+ action: "invited you to",
50
+ target: "Project Alpha",
51
+ time: "3 hours ago",
52
+ unread: false,
53
+ },
54
+ ]
55
+
56
+ export function Pattern() {
57
+ return (
58
+ <div className="flex items-center justify-center">
59
+ <DropdownMenu>
60
+ <DropdownMenuTrigger asChild>
61
+ <Button variant="outline" size="icon" className="relative">
62
+ <BellIcon aria-hidden="true" />
63
+ <Badge
64
+ variant="destructive"
65
+ size="sm"
66
+ className="absolute -top-1.5 -right-2 rounded-full px-1"
67
+ aria-hidden="true"
68
+ >
69
+ 8
70
+ </Badge>
71
+ </Button>
72
+ </DropdownMenuTrigger>
73
+ <DropdownMenuContent className="w-80" align="end" sideOffset={8}>
74
+ <DropdownMenuGroup>
75
+ <DropdownMenuLabel className="flex items-center justify-between">
76
+ <span>Notifications</span>
77
+ <button className="text-foreground text-xs font-normal underline-offset-2 hover:underline">
78
+ Mark all as read
79
+ </button>
80
+ </DropdownMenuLabel>
81
+ <DropdownMenuSeparator />
82
+ <DropdownMenuGroup>
83
+ {notifications.map((notification) => (
84
+ <DropdownMenuItem
85
+ key={notification.id}
86
+ className="flex items-start gap-2 py-1"
87
+ >
88
+ <Avatar className="mt-0.5 size-6 shrink-0">
89
+ <AvatarImage
90
+ src={notification.avatar}
91
+ alt={notification.user}
92
+ />
93
+ <AvatarFallback>{notification.initials}</AvatarFallback>
94
+ </Avatar>
95
+ <div className="flex flex-1 flex-col gap-px">
96
+ <p className="leading-snug">
97
+ <span className="font-medium">{notification.user}</span>{" "}
98
+ <span className="text-muted-foreground">
99
+ {notification.action}
100
+ </span>{" "}
101
+ <span className="font-medium">{notification.target}</span>
102
+ </p>
103
+ <span className="text-muted-foreground">
104
+ {notification.time}
105
+ </span>
106
+ </div>
107
+ {notification.unread && (
108
+ <span className="bg-primary mt-2 size-1.5 shrink-0 rounded-full" />
109
+ )}
110
+ </DropdownMenuItem>
111
+ ))}
112
+ </DropdownMenuGroup>
113
+ <DropdownMenuSeparator />
114
+ <DropdownMenuItem className="justify-center">
115
+ View all notifications
116
+ </DropdownMenuItem>
117
+ </DropdownMenuGroup>
118
+ </DropdownMenuContent>
119
+ </DropdownMenu>
120
+ </div>
121
+ )
122
+ }
@@ -0,0 +1,165 @@
1
+ "use client"
2
+
3
+ import { useState } from "react"
4
+ import { Badge } from "@/components/reui/badge"
5
+
6
+ import { cn } from "@/lib/utils"
7
+ import {
8
+ Avatar,
9
+ AvatarFallback,
10
+ AvatarImage,
11
+ } from "@/components/ui/avatar"
12
+ import { Button } from "@/components/ui/button"
13
+ import {
14
+ DropdownMenu,
15
+ DropdownMenuContent,
16
+ DropdownMenuGroup,
17
+ DropdownMenuItem,
18
+ DropdownMenuSeparator,
19
+ DropdownMenuSub,
20
+ DropdownMenuSubContent,
21
+ DropdownMenuSubTrigger,
22
+ DropdownMenuTrigger,
23
+ } from "@/components/ui/dropdown-menu"
24
+ import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"
25
+ import { ChevronsUpDownIcon, SunIcon, MoonIcon, MonitorIcon, BuildingIcon, PhoneIcon, CheckIcon, SettingsIcon, LifeBuoyIcon, LogOutIcon } from "lucide-react"
26
+
27
+ const statuses = [
28
+ { value: "available", label: "Available", color: "bg-green-500" },
29
+ { value: "away", label: "Away", color: "bg-amber-500" },
30
+ { value: "busy", label: "Busy", color: "bg-red-500" },
31
+ { value: "offline", label: "Offline", color: "bg-gray-400" },
32
+ ]
33
+
34
+ export function Pattern() {
35
+ const [status, setStatus] = useState("available")
36
+ const [theme, setTheme] = useState("light")
37
+
38
+ const activeStatus = statuses.find((s) => s.value === status) || statuses[0]
39
+
40
+ return (
41
+ <div className="flex items-center justify-center">
42
+ <DropdownMenu>
43
+ <DropdownMenuTrigger asChild>
44
+ <Button variant="outline" className="w-40">
45
+ <Avatar className="size-4">
46
+ <AvatarImage src="https://github.com/shadcn.png" />
47
+ <AvatarFallback>CN</AvatarFallback>
48
+ </Avatar>
49
+ <span className="text-sm font-medium">shadcn</span>
50
+
51
+ <ChevronsUpDownIcon className="ml-auto opacity-60" aria-hidden="true" />
52
+ </Button>
53
+ </DropdownMenuTrigger>
54
+ <DropdownMenuContent className="w-60" align="start" sideOffset={8}>
55
+ <div className="flex items-center gap-3 px-1 pt-1.5">
56
+ <Avatar className="size-8">
57
+ <AvatarImage src="https://github.com/shadcn.png" />
58
+ <AvatarFallback>CN</AvatarFallback>
59
+ </Avatar>
60
+ <div className="flex flex-col">
61
+ <span className="text-foreground text-sm font-medium">
62
+ shadcn
63
+ </span>
64
+ <span className="text-muted-foreground text-xs">
65
+ ui@shadcn.com
66
+ </span>
67
+ </div>
68
+ </div>
69
+ <div className="py-2.5">
70
+ <Tabs value={theme} onValueChange={setTheme}>
71
+ <TabsList className="w-full">
72
+ <TabsTrigger value="light" className="h-6 flex-1">
73
+ <SunIcon className="size-4" aria-hidden="true" />
74
+ </TabsTrigger>
75
+ <TabsTrigger value="dark" className="h-6 flex-1">
76
+ <MoonIcon className="size-4" aria-hidden="true" />
77
+ </TabsTrigger>
78
+ <TabsTrigger value="system" className="h-6 flex-1">
79
+ <MonitorIcon className="size-4" aria-hidden="true" />
80
+ </TabsTrigger>
81
+ </TabsList>
82
+ </Tabs>
83
+ </div>
84
+ <DropdownMenuGroup>
85
+ <DropdownMenuItem
86
+ onSelect={(e) => e.preventDefault()}
87
+ className="justify-between"
88
+ >
89
+ <span className="flex items-center gap-2">
90
+ <BuildingIcon aria-hidden="true" />
91
+ Your Companies
92
+ </span>
93
+ <Badge
94
+ variant="secondary"
95
+ size="sm"
96
+ className="rounded-full px-1.5"
97
+ >
98
+ 12
99
+ </Badge>
100
+ </DropdownMenuItem>
101
+ <DropdownMenuItem
102
+ onSelect={(e) => e.preventDefault()}
103
+ className="justify-between"
104
+ >
105
+ <span className="flex items-center gap-2">
106
+ <PhoneIcon aria-hidden="true" />
107
+ Your Numbers
108
+ </span>
109
+ <Badge
110
+ variant="secondary"
111
+ size="sm"
112
+ className="rounded-full px-1.5"
113
+ >
114
+ 2
115
+ </Badge>
116
+ </DropdownMenuItem>
117
+ </DropdownMenuGroup>
118
+ <DropdownMenuSub>
119
+ <DropdownMenuSubTrigger>
120
+ <span className="flex items-center gap-2">
121
+ <span
122
+ className={cn("size-2 rounded-full", activeStatus.color)}
123
+ />
124
+ {activeStatus.label}
125
+ </span>
126
+ </DropdownMenuSubTrigger>
127
+ <DropdownMenuSubContent className="w-40">
128
+ {statuses.map((s) => (
129
+ <DropdownMenuItem
130
+ key={s.value}
131
+ onClick={() => setStatus(s.value)}
132
+ className="justify-between"
133
+ >
134
+ <span className="flex items-center gap-2">
135
+ <span className={`size-2 rounded-full ${s.color}`} />
136
+ {s.label}
137
+ </span>
138
+ {status === s.value && (
139
+ <CheckIcon className="text-muted-foreground size-4" aria-hidden="true" />
140
+ )}
141
+ </DropdownMenuItem>
142
+ ))}
143
+ </DropdownMenuSubContent>
144
+ </DropdownMenuSub>
145
+ <DropdownMenuSeparator />
146
+ <DropdownMenuGroup>
147
+ <DropdownMenuItem>
148
+ <SettingsIcon aria-hidden="true" />
149
+ Settings
150
+ </DropdownMenuItem>
151
+ <DropdownMenuItem>
152
+ <LifeBuoyIcon aria-hidden="true" />
153
+ Help Center
154
+ </DropdownMenuItem>
155
+ </DropdownMenuGroup>
156
+ <DropdownMenuSeparator />
157
+ <DropdownMenuItem variant="destructive">
158
+ <LogOutIcon aria-hidden="true" />
159
+ Logout
160
+ </DropdownMenuItem>
161
+ </DropdownMenuContent>
162
+ </DropdownMenu>
163
+ </div>
164
+ )
165
+ }
@@ -0,0 +1,108 @@
1
+ import {
2
+ Avatar,
3
+ AvatarFallback,
4
+ AvatarImage,
5
+ } from "@/components/ui/avatar"
6
+ import { Button } from "@/components/ui/button"
7
+ import {
8
+ DropdownMenu,
9
+ DropdownMenuContent,
10
+ DropdownMenuGroup,
11
+ DropdownMenuItem,
12
+ DropdownMenuLabel,
13
+ DropdownMenuSeparator,
14
+ DropdownMenuShortcut,
15
+ DropdownMenuTrigger,
16
+ } from "@/components/ui/dropdown-menu"
17
+ import { ChevronDownIcon, UserIcon, CreditCardIcon, SettingsIcon, UsersIcon, PlusIcon, LifeBuoyIcon, BookOpenIcon, LogOutIcon } from "lucide-react"
18
+
19
+ export function Pattern() {
20
+ return (
21
+ <div className="flex items-center justify-center">
22
+ <DropdownMenu>
23
+ <DropdownMenuTrigger asChild>
24
+ <Button variant="outline" className="w-52">
25
+ <div className="gap-1.5 flex items-center">
26
+ <Avatar className="size-5">
27
+ <AvatarImage
28
+ src="https://images.unsplash.com/photo-1535713875002-d1d0cf377fde?w=96&h=96&dpr=2&q=80"
29
+ alt="Alex Johnson"
30
+ />
31
+ <AvatarFallback>AJ</AvatarFallback>
32
+ </Avatar>
33
+ <span className="text-sm font-medium">Alex Johnson</span>
34
+ </div>
35
+
36
+ <ChevronDownIcon className="ml-auto size-4 opacity-60" aria-hidden="true" />
37
+ </Button>
38
+ </DropdownMenuTrigger>
39
+ <DropdownMenuContent className="w-56" align="end" sideOffset={8}>
40
+ <DropdownMenuGroup>
41
+ <DropdownMenuLabel className="flex items-center gap-2 py-2">
42
+ <Avatar className="size-8">
43
+ <AvatarImage
44
+ src="https://images.unsplash.com/photo-1535713875002-d1d0cf377fde?w=96&h=96&dpr=2&q=80"
45
+ alt="Alex Johnson"
46
+ />
47
+ <AvatarFallback>AJ</AvatarFallback>
48
+ </Avatar>
49
+ <div className="flex flex-col">
50
+ <span className="text-foreground text-sm font-medium">
51
+ Alex Johnson
52
+ </span>
53
+ <span className="text-muted-foreground text-xs font-normal">
54
+ alex@example.com
55
+ </span>
56
+ </div>
57
+ </DropdownMenuLabel>
58
+ <DropdownMenuSeparator />
59
+ <DropdownMenuGroup>
60
+ <DropdownMenuItem>
61
+ <UserIcon aria-hidden="true" />
62
+ Profile
63
+ <DropdownMenuShortcut>⇧⌘P</DropdownMenuShortcut>
64
+ </DropdownMenuItem>
65
+ <DropdownMenuItem>
66
+ <CreditCardIcon aria-hidden="true" />
67
+ Billing
68
+ </DropdownMenuItem>
69
+ <DropdownMenuItem>
70
+ <SettingsIcon aria-hidden="true" />
71
+ Settings
72
+ <DropdownMenuShortcut>⌘S</DropdownMenuShortcut>
73
+ </DropdownMenuItem>
74
+ </DropdownMenuGroup>
75
+ <DropdownMenuSeparator />
76
+ <DropdownMenuGroup>
77
+ <DropdownMenuItem>
78
+ <UsersIcon aria-hidden="true" />
79
+ Team
80
+ </DropdownMenuItem>
81
+ <DropdownMenuItem>
82
+ <PlusIcon aria-hidden="true" />
83
+ Invite Members
84
+ </DropdownMenuItem>
85
+ </DropdownMenuGroup>
86
+ <DropdownMenuSeparator />
87
+ <DropdownMenuGroup>
88
+ <DropdownMenuItem>
89
+ <LifeBuoyIcon aria-hidden="true" />
90
+ Support
91
+ </DropdownMenuItem>
92
+ <DropdownMenuItem>
93
+ <BookOpenIcon aria-hidden="true" />
94
+ Documentation
95
+ </DropdownMenuItem>
96
+ </DropdownMenuGroup>
97
+ <DropdownMenuSeparator />
98
+ <DropdownMenuItem variant="destructive">
99
+ <LogOutIcon aria-hidden="true" />
100
+ Log out
101
+ <DropdownMenuShortcut>⇧⌘Q</DropdownMenuShortcut>
102
+ </DropdownMenuItem>
103
+ </DropdownMenuGroup>
104
+ </DropdownMenuContent>
105
+ </DropdownMenu>
106
+ </div>
107
+ )
108
+ }
@@ -0,0 +1,34 @@
1
+ import { Button } from "@/components/ui/button"
2
+ import {
3
+ Empty,
4
+ EmptyContent,
5
+ EmptyDescription,
6
+ EmptyHeader,
7
+ EmptyTitle,
8
+ } from "@/components/ui/empty"
9
+ import { ArrowUpRightIcon } from "lucide-react"
10
+
11
+ export function Pattern() {
12
+ return (
13
+ <div className="flex items-center justify-center">
14
+ <Empty className="bg-muted">
15
+ <EmptyHeader>
16
+ <EmptyTitle>No results found</EmptyTitle>
17
+ <EmptyDescription>
18
+ No results found for your search. Try adjusting your search terms.
19
+ </EmptyDescription>
20
+ </EmptyHeader>
21
+ <EmptyContent>
22
+ <Button>Try again</Button>
23
+ <Button variant="link" asChild className="text-muted-foreground">
24
+ <a href="#">
25
+ Learn more{" "}
26
+ <ArrowUpRightIcon
27
+ />
28
+ </a>
29
+ </Button>
30
+ </EmptyContent>
31
+ </Empty>
32
+ </div>
33
+ )
34
+ }