@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.
Files changed (120) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +28 -41
  3. package/dist/index.js +972 -655
  4. package/dist/index.mjs +826 -525
  5. package/package.json +5 -4
  6. package/src/advanced/BulkActions/BulkActions.tsx +2 -2
  7. package/src/advanced/CommandBar/CommandBar.tsx +2 -2
  8. package/src/advanced/Filters/Filters.tsx +6 -6
  9. package/src/advanced/Kanban/Kanban.tsx +1 -1
  10. package/src/advanced/ToggleButton/ToggleButton.tsx +1 -1
  11. package/src/advanced/bulk-actions.tsx +1 -46
  12. package/src/advanced/command-bar.tsx +1 -115
  13. package/src/advanced/filters.tsx +1 -103
  14. package/src/advanced/kanban.tsx +1 -95
  15. package/src/advanced/toggle-button.tsx +1 -78
  16. package/src/blocks/ChatDetail/ChatDetail.tsx +4 -4
  17. package/src/blocks/FeedbackModal/FeedbackModal.tsx +3 -3
  18. package/src/blocks/FileCards/FileCards.tsx +2 -2
  19. package/src/blocks/FilesList/FilesList.tsx +3 -3
  20. package/src/blocks/IntegrationCard/IntegrationCard.tsx +3 -3
  21. package/src/blocks/InviteModal/InviteModal.tsx +7 -7
  22. package/src/blocks/ManageTagsModal/ManageTagsModal.tsx +4 -4
  23. package/src/blocks/MessagesCard/MessagesCard.tsx +3 -3
  24. package/src/blocks/MetricCard/MetricCard.tsx +3 -3
  25. package/src/blocks/NotificationSettings/NotificationSettings.tsx +4 -4
  26. package/src/blocks/OrganizationMenu/OrganizationMenu.tsx +30 -11
  27. package/src/blocks/RolesMenu/RolesMenu.tsx +4 -4
  28. package/src/blocks/SelectUsersModal/SelectUsersModal.tsx +5 -5
  29. package/src/blocks/SidebarLayoutDashboard/SidebarLayoutDashboard.tsx +5 -5
  30. package/src/blocks/SidebarLayoutGroups/SidebarLayoutGroups.tsx +2 -2
  31. package/src/blocks/SidebarLayoutMinimal/SidebarLayoutMinimal.tsx +3 -3
  32. package/src/blocks/SidebarLayoutSearch/SidebarLayoutSearch.tsx +3 -3
  33. package/src/blocks/SidebarLayoutUser/SidebarLayoutUser.tsx +4 -4
  34. package/src/blocks/SortableTaskList/SortableTaskList.tsx +3 -3
  35. package/src/blocks/StackedLayoutBranded/StackedLayoutBranded.tsx +4 -4
  36. package/src/blocks/StackedLayoutTabs/StackedLayoutTabs.tsx +4 -4
  37. package/src/blocks/TaskCard/TaskCard.tsx +4 -4
  38. package/src/blocks/UserMenu/UserMenu.tsx +3 -3
  39. package/src/blocks/WorkspaceMembers/WorkspaceMembers.tsx +5 -5
  40. package/src/blocks/chat-detail.tsx +1 -73
  41. package/src/blocks/feedback-modal.tsx +1 -68
  42. package/src/blocks/file-cards.tsx +1 -56
  43. package/src/blocks/files-list.tsx +1 -72
  44. package/src/blocks/integration-card.tsx +1 -50
  45. package/src/blocks/invite-modal.tsx +1 -69
  46. package/src/blocks/manage-tags-modal.tsx +1 -63
  47. package/src/blocks/messages-card.tsx +1 -59
  48. package/src/blocks/metric-card.tsx +1 -79
  49. package/src/blocks/notification-settings.tsx +1 -61
  50. package/src/blocks/organization-menu.tsx +1 -85
  51. package/src/blocks/roles-menu.tsx +1 -61
  52. package/src/blocks/select-users-modal.tsx +1 -64
  53. package/src/blocks/sidebar-layout-dashboard.tsx +1 -97
  54. package/src/blocks/sidebar-layout-groups.tsx +1 -79
  55. package/src/blocks/sidebar-layout-minimal.tsx +1 -73
  56. package/src/blocks/sidebar-layout-search.tsx +1 -79
  57. package/src/blocks/sidebar-layout-user.tsx +1 -82
  58. package/src/blocks/sortable-task-list.tsx +1 -70
  59. package/src/blocks/stacked-layout-branded.tsx +1 -64
  60. package/src/blocks/stacked-layout-tabs.tsx +1 -80
  61. package/src/blocks/task-card.tsx +1 -88
  62. package/src/blocks/user-menu.tsx +1 -62
  63. package/src/blocks/workspace-members.tsx +1 -61
  64. package/src/charts/sparkline.tsx +1 -1
  65. package/src/components/DataGrid/DataGrid.tsx +1 -1
  66. package/src/components/DataTable/DataTable.tsx +4 -4
  67. package/src/components/EmptyState/EmptyState.tsx +6 -22
  68. package/src/components/Persona/Persona.tsx +1 -1
  69. package/src/components/Stat/Stat.tsx +1 -1
  70. package/src/components/Stepper/Stepper.tsx +13 -2
  71. package/src/components/banner.tsx +1 -203
  72. package/src/components/breadcrumb.tsx +1 -58
  73. package/src/components/command-item.tsx +1 -36
  74. package/src/components/data-grid.tsx +1 -125
  75. package/src/components/data-table.tsx +1 -139
  76. package/src/components/empty-state.tsx +1 -31
  77. package/src/components/hotkeys.tsx +1 -31
  78. package/src/components/icon-badge.tsx +1 -35
  79. package/src/components/loading-overlay.tsx +1 -38
  80. package/src/components/persona.tsx +1 -38
  81. package/src/components/property-list.tsx +1 -29
  82. package/src/components/stat.tsx +1 -51
  83. package/src/components/stepper.tsx +1 -69
  84. package/src/components/structured-list.tsx +1 -51
  85. package/src/components/timeline.tsx +1 -69
  86. package/src/components/toaster.tsx +1 -42
  87. package/src/data/DataList.tsx +1 -1
  88. package/src/engagement/Tour/Tour.tsx +1 -1
  89. package/src/engagement/beacon.tsx +1 -50
  90. package/src/engagement/tour.tsx +1 -106
  91. package/src/forms/ArrayField/ArrayField.tsx +2 -2
  92. package/src/forms/AutoForm/AutoForm.tsx +9 -8
  93. package/src/forms/DatePicker/DatePicker.tsx +3 -3
  94. package/src/forms/FloatingLabelInput/FloatingLabelInput.tsx +104 -0
  95. package/src/forms/FloatingLabelInput/index.ts +1 -0
  96. package/src/forms/PasswordInput/PasswordInput.tsx +2 -2
  97. package/src/forms/SearchInput/SearchInput.tsx +2 -2
  98. package/src/forms/StepForm/StepForm.tsx +2 -2
  99. package/src/forms/array-field.tsx +1 -61
  100. package/src/forms/auto-form.tsx +1 -133
  101. package/src/forms/date-picker.tsx +1 -109
  102. package/src/forms/field.tsx +1 -62
  103. package/src/forms/file-upload.tsx +1 -101
  104. package/src/forms/floating-label-input.tsx +1 -0
  105. package/src/forms/form.tsx +1 -57
  106. package/src/forms/object-field.tsx +1 -53
  107. package/src/forms/password-input.tsx +1 -54
  108. package/src/forms/search-input.tsx +1 -64
  109. package/src/forms/step-form.tsx +1 -113
  110. package/src/index.ts +11 -3
  111. package/src/primitives/Button/Button.tsx +18 -0
  112. package/src/primitives/cart-line-item.tsx +81 -0
  113. package/src/primitives/code-block.tsx +1 -1
  114. package/src/primitives/comparison.tsx +1 -1
  115. package/src/primitives/empty-cart.tsx +52 -0
  116. package/src/primitives/price-tag.tsx +62 -0
  117. package/src/primitives/qr-code.tsx +5 -1
  118. package/src/primitives/quantity-stepper.tsx +92 -0
  119. package/src/primitives/variant-picker.tsx +74 -0
  120. package/src/themes/base.css +18 -0
@@ -1,97 +1 @@
1
- // @ts-nocheck
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
- // @ts-nocheck
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
- // @ts-nocheck
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
- // @ts-nocheck
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
- // @ts-nocheck
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
- import React, { useState } from 'react'
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
- import React from 'react'
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
- import React, { useState } from 'react'
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'