@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.
- package/.claude/ralph-loop.local.md +9 -0
- package/README.md +172 -0
- package/bin/init.js +269 -0
- package/bun.lock +401 -0
- package/components.json +28 -0
- package/package.json +74 -0
- package/scripts/publish-npm.sh +202 -0
- package/src/AppShell.tsx +847 -0
- package/src/components/PageHeader.tsx +160 -0
- package/src/components/data-table/README.md +447 -0
- package/src/components/data-table/data-table-preferences.tsx +184 -0
- package/src/components/data-table/data-table-toolbar.tsx +118 -0
- package/src/components/data-table/data-table.tsx +37 -0
- package/src/components/data-table/index.ts +32 -0
- package/src/components/global-header/AllServicesButton.tsx +127 -0
- package/src/components/global-header/CategoriesButton.tsx +120 -0
- package/src/components/global-header/GlobalHeader.tsx +59 -0
- package/src/components/global-header/GlobalHeaderSearch.tsx +57 -0
- package/src/components/global-header/HeaderUtilities.tsx +243 -0
- package/src/components/global-header/ServicesMenu.tsx +246 -0
- package/src/components/layout/AppBreadcrumb.tsx +70 -0
- package/src/components/layout/AppFlashbar.tsx +95 -0
- package/src/components/layout/AppLayout.tsx +271 -0
- package/src/components/layout/AppNavigation.tsx +313 -0
- package/src/components/layout/AppSidebar.tsx +229 -0
- package/src/components/patterns/index.ts +14 -0
- package/src/components/patterns/p-alert-5.tsx +19 -0
- package/src/components/patterns/p-autocomplete-5.tsx +89 -0
- package/src/components/patterns/p-breadcrumb-1.tsx +28 -0
- package/src/components/patterns/p-button-42.tsx +37 -0
- package/src/components/patterns/p-button-51.tsx +14 -0
- package/src/components/patterns/p-button-6.tsx +5 -0
- package/src/components/patterns/p-calendar-1.tsx +18 -0
- package/src/components/patterns/p-card-1.tsx +33 -0
- package/src/components/patterns/p-card-2.tsx +26 -0
- package/src/components/patterns/p-card-5.tsx +31 -0
- package/src/components/patterns/p-collapsible-7.tsx +121 -0
- package/src/components/patterns/p-command-6.tsx +113 -0
- package/src/components/patterns/p-dialog-1.tsx +56 -0
- package/src/components/patterns/p-dropdown-menu-1.tsx +38 -0
- package/src/components/patterns/p-dropdown-menu-11.tsx +122 -0
- package/src/components/patterns/p-dropdown-menu-14.tsx +165 -0
- package/src/components/patterns/p-dropdown-menu-9.tsx +108 -0
- package/src/components/patterns/p-empty-2.tsx +34 -0
- package/src/components/patterns/p-file-upload-1.tsx +72 -0
- package/src/components/patterns/p-filters-1.tsx +666 -0
- package/src/components/patterns/p-frame-2.tsx +26 -0
- package/src/components/patterns/p-tabs-2.tsx +129 -0
- package/src/components/reui/alert.tsx +92 -0
- package/src/components/reui/autocomplete.tsx +343 -0
- package/src/components/reui/badge.tsx +87 -0
- package/src/components/reui/data-grid/data-grid-column-filter.tsx +165 -0
- package/src/components/reui/data-grid/data-grid-column-header.tsx +339 -0
- package/src/components/reui/data-grid/data-grid-column-visibility.tsx +55 -0
- package/src/components/reui/data-grid/data-grid-pagination.tsx +224 -0
- package/src/components/reui/data-grid/data-grid-table-dnd-rows.tsx +260 -0
- package/src/components/reui/data-grid/data-grid-table-dnd.tsx +253 -0
- package/src/components/reui/data-grid/data-grid-table.tsx +639 -0
- package/src/components/reui/data-grid/data-grid.tsx +209 -0
- package/src/components/reui/date-selector.tsx +1330 -0
- package/src/components/reui/filters.tsx +1869 -0
- package/src/components/reui/frame.tsx +134 -0
- package/src/components/reui/index.ts +17 -0
- package/src/components/reui/timeline.tsx +219 -0
- package/src/components/search/Autocomplete.tsx +183 -0
- package/src/components/search/AutocompleteClient.tsx +293 -0
- package/src/components/search/GlobalSearch.tsx +187 -0
- package/src/components/section-drawer/deal-drawer-content.tsx +891 -0
- package/src/components/section-drawer/index.ts +19 -0
- package/src/components/section-drawer/section-drawer.css +665 -0
- package/src/components/section-drawer/section-drawer.tsx +467 -0
- package/src/components/sectioned-list-board/README.md +78 -0
- package/src/components/sectioned-list-board/board-card-content.tsx +340 -0
- package/src/components/sectioned-list-board/date-range-filter.tsx +249 -0
- package/src/components/sectioned-list-board/index.ts +19 -0
- package/src/components/sectioned-list-board/sectioned-list-board.css +564 -0
- package/src/components/sectioned-list-board/sectioned-list-board.tsx +731 -0
- package/src/components/sectioned-list-board/sortable-card.tsx +314 -0
- package/src/components/sectioned-list-board/sortable-section.tsx +319 -0
- package/src/components/sectioned-list-board/types.ts +216 -0
- package/src/components/sectioned-list-table/README.md +80 -0
- package/src/components/sectioned-list-table/index.ts +14 -0
- package/src/components/sectioned-list-table/sectioned-list-table.css +534 -0
- package/src/components/sectioned-list-table/sectioned-list-table.tsx +740 -0
- package/src/components/sectioned-list-table/sortable-column-header.tsx +120 -0
- package/src/components/sectioned-list-table/sortable-row.tsx +420 -0
- package/src/components/sectioned-list-table/sortable-section.tsx +251 -0
- package/src/components/sectioned-list-table/table-cell-content.tsx +129 -0
- package/src/components/sectioned-list-table/types.ts +120 -0
- package/src/components/sectioned-list-table/use-column-preferences.ts +103 -0
- package/src/components/ui/actions-dropdown.tsx +109 -0
- package/src/components/ui/assignee-selector.tsx +209 -0
- package/src/components/ui/avatar.tsx +107 -0
- package/src/components/ui/breadcrumb.tsx +109 -0
- package/src/components/ui/button-group.tsx +83 -0
- package/src/components/ui/button.tsx +64 -0
- package/src/components/ui/calendar.tsx +220 -0
- package/src/components/ui/card.tsx +92 -0
- package/src/components/ui/chart.tsx +376 -0
- package/src/components/ui/checkbox.tsx +30 -0
- package/src/components/ui/collapsible.tsx +33 -0
- package/src/components/ui/command.tsx +182 -0
- package/src/components/ui/context-menu.tsx +250 -0
- package/src/components/ui/create-button-group.tsx +128 -0
- package/src/components/ui/dialog.tsx +156 -0
- package/src/components/ui/drawer.tsx +133 -0
- package/src/components/ui/dropdown-menu.tsx +255 -0
- package/src/components/ui/empty.tsx +104 -0
- package/src/components/ui/field.tsx +248 -0
- package/src/components/ui/form.tsx +165 -0
- package/src/components/ui/index.ts +37 -0
- package/src/components/ui/input-group.tsx +168 -0
- package/src/components/ui/input.tsx +21 -0
- package/src/components/ui/kbd.tsx +28 -0
- package/src/components/ui/label.tsx +22 -0
- package/src/components/ui/navigation-menu.tsx +168 -0
- package/src/components/ui/page-header.tsx +80 -0
- package/src/components/ui/popover.tsx +87 -0
- package/src/components/ui/scroll-area.tsx +56 -0
- package/src/components/ui/select.tsx +190 -0
- package/src/components/ui/separator.tsx +26 -0
- package/src/components/ui/sheet.tsx +141 -0
- package/src/components/ui/sidebar.tsx +726 -0
- package/src/components/ui/skeleton.tsx +13 -0
- package/src/components/ui/sonner.tsx +38 -0
- package/src/components/ui/switch.tsx +33 -0
- package/src/components/ui/tabs.tsx +91 -0
- package/src/components/ui/textarea.tsx +18 -0
- package/src/components/ui/toggle-group.tsx +83 -0
- package/src/components/ui/toggle.tsx +45 -0
- package/src/components/ui/tooltip.tsx +57 -0
- package/src/hooks/use-copy-to-clipboard.ts +37 -0
- package/src/hooks/use-file-upload.ts +415 -0
- package/src/hooks/use-mobile.ts +19 -0
- package/src/index.ts +95 -0
- package/src/lib/utils.ts +6 -0
- package/src/styles.css +1859 -0
- package/src/urls.ts +83 -0
- package/src/vite.d.ts +22 -0
- package/src/vite.js +241 -0
- package/tsconfig.base.json +18 -0
- 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'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
|
+
}
|