@fayz-ai/ui 0.9.0-next.1 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. package/README.md +2 -2
  2. package/dist/analytics/AnalyticsCard.d.ts +10 -0
  3. package/dist/analytics/AnalyticsCard.d.ts.map +1 -0
  4. package/dist/analytics/AnalyticsControls.d.ts +18 -0
  5. package/dist/analytics/AnalyticsControls.d.ts.map +1 -0
  6. package/dist/analytics/AnalyticsGrid.d.ts +16 -0
  7. package/dist/analytics/AnalyticsGrid.d.ts.map +1 -0
  8. package/dist/analytics/context.d.ts +58 -0
  9. package/dist/analytics/context.d.ts.map +1 -0
  10. package/dist/analytics/derive.d.ts +81 -0
  11. package/dist/analytics/derive.d.ts.map +1 -0
  12. package/dist/analytics/index.d.ts +13 -0
  13. package/dist/analytics/index.d.ts.map +1 -0
  14. package/dist/analytics/range.d.ts +22 -0
  15. package/dist/analytics/range.d.ts.map +1 -0
  16. package/dist/{chunk-PVIB724L.js → chunk-22EVFMS3.js} +5 -3
  17. package/dist/chunk-22EVFMS3.js.map +1 -0
  18. package/dist/{chunk-G6OKZ7WL.js → chunk-6V5MGYXH.js} +13 -9
  19. package/dist/chunk-6V5MGYXH.js.map +1 -0
  20. package/dist/{chunk-KV3MHB6N.js → chunk-H63TOSQ7.js} +196 -53
  21. package/dist/chunk-H63TOSQ7.js.map +1 -0
  22. package/dist/{chunk-OUPJDGKE.js → chunk-I6CPKAOD.js} +134 -37
  23. package/dist/chunk-I6CPKAOD.js.map +1 -0
  24. package/dist/{chunk-YRVE4FOD.js → chunk-OKZ6WQB3.js} +3 -3
  25. package/dist/chunk-OKZ6WQB3.js.map +1 -0
  26. package/dist/{chunk-S5KNM4D2.js → chunk-SEIY2F5F.js} +235 -107
  27. package/dist/chunk-SEIY2F5F.js.map +1 -0
  28. package/dist/{chunk-75EFDLST.js → chunk-VAKWUUVC.js} +157 -6
  29. package/dist/chunk-VAKWUUVC.js.map +1 -0
  30. package/dist/dashboard/OnboardingWidget.d.ts +2 -14
  31. package/dist/dashboard/OnboardingWidget.d.ts.map +1 -1
  32. package/dist/dashboard/SidebarOnboarding.d.ts +28 -0
  33. package/dist/dashboard/SidebarOnboarding.d.ts.map +1 -0
  34. package/dist/dashboard/index.d.ts +2 -0
  35. package/dist/dashboard/index.d.ts.map +1 -1
  36. package/dist/dashboard/index.js +4 -4
  37. package/dist/dashboard/types.d.ts +18 -0
  38. package/dist/dashboard/types.d.ts.map +1 -1
  39. package/dist/dashboard/useOnboardingProgress.d.ts +28 -0
  40. package/dist/dashboard/useOnboardingProgress.d.ts.map +1 -0
  41. package/dist/icons.js +1 -1
  42. package/dist/index.d.ts +5 -4
  43. package/dist/index.d.ts.map +1 -1
  44. package/dist/index.js +960 -14
  45. package/dist/index.js.map +1 -1
  46. package/dist/layout/AppShell.d.ts +14 -1
  47. package/dist/layout/AppShell.d.ts.map +1 -1
  48. package/dist/layout/ModulePage.d.ts +11 -2
  49. package/dist/layout/ModulePage.d.ts.map +1 -1
  50. package/dist/layout/Sidebar.d.ts +8 -1
  51. package/dist/layout/Sidebar.d.ts.map +1 -1
  52. package/dist/layout/index.js +4 -4
  53. package/dist/lib/cn.d.ts.map +1 -0
  54. package/dist/preset.d.ts +3 -0
  55. package/dist/preset.d.ts.map +1 -0
  56. package/dist/primitives/data-table.d.ts +19 -4
  57. package/dist/primitives/data-table.d.ts.map +1 -1
  58. package/dist/primitives/date-picker.d.ts +4 -2
  59. package/dist/primitives/date-picker.d.ts.map +1 -1
  60. package/dist/primitives/filter-chip.d.ts +26 -0
  61. package/dist/primitives/filter-chip.d.ts.map +1 -0
  62. package/dist/primitives/header-tabs.d.ts +32 -0
  63. package/dist/primitives/header-tabs.d.ts.map +1 -0
  64. package/dist/primitives/hint.d.ts +25 -0
  65. package/dist/primitives/hint.d.ts.map +1 -0
  66. package/dist/primitives/index.d.ts +7 -0
  67. package/dist/primitives/index.d.ts.map +1 -1
  68. package/dist/primitives/index.js +4 -4
  69. package/dist/primitives/list-view.d.ts +14 -1
  70. package/dist/primitives/list-view.d.ts.map +1 -1
  71. package/dist/primitives/page-header.d.ts +23 -0
  72. package/dist/primitives/page-header.d.ts.map +1 -0
  73. package/dist/primitives/stat-tiles.d.ts +30 -0
  74. package/dist/primitives/stat-tiles.d.ts.map +1 -0
  75. package/dist/primitives/switch.d.ts +11 -0
  76. package/dist/primitives/switch.d.ts.map +1 -0
  77. package/dist/stores/index.d.ts +1 -0
  78. package/dist/stores/index.d.ts.map +1 -1
  79. package/dist/stores/page-header.store.d.ts +69 -0
  80. package/dist/stores/page-header.store.d.ts.map +1 -0
  81. package/package.json +4 -13
  82. package/dist/chunk-75EFDLST.js.map +0 -1
  83. package/dist/chunk-G6OKZ7WL.js.map +0 -1
  84. package/dist/chunk-KV3MHB6N.js.map +0 -1
  85. package/dist/chunk-OUPJDGKE.js.map +0 -1
  86. package/dist/chunk-PVIB724L.js.map +0 -1
  87. package/dist/chunk-S5KNM4D2.js.map +0 -1
  88. package/dist/chunk-YRVE4FOD.js.map +0 -1
  89. package/dist/utils/cn.d.ts.map +0 -1
  90. package/src/dashboard/ChartWidget.tsx +0 -50
  91. package/src/dashboard/ChartWidgetInner.tsx +0 -83
  92. package/src/dashboard/DashboardCanvas.tsx +0 -210
  93. package/src/dashboard/DashboardCustomizeMenu.tsx +0 -53
  94. package/src/dashboard/DashboardGrid.tsx +0 -35
  95. package/src/dashboard/DashboardRange.tsx +0 -62
  96. package/src/dashboard/GoldCard.tsx +0 -38
  97. package/src/dashboard/KpiCard.tsx +0 -238
  98. package/src/dashboard/OnboardingWidget.tsx +0 -140
  99. package/src/dashboard/TableWidget.tsx +0 -51
  100. package/src/dashboard/define.ts +0 -53
  101. package/src/dashboard/icon.ts +0 -13
  102. package/src/dashboard/index.ts +0 -38
  103. package/src/dashboard/layout.ts +0 -50
  104. package/src/dashboard/preferences.ts +0 -71
  105. package/src/dashboard/types.ts +0 -41
  106. package/src/icons.ts +0 -66
  107. package/src/index.ts +0 -238
  108. package/src/layout/AppShell.tsx +0 -987
  109. package/src/layout/ContentSplit.tsx +0 -153
  110. package/src/layout/ModulePage.tsx +0 -466
  111. package/src/layout/PageTransition.tsx +0 -57
  112. package/src/layout/SaveBar.tsx +0 -247
  113. package/src/layout/Sidebar.tsx +0 -535
  114. package/src/layout/Topbar.tsx +0 -462
  115. package/src/layout/index.ts +0 -39
  116. package/src/layout/useCrossModuleBack.ts +0 -30
  117. package/src/primitives/Avatar.tsx +0 -48
  118. package/src/primitives/SearchCombobox.tsx +0 -216
  119. package/src/primitives/badge.tsx +0 -68
  120. package/src/primitives/breadcrumb.tsx +0 -43
  121. package/src/primitives/button.tsx +0 -60
  122. package/src/primitives/card.tsx +0 -79
  123. package/src/primitives/checkbox.tsx +0 -53
  124. package/src/primitives/confirm-dialog.tsx +0 -125
  125. package/src/primitives/currency-input.tsx +0 -121
  126. package/src/primitives/data-table.tsx +0 -160
  127. package/src/primitives/date-picker.tsx +0 -225
  128. package/src/primitives/dropdown.tsx +0 -196
  129. package/src/primitives/index.ts +0 -87
  130. package/src/primitives/input.tsx +0 -50
  131. package/src/primitives/list-view.tsx +0 -252
  132. package/src/primitives/markdown-editor.tsx +0 -75
  133. package/src/primitives/markdown.tsx +0 -169
  134. package/src/primitives/modal.tsx +0 -156
  135. package/src/primitives/phone-link.tsx +0 -138
  136. package/src/primitives/popover.tsx +0 -30
  137. package/src/primitives/search-select.tsx +0 -314
  138. package/src/primitives/segmented-control.tsx +0 -66
  139. package/src/primitives/select.tsx +0 -157
  140. package/src/primitives/separator.tsx +0 -29
  141. package/src/primitives/sheet.tsx +0 -181
  142. package/src/primitives/skeleton.tsx +0 -46
  143. package/src/primitives/tabs.tsx +0 -53
  144. package/src/primitives/time-picker.tsx +0 -148
  145. package/src/primitives/toast.tsx +0 -46
  146. package/src/primitives/tooltip.tsx +0 -28
  147. package/src/stores/index.ts +0 -10
  148. package/src/stores/layout.store.ts +0 -31
  149. package/src/stores/theme.store.ts +0 -457
  150. package/src/styles.css +0 -461
  151. package/src/tailwind.ts +0 -43
  152. package/src/theme/index.ts +0 -94
  153. package/src/theme/preset.ts +0 -209
  154. package/src/theme/presets.ts +0 -106
  155. package/src/utils/cn.ts +0 -6
  156. /package/dist/{utils → lib}/cn.d.ts +0 -0
