@srcroot/ui 0.0.45 → 0.0.48
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/package.json +3 -1
- package/src/registry/themes/v3/slate.css +1 -1
- package/src/registry/themes/v4/slate.css +1 -1
- package/src/registry/ui/calendar.tsx +2 -2
- package/src/registry/ui/date-picker.tsx +64 -15
- package/src/registry/ui/dropdown-menu.tsx +1 -1
- package/src/registry/ui/popover.tsx +3 -2
- package/src/registry/ui/sheet.tsx +1 -1
- package/src/registry/ui/sidebar.tsx +17 -9
- package/src/registry/ui/table.tsx +3 -3
package/package.json
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@srcroot/ui",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.48",
|
|
4
4
|
"description": "A UI library with polymorphic, accessible React components",
|
|
5
|
+
"author": "Shifaul Islam",
|
|
6
|
+
"license": "MIT",
|
|
5
7
|
"type": "module",
|
|
6
8
|
"bin": {
|
|
7
9
|
"srcroot-ui": "./dist/index.js"
|
|
@@ -72,7 +72,8 @@ function getDaysInMonth(year: number, month: number): Date[] {
|
|
|
72
72
|
return days
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
function isSameDay(d1: Date, d2: Date): boolean {
|
|
75
|
+
function isSameDay(d1: Date | undefined | null, d2: Date | undefined | null): boolean {
|
|
76
|
+
if (!d1 || !d2) return false
|
|
76
77
|
return d1.getDate() === d2.getDate() &&
|
|
77
78
|
d1.getMonth() === d2.getMonth() &&
|
|
78
79
|
d1.getFullYear() === d2.getFullYear()
|
|
@@ -509,4 +510,3 @@ const Calendar = React.forwardRef<HTMLDivElement, CalendarProps>(
|
|
|
509
510
|
Calendar.displayName = "Calendar"
|
|
510
511
|
|
|
511
512
|
export { Calendar }
|
|
512
|
-
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client"
|
|
2
2
|
|
|
3
3
|
import * as React from "react"
|
|
4
|
+
import { addDays, startOfMonth, endOfMonth } from "date-fns"
|
|
4
5
|
import { Calendar } from "./calendar"
|
|
5
6
|
import { Popover, PopoverContent, PopoverTrigger } from "./popover"
|
|
6
7
|
import { Button } from "./button"
|
|
@@ -125,6 +126,31 @@ const DatePicker = React.forwardRef<HTMLButtonElement, DatePickerProps>(
|
|
|
125
126
|
: mode === "multiple" ? "Pick dates"
|
|
126
127
|
: "Pick a date range"
|
|
127
128
|
|
|
129
|
+
// Presets for Range Mode
|
|
130
|
+
const presets = [
|
|
131
|
+
{
|
|
132
|
+
label: "Last 7 Days",
|
|
133
|
+
getValue: () => {
|
|
134
|
+
const today = new Date()
|
|
135
|
+
return [addDays(today, -7), today]
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
label: "Last 30 Days",
|
|
140
|
+
getValue: () => {
|
|
141
|
+
const today = new Date()
|
|
142
|
+
return [addDays(today, -30), today]
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
label: "This Month",
|
|
147
|
+
getValue: () => {
|
|
148
|
+
const today = new Date()
|
|
149
|
+
return [startOfMonth(today), endOfMonth(today)]
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
]
|
|
153
|
+
|
|
128
154
|
// Handle selection
|
|
129
155
|
const handleSelect = (value: any) => {
|
|
130
156
|
if (mode === "single") {
|
|
@@ -135,10 +161,18 @@ const DatePicker = React.forwardRef<HTMLButtonElement, DatePickerProps>(
|
|
|
135
161
|
} else {
|
|
136
162
|
const dates = value || []
|
|
137
163
|
; (onSelect as ((dates: Date[]) => void))?.(dates)
|
|
138
|
-
//
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
164
|
+
// Do not close automatically for range to allow adjustment
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const handlePresetSelect = (preset: { getValue: () => Date[] }) => {
|
|
169
|
+
if (mode === "range") {
|
|
170
|
+
const dates = preset.getValue()
|
|
171
|
+
; (onSelect as ((dates: Date[]) => void))?.(dates)
|
|
172
|
+
// Update internal state if uncontrolled (not covered here fully but ensures trigger updates if parent consumes correctly)
|
|
173
|
+
// For this component to be fully controlled, parent must pass `selected`.
|
|
174
|
+
// If we want it to close on preset select:
|
|
175
|
+
setOpen(false)
|
|
142
176
|
}
|
|
143
177
|
}
|
|
144
178
|
|
|
@@ -150,9 +184,8 @@ const DatePicker = React.forwardRef<HTMLButtonElement, DatePickerProps>(
|
|
|
150
184
|
variant="outline"
|
|
151
185
|
disabled={disabled}
|
|
152
186
|
className={cn(
|
|
153
|
-
"w-[
|
|
187
|
+
"w-[260px] justify-start text-left font-normal",
|
|
154
188
|
!displayText && "text-muted-foreground",
|
|
155
|
-
numberOfMonths === 2 && "w-[320px]",
|
|
156
189
|
className
|
|
157
190
|
)}
|
|
158
191
|
>
|
|
@@ -160,15 +193,31 @@ const DatePicker = React.forwardRef<HTMLButtonElement, DatePickerProps>(
|
|
|
160
193
|
{displayText || <span>{placeholder || defaultPlaceholder}</span>}
|
|
161
194
|
</Button>
|
|
162
195
|
</PopoverTrigger>
|
|
163
|
-
<PopoverContent className="w-auto p-0">
|
|
164
|
-
<
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
196
|
+
<PopoverContent className="w-auto p-0" align="end">
|
|
197
|
+
<div className="flex">
|
|
198
|
+
{mode === "range" && (
|
|
199
|
+
<div className="border-r p-2 space-y-1 w-[140px]">
|
|
200
|
+
{presets.map((preset) => (
|
|
201
|
+
<Button
|
|
202
|
+
key={preset.label}
|
|
203
|
+
variant="ghost"
|
|
204
|
+
className="w-full justify-start font-normal"
|
|
205
|
+
onClick={() => handlePresetSelect(preset)}
|
|
206
|
+
>
|
|
207
|
+
{preset.label}
|
|
208
|
+
</Button>
|
|
209
|
+
))}
|
|
210
|
+
</div>
|
|
211
|
+
)}
|
|
212
|
+
<Calendar
|
|
213
|
+
mode={mode}
|
|
214
|
+
numberOfMonths={numberOfMonths}
|
|
215
|
+
size={size}
|
|
216
|
+
selected={selected}
|
|
217
|
+
onSelect={handleSelect}
|
|
218
|
+
className="rounded-md border-0 shadow-none"
|
|
219
|
+
/>
|
|
220
|
+
</div>
|
|
172
221
|
</PopoverContent>
|
|
173
222
|
</Popover>
|
|
174
223
|
)
|
|
@@ -277,7 +277,7 @@ const DropdownMenuItem = React.forwardRef<
|
|
|
277
277
|
tabIndex={disabled ? -1 : 0}
|
|
278
278
|
aria-disabled={disabled}
|
|
279
279
|
className={cn(
|
|
280
|
-
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground hover:bg-accent hover:text-accent-foreground",
|
|
280
|
+
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground hover:bg-accent hover:text-accent-foreground cursor-pointer",
|
|
281
281
|
inset && "pl-8",
|
|
282
282
|
disabled && "pointer-events-none opacity-50",
|
|
283
283
|
className
|
|
@@ -83,8 +83,8 @@ PopoverTrigger.displayName = "PopoverTrigger"
|
|
|
83
83
|
|
|
84
84
|
const PopoverContent = React.forwardRef<
|
|
85
85
|
HTMLDivElement,
|
|
86
|
-
React.HTMLAttributes<HTMLDivElement>
|
|
87
|
-
>(({ className, children, ...props }, ref) => {
|
|
86
|
+
React.HTMLAttributes<HTMLDivElement> & { align?: "start" | "center" | "end" }
|
|
87
|
+
>(({ className, children, align = "center", ...props }, ref) => {
|
|
88
88
|
const context = React.useContext(PopoverContext)
|
|
89
89
|
if (!context) throw new Error("PopoverContent must be used within Popover")
|
|
90
90
|
|
|
@@ -121,6 +121,7 @@ const PopoverContent = React.forwardRef<
|
|
|
121
121
|
ref={ref}
|
|
122
122
|
className={cn(
|
|
123
123
|
"absolute z-50 mt-2 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none",
|
|
124
|
+
align === "end" ? "right-0" : align === "start" ? "left-0" : "left-1/2 -translate-x-1/2",
|
|
124
125
|
className
|
|
125
126
|
)}
|
|
126
127
|
onClick={(e) => e.stopPropagation()}
|
|
@@ -181,7 +181,7 @@ const SheetContent = React.forwardRef<HTMLDivElement, SheetContentProps>(
|
|
|
181
181
|
>
|
|
182
182
|
{children}
|
|
183
183
|
<button
|
|
184
|
-
className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2"
|
|
184
|
+
className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 cursor-pointer"
|
|
185
185
|
onClick={() => context.onOpenChange(false)}
|
|
186
186
|
>
|
|
187
187
|
<svg className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
|
@@ -63,7 +63,7 @@ const SidebarProvider = React.forwardRef<
|
|
|
63
63
|
|
|
64
64
|
React.useEffect(() => {
|
|
65
65
|
const checkMobile = () => {
|
|
66
|
-
setIsMobile(window.innerWidth <
|
|
66
|
+
setIsMobile(window.innerWidth < 768) // md breakpoint
|
|
67
67
|
}
|
|
68
68
|
checkMobile()
|
|
69
69
|
window.addEventListener("resize", checkMobile)
|
|
@@ -169,7 +169,7 @@ const Sidebar = React.forwardRef<
|
|
|
169
169
|
<div
|
|
170
170
|
ref={ref}
|
|
171
171
|
className={cn(
|
|
172
|
-
"group peer
|
|
172
|
+
"group peer md:block text-sidebar-foreground",
|
|
173
173
|
className
|
|
174
174
|
)}
|
|
175
175
|
data-state={state}
|
|
@@ -192,7 +192,7 @@ const Sidebar = React.forwardRef<
|
|
|
192
192
|
{/* Actual Fixed Sidebar */}
|
|
193
193
|
<div
|
|
194
194
|
className={cn(
|
|
195
|
-
"duration-200 fixed inset-y-0 z-10
|
|
195
|
+
"duration-200 fixed inset-y-0 z-10 h-svh w-[var(--sidebar-width)] transition-[left,right,width] ease-linear md:flex",
|
|
196
196
|
side === "left"
|
|
197
197
|
? "left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]"
|
|
198
198
|
: "right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]",
|
|
@@ -448,14 +448,10 @@ const SidebarMenuButton = React.forwardRef<
|
|
|
448
448
|
},
|
|
449
449
|
ref
|
|
450
450
|
) => {
|
|
451
|
-
const
|
|
452
|
-
// manual asChild handling
|
|
453
|
-
// const Comp = asChild ? Slot : "button"
|
|
454
|
-
// But we want to avoid Slot if possible per user request?
|
|
455
|
-
// Actually, if I can use the same cloneElement approach:
|
|
451
|
+
const { isMobile, state, setOpen } = useSidebar()
|
|
456
452
|
|
|
457
453
|
const buttonClass = cn(
|
|
458
|
-
"peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-none ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0",
|
|
454
|
+
"cursor-pointer peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-none ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0",
|
|
459
455
|
"data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground",
|
|
460
456
|
"data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground",
|
|
461
457
|
"group-data-[collapsible=icon]:!size-8 group-data-[collapsible=icon]:!p-2 [&>span:last-child]:group-data-[collapsible=icon]:hidden",
|
|
@@ -464,6 +460,13 @@ const SidebarMenuButton = React.forwardRef<
|
|
|
464
460
|
|
|
465
461
|
// If tooltip is needed, we should implement it. For now, ignoring complexity of tooltip.
|
|
466
462
|
|
|
463
|
+
const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
|
|
464
|
+
props.onClick?.(e)
|
|
465
|
+
if (!isMobile && state === "collapsed") {
|
|
466
|
+
setOpen(true)
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
|
|
467
470
|
if (asChild) {
|
|
468
471
|
const child = React.Children.only(props.children) as React.ReactElement<any>
|
|
469
472
|
|
|
@@ -474,6 +477,10 @@ const SidebarMenuButton = React.forwardRef<
|
|
|
474
477
|
"data-sidebar": "menu-button",
|
|
475
478
|
"data-size": size,
|
|
476
479
|
...props,
|
|
480
|
+
onClick: (e: React.MouseEvent<HTMLButtonElement>) => {
|
|
481
|
+
handleClick(e)
|
|
482
|
+
child.props.onClick?.(e)
|
|
483
|
+
},
|
|
477
484
|
children: child.props.children
|
|
478
485
|
})
|
|
479
486
|
}
|
|
@@ -485,6 +492,7 @@ const SidebarMenuButton = React.forwardRef<
|
|
|
485
492
|
data-size={size}
|
|
486
493
|
data-active={isActive}
|
|
487
494
|
className={buttonClass}
|
|
495
|
+
onClick={handleClick}
|
|
488
496
|
{...props}
|
|
489
497
|
/>
|
|
490
498
|
)
|
|
@@ -23,9 +23,9 @@ import { cn } from "@/lib/utils"
|
|
|
23
23
|
|
|
24
24
|
const Table = React.forwardRef<
|
|
25
25
|
HTMLTableElement,
|
|
26
|
-
React.HTMLAttributes<HTMLTableElement>
|
|
27
|
-
>(({ className, ...props }, ref) => (
|
|
28
|
-
<div className="relative w-full overflow-auto">
|
|
26
|
+
React.HTMLAttributes<HTMLTableElement> & { containerClassName?: string }
|
|
27
|
+
>(({ className, containerClassName, ...props }, ref) => (
|
|
28
|
+
<div className={cn("relative w-full overflow-auto", containerClassName)}>
|
|
29
29
|
<table
|
|
30
30
|
ref={ref}
|
|
31
31
|
className={cn("w-full caption-bottom text-sm", className)}
|