@otfdashkit/ui 0.1.1 → 0.1.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/LICENSE +1 -1
- package/README.md +28 -41
- package/dist/index.js +972 -655
- package/dist/index.mjs +826 -525
- package/package.json +5 -4
- package/src/advanced/BulkActions/BulkActions.tsx +2 -2
- package/src/advanced/CommandBar/CommandBar.tsx +2 -2
- package/src/advanced/Filters/Filters.tsx +6 -6
- package/src/advanced/Kanban/Kanban.tsx +1 -1
- package/src/advanced/ToggleButton/ToggleButton.tsx +1 -1
- package/src/advanced/bulk-actions.tsx +1 -46
- package/src/advanced/command-bar.tsx +1 -115
- package/src/advanced/filters.tsx +1 -103
- package/src/advanced/kanban.tsx +1 -95
- package/src/advanced/toggle-button.tsx +1 -78
- package/src/blocks/ChatDetail/ChatDetail.tsx +4 -4
- package/src/blocks/FeedbackModal/FeedbackModal.tsx +3 -3
- package/src/blocks/FileCards/FileCards.tsx +2 -2
- package/src/blocks/FilesList/FilesList.tsx +3 -3
- package/src/blocks/IntegrationCard/IntegrationCard.tsx +3 -3
- package/src/blocks/InviteModal/InviteModal.tsx +7 -7
- package/src/blocks/ManageTagsModal/ManageTagsModal.tsx +4 -4
- package/src/blocks/MessagesCard/MessagesCard.tsx +3 -3
- package/src/blocks/MetricCard/MetricCard.tsx +3 -3
- package/src/blocks/NotificationSettings/NotificationSettings.tsx +4 -4
- package/src/blocks/OrganizationMenu/OrganizationMenu.tsx +30 -11
- package/src/blocks/RolesMenu/RolesMenu.tsx +4 -4
- package/src/blocks/SelectUsersModal/SelectUsersModal.tsx +5 -5
- package/src/blocks/SidebarLayoutDashboard/SidebarLayoutDashboard.tsx +5 -5
- package/src/blocks/SidebarLayoutGroups/SidebarLayoutGroups.tsx +2 -2
- package/src/blocks/SidebarLayoutMinimal/SidebarLayoutMinimal.tsx +3 -3
- package/src/blocks/SidebarLayoutSearch/SidebarLayoutSearch.tsx +3 -3
- package/src/blocks/SidebarLayoutUser/SidebarLayoutUser.tsx +4 -4
- package/src/blocks/SortableTaskList/SortableTaskList.tsx +3 -3
- package/src/blocks/StackedLayoutBranded/StackedLayoutBranded.tsx +4 -4
- package/src/blocks/StackedLayoutTabs/StackedLayoutTabs.tsx +4 -4
- package/src/blocks/TaskCard/TaskCard.tsx +4 -4
- package/src/blocks/UserMenu/UserMenu.tsx +3 -3
- package/src/blocks/WorkspaceMembers/WorkspaceMembers.tsx +5 -5
- package/src/blocks/chat-detail.tsx +1 -73
- package/src/blocks/feedback-modal.tsx +1 -68
- package/src/blocks/file-cards.tsx +1 -56
- package/src/blocks/files-list.tsx +1 -72
- package/src/blocks/integration-card.tsx +1 -50
- package/src/blocks/invite-modal.tsx +1 -69
- package/src/blocks/manage-tags-modal.tsx +1 -63
- package/src/blocks/messages-card.tsx +1 -59
- package/src/blocks/metric-card.tsx +1 -79
- package/src/blocks/notification-settings.tsx +1 -61
- package/src/blocks/organization-menu.tsx +1 -85
- package/src/blocks/roles-menu.tsx +1 -61
- package/src/blocks/select-users-modal.tsx +1 -64
- package/src/blocks/sidebar-layout-dashboard.tsx +1 -97
- package/src/blocks/sidebar-layout-groups.tsx +1 -79
- package/src/blocks/sidebar-layout-minimal.tsx +1 -73
- package/src/blocks/sidebar-layout-search.tsx +1 -79
- package/src/blocks/sidebar-layout-user.tsx +1 -82
- package/src/blocks/sortable-task-list.tsx +1 -70
- package/src/blocks/stacked-layout-branded.tsx +1 -64
- package/src/blocks/stacked-layout-tabs.tsx +1 -80
- package/src/blocks/task-card.tsx +1 -88
- package/src/blocks/user-menu.tsx +1 -62
- package/src/blocks/workspace-members.tsx +1 -61
- package/src/charts/sparkline.tsx +1 -1
- package/src/components/DataGrid/DataGrid.tsx +1 -1
- package/src/components/DataTable/DataTable.tsx +4 -4
- package/src/components/EmptyState/EmptyState.tsx +6 -22
- package/src/components/Persona/Persona.tsx +1 -1
- package/src/components/Stat/Stat.tsx +1 -1
- package/src/components/Stepper/Stepper.tsx +13 -2
- package/src/components/banner.tsx +1 -203
- package/src/components/breadcrumb.tsx +1 -58
- package/src/components/command-item.tsx +1 -36
- package/src/components/data-grid.tsx +1 -125
- package/src/components/data-table.tsx +1 -139
- package/src/components/empty-state.tsx +1 -31
- package/src/components/hotkeys.tsx +1 -31
- package/src/components/icon-badge.tsx +1 -35
- package/src/components/loading-overlay.tsx +1 -38
- package/src/components/persona.tsx +1 -38
- package/src/components/property-list.tsx +1 -29
- package/src/components/stat.tsx +1 -51
- package/src/components/stepper.tsx +1 -69
- package/src/components/structured-list.tsx +1 -51
- package/src/components/timeline.tsx +1 -69
- package/src/components/toaster.tsx +1 -42
- package/src/data/DataList.tsx +1 -1
- package/src/engagement/Tour/Tour.tsx +1 -1
- package/src/engagement/beacon.tsx +1 -50
- package/src/engagement/tour.tsx +1 -106
- package/src/forms/ArrayField/ArrayField.tsx +2 -2
- package/src/forms/AutoForm/AutoForm.tsx +9 -8
- package/src/forms/DatePicker/DatePicker.tsx +3 -3
- package/src/forms/FloatingLabelInput/FloatingLabelInput.tsx +104 -0
- package/src/forms/FloatingLabelInput/index.ts +1 -0
- package/src/forms/PasswordInput/PasswordInput.tsx +2 -2
- package/src/forms/SearchInput/SearchInput.tsx +2 -2
- package/src/forms/StepForm/StepForm.tsx +2 -2
- package/src/forms/array-field.tsx +1 -61
- package/src/forms/auto-form.tsx +1 -133
- package/src/forms/date-picker.tsx +1 -109
- package/src/forms/field.tsx +1 -62
- package/src/forms/file-upload.tsx +1 -101
- package/src/forms/floating-label-input.tsx +1 -0
- package/src/forms/form.tsx +1 -57
- package/src/forms/object-field.tsx +1 -53
- package/src/forms/password-input.tsx +1 -54
- package/src/forms/search-input.tsx +1 -64
- package/src/forms/step-form.tsx +1 -113
- package/src/index.ts +11 -3
- package/src/primitives/Button/Button.tsx +18 -0
- package/src/primitives/cart-line-item.tsx +81 -0
- package/src/primitives/code-block.tsx +1 -1
- package/src/primitives/comparison.tsx +1 -1
- package/src/primitives/empty-cart.tsx +52 -0
- package/src/primitives/price-tag.tsx +62 -0
- package/src/primitives/qr-code.tsx +5 -1
- package/src/primitives/quantity-stepper.tsx +92 -0
- package/src/primitives/variant-picker.tsx +74 -0
- package/src/themes/base.css +18 -0
|
@@ -1,97 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import React from 'react'
|
|
3
|
-
import { LayoutDashboard, FolderOpen, Users, Settings, Bell, TrendingUp } from 'lucide-react'
|
|
4
|
-
import { cn } from '../utils/cn'
|
|
5
|
-
import { Avatar, AvatarFallback } from '../primitives/avatar'
|
|
6
|
-
import { Badge } from '../primitives/badge'
|
|
7
|
-
import { Separator } from '../primitives/separator'
|
|
8
|
-
import { Card, CardContent } from '../primitives/card'
|
|
9
|
-
|
|
10
|
-
const NAV_ITEMS = [
|
|
11
|
-
{ icon: LayoutDashboard, label: 'Dashboard', active: true },
|
|
12
|
-
{ icon: FolderOpen, label: 'Projects' },
|
|
13
|
-
{ icon: Users, label: 'Team' },
|
|
14
|
-
{ icon: Settings, label: 'Settings' },
|
|
15
|
-
]
|
|
16
|
-
|
|
17
|
-
const KPIS = [
|
|
18
|
-
{ label: 'Total Revenue', value: '$48,295', change: '+12.5%' },
|
|
19
|
-
{ label: 'Active Users', value: '2,847', change: '+8.3%' },
|
|
20
|
-
{ label: 'New Orders', value: '384', change: '+3.1%' },
|
|
21
|
-
{ label: 'Conversion', value: '3.24%', change: '-0.4%' },
|
|
22
|
-
]
|
|
23
|
-
|
|
24
|
-
const TABLE_ROWS = [
|
|
25
|
-
{ name: 'Alpha Project', status: 'Active', team: 'Design', progress: 72 },
|
|
26
|
-
{ name: 'Beta Launch', status: 'Review', team: 'Engineering', progress: 45 },
|
|
27
|
-
{ name: 'Gamma Research', status: 'Active', team: 'Product', progress: 89 },
|
|
28
|
-
]
|
|
29
|
-
|
|
30
|
-
function NavItem({ icon: Icon, label, active }: { icon: React.ElementType; label: string; active?: boolean }) {
|
|
31
|
-
return (
|
|
32
|
-
<button className={cn(
|
|
33
|
-
'flex w-full items-center gap-3 rounded-md px-3 py-2 text-sm transition-colors',
|
|
34
|
-
active ? 'bg-[hsl(var(--accent))] font-medium' : 'text-[hsl(var(--muted-foreground))] hover:bg-[hsl(var(--accent))]'
|
|
35
|
-
)}>
|
|
36
|
-
<Icon className="h-4 w-4 shrink-0" />
|
|
37
|
-
{label}
|
|
38
|
-
</button>
|
|
39
|
-
)
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export interface SidebarLayoutDashboardProps { className?: string }
|
|
43
|
-
|
|
44
|
-
export function SidebarLayoutDashboard({ className }: SidebarLayoutDashboardProps) {
|
|
45
|
-
return (
|
|
46
|
-
<div className={cn('flex h-[600px] overflow-hidden rounded-md border border-[hsl(var(--border))] bg-[hsl(var(--background))]', className)}>
|
|
47
|
-
<aside className="flex w-56 flex-col border-r border-[hsl(var(--border))] bg-[hsl(var(--card))]">
|
|
48
|
-
<div className="flex h-12 items-center px-4 font-semibold text-sm">Acme Inc</div>
|
|
49
|
-
<Separator />
|
|
50
|
-
<nav className="flex-1 space-y-0.5 p-2">
|
|
51
|
-
{NAV_ITEMS.map(i => <NavItem key={i.label} {...i} />)}
|
|
52
|
-
</nav>
|
|
53
|
-
<Separator />
|
|
54
|
-
<div className="flex items-center gap-2 p-3">
|
|
55
|
-
<Avatar className="h-7 w-7"><AvatarFallback className="text-xs">JD</AvatarFallback></Avatar>
|
|
56
|
-
<span className="truncate text-xs font-medium">Jane Doe</span>
|
|
57
|
-
</div>
|
|
58
|
-
</aside>
|
|
59
|
-
<div className="flex flex-1 flex-col overflow-hidden">
|
|
60
|
-
<header className="flex h-12 items-center justify-between border-b border-[hsl(var(--border))] px-4">
|
|
61
|
-
<h1 className="text-sm font-semibold">Dashboard</h1>
|
|
62
|
-
<button className="relative"><Bell className="h-4 w-4" /><span className="absolute -top-1 -right-1 h-2 w-2 rounded-full bg-[hsl(var(--primary))]" /></button>
|
|
63
|
-
</header>
|
|
64
|
-
<div className="flex-1 overflow-auto p-4 space-y-4">
|
|
65
|
-
<div className="grid gap-3" style={{ gridTemplateColumns: 'repeat(auto-fill, minmax(min(100%,130px),1fr))' }}>
|
|
66
|
-
{KPIS.map(k => (
|
|
67
|
-
<Card key={k.label}>
|
|
68
|
-
<CardContent className="p-3">
|
|
69
|
-
<p className="text-xs text-[hsl(var(--muted-foreground))]">{k.label}</p>
|
|
70
|
-
<p className="text-lg font-bold mt-0.5">{k.value}</p>
|
|
71
|
-
<p className={cn('text-xs flex items-center gap-0.5 mt-0.5', k.change.startsWith('+') ? 'text-green-600' : 'text-red-500')}>
|
|
72
|
-
<TrendingUp className="h-3 w-3" />{k.change}
|
|
73
|
-
</p>
|
|
74
|
-
</CardContent>
|
|
75
|
-
</Card>
|
|
76
|
-
))}
|
|
77
|
-
</div>
|
|
78
|
-
<div className="rounded-md border border-[hsl(var(--border))]">
|
|
79
|
-
<table className="w-full text-xs">
|
|
80
|
-
<thead><tr className="border-b border-[hsl(var(--border))]">
|
|
81
|
-
{['Project', 'Status', 'Team', 'Progress'].map(h => <th key={h} className="px-3 py-2 text-left font-medium text-[hsl(var(--muted-foreground))]">{h}</th>)}
|
|
82
|
-
</tr></thead>
|
|
83
|
-
<tbody>{TABLE_ROWS.map(r => (
|
|
84
|
-
<tr key={r.name} className="border-b border-[hsl(var(--border))] last:border-0">
|
|
85
|
-
<td className="px-3 py-2 font-medium">{r.name}</td>
|
|
86
|
-
<td className="px-3 py-2"><Badge variant="secondary" className="text-[10px]">{r.status}</Badge></td>
|
|
87
|
-
<td className="px-3 py-2 text-[hsl(var(--muted-foreground))]">{r.team}</td>
|
|
88
|
-
<td className="px-3 py-2"><div className="h-1.5 w-20 rounded-full bg-[hsl(var(--muted))]"><div className="h-full rounded-full bg-[hsl(var(--primary))]" style={{ width: `${r.progress}%` }} /></div></td>
|
|
89
|
-
</tr>
|
|
90
|
-
))}</tbody>
|
|
91
|
-
</table>
|
|
92
|
-
</div>
|
|
93
|
-
</div>
|
|
94
|
-
</div>
|
|
95
|
-
</div>
|
|
96
|
-
)
|
|
97
|
-
}
|
|
1
|
+
export * from './SidebarLayoutDashboard'
|
|
@@ -1,79 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import React from 'react'
|
|
3
|
-
import { LayoutDashboard, BarChart2, FolderOpen, Users, CreditCard, Settings, HelpCircle } from 'lucide-react'
|
|
4
|
-
import { cn } from '../utils/cn'
|
|
5
|
-
import { Separator } from '../primitives/separator'
|
|
6
|
-
|
|
7
|
-
const NAV_GROUPS = [
|
|
8
|
-
{
|
|
9
|
-
label: 'Main',
|
|
10
|
-
items: [{ icon: LayoutDashboard, label: 'Dashboard', active: true }, { icon: BarChart2, label: 'Analytics' }],
|
|
11
|
-
},
|
|
12
|
-
{
|
|
13
|
-
label: 'Management',
|
|
14
|
-
items: [{ icon: FolderOpen, label: 'Projects' }, { icon: Users, label: 'Team' }, { icon: CreditCard, label: 'Billing' }],
|
|
15
|
-
},
|
|
16
|
-
{
|
|
17
|
-
label: 'Account',
|
|
18
|
-
items: [{ icon: Settings, label: 'Settings' }, { icon: HelpCircle, label: 'Help' }],
|
|
19
|
-
},
|
|
20
|
-
]
|
|
21
|
-
|
|
22
|
-
interface NavItemProps { icon: React.ElementType; label: string; active?: boolean }
|
|
23
|
-
|
|
24
|
-
function NavItem({ icon: Icon, label, active }: NavItemProps) {
|
|
25
|
-
return (
|
|
26
|
-
<button className={cn(
|
|
27
|
-
'flex w-full items-center gap-3 rounded-md px-3 py-2 text-sm transition-colors',
|
|
28
|
-
active
|
|
29
|
-
? 'bg-[hsl(var(--accent))] text-[hsl(var(--accent-foreground))] font-medium'
|
|
30
|
-
: 'text-[hsl(var(--muted-foreground))] hover:bg-[hsl(var(--accent))] hover:text-[hsl(var(--accent-foreground))]'
|
|
31
|
-
)}>
|
|
32
|
-
<Icon className="h-4 w-4 shrink-0" />
|
|
33
|
-
{label}
|
|
34
|
-
</button>
|
|
35
|
-
)
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export interface SidebarLayoutGroupsProps {
|
|
39
|
-
brandName?: string
|
|
40
|
-
pageTitle?: string
|
|
41
|
-
children?: React.ReactNode
|
|
42
|
-
className?: string
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export function SidebarLayoutGroups({
|
|
46
|
-
brandName = 'Acme Inc',
|
|
47
|
-
pageTitle = 'Dashboard',
|
|
48
|
-
children,
|
|
49
|
-
className,
|
|
50
|
-
}: SidebarLayoutGroupsProps) {
|
|
51
|
-
return (
|
|
52
|
-
<div className={cn('flex h-[600px] overflow-hidden rounded-md border border-[hsl(var(--border))] bg-[hsl(var(--background))]', className)}>
|
|
53
|
-
<aside className="flex w-60 flex-col border-r border-[hsl(var(--border))] bg-[hsl(var(--card))]">
|
|
54
|
-
<div className="flex h-14 items-center px-4 font-semibold text-sm">{brandName}</div>
|
|
55
|
-
<Separator />
|
|
56
|
-
<nav className="flex-1 overflow-auto p-2 space-y-4">
|
|
57
|
-
{NAV_GROUPS.map(group => (
|
|
58
|
-
<div key={group.label}>
|
|
59
|
-
<p className="mb-1 px-3 text-[10px] font-semibold uppercase tracking-wider text-[hsl(var(--muted-foreground))]">
|
|
60
|
-
{group.label}
|
|
61
|
-
</p>
|
|
62
|
-
<div className="space-y-0.5">
|
|
63
|
-
{group.items.map(item => <NavItem key={item.label} {...item} />)}
|
|
64
|
-
</div>
|
|
65
|
-
</div>
|
|
66
|
-
))}
|
|
67
|
-
</nav>
|
|
68
|
-
</aside>
|
|
69
|
-
<main className="flex flex-1 flex-col overflow-auto">
|
|
70
|
-
<header className="flex h-14 items-center border-b border-[hsl(var(--border))] px-6">
|
|
71
|
-
<h1 className="text-sm font-semibold">{pageTitle}</h1>
|
|
72
|
-
</header>
|
|
73
|
-
<div className="flex-1 p-6 text-sm text-[hsl(var(--muted-foreground))]">
|
|
74
|
-
{children ?? 'Page content goes here.'}
|
|
75
|
-
</div>
|
|
76
|
-
</main>
|
|
77
|
-
</div>
|
|
78
|
-
)
|
|
79
|
-
}
|
|
1
|
+
export * from './SidebarLayoutGroups'
|
|
@@ -1,73 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import React from 'react'
|
|
3
|
-
import { LayoutDashboard, FolderOpen, Users, Settings } from 'lucide-react'
|
|
4
|
-
import { cn } from '../utils/cn'
|
|
5
|
-
import { Avatar, AvatarFallback } from '../primitives/avatar'
|
|
6
|
-
import { Separator } from '../primitives/separator'
|
|
7
|
-
|
|
8
|
-
const NAV_ITEMS = [
|
|
9
|
-
{ icon: LayoutDashboard, label: 'Dashboard', active: true },
|
|
10
|
-
{ icon: FolderOpen, label: 'Projects' },
|
|
11
|
-
{ icon: Users, label: 'Team' },
|
|
12
|
-
{ icon: Settings, label: 'Settings' },
|
|
13
|
-
]
|
|
14
|
-
|
|
15
|
-
interface NavItemProps { icon: React.ElementType; label: string; active?: boolean }
|
|
16
|
-
|
|
17
|
-
function NavItem({ icon: Icon, label, active }: NavItemProps) {
|
|
18
|
-
return (
|
|
19
|
-
<button className={cn(
|
|
20
|
-
'flex w-full items-center gap-3 rounded-md px-3 py-2 text-sm transition-colors',
|
|
21
|
-
active
|
|
22
|
-
? 'bg-[hsl(var(--accent))] text-[hsl(var(--accent-foreground))] font-medium'
|
|
23
|
-
: 'text-[hsl(var(--muted-foreground))] hover:bg-[hsl(var(--accent))] hover:text-[hsl(var(--accent-foreground))]'
|
|
24
|
-
)}>
|
|
25
|
-
<Icon className="h-4 w-4 shrink-0" />
|
|
26
|
-
{label}
|
|
27
|
-
</button>
|
|
28
|
-
)
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export interface SidebarLayoutMinimalProps {
|
|
32
|
-
brandName?: string
|
|
33
|
-
pageTitle?: string
|
|
34
|
-
children?: React.ReactNode
|
|
35
|
-
className?: string
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export function SidebarLayoutMinimal({
|
|
39
|
-
brandName = 'Acme Inc',
|
|
40
|
-
pageTitle = 'Dashboard',
|
|
41
|
-
children,
|
|
42
|
-
className,
|
|
43
|
-
}: SidebarLayoutMinimalProps) {
|
|
44
|
-
return (
|
|
45
|
-
<div className={cn('flex h-[600px] overflow-hidden rounded-md border border-[hsl(var(--border))] bg-[hsl(var(--background))]', className)}>
|
|
46
|
-
<aside className="flex w-60 flex-col border-r border-[hsl(var(--border))] bg-[hsl(var(--card))]">
|
|
47
|
-
<div className="flex h-14 items-center px-4 font-semibold text-sm">{brandName}</div>
|
|
48
|
-
<Separator />
|
|
49
|
-
<nav className="flex-1 space-y-0.5 p-2">
|
|
50
|
-
{NAV_ITEMS.map(item => <NavItem key={item.label} {...item} />)}
|
|
51
|
-
</nav>
|
|
52
|
-
<Separator />
|
|
53
|
-
<div className="flex items-center gap-3 p-4">
|
|
54
|
-
<Avatar className="h-8 w-8">
|
|
55
|
-
<AvatarFallback className="text-xs">JD</AvatarFallback>
|
|
56
|
-
</Avatar>
|
|
57
|
-
<div className="min-w-0">
|
|
58
|
-
<p className="truncate text-sm font-medium">Jane Doe</p>
|
|
59
|
-
<p className="truncate text-xs text-[hsl(var(--muted-foreground))]">jane@acme.com</p>
|
|
60
|
-
</div>
|
|
61
|
-
</div>
|
|
62
|
-
</aside>
|
|
63
|
-
<main className="flex flex-1 flex-col overflow-auto">
|
|
64
|
-
<header className="flex h-14 items-center border-b border-[hsl(var(--border))] px-6">
|
|
65
|
-
<h1 className="text-sm font-semibold">{pageTitle}</h1>
|
|
66
|
-
</header>
|
|
67
|
-
<div className="flex-1 p-6 text-sm text-[hsl(var(--muted-foreground))]">
|
|
68
|
-
{children ?? 'Page content goes here.'}
|
|
69
|
-
</div>
|
|
70
|
-
</main>
|
|
71
|
-
</div>
|
|
72
|
-
)
|
|
73
|
-
}
|
|
1
|
+
export * from './SidebarLayoutMinimal'
|
|
@@ -1,79 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import React, { useState } from 'react'
|
|
3
|
-
import { LayoutDashboard, FolderOpen, Users, Settings, BarChart2, Bell } from 'lucide-react'
|
|
4
|
-
import { cn } from '../utils/cn'
|
|
5
|
-
import { Input } from '../primitives/input'
|
|
6
|
-
import { Separator } from '../primitives/separator'
|
|
7
|
-
|
|
8
|
-
const ALL_ITEMS = [
|
|
9
|
-
{ icon: LayoutDashboard, label: 'Dashboard', active: true },
|
|
10
|
-
{ icon: BarChart2, label: 'Analytics' },
|
|
11
|
-
{ icon: FolderOpen, label: 'Projects' },
|
|
12
|
-
{ icon: Users, label: 'Team' },
|
|
13
|
-
{ icon: Bell, label: 'Notifications' },
|
|
14
|
-
{ icon: Settings, label: 'Settings' },
|
|
15
|
-
]
|
|
16
|
-
|
|
17
|
-
interface NavItemProps { icon: React.ElementType; label: string; active?: boolean }
|
|
18
|
-
|
|
19
|
-
function NavItem({ icon: Icon, label, active }: NavItemProps) {
|
|
20
|
-
return (
|
|
21
|
-
<button className={cn(
|
|
22
|
-
'flex w-full items-center gap-3 rounded-md px-3 py-2 text-sm transition-colors',
|
|
23
|
-
active
|
|
24
|
-
? 'bg-[hsl(var(--accent))] text-[hsl(var(--accent-foreground))] font-medium'
|
|
25
|
-
: 'text-[hsl(var(--muted-foreground))] hover:bg-[hsl(var(--accent))] hover:text-[hsl(var(--accent-foreground))]'
|
|
26
|
-
)}>
|
|
27
|
-
<Icon className="h-4 w-4 shrink-0" />
|
|
28
|
-
{label}
|
|
29
|
-
</button>
|
|
30
|
-
)
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export interface SidebarLayoutSearchProps {
|
|
34
|
-
brandName?: string
|
|
35
|
-
pageTitle?: string
|
|
36
|
-
children?: React.ReactNode
|
|
37
|
-
className?: string
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export function SidebarLayoutSearch({
|
|
41
|
-
brandName = 'Acme Inc',
|
|
42
|
-
pageTitle = 'Dashboard',
|
|
43
|
-
children,
|
|
44
|
-
className,
|
|
45
|
-
}: SidebarLayoutSearchProps) {
|
|
46
|
-
const [query, setQuery] = useState('')
|
|
47
|
-
const filtered = ALL_ITEMS.filter(i => i.label.toLowerCase().includes(query.toLowerCase()))
|
|
48
|
-
|
|
49
|
-
return (
|
|
50
|
-
<div className={cn('flex h-[600px] overflow-hidden rounded-md border border-[hsl(var(--border))] bg-[hsl(var(--background))]', className)}>
|
|
51
|
-
<aside className="flex w-60 flex-col border-r border-[hsl(var(--border))] bg-[hsl(var(--card))]">
|
|
52
|
-
<div className="flex h-14 items-center px-4 font-semibold text-sm">{brandName}</div>
|
|
53
|
-
<Separator />
|
|
54
|
-
<div className="p-2">
|
|
55
|
-
<Input
|
|
56
|
-
placeholder="Search..."
|
|
57
|
-
value={query}
|
|
58
|
-
onChange={e => setQuery(e.target.value)}
|
|
59
|
-
className="h-8 text-sm"
|
|
60
|
-
/>
|
|
61
|
-
</div>
|
|
62
|
-
<nav className="flex-1 space-y-0.5 overflow-auto p-2">
|
|
63
|
-
{filtered.length > 0
|
|
64
|
-
? filtered.map(item => <NavItem key={item.label} {...item} />)
|
|
65
|
-
: <p className="px-3 py-2 text-xs text-[hsl(var(--muted-foreground))]">No results</p>
|
|
66
|
-
}
|
|
67
|
-
</nav>
|
|
68
|
-
</aside>
|
|
69
|
-
<main className="flex flex-1 flex-col overflow-auto">
|
|
70
|
-
<header className="flex h-14 items-center border-b border-[hsl(var(--border))] px-6">
|
|
71
|
-
<h1 className="text-sm font-semibold">{pageTitle}</h1>
|
|
72
|
-
</header>
|
|
73
|
-
<div className="flex-1 p-6 text-sm text-[hsl(var(--muted-foreground))]">
|
|
74
|
-
{children ?? 'Page content goes here.'}
|
|
75
|
-
</div>
|
|
76
|
-
</main>
|
|
77
|
-
</div>
|
|
78
|
-
)
|
|
79
|
-
}
|
|
1
|
+
export * from './SidebarLayoutSearch'
|
|
@@ -1,82 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import React from 'react'
|
|
3
|
-
import { LayoutDashboard, FolderOpen, Users, Settings, Bell } from 'lucide-react'
|
|
4
|
-
import { cn } from '../utils/cn'
|
|
5
|
-
import { Avatar, AvatarFallback } from '../primitives/avatar'
|
|
6
|
-
import { Badge } from '../primitives/badge'
|
|
7
|
-
import { Separator } from '../primitives/separator'
|
|
8
|
-
|
|
9
|
-
const NAV_ITEMS = [
|
|
10
|
-
{ icon: LayoutDashboard, label: 'Dashboard', active: true },
|
|
11
|
-
{ icon: FolderOpen, label: 'Projects' },
|
|
12
|
-
{ icon: Users, label: 'Team' },
|
|
13
|
-
{ icon: Bell, label: 'Notifications', badge: '3' },
|
|
14
|
-
{ icon: Settings, label: 'Settings' },
|
|
15
|
-
]
|
|
16
|
-
|
|
17
|
-
interface NavItemProps { icon: React.ElementType; label: string; active?: boolean; badge?: string }
|
|
18
|
-
|
|
19
|
-
function NavItem({ icon: Icon, label, active, badge }: NavItemProps) {
|
|
20
|
-
return (
|
|
21
|
-
<button className={cn(
|
|
22
|
-
'flex w-full items-center gap-3 rounded-md px-3 py-2 text-sm transition-colors',
|
|
23
|
-
active
|
|
24
|
-
? 'bg-[hsl(var(--accent))] text-[hsl(var(--accent-foreground))] font-medium'
|
|
25
|
-
: 'text-[hsl(var(--muted-foreground))] hover:bg-[hsl(var(--accent))] hover:text-[hsl(var(--accent-foreground))]'
|
|
26
|
-
)}>
|
|
27
|
-
<Icon className="h-4 w-4 shrink-0" />
|
|
28
|
-
<span className="flex-1 text-left">{label}</span>
|
|
29
|
-
{badge && <Badge className="text-[10px] px-1.5 py-0.5 shrink-0">{badge}</Badge>}
|
|
30
|
-
</button>
|
|
31
|
-
)
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export interface SidebarLayoutUserProps {
|
|
35
|
-
userName?: string
|
|
36
|
-
userEmail?: string
|
|
37
|
-
userRole?: string
|
|
38
|
-
pageTitle?: string
|
|
39
|
-
children?: React.ReactNode
|
|
40
|
-
className?: string
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export function SidebarLayoutUser({
|
|
44
|
-
userName = 'Jane Doe',
|
|
45
|
-
userEmail = 'jane@acme.com',
|
|
46
|
-
userRole = 'Admin',
|
|
47
|
-
pageTitle = 'Dashboard',
|
|
48
|
-
children,
|
|
49
|
-
className,
|
|
50
|
-
}: SidebarLayoutUserProps) {
|
|
51
|
-
return (
|
|
52
|
-
<div className={cn('flex h-[600px] overflow-hidden rounded-md border border-[hsl(var(--border))] bg-[hsl(var(--background))]', className)}>
|
|
53
|
-
<aside className="flex w-60 flex-col border-r border-[hsl(var(--border))] bg-[hsl(var(--card))]">
|
|
54
|
-
<div className="p-4">
|
|
55
|
-
<div className="flex items-center gap-3 rounded-md bg-[hsl(var(--accent))] p-3">
|
|
56
|
-
<Avatar className="h-9 w-9">
|
|
57
|
-
<AvatarFallback className="text-xs">{userName.slice(0, 2).toUpperCase()}</AvatarFallback>
|
|
58
|
-
</Avatar>
|
|
59
|
-
<div className="min-w-0">
|
|
60
|
-
<p className="truncate text-sm font-semibold">{userName}</p>
|
|
61
|
-
<p className="truncate text-xs text-[hsl(var(--muted-foreground))]">{userRole}</p>
|
|
62
|
-
</div>
|
|
63
|
-
</div>
|
|
64
|
-
</div>
|
|
65
|
-
<Separator />
|
|
66
|
-
<nav className="flex-1 space-y-0.5 p-2">
|
|
67
|
-
{NAV_ITEMS.map(item => <NavItem key={item.label} {...item} />)}
|
|
68
|
-
</nav>
|
|
69
|
-
<Separator />
|
|
70
|
-
<p className="p-4 text-xs text-[hsl(var(--muted-foreground))] truncate">{userEmail}</p>
|
|
71
|
-
</aside>
|
|
72
|
-
<main className="flex flex-1 flex-col overflow-auto">
|
|
73
|
-
<header className="flex h-14 items-center border-b border-[hsl(var(--border))] px-6">
|
|
74
|
-
<h1 className="text-sm font-semibold">{pageTitle}</h1>
|
|
75
|
-
</header>
|
|
76
|
-
<div className="flex-1 p-6 text-sm text-[hsl(var(--muted-foreground))]">
|
|
77
|
-
{children ?? 'Page content goes here.'}
|
|
78
|
-
</div>
|
|
79
|
-
</main>
|
|
80
|
-
</div>
|
|
81
|
-
)
|
|
82
|
-
}
|
|
1
|
+
export * from './SidebarLayoutUser'
|
|
@@ -1,70 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { cn } from '../utils/cn'
|
|
3
|
-
import { Checkbox } from '../primitives/checkbox'
|
|
4
|
-
import { Avatar, AvatarFallback } from '../primitives/avatar'
|
|
5
|
-
|
|
6
|
-
type Priority = 'High' | 'Medium' | 'Low'
|
|
7
|
-
type Status = 'In Progress' | 'Todo' | 'Done'
|
|
8
|
-
|
|
9
|
-
interface Task {
|
|
10
|
-
id: string
|
|
11
|
-
title: string
|
|
12
|
-
assignee: string
|
|
13
|
-
priority: Priority
|
|
14
|
-
status: Status
|
|
15
|
-
done: boolean
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const TASKS: Task[] = [
|
|
19
|
-
{ id: '1', title: 'Redesign onboarding flow', assignee: 'AC', priority: 'High', status: 'In Progress', done: false },
|
|
20
|
-
{ id: '2', title: 'Fix authentication bug', assignee: 'BS', priority: 'High', status: 'In Progress', done: false },
|
|
21
|
-
{ id: '3', title: 'Write API documentation', assignee: 'JD', priority: 'Medium', status: 'Todo', done: false },
|
|
22
|
-
{ id: '4', title: 'Add dark mode support', assignee: 'TR', priority: 'Low', status: 'Todo', done: false },
|
|
23
|
-
{ id: '5', title: 'Setup CI/CD pipeline', assignee: 'SK', priority: 'Medium', status: 'Done', done: true },
|
|
24
|
-
{ id: '6', title: 'Deploy to staging', assignee: 'ML', priority: 'Low', status: 'Done', done: true },
|
|
25
|
-
]
|
|
26
|
-
|
|
27
|
-
const PRIORITY_DOT: Record<Priority, string> = {
|
|
28
|
-
High: 'bg-red-500',
|
|
29
|
-
Medium: 'bg-yellow-500',
|
|
30
|
-
Low: 'bg-green-500',
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const SECTIONS: Status[] = ['In Progress', 'Todo', 'Done']
|
|
34
|
-
|
|
35
|
-
function TaskRow({ task, onToggle }: { task: Task; onToggle: () => void }) {
|
|
36
|
-
return (
|
|
37
|
-
<div className="flex items-center gap-3 px-3 py-2.5 hover:bg-[hsl(var(--accent)/0.5)] rounded-md transition-colors">
|
|
38
|
-
<Checkbox checked={task.done} onCheckedChange={onToggle} />
|
|
39
|
-
<span className={cn('flex-1 text-sm', task.done && 'line-through text-[hsl(var(--muted-foreground))]')}>{task.title}</span>
|
|
40
|
-
<span className={cn('h-2 w-2 rounded-full shrink-0', PRIORITY_DOT[task.priority])} title={task.priority} />
|
|
41
|
-
<Avatar className="h-6 w-6 shrink-0"><AvatarFallback className="text-[10px]">{task.assignee}</AvatarFallback></Avatar>
|
|
42
|
-
</div>
|
|
43
|
-
)
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export interface SortableTaskListProps { className?: string }
|
|
47
|
-
|
|
48
|
-
export function SortableTaskList({ className }: SortableTaskListProps) {
|
|
49
|
-
const [tasks, setTasks] = useState(TASKS)
|
|
50
|
-
const toggle = (id: string) => setTasks(ts => ts.map(t => t.id === id ? { ...t, done: !t.done } : t))
|
|
51
|
-
|
|
52
|
-
return (
|
|
53
|
-
<div className={cn('rounded-md border border-[hsl(var(--border))] bg-[hsl(var(--card))] p-4 space-y-4', className)}>
|
|
54
|
-
{SECTIONS.map(section => {
|
|
55
|
-
const items = tasks.filter(t => t.status === section)
|
|
56
|
-
return (
|
|
57
|
-
<div key={section}>
|
|
58
|
-
<div className="flex items-center gap-2 mb-1.5">
|
|
59
|
-
<p className="text-xs font-semibold text-[hsl(var(--muted-foreground))] uppercase tracking-wide">{section}</p>
|
|
60
|
-
<span className="text-xs text-[hsl(var(--muted-foreground))]">({items.length})</span>
|
|
61
|
-
</div>
|
|
62
|
-
<div className="space-y-0.5">
|
|
63
|
-
{items.map(task => <TaskRow key={task.id} task={task} onToggle={() => toggle(task.id)} />)}
|
|
64
|
-
</div>
|
|
65
|
-
</div>
|
|
66
|
-
)
|
|
67
|
-
})}
|
|
68
|
-
</div>
|
|
69
|
-
)
|
|
70
|
-
}
|
|
1
|
+
export * from './SortableTaskList'
|
|
@@ -1,64 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { Bell, ChevronDown, ArrowRight } from 'lucide-react'
|
|
3
|
-
import { cn } from '../utils/cn'
|
|
4
|
-
import { Avatar, AvatarFallback } from '../primitives/avatar'
|
|
5
|
-
import { Button } from '../primitives/button'
|
|
6
|
-
import { Card, CardContent, CardHeader, CardTitle } from '../primitives/card'
|
|
7
|
-
|
|
8
|
-
const NAV_LINKS = ['Features', 'Pricing', 'Docs', 'Blog']
|
|
9
|
-
|
|
10
|
-
export interface StackedLayoutBrandedProps {
|
|
11
|
-
brandName?: string
|
|
12
|
-
children?: React.ReactNode
|
|
13
|
-
className?: string
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export function StackedLayoutBranded({
|
|
17
|
-
brandName = 'Acme',
|
|
18
|
-
children,
|
|
19
|
-
className,
|
|
20
|
-
}: StackedLayoutBrandedProps) {
|
|
21
|
-
return (
|
|
22
|
-
<div className={cn('flex h-[600px] flex-col overflow-hidden rounded-md border border-[hsl(var(--border))] bg-[hsl(var(--background))]', className)}>
|
|
23
|
-
<nav className="flex h-14 items-center gap-6 bg-[hsl(var(--primary))] px-6">
|
|
24
|
-
<span className="font-bold text-sm text-[hsl(var(--primary-foreground))] shrink-0">{brandName}</span>
|
|
25
|
-
<div className="flex flex-1 items-center gap-1">
|
|
26
|
-
{NAV_LINKS.map(link => (
|
|
27
|
-
<button key={link} className="px-3 py-1.5 text-sm text-[hsl(var(--primary-foreground)/0.8)] hover:text-[hsl(var(--primary-foreground))] transition-colors rounded-md hover:bg-white/10">
|
|
28
|
-
{link}
|
|
29
|
-
</button>
|
|
30
|
-
))}
|
|
31
|
-
</div>
|
|
32
|
-
<div className="flex items-center gap-3">
|
|
33
|
-
<Bell className="h-4 w-4 text-[hsl(var(--primary-foreground)/0.8)]" />
|
|
34
|
-
<div className="flex items-center gap-1.5 cursor-pointer">
|
|
35
|
-
<Avatar className="h-7 w-7 ring-2 ring-white/30">
|
|
36
|
-
<AvatarFallback className="bg-white/20 text-[hsl(var(--primary-foreground))] text-xs">JD</AvatarFallback>
|
|
37
|
-
</Avatar>
|
|
38
|
-
<ChevronDown className="h-3 w-3 text-[hsl(var(--primary-foreground)/0.8)]" />
|
|
39
|
-
</div>
|
|
40
|
-
</div>
|
|
41
|
-
</nav>
|
|
42
|
-
<main className="flex-1 overflow-auto">
|
|
43
|
-
{children ?? (
|
|
44
|
-
<div className="p-6 space-y-6">
|
|
45
|
-
<div>
|
|
46
|
-
<h1 className="text-xl font-bold">Welcome back, Jane</h1>
|
|
47
|
-
<p className="text-sm text-[hsl(var(--muted-foreground))] mt-1">Here's what's happening today.</p>
|
|
48
|
-
</div>
|
|
49
|
-
<div className="grid gap-4" style={{ gridTemplateColumns: 'repeat(auto-fill, minmax(min(100%,200px),1fr))' }}>
|
|
50
|
-
{['Get Started', 'View Analytics', 'Manage Team'].map(action => (
|
|
51
|
-
<Card key={action} className="cursor-pointer hover:shadow-md transition-shadow">
|
|
52
|
-
<CardContent className="p-4 flex items-center justify-between">
|
|
53
|
-
<span className="text-sm font-medium">{action}</span>
|
|
54
|
-
<ArrowRight className="h-4 w-4 text-[hsl(var(--muted-foreground))]" />
|
|
55
|
-
</CardContent>
|
|
56
|
-
</Card>
|
|
57
|
-
))}
|
|
58
|
-
</div>
|
|
59
|
-
</div>
|
|
60
|
-
)}
|
|
61
|
-
</main>
|
|
62
|
-
</div>
|
|
63
|
-
)
|
|
64
|
-
}
|
|
1
|
+
export * from './StackedLayoutBranded'
|
|
@@ -1,80 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { Bell, ChevronDown } from 'lucide-react'
|
|
3
|
-
import { cn } from '../utils/cn'
|
|
4
|
-
import { Avatar, AvatarFallback } from '../primitives/avatar'
|
|
5
|
-
import { Button } from '../primitives/button'
|
|
6
|
-
import { Separator } from '../primitives/separator'
|
|
7
|
-
|
|
8
|
-
const NAV_LINKS = ['Home', 'Projects', 'Analytics', 'Team']
|
|
9
|
-
const TABS = ['Overview', 'Activity', 'Settings', 'Billing']
|
|
10
|
-
|
|
11
|
-
export interface StackedLayoutTabsProps {
|
|
12
|
-
brandName?: string
|
|
13
|
-
children?: React.ReactNode
|
|
14
|
-
className?: string
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export function StackedLayoutTabs({
|
|
18
|
-
brandName = 'Acme Inc',
|
|
19
|
-
children,
|
|
20
|
-
className,
|
|
21
|
-
}: StackedLayoutTabsProps) {
|
|
22
|
-
const [activeTab, setActiveTab] = useState(0)
|
|
23
|
-
const [activeNav, setActiveNav] = useState(0)
|
|
24
|
-
|
|
25
|
-
return (
|
|
26
|
-
<div className={cn('flex h-[600px] flex-col overflow-hidden rounded-md border border-[hsl(var(--border))] bg-[hsl(var(--background))]', className)}>
|
|
27
|
-
<nav className="flex h-14 items-center gap-6 border-b border-[hsl(var(--border))] bg-[hsl(var(--card))] px-4">
|
|
28
|
-
<span className="font-semibold text-sm shrink-0">{brandName}</span>
|
|
29
|
-
<div className="flex flex-1 items-center gap-1">
|
|
30
|
-
{NAV_LINKS.map((link, i) => (
|
|
31
|
-
<button
|
|
32
|
-
key={link}
|
|
33
|
-
onClick={() => setActiveNav(i)}
|
|
34
|
-
className={cn(
|
|
35
|
-
'px-3 py-1.5 text-sm rounded-md transition-colors',
|
|
36
|
-
i === activeNav
|
|
37
|
-
? 'bg-[hsl(var(--accent))] font-medium'
|
|
38
|
-
: 'text-[hsl(var(--muted-foreground))] hover:bg-[hsl(var(--accent))]'
|
|
39
|
-
)}
|
|
40
|
-
>
|
|
41
|
-
{link}
|
|
42
|
-
</button>
|
|
43
|
-
))}
|
|
44
|
-
</div>
|
|
45
|
-
<div className="flex items-center gap-2">
|
|
46
|
-
<Bell className="h-4 w-4 text-[hsl(var(--muted-foreground))]" />
|
|
47
|
-
<div className="flex items-center gap-1.5 cursor-pointer">
|
|
48
|
-
<Avatar className="h-7 w-7"><AvatarFallback className="text-xs">JD</AvatarFallback></Avatar>
|
|
49
|
-
<ChevronDown className="h-3 w-3 text-[hsl(var(--muted-foreground))]" />
|
|
50
|
-
</div>
|
|
51
|
-
</div>
|
|
52
|
-
</nav>
|
|
53
|
-
<div className="border-b border-[hsl(var(--border))] bg-[hsl(var(--card))] px-4">
|
|
54
|
-
<div className="flex gap-4">
|
|
55
|
-
{TABS.map((tab, i) => (
|
|
56
|
-
<button
|
|
57
|
-
key={tab}
|
|
58
|
-
onClick={() => setActiveTab(i)}
|
|
59
|
-
className={cn(
|
|
60
|
-
'py-3 text-sm transition-colors border-b-2',
|
|
61
|
-
i === activeTab
|
|
62
|
-
? 'border-[hsl(var(--primary))] font-medium'
|
|
63
|
-
: 'border-transparent text-[hsl(var(--muted-foreground))] hover:text-[hsl(var(--foreground))]'
|
|
64
|
-
)}
|
|
65
|
-
>
|
|
66
|
-
{tab}
|
|
67
|
-
</button>
|
|
68
|
-
))}
|
|
69
|
-
</div>
|
|
70
|
-
</div>
|
|
71
|
-
<main className="flex-1 overflow-auto p-6">
|
|
72
|
-
{children ?? (
|
|
73
|
-
<div className="text-sm text-[hsl(var(--muted-foreground))]">
|
|
74
|
-
Content for <strong>{TABS[activeTab]}</strong> tab
|
|
75
|
-
</div>
|
|
76
|
-
)}
|
|
77
|
-
</main>
|
|
78
|
-
</div>
|
|
79
|
-
)
|
|
80
|
-
}
|
|
1
|
+
export * from './StackedLayoutTabs'
|