@@ -1,196 +0,0 @@
1
- import * as React from 'react'
2
- import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu'
3
- import { Check, ChevronRight, Circle } from 'lucide-react'
4
-
5
- import { cn } from '../utils/cn'
6
-
7
- const Dropdown = DropdownMenuPrimitive.Root
8
-
9
- const DropdownTrigger = DropdownMenuPrimitive.Trigger
10
-
11
- const DropdownGroup = DropdownMenuPrimitive.Group
12
-
13
- const DropdownPortal = DropdownMenuPrimitive.Portal
14
-
15
- const DropdownSub = DropdownMenuPrimitive.Sub
16
-
17
- const DropdownRadioGroup = DropdownMenuPrimitive.RadioGroup
18
-
19
- const DropdownSubTrigger = React.forwardRef<
20
- React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,
21
- React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {
22
- inset?: boolean
23
- }
24
- >(({ className, inset, children, ...props }, ref) => (
25
- <DropdownMenuPrimitive.SubTrigger
26
- ref={ref}
27
- className={cn(
28
- 'flex cursor-default select-none items-center rounded-button px-2 py-1.5 text-sm outline-none focus:bg-muted data-[state=open]:bg-muted',
29
- inset && 'pl-8',
30
- className
31
- )}
32
- {...props}
33
- >
34
- {children}
35
- <ChevronRight className="ml-auto h-4 w-4" />
36
- </DropdownMenuPrimitive.SubTrigger>
37
- ))
38
- DropdownSubTrigger.displayName = DropdownMenuPrimitive.SubTrigger.displayName
39
-
40
- const DropdownSubContent = React.forwardRef<
41
- React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,
42
- React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent>
43
- >(({ className, ...props }, ref) => (
44
- <DropdownMenuPrimitive.SubContent
45
- ref={ref}
46
- className={cn(
47
- 'fayz-glass-surface z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
48
- className
49
- )}
50
- {...props}
51
- />
52
- ))
53
- DropdownSubContent.displayName = DropdownMenuPrimitive.SubContent.displayName
54
-
55
- const DropdownContent = React.forwardRef<
56
- React.ElementRef<typeof DropdownMenuPrimitive.Content>,
57
- React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>
58
- >(({ className, sideOffset = 4, ...props }, ref) => (
59
- <DropdownMenuPrimitive.Portal>
60
- <DropdownMenuPrimitive.Content
61
- ref={ref}
62
- sideOffset={sideOffset}
63
- className={cn(
64
- 'fayz-glass-surface z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
65
- className
66
- )}
67
- {...props}
68
- />
69
- </DropdownMenuPrimitive.Portal>
70
- ))
71
- DropdownContent.displayName = DropdownMenuPrimitive.Content.displayName
72
-
73
- const DropdownItem = React.forwardRef<
74
- React.ElementRef<typeof DropdownMenuPrimitive.Item>,
75
- React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {
76
- inset?: boolean
77
- }
78
- >(({ className, inset, ...props }, ref) => (
79
- <DropdownMenuPrimitive.Item
80
- ref={ref}
81
- className={cn(
82
- 'relative flex cursor-default select-none items-center rounded-button px-2 py-1.5 text-sm outline-none transition-colors focus:bg-muted data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
83
- inset && 'pl-8',
84
- className
85
- )}
86
- {...props}
87
- />
88
- ))
89
- DropdownItem.displayName = DropdownMenuPrimitive.Item.displayName
90
-
91
- const DropdownCheckboxItem = React.forwardRef<
92
- React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,
93
- React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem>
94
- >(({ className, children, checked, ...props }, ref) => (
95
- <DropdownMenuPrimitive.CheckboxItem
96
- ref={ref}
97
- className={cn(
98
- 'relative flex cursor-default select-none items-center rounded-button py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-muted data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
99
- className
100
- )}
101
- checked={checked}
102
- {...props}
103
- >
104
- <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
105
- <DropdownMenuPrimitive.ItemIndicator>
106
- <Check className="h-4 w-4" />
107
- </DropdownMenuPrimitive.ItemIndicator>
108
- </span>
109
- {children}
110
- </DropdownMenuPrimitive.CheckboxItem>
111
- ))
112
- DropdownCheckboxItem.displayName =
113
- DropdownMenuPrimitive.CheckboxItem.displayName
114
-
115
- const DropdownRadioItem = React.forwardRef<
116
- React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,
117
- React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem>
118
- >(({ className, children, ...props }, ref) => (
119
- <DropdownMenuPrimitive.RadioItem
120
- ref={ref}
121
- className={cn(
122
- 'relative flex cursor-default select-none items-center rounded-button py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-muted data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
123
- className
124
- )}
125
- {...props}
126
- >
127
- <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
128
- <DropdownMenuPrimitive.ItemIndicator>
129
- <Circle className="h-2 w-2 fill-current" />
130
- </DropdownMenuPrimitive.ItemIndicator>
131
- </span>
132
- {children}
133
- </DropdownMenuPrimitive.RadioItem>
134
- ))
135
- DropdownRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName
136
-
137
- const DropdownLabel = React.forwardRef<
138
- React.ElementRef<typeof DropdownMenuPrimitive.Label>,
139
- React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & {
140
- inset?: boolean
141
- }
142
- >(({ className, inset, ...props }, ref) => (
143
- <DropdownMenuPrimitive.Label
144
- ref={ref}
145
- className={cn(
146
- 'px-2 py-1.5 text-sm font-semibold',
147
- inset && 'pl-8',
148
- className
149
- )}
150
- {...props}
151
- />
152
- ))
153
- DropdownLabel.displayName = DropdownMenuPrimitive.Label.displayName
154
-
155
- const DropdownSeparator = React.forwardRef<
156
- React.ElementRef<typeof DropdownMenuPrimitive.Separator>,
157
- React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>
158
- >(({ className, ...props }, ref) => (
159
- <DropdownMenuPrimitive.Separator
160
- ref={ref}
161
- className={cn('-mx-1 my-1 h-px bg-muted', className)}
162
- {...props}
163
- />
164
- ))
165
- DropdownSeparator.displayName = DropdownMenuPrimitive.Separator.displayName
166
-
167
- const DropdownShortcut = ({
168
- className,
169
- ...props
170
- }: React.HTMLAttributes<HTMLSpanElement>) => {
171
- return (
172
- <span
173
- className={cn('ml-auto text-xs tracking-widest opacity-60', className)}
174
- {...props}
175
- />
176
- )
177
- }
178
- DropdownShortcut.displayName = 'DropdownShortcut'
179
-
180
- export {
181
- Dropdown,
182
- DropdownTrigger,
183
- DropdownContent,
184
- DropdownItem,
185
- DropdownCheckboxItem,
186
- DropdownRadioItem,
187
- DropdownLabel,
188
- DropdownSeparator,
189
- DropdownShortcut,
190
- DropdownGroup,
191
- DropdownPortal,
192
- DropdownSub,
193
- DropdownSubContent,
194
- DropdownSubTrigger,
195
- DropdownRadioGroup,
196
- }
@@ -1,87 +0,0 @@
1
- export { Button, buttonVariants, type ButtonProps } from './button'
2
- export { Input, type InputProps } from './input'
3
- export { Badge, badgeVariants, type BadgeProps } from './badge'
4
- export {
5
- Card,
6
- CardHeader,
7
- CardFooter,
8
- CardTitle,
9
- CardDescription,
10
- CardContent,
11
- } from './card'
12
- export {
13
- Modal,
14
- ModalPortal,
15
- ModalOverlay,
16
- ModalClose,
17
- ModalTrigger,
18
- ModalContent,
19
- ModalHeader,
20
- ModalBody,
21
- ModalFooter,
22
- ModalTitle,
23
- ModalDescription,
24
- type ModalSize,
25
- } from './modal'
26
- export {
27
- Dropdown,
28
- DropdownTrigger,
29
- DropdownContent,
30
- DropdownItem,
31
- DropdownCheckboxItem,
32
- DropdownRadioItem,
33
- DropdownLabel,
34
- DropdownSeparator,
35
- DropdownShortcut,
36
- DropdownGroup,
37
- DropdownPortal,
38
- DropdownSub,
39
- DropdownSubContent,
40
- DropdownSubTrigger,
41
- DropdownRadioGroup,
42
- } from './dropdown'
43
- export { DataTable, type DataTableProps } from './data-table'
44
- export { Checkbox, type CheckboxColor, type CheckboxProps } from './checkbox'
45
- export {
46
- Sheet,
47
- SheetPortal,
48
- SheetOverlay,
49
- SheetTrigger,
50
- SheetClose,
51
- SheetContent,
52
- SheetHeader,
53
- SheetBody,
54
- SheetFooter,
55
- SheetTitle,
56
- SheetDescription,
57
- } from './sheet'
58
- export {
59
- Select,
60
- SelectGroup,
61
- SelectValue,
62
- SelectTrigger,
63
- SelectContent,
64
- SelectLabel,
65
- SelectItem,
66
- SelectSeparator,
67
- SelectScrollUpButton,
68
- SelectScrollDownButton,
69
- } from './select'
70
- export { ToastProvider, toast } from './toast'
71
- export { Tabs, TabsList, TabsTrigger, TabsContent } from './tabs'
72
- export { Separator } from './separator'
73
- export { PhoneLink, normalizePhone, type PhoneLinkProps, type NormalizedPhone } from './phone-link'
74
- export { Breadcrumb } from './breadcrumb'
75
- export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from './tooltip'
76
- export { TimePicker } from './time-picker'
77
- export { Skeleton, TableSkeleton } from './skeleton'
78
- export { ListView } from './list-view'
79
- export { DatePicker } from './date-picker'
80
- export { SearchSelect, type SearchSelectOption, type SearchSelectProps } from './search-select'
81
- export { CurrencyInput } from './currency-input'
82
- export { ConfirmDialog } from './confirm-dialog'
83
- export { Popover, PopoverTrigger, PopoverContent } from './popover'
84
- export { SegmentedControl, type SegmentedControlProps, type SegmentedOption } from './segmented-control'
85
- export { Markdown, renderMarkdown, type MarkdownProps } from './markdown'
86
- export { MarkdownEditor, type MarkdownEditorProps } from './markdown-editor'
87
- export { SearchCombobox, type ComboboxOption } from './SearchCombobox'
@@ -1,50 +0,0 @@
1
- import * as React from 'react'
2
-
3
- import { cn } from '../utils/cn'
4
-
5
- export interface InputProps
6
- extends React.InputHTMLAttributes<HTMLInputElement> {
7
- error?: string
8
- description?: string
9
- }
10
-
11
- const Input = React.forwardRef<HTMLInputElement, InputProps>(
12
- ({ className, type, error, description, id, ...props }, ref) => {
13
- const inputId = id ?? React.useId()
14
- const descriptionId = description ? `${inputId}-description` : undefined
15
- const errorId = error ? `${inputId}-error` : undefined
16
-
17
- return (
18
- <div className="w-full">
19
- <input
20
- type={type}
21
- id={inputId}
22
- className={cn(
23
- 'flex h-8 w-full rounded-input border border-input bg-card px-2 py-1 text-sm text-foreground shadow-[inset_0_1px_0_rgb(0_0_0_/0.06)] ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
24
- error && 'border-destructive focus-visible:ring-destructive',
25
- className,
26
- )}
27
- ref={ref}
28
- aria-invalid={error ? true : undefined}
29
- aria-describedby={
30
- [descriptionId, errorId].filter(Boolean).join(' ') || undefined
31
- }
32
- {...props}
33
- />
34
- {description && !error && (
35
- <p id={descriptionId} className="mt-1.5 text-xs text-muted-foreground">
36
- {description}
37
- </p>
38
- )}
39
- {error && (
40
- <p id={errorId} className="mt-1.5 text-xs text-destructive">
41
- {error}
42
- </p>
43
- )}
44
- </div>
45
- )
46
- }
47
- )
48
- Input.displayName = 'Input'
49
-
50
- export { Input }
@@ -1,252 +0,0 @@
1
- import * as React from 'react'
2
- import { useState, useCallback, useRef, useEffect } from 'react'
3
- import type { ColumnDef } from '@tanstack/react-table'
4
- import { Search, Plus, ChevronLeft, ChevronRight } from 'lucide-react'
5
- import { DataTable } from './data-table'
6
- import { Button } from './button'
7
- import { cn } from '../utils/cn'
8
-
9
- // ---------------------------------------------------------------------------
10
- // Types
11
- // ---------------------------------------------------------------------------
12
-
13
- export interface TagFilter {
14
- value: string
15
- label: string
16
- }
17
-
18
- export interface ListViewProps<TData> {
19
- /** TanStack column definitions */
20
- columns: ColumnDef<TData, any>[]
21
- /** Data rows */
22
- data: TData[]
23
- /** Loading state — shows skeleton */
24
- loading?: boolean
25
-
26
- // ── Search ──
27
- /** Placeholder for search input (default: "Search...") */
28
- searchPlaceholder?: string
29
- /** Controlled search value */
30
- search?: string
31
- /** Called on search change (debounced internally if onSearchDebounced is used) */
32
- onSearchChange?: (value: string) => void
33
- /** Debounce delay in ms (default: 300). Set 0 for no debounce. */
34
- searchDebounce?: number
35
-
36
- // ── Tag filters ──
37
- /** Available filter tags (e.g. status values, types) */
38
- tags?: TagFilter[]
39
- /** "All" label (default: "All") */
40
- allTagLabel?: string
41
- /** Currently active tag value (undefined = all) */
42
- activeTag?: string
43
- /** Called when a tag is selected (undefined = all) */
44
- onTagChange?: (value: string | undefined) => void
45
-
46
- // ── CTA button ──
47
- /** Label for the "New" button (e.g. "New Product") */
48
- newLabel?: string
49
- /** Called when the "New" button is clicked */
50
- onNew?: () => void
51
-
52
- // ── Row interaction ──
53
- /** Called when a row is clicked */
54
- onRowClick?: (row: TData) => void
55
-
56
- // ── Empty state ──
57
- /** Icon component for the empty state */
58
- emptyIcon?: React.ComponentType<{ className?: string }>
59
- /** Empty state message */
60
- emptyMessage?: string
61
- /** Empty state action label (e.g. "Create your first product") */
62
- emptyActionLabel?: string
63
- /** Called when the empty action is clicked */
64
- onEmptyAction?: () => void
65
-
66
- // ── Pagination ──
67
- /** Total item count (enables pagination when > pageSize) */
68
- total?: number
69
- /** Current page (1-indexed) */
70
- page?: number
71
- /** Page size (default: 25) */
72
- pageSize?: number
73
- /** Called when page changes */
74
- onPageChange?: (page: number) => void
75
-
76
- // ── Extra ──
77
- /** Additional content rendered between filters and table */
78
- toolbar?: React.ReactNode
79
- /** CSS class for the outer wrapper */
80
- className?: string
81
- }
82
-
83
- // ---------------------------------------------------------------------------
84
- // Component
85
- // ---------------------------------------------------------------------------
86
-
87
- export function ListView<TData>({
88
- columns,
89
- data,
90
- loading = false,
91
- searchPlaceholder = 'Search...',
92
- search: controlledSearch,
93
- onSearchChange,
94
- searchDebounce = 300,
95
- tags,
96
- allTagLabel = 'All',
97
- activeTag,
98
- onTagChange,
99
- newLabel,
100
- onNew,
101
- onRowClick,
102
- emptyIcon: EmptyIcon,
103
- emptyMessage = 'No results found.',
104
- emptyActionLabel,
105
- onEmptyAction,
106
- total,
107
- page = 1,
108
- pageSize = 25,
109
- onPageChange,
110
- toolbar,
111
- className,
112
- }: ListViewProps<TData>) {
113
- // Internal search state for debouncing
114
- const [internalSearch, setInternalSearch] = useState(controlledSearch ?? '')
115
- const debounceRef = useRef<ReturnType<typeof setTimeout>>()
116
-
117
- // Sync controlled → internal
118
- useEffect(() => {
119
- if (controlledSearch !== undefined) setInternalSearch(controlledSearch)
120
- }, [controlledSearch])
121
-
122
- const handleSearchChange = useCallback((value: string) => {
123
- setInternalSearch(value)
124
- if (searchDebounce === 0) {
125
- onSearchChange?.(value)
126
- } else {
127
- if (debounceRef.current) clearTimeout(debounceRef.current)
128
- debounceRef.current = setTimeout(() => onSearchChange?.(value), searchDebounce)
129
- }
130
- }, [onSearchChange, searchDebounce])
131
-
132
- // Pagination
133
- const totalItems = total ?? data.length
134
- const totalPages = Math.max(1, Math.ceil(totalItems / pageSize))
135
- const showPagination = onPageChange && totalItems > pageSize
136
-
137
- const showSearch = !!onSearchChange
138
- const showTags = tags && tags.length > 0 && onTagChange
139
- const showToolbar = showSearch || onNew || showTags
140
-
141
- return (
142
- <div className={cn('space-y-4', className)}>
143
- {/* ── Search + CTA row ── */}
144
- {showToolbar && (
145
- <div className="flex flex-col sm:flex-row gap-3 sm:items-center sm:justify-between">
146
- {showSearch && (
147
- <div className="relative flex-1 max-w-sm">
148
- <Search className="absolute left-2.5 top-1/2 -translate-y-1/2 h-3.5 w-3.5 text-muted-foreground" />
149
- <input
150
- type="text"
151
- placeholder={searchPlaceholder}
152
- value={internalSearch}
153
- onChange={(e) => handleSearchChange(e.target.value)}
154
- className="w-full rounded-input border border-input bg-card shadow-[inset_0_1px_0_rgb(0_0_0_/0.06)] pl-8 pr-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring"
155
- />
156
- </div>
157
- )}
158
- {onNew && newLabel && (
159
- <Button variant="default" size="default" onClick={onNew} className="shrink-0">
160
- <Plus className="h-3.5 w-3.5" />
161
- {newLabel}
162
- </Button>
163
- )}
164
- </div>
165
- )}
166
-
167
- {/* ── Tag filters ── */}
168
- {showTags && (
169
- <div className="flex gap-1.5 flex-wrap">
170
- <button
171
- onClick={() => onTagChange!(undefined)}
172
- className={cn(
173
- 'rounded-full px-2.5 py-1 text-xs font-medium transition-colors',
174
- !activeTag
175
- ? 'bg-primary text-primary-foreground'
176
- : 'bg-muted/50 text-muted-foreground hover:bg-muted'
177
- )}
178
- >
179
- {allTagLabel}
180
- </button>
181
- {tags!.map((tag) => (
182
- <button
183
- key={tag.value}
184
- onClick={() => onTagChange!(tag.value)}
185
- className={cn(
186
- 'rounded-full px-2.5 py-1 text-xs font-medium transition-colors',
187
- activeTag === tag.value
188
- ? 'bg-primary text-primary-foreground'
189
- : 'bg-muted/50 text-muted-foreground hover:bg-muted'
190
- )}
191
- >
192
- {tag.label}
193
- </button>
194
- ))}
195
- </div>
196
- )}
197
-
198
- {/* ── Extra toolbar ── */}
199
- {toolbar}
200
-
201
- {/* ── Table / Empty / Loading ── */}
202
- {loading ? (
203
- <DataTable columns={columns} data={[]} loading variant="card" />
204
- ) : data.length === 0 ? (
205
- <div className="flex flex-col items-center justify-center py-12 text-center rounded-lg border-2 border-dashed border-muted">
206
- {EmptyIcon && <EmptyIcon className="h-8 w-8 text-muted-foreground/30 mb-2" />}
207
- <p className="text-sm text-muted-foreground">{emptyMessage}</p>
208
- {emptyActionLabel && onEmptyAction && (
209
- <button onClick={onEmptyAction} className="text-xs text-primary hover:underline mt-1">
210
- {emptyActionLabel}
211
- </button>
212
- )}
213
- </div>
214
- ) : (
215
- <DataTable
216
- columns={columns}
217
- data={data}
218
- onRowClick={onRowClick}
219
- variant="card"
220
- />
221
- )}
222
-
223
- {/* ── Pagination ── */}
224
- {showPagination && (
225
- <div className="flex items-center justify-between px-1">
226
- <p className="text-xs text-muted-foreground">
227
- {((page - 1) * pageSize) + 1}–{Math.min(page * pageSize, totalItems)} of {totalItems}
228
- </p>
229
- <div className="flex items-center gap-1">
230
- <button
231
- onClick={() => onPageChange!(page - 1)}
232
- disabled={page <= 1}
233
- className="flex h-7 w-7 items-center justify-center rounded-md border hover:bg-muted bg-card shadow-button active:shadow-button-inset transition-colors disabled:opacity-30 disabled:cursor-not-allowed"
234
- >
235
- <ChevronLeft className="h-3.5 w-3.5" />
236
- </button>
237
- <span className="text-xs text-muted-foreground px-2">
238
- {page} / {totalPages}
239
- </span>
240
- <button
241
- onClick={() => onPageChange!(page + 1)}
242
- disabled={page >= totalPages}
243
- className="flex h-7 w-7 items-center justify-center rounded-md border hover:bg-muted bg-card shadow-button active:shadow-button-inset transition-colors disabled:opacity-30 disabled:cursor-not-allowed"
244
- >
245
- <ChevronRight className="h-3.5 w-3.5" />
246
- </button>
247
- </div>
248
- </div>
249
- )}
250
- </div>
251
- )
252
- }
@@ -1,75 +0,0 @@
1
- import * as React from 'react'
2
- import { cn } from '../utils/cn'
3
- import { SegmentedControl } from './segmented-control'
4
- import { Markdown } from './markdown'
5
-
6
- export interface MarkdownEditorProps {
7
- value: string
8
- onChange: (value: string) => void
9
- placeholder?: string
10
- /** Minimum visible rows for the textarea. */
11
- minRows?: number
12
- defaultMode?: 'edit' | 'preview'
13
- /** i18n injection — ui stays locale-agnostic. */
14
- editLabel?: string
15
- previewLabel?: string
16
- /** Custom preview renderer — e.g. a social-post mockup instead of plain
17
- * markdown. Defaults to the built-in Markdown renderer. */
18
- renderPreview?: (value: string) => React.ReactNode
19
- className?: string
20
- }
21
-
22
- /**
23
- * Markdown editor with an edit/preview toggle. Plain textarea + the zero-dep
24
- * Markdown renderer — deliberately not a block/WYSIWYG editor (that's a later
25
- * milestone); this covers script/brief documents today.
26
- */
27
- export function MarkdownEditor({
28
- value,
29
- onChange,
30
- placeholder,
31
- minRows = 16,
32
- defaultMode = 'preview',
33
- editLabel = 'Edit',
34
- previewLabel = 'Preview',
35
- renderPreview,
36
- className,
37
- }: MarkdownEditorProps) {
38
- const [mode, setMode] = React.useState<'edit' | 'preview'>(
39
- defaultMode === 'preview' && value.trim() === '' ? 'edit' : defaultMode,
40
- )
41
- return (
42
- <div className={cn('flex flex-col gap-2', className)}>
43
- <div className="flex justify-end">
44
- <SegmentedControl
45
- aria-label="Markdown mode"
46
- options={[
47
- { value: 'edit', label: editLabel },
48
- { value: 'preview', label: previewLabel },
49
- ]}
50
- value={mode}
51
- onChange={(m) => setMode(m as 'edit' | 'preview')}
52
- />
53
- </div>
54
- {mode === 'edit' ? (
55
- <textarea
56
- value={value}
57
- onChange={(e) => onChange(e.target.value)}
58
- placeholder={placeholder}
59
- rows={Math.max(minRows, value.split('\n').length + 1)}
60
- className={cn(
61
- 'w-full resize-y rounded-md border border-border bg-card p-3',
62
- 'font-mono text-[13px] leading-relaxed text-foreground',
63
- 'placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring',
64
- )}
65
- />
66
- ) : renderPreview ? (
67
- renderPreview(value)
68
- ) : (
69
- <div className="rounded-md border border-border bg-card p-4">
70
- <Markdown source={value} />
71
- </div>
72
- )}
73
- </div>
74
- )
75
- }