@otfdashkit/ui 0.1.1 → 0.1.2
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 +206 -125
- package/dist/index.mjs +194 -124
- 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 +6 -3
- package/src/primitives/Button/Button.tsx +18 -0
- package/src/primitives/code-block.tsx +1 -1
- package/src/primitives/comparison.tsx +1 -1
- package/src/primitives/qr-code.tsx +5 -1
- package/src/themes/base.css +18 -0
|
@@ -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'
|
package/src/blocks/task-card.tsx
CHANGED
|
@@ -1,88 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { Calendar } from 'lucide-react'
|
|
3
|
-
import { cn } from '../utils/cn'
|
|
4
|
-
import { Card, CardContent } from '../primitives/card'
|
|
5
|
-
import { Avatar, AvatarFallback } from '../primitives/avatar'
|
|
6
|
-
import { Badge } from '../primitives/badge'
|
|
7
|
-
|
|
8
|
-
const PRIORITY_COLOR: Record<string, string> = {
|
|
9
|
-
High: 'bg-red-500/10 text-red-600 border-red-200',
|
|
10
|
-
Medium: 'bg-yellow-500/10 text-yellow-600 border-yellow-200',
|
|
11
|
-
Low: 'bg-green-500/10 text-green-600 border-green-200',
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export interface TaskCardProps {
|
|
15
|
-
title?: string
|
|
16
|
-
description?: string
|
|
17
|
-
assigneeInitials?: string
|
|
18
|
-
dueDate?: string
|
|
19
|
-
priority?: 'High' | 'Medium' | 'Low'
|
|
20
|
-
className?: string
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export function TaskCard({
|
|
24
|
-
title = 'Redesign onboarding flow',
|
|
25
|
-
description = 'Update the user onboarding experience with the new design language.',
|
|
26
|
-
assigneeInitials = 'AC',
|
|
27
|
-
dueDate = 'Mar 20',
|
|
28
|
-
priority = 'High',
|
|
29
|
-
className,
|
|
30
|
-
}: TaskCardProps) {
|
|
31
|
-
return (
|
|
32
|
-
<Card className={cn('cursor-pointer hover:shadow-md transition-shadow', className)}>
|
|
33
|
-
<CardContent className="p-4 space-y-3">
|
|
34
|
-
<div className="flex items-start justify-between gap-2">
|
|
35
|
-
<p className="text-sm font-semibold leading-tight">{title}</p>
|
|
36
|
-
<span className={cn('text-[10px] px-1.5 py-0.5 rounded-full border font-medium shrink-0', PRIORITY_COLOR[priority])}>
|
|
37
|
-
{priority}
|
|
38
|
-
</span>
|
|
39
|
-
</div>
|
|
40
|
-
<p className="text-xs text-[hsl(var(--muted-foreground))] leading-relaxed line-clamp-2">{description}</p>
|
|
41
|
-
<div className="flex items-center justify-between">
|
|
42
|
-
<Avatar className="h-6 w-6"><AvatarFallback className="text-[10px]">{assigneeInitials}</AvatarFallback></Avatar>
|
|
43
|
-
<div className="flex items-center gap-1 text-xs text-[hsl(var(--muted-foreground))]">
|
|
44
|
-
<Calendar className="h-3 w-3" />{dueDate}
|
|
45
|
-
</div>
|
|
46
|
-
</div>
|
|
47
|
-
</CardContent>
|
|
48
|
-
</Card>
|
|
49
|
-
)
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const LABEL_COLORS = ['bg-blue-100 text-blue-700', 'bg-purple-100 text-purple-700', 'bg-green-100 text-green-700']
|
|
53
|
-
|
|
54
|
-
export interface TaskCardWithLabelsProps extends TaskCardProps {
|
|
55
|
-
labels?: string[]
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export function TaskCardWithLabels({
|
|
59
|
-
labels = ['Design', 'Frontend'],
|
|
60
|
-
...props
|
|
61
|
-
}: TaskCardWithLabelsProps) {
|
|
62
|
-
return (
|
|
63
|
-
<Card className={cn('cursor-pointer hover:shadow-md transition-shadow', props.className)}>
|
|
64
|
-
<CardContent className="p-4 space-y-3">
|
|
65
|
-
<div className="flex items-start justify-between gap-2">
|
|
66
|
-
<p className="text-sm font-semibold leading-tight">{props.title ?? 'Redesign onboarding flow'}</p>
|
|
67
|
-
<span className={cn('text-[10px] px-1.5 py-0.5 rounded-full border font-medium shrink-0', PRIORITY_COLOR[props.priority ?? 'High'])}>
|
|
68
|
-
{props.priority ?? 'High'}
|
|
69
|
-
</span>
|
|
70
|
-
</div>
|
|
71
|
-
<p className="text-xs text-[hsl(var(--muted-foreground))] leading-relaxed line-clamp-2">
|
|
72
|
-
{props.description ?? 'Update the user onboarding experience with the new design language.'}
|
|
73
|
-
</p>
|
|
74
|
-
<div className="flex flex-wrap gap-1">
|
|
75
|
-
{labels.map((l, i) => (
|
|
76
|
-
<span key={l} className={cn('text-[10px] px-1.5 py-0.5 rounded-full font-medium', LABEL_COLORS[i % LABEL_COLORS.length])}>{l}</span>
|
|
77
|
-
))}
|
|
78
|
-
</div>
|
|
79
|
-
<div className="flex items-center justify-between">
|
|
80
|
-
<Avatar className="h-6 w-6"><AvatarFallback className="text-[10px]">{props.assigneeInitials ?? 'AC'}</AvatarFallback></Avatar>
|
|
81
|
-
<div className="flex items-center gap-1 text-xs text-[hsl(var(--muted-foreground))]">
|
|
82
|
-
<Calendar className="h-3 w-3" />{props.dueDate ?? 'Mar 20'}
|
|
83
|
-
</div>
|
|
84
|
-
</div>
|
|
85
|
-
</CardContent>
|
|
86
|
-
</Card>
|
|
87
|
-
)
|
|
88
|
-
}
|
|
1
|
+
export * from './TaskCard'
|
package/src/blocks/user-menu.tsx
CHANGED
|
@@ -1,62 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { ChevronDown, User, Settings, CreditCard, LogOut } from 'lucide-react'
|
|
3
|
-
import { cn } from '../utils/cn'
|
|
4
|
-
import { Avatar, AvatarFallback } from '../primitives/avatar'
|
|
5
|
-
import {
|
|
6
|
-
DropdownMenu,
|
|
7
|
-
DropdownMenuContent,
|
|
8
|
-
DropdownMenuItem,
|
|
9
|
-
DropdownMenuLabel,
|
|
10
|
-
DropdownMenuSeparator,
|
|
11
|
-
DropdownMenuTrigger,
|
|
12
|
-
} from '../primitives/dropdown-menu'
|
|
13
|
-
|
|
14
|
-
export interface UserMenuProps {
|
|
15
|
-
name?: string
|
|
16
|
-
email?: string
|
|
17
|
-
initials?: string
|
|
18
|
-
onProfile?: () => void
|
|
19
|
-
onSettings?: () => void
|
|
20
|
-
onBilling?: () => void
|
|
21
|
-
onSignOut?: () => void
|
|
22
|
-
className?: string
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export function UserMenu({
|
|
26
|
-
name = 'Jane Doe',
|
|
27
|
-
email = 'jane@acme.com',
|
|
28
|
-
initials = 'JD',
|
|
29
|
-
onProfile,
|
|
30
|
-
onSettings,
|
|
31
|
-
onBilling,
|
|
32
|
-
onSignOut,
|
|
33
|
-
className,
|
|
34
|
-
}: UserMenuProps) {
|
|
35
|
-
return (
|
|
36
|
-
<DropdownMenu>
|
|
37
|
-
<DropdownMenuTrigger asChild>
|
|
38
|
-
<button className={cn('flex items-center gap-2 rounded-md px-2 py-1.5 text-sm transition-colors hover:bg-[hsl(var(--accent))] focus:outline-none', className)}>
|
|
39
|
-
<Avatar className="h-7 w-7">
|
|
40
|
-
<AvatarFallback className="text-xs">{initials}</AvatarFallback>
|
|
41
|
-
</Avatar>
|
|
42
|
-
<span className="font-medium max-w-[120px] truncate">{name}</span>
|
|
43
|
-
<ChevronDown className="h-3.5 w-3.5 text-[hsl(var(--muted-foreground))]" />
|
|
44
|
-
</button>
|
|
45
|
-
</DropdownMenuTrigger>
|
|
46
|
-
<DropdownMenuContent align="end" className="w-52">
|
|
47
|
-
<DropdownMenuLabel className="font-normal">
|
|
48
|
-
<p className="text-sm font-medium">{name}</p>
|
|
49
|
-
<p className="text-xs text-[hsl(var(--muted-foreground))]">{email}</p>
|
|
50
|
-
</DropdownMenuLabel>
|
|
51
|
-
<DropdownMenuSeparator />
|
|
52
|
-
<DropdownMenuItem onClick={onProfile}><User className="h-4 w-4" />Profile</DropdownMenuItem>
|
|
53
|
-
<DropdownMenuItem onClick={onSettings}><Settings className="h-4 w-4" />Account Settings</DropdownMenuItem>
|
|
54
|
-
<DropdownMenuItem onClick={onBilling}><CreditCard className="h-4 w-4" />Billing</DropdownMenuItem>
|
|
55
|
-
<DropdownMenuSeparator />
|
|
56
|
-
<DropdownMenuItem onClick={onSignOut} className="text-[hsl(var(--destructive))] focus:text-[hsl(var(--destructive))]">
|
|
57
|
-
<LogOut className="h-4 w-4" />Sign out
|
|
58
|
-
</DropdownMenuItem>
|
|
59
|
-
</DropdownMenuContent>
|
|
60
|
-
</DropdownMenu>
|
|
61
|
-
)
|
|
62
|
-
}
|
|
1
|
+
export * from './UserMenu'
|