@eggspot/ui 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/eslint.config.js +4 -0
- package/package.json +66 -0
- package/postcss.config.mjs +1 -0
- package/src/components/Button.machine.tsx +50 -0
- package/src/components/Button.tsx +249 -0
- package/src/components/Button.variants.tsx +186 -0
- package/src/components/ButtonGroup.tsx +56 -0
- package/src/components/Calendar.tsx +275 -0
- package/src/components/Calendar.utils.tsx +22 -0
- package/src/components/Checkbox.tsx +199 -0
- package/src/components/ConfirmDialog.tsx +183 -0
- package/src/components/DashboardLayout/DashboardLayout.tsx +348 -0
- package/src/components/DashboardLayout/SidebarNav.tsx +509 -0
- package/src/components/DashboardLayout/index.ts +33 -0
- package/src/components/DataTable/DataTable.tsx +557 -0
- package/src/components/DataTable/DataTableColumnHeader.tsx +122 -0
- package/src/components/DataTable/DataTableDisplaySettings.tsx +265 -0
- package/src/components/DataTable/DataTableFloatingBar.tsx +44 -0
- package/src/components/DataTable/DataTablePagination.tsx +168 -0
- package/src/components/DataTable/DataTableStates.tsx +69 -0
- package/src/components/DataTable/DataTableToolbarContainer.tsx +47 -0
- package/src/components/DataTable/hooks/use-data-table-settings.ts +101 -0
- package/src/components/DataTable/index.ts +7 -0
- package/src/components/DataTable/types/data-table.ts +97 -0
- package/src/components/DatePicker.tsx +213 -0
- package/src/components/DatePicker.utils.tsx +38 -0
- package/src/components/Datefield.tsx +109 -0
- package/src/components/Datefield.utils.ts +10 -0
- package/src/components/Dialog.tsx +167 -0
- package/src/components/Field.tsx +49 -0
- package/src/components/Filter/Filter.store.tsx +122 -0
- package/src/components/Filter/Filter.tsx +11 -0
- package/src/components/Filter/Filter.types.ts +107 -0
- package/src/components/Filter/FilterBar.tsx +38 -0
- package/src/components/Filter/FilterBuilder.tsx +158 -0
- package/src/components/Filter/FilterField/DateModeRowValue.tsx +250 -0
- package/src/components/Filter/FilterField/FilterAsyncSelect.tsx +191 -0
- package/src/components/Filter/FilterField/FilterDateMode.tsx +241 -0
- package/src/components/Filter/FilterField/FilterDateRange.tsx +169 -0
- package/src/components/Filter/FilterField/FilterSelect.tsx +208 -0
- package/src/components/Filter/FilterField/FilterSingleDate.tsx +277 -0
- package/src/components/Filter/FilterField/OptionItem.tsx +112 -0
- package/src/components/Filter/FilterField/index.ts +6 -0
- package/src/components/Filter/FilterRow.tsx +527 -0
- package/src/components/Filter/index.ts +17 -0
- package/src/components/Form.tsx +195 -0
- package/src/components/Heading.tsx +41 -0
- package/src/components/Input.tsx +221 -0
- package/src/components/InputOTP.tsx +78 -0
- package/src/components/Label.tsx +65 -0
- package/src/components/Layout.tsx +129 -0
- package/src/components/ListBox.tsx +97 -0
- package/src/components/Menu.tsx +152 -0
- package/src/components/NativeSelect.tsx +77 -0
- package/src/components/NumberInput.tsx +114 -0
- package/src/components/Popover.tsx +44 -0
- package/src/components/Provider.tsx +22 -0
- package/src/components/RadioGroup.tsx +191 -0
- package/src/components/Resizable.tsx +71 -0
- package/src/components/ScrollArea.tsx +57 -0
- package/src/components/Select.tsx +626 -0
- package/src/components/Select.utils.tsx +64 -0
- package/src/components/Separator.tsx +25 -0
- package/src/components/Sheet.tsx +147 -0
- package/src/components/Sonner.tsx +96 -0
- package/src/components/Spinner.tsx +30 -0
- package/src/components/Switch.tsx +51 -0
- package/src/components/Text.tsx +35 -0
- package/src/components/Tooltip.tsx +58 -0
- package/src/consts/config.ts +2 -0
- package/src/hooks/.gitkeep +0 -0
- package/src/lib/utils.ts +10 -0
- package/tsconfig.json +11 -0
- package/tsconfig.lint.json +8 -0
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import {
|
|
5
|
+
ResizableHandle,
|
|
6
|
+
ResizablePanel,
|
|
7
|
+
ResizablePanelGroup,
|
|
8
|
+
} from "@eggspot/ui/components/Resizable"
|
|
9
|
+
import { ScrollArea } from "@eggspot/ui/components/ScrollArea"
|
|
10
|
+
import { cn } from "@eggspot/ui/lib/utils"
|
|
11
|
+
import type { ImperativePanelHandle } from "react-resizable-panels"
|
|
12
|
+
|
|
13
|
+
// Context for sidebar state
|
|
14
|
+
interface DashboardContextValue {
|
|
15
|
+
sidebarState: "expanded" | "collapsed"
|
|
16
|
+
setSidebarState: (state: "expanded" | "collapsed") => void
|
|
17
|
+
toggleSidebar: () => void
|
|
18
|
+
isMobile: boolean
|
|
19
|
+
expandedGroups: string[]
|
|
20
|
+
toggleGroup: (title: string) => void
|
|
21
|
+
activePath: string
|
|
22
|
+
setActivePath: (path: string) => void
|
|
23
|
+
/** Currently open flyout group (for collapsed sidebar) */
|
|
24
|
+
flyoutGroup: string | null
|
|
25
|
+
setFlyoutGroup: (group: string | null) => void
|
|
26
|
+
closeFlyout: () => void
|
|
27
|
+
/** Sidebar bounding rect for flyout positioning */
|
|
28
|
+
sidebarRect: DOMRect | null
|
|
29
|
+
/** @deprecated Use sidebarRect.width instead */
|
|
30
|
+
sidebarWidth: number
|
|
31
|
+
setSidebarRect: (rect: DOMRect | null) => void
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const DashboardContext = React.createContext<DashboardContextValue | null>(null)
|
|
35
|
+
|
|
36
|
+
export function useDashboard() {
|
|
37
|
+
const context = React.useContext(DashboardContext)
|
|
38
|
+
if (!context) {
|
|
39
|
+
throw new Error("useDashboard must be used within a DashboardLayout")
|
|
40
|
+
}
|
|
41
|
+
return context
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface DashboardLayoutProps {
|
|
45
|
+
children: React.ReactNode
|
|
46
|
+
/** Default sidebar size as percentage (default: 14) */
|
|
47
|
+
defaultSidebarSize?: number
|
|
48
|
+
/** Minimum sidebar size as percentage (default: 5) */
|
|
49
|
+
minSidebarSize?: number
|
|
50
|
+
/** Maximum sidebar size as percentage (default: 20) */
|
|
51
|
+
maxSidebarSize?: number
|
|
52
|
+
/** Collapsed sidebar size as percentage (default: 5) */
|
|
53
|
+
collapsedSidebarSize?: number
|
|
54
|
+
/** Size threshold to switch to collapsed mode (default: 8) */
|
|
55
|
+
collapseThreshold?: number
|
|
56
|
+
/** Default expanded nav groups */
|
|
57
|
+
defaultExpandedGroups?: string[]
|
|
58
|
+
/** Current active path for highlighting */
|
|
59
|
+
activePath?: string
|
|
60
|
+
className?: string
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function DashboardLayout({
|
|
64
|
+
children,
|
|
65
|
+
defaultSidebarSize = 14,
|
|
66
|
+
minSidebarSize = 5,
|
|
67
|
+
maxSidebarSize = 20,
|
|
68
|
+
collapsedSidebarSize = 5,
|
|
69
|
+
collapseThreshold = 7,
|
|
70
|
+
defaultExpandedGroups = [],
|
|
71
|
+
activePath: initialActivePath = "/",
|
|
72
|
+
className,
|
|
73
|
+
}: DashboardLayoutProps) {
|
|
74
|
+
const [sidebarState, setSidebarState] = React.useState<
|
|
75
|
+
"expanded" | "collapsed"
|
|
76
|
+
>("expanded")
|
|
77
|
+
const [isMobile, setIsMobile] = React.useState(false)
|
|
78
|
+
const [expandedGroups, setExpandedGroups] = React.useState<string[]>(
|
|
79
|
+
defaultExpandedGroups
|
|
80
|
+
)
|
|
81
|
+
const [activePath, setActivePath] = React.useState(initialActivePath)
|
|
82
|
+
const [flyoutGroup, setFlyoutGroup] = React.useState<string | null>(null)
|
|
83
|
+
const [sidebarRect, setSidebarRect] = React.useState<DOMRect | null>(null)
|
|
84
|
+
const sidebarPanelRef = React.useRef<ImperativePanelHandle>(null)
|
|
85
|
+
|
|
86
|
+
const closeFlyout = React.useCallback(() => setFlyoutGroup(null), [])
|
|
87
|
+
|
|
88
|
+
const toggleSidebar = React.useCallback(() => {
|
|
89
|
+
const panel = sidebarPanelRef.current
|
|
90
|
+
if (!panel) return
|
|
91
|
+
|
|
92
|
+
if (panel.isCollapsed()) {
|
|
93
|
+
panel.expand()
|
|
94
|
+
} else {
|
|
95
|
+
panel.collapse()
|
|
96
|
+
}
|
|
97
|
+
}, [])
|
|
98
|
+
|
|
99
|
+
const toggleGroup = React.useCallback((title: string) => {
|
|
100
|
+
setExpandedGroups((prev) =>
|
|
101
|
+
prev.includes(title) ? prev.filter((g) => g !== title) : [...prev, title]
|
|
102
|
+
)
|
|
103
|
+
}, [])
|
|
104
|
+
|
|
105
|
+
React.useEffect(() => {
|
|
106
|
+
const checkMobile = () => setIsMobile(window.innerWidth < 768)
|
|
107
|
+
checkMobile()
|
|
108
|
+
window.addEventListener("resize", checkMobile)
|
|
109
|
+
return () => window.removeEventListener("resize", checkMobile)
|
|
110
|
+
}, [])
|
|
111
|
+
|
|
112
|
+
const handleResize = React.useCallback(
|
|
113
|
+
(size: number) => {
|
|
114
|
+
const newState = size <= collapseThreshold ? "collapsed" : "expanded"
|
|
115
|
+
setSidebarState(newState)
|
|
116
|
+
if (newState === "expanded") {
|
|
117
|
+
setFlyoutGroup(null)
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
[collapseThreshold]
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
const sidebar = React.Children.toArray(children).find(
|
|
124
|
+
(child) => React.isValidElement(child) && child.type === Sidebar
|
|
125
|
+
)
|
|
126
|
+
const content = React.Children.toArray(children).find(
|
|
127
|
+
(child) => React.isValidElement(child) && child.type === DashboardContent
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
return (
|
|
131
|
+
<DashboardContext.Provider
|
|
132
|
+
value={{
|
|
133
|
+
sidebarState,
|
|
134
|
+
setSidebarState,
|
|
135
|
+
toggleSidebar,
|
|
136
|
+
isMobile,
|
|
137
|
+
expandedGroups,
|
|
138
|
+
toggleGroup,
|
|
139
|
+
activePath,
|
|
140
|
+
setActivePath,
|
|
141
|
+
flyoutGroup,
|
|
142
|
+
setFlyoutGroup,
|
|
143
|
+
closeFlyout,
|
|
144
|
+
sidebarRect,
|
|
145
|
+
sidebarWidth: sidebarRect?.width ?? 0,
|
|
146
|
+
setSidebarRect,
|
|
147
|
+
}}
|
|
148
|
+
>
|
|
149
|
+
<div className={cn("bg-gray-1 h-full", className)}>
|
|
150
|
+
<ResizablePanelGroup direction="horizontal" className="h-full">
|
|
151
|
+
<ResizablePanel
|
|
152
|
+
ref={sidebarPanelRef}
|
|
153
|
+
order={1}
|
|
154
|
+
defaultSize={defaultSidebarSize}
|
|
155
|
+
minSize={minSidebarSize}
|
|
156
|
+
maxSize={maxSidebarSize}
|
|
157
|
+
collapsible
|
|
158
|
+
collapsedSize={collapsedSidebarSize}
|
|
159
|
+
onResize={handleResize}
|
|
160
|
+
>
|
|
161
|
+
{sidebar}
|
|
162
|
+
</ResizablePanel>
|
|
163
|
+
|
|
164
|
+
<ResizableHandle />
|
|
165
|
+
|
|
166
|
+
<ResizablePanel
|
|
167
|
+
order={2}
|
|
168
|
+
defaultSize={100 - defaultSidebarSize}
|
|
169
|
+
minSize={60}
|
|
170
|
+
>
|
|
171
|
+
{content}
|
|
172
|
+
</ResizablePanel>
|
|
173
|
+
</ResizablePanelGroup>
|
|
174
|
+
</div>
|
|
175
|
+
</DashboardContext.Provider>
|
|
176
|
+
)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Sidebar Component
|
|
180
|
+
export interface SidebarProps {
|
|
181
|
+
children: React.ReactNode
|
|
182
|
+
className?: string
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export function Sidebar({ children, className }: SidebarProps) {
|
|
186
|
+
const { sidebarState, setSidebarRect } = useDashboard()
|
|
187
|
+
const sidebarRef = React.useRef<HTMLDivElement>(null)
|
|
188
|
+
|
|
189
|
+
// Update sidebar rect on resize and scroll
|
|
190
|
+
React.useEffect(() => {
|
|
191
|
+
const element = sidebarRef.current
|
|
192
|
+
if (!element) return
|
|
193
|
+
|
|
194
|
+
const updateRect = () => {
|
|
195
|
+
setSidebarRect(element.getBoundingClientRect())
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const observer = new ResizeObserver(() => {
|
|
199
|
+
updateRect()
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
observer.observe(element)
|
|
203
|
+
updateRect()
|
|
204
|
+
|
|
205
|
+
// Also listen for scroll events in case sidebar moves
|
|
206
|
+
window.addEventListener("scroll", updateRect, true)
|
|
207
|
+
|
|
208
|
+
return () => {
|
|
209
|
+
observer.disconnect()
|
|
210
|
+
window.removeEventListener("scroll", updateRect, true)
|
|
211
|
+
}
|
|
212
|
+
}, [setSidebarRect])
|
|
213
|
+
|
|
214
|
+
return (
|
|
215
|
+
<div
|
|
216
|
+
ref={sidebarRef}
|
|
217
|
+
className={cn(
|
|
218
|
+
"flex h-full flex-col overflow-hidden",
|
|
219
|
+
"from-gray-2 to-gray-1 bg-linear-to-b",
|
|
220
|
+
className
|
|
221
|
+
)}
|
|
222
|
+
data-state={sidebarState}
|
|
223
|
+
>
|
|
224
|
+
{children}
|
|
225
|
+
</div>
|
|
226
|
+
)
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Sidebar Header (Logo area)
|
|
230
|
+
export interface SidebarHeaderProps {
|
|
231
|
+
children: React.ReactNode
|
|
232
|
+
className?: string
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export function SidebarHeader({ children, className }: SidebarHeaderProps) {
|
|
236
|
+
const { sidebarState } = useDashboard()
|
|
237
|
+
|
|
238
|
+
return (
|
|
239
|
+
<div
|
|
240
|
+
className={cn(
|
|
241
|
+
"flex shrink-0 items-center p-4 pb-0",
|
|
242
|
+
sidebarState === "collapsed"
|
|
243
|
+
? "flex-col justify-center gap-2 px-3 pt-4"
|
|
244
|
+
: "justify-between",
|
|
245
|
+
className
|
|
246
|
+
)}
|
|
247
|
+
>
|
|
248
|
+
{children}
|
|
249
|
+
</div>
|
|
250
|
+
)
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Sidebar Nav Container
|
|
254
|
+
export interface SidebarNavProps {
|
|
255
|
+
children: React.ReactNode
|
|
256
|
+
className?: string
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
export function SidebarNav({ children, className }: SidebarNavProps) {
|
|
260
|
+
return (
|
|
261
|
+
<ScrollArea
|
|
262
|
+
className={cn(
|
|
263
|
+
"min-h-0 flex-1 overflow-hidden",
|
|
264
|
+
"[&>[data-slot=scroll-area-viewport]>div]:!block",
|
|
265
|
+
className
|
|
266
|
+
)}
|
|
267
|
+
>
|
|
268
|
+
<nav className="px-3 py-2">
|
|
269
|
+
<div className="space-y-2">{children}</div>
|
|
270
|
+
</nav>
|
|
271
|
+
</ScrollArea>
|
|
272
|
+
)
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Sidebar Footer (pinned to bottom)
|
|
276
|
+
export interface SidebarFooterProps {
|
|
277
|
+
children: React.ReactNode
|
|
278
|
+
className?: string
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
export function SidebarFooter({ children, className }: SidebarFooterProps) {
|
|
282
|
+
const { sidebarState } = useDashboard()
|
|
283
|
+
|
|
284
|
+
return (
|
|
285
|
+
<div
|
|
286
|
+
className={cn(
|
|
287
|
+
"border-gray-6/50 mt-auto shrink-0 border-t p-3",
|
|
288
|
+
sidebarState === "collapsed" && "py-3",
|
|
289
|
+
className
|
|
290
|
+
)}
|
|
291
|
+
>
|
|
292
|
+
<div className="space-y-1.5">{children}</div>
|
|
293
|
+
</div>
|
|
294
|
+
)
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Dashboard Content Area
|
|
298
|
+
export interface DashboardContentProps {
|
|
299
|
+
children: React.ReactNode
|
|
300
|
+
className?: string
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
export function DashboardContent({
|
|
304
|
+
children,
|
|
305
|
+
className,
|
|
306
|
+
}: DashboardContentProps) {
|
|
307
|
+
return (
|
|
308
|
+
<div
|
|
309
|
+
className={cn(
|
|
310
|
+
"bg-gray-1 flex h-full flex-col overflow-hidden",
|
|
311
|
+
className
|
|
312
|
+
)}
|
|
313
|
+
>
|
|
314
|
+
{children}
|
|
315
|
+
</div>
|
|
316
|
+
)
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Dashboard Header (in main content area)
|
|
320
|
+
export interface DashboardHeaderProps {
|
|
321
|
+
children?: React.ReactNode
|
|
322
|
+
className?: string
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
export function DashboardHeader({ children, className }: DashboardHeaderProps) {
|
|
326
|
+
return (
|
|
327
|
+
<header
|
|
328
|
+
className={cn(
|
|
329
|
+
"border-gray-6 flex h-14 shrink-0 items-center justify-between border-b px-4",
|
|
330
|
+
className
|
|
331
|
+
)}
|
|
332
|
+
>
|
|
333
|
+
{children}
|
|
334
|
+
</header>
|
|
335
|
+
)
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// Dashboard Main Content
|
|
339
|
+
export interface DashboardMainProps {
|
|
340
|
+
children: React.ReactNode
|
|
341
|
+
className?: string
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
export function DashboardMain({ children, className }: DashboardMainProps) {
|
|
345
|
+
return (
|
|
346
|
+
<main className={cn("flex-1 overflow-auto", className)}>{children}</main>
|
|
347
|
+
)
|
|
348
|
+
}
|