@dilipod/ui 0.2.15 → 0.3.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 (44) hide show
  1. package/dist/components/alert-dialog.d.ts +34 -0
  2. package/dist/components/alert-dialog.d.ts.map +1 -0
  3. package/dist/components/breadcrumbs.d.ts +30 -0
  4. package/dist/components/breadcrumbs.d.ts.map +1 -0
  5. package/dist/components/date-range-picker.d.ts +36 -0
  6. package/dist/components/date-range-picker.d.ts.map +1 -0
  7. package/dist/components/pagination.d.ts +29 -0
  8. package/dist/components/pagination.d.ts.map +1 -0
  9. package/dist/components/popover.d.ts +10 -0
  10. package/dist/components/popover.d.ts.map +1 -0
  11. package/dist/components/radio-group.d.ts +17 -0
  12. package/dist/components/radio-group.d.ts.map +1 -0
  13. package/dist/components/settings-nav.d.ts +35 -0
  14. package/dist/components/settings-nav.d.ts.map +1 -0
  15. package/dist/components/skeleton.d.ts +28 -0
  16. package/dist/components/skeleton.d.ts.map +1 -0
  17. package/dist/components/step-progress.d.ts +28 -0
  18. package/dist/components/step-progress.d.ts.map +1 -0
  19. package/dist/components/switch.d.ts +15 -0
  20. package/dist/components/switch.d.ts.map +1 -0
  21. package/dist/components/tabs.d.ts +10 -0
  22. package/dist/components/tabs.d.ts.map +1 -0
  23. package/dist/components/tooltip.d.ts +17 -0
  24. package/dist/components/tooltip.d.ts.map +1 -0
  25. package/dist/index.d.ts +22 -0
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +1263 -87
  28. package/dist/index.js.map +1 -1
  29. package/dist/index.mjs +1209 -89
  30. package/dist/index.mjs.map +1 -1
  31. package/package.json +7 -1
  32. package/src/components/alert-dialog.tsx +203 -0
  33. package/src/components/breadcrumbs.tsx +160 -0
  34. package/src/components/date-range-picker.tsx +183 -0
  35. package/src/components/pagination.tsx +220 -0
  36. package/src/components/popover.tsx +53 -0
  37. package/src/components/radio-group.tsx +125 -0
  38. package/src/components/settings-nav.tsx +137 -0
  39. package/src/components/skeleton.tsx +103 -0
  40. package/src/components/step-progress.tsx +205 -0
  41. package/src/components/switch.tsx +95 -0
  42. package/src/components/tabs.tsx +92 -0
  43. package/src/components/tooltip.tsx +78 -0
  44. package/src/index.ts +78 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dilipod/ui",
3
- "version": "0.2.15",
3
+ "version": "0.3.0",
4
4
  "description": "Dilipod Design System - Shared UI components and styles",
5
5
  "author": "Dilipod <hello@dilipod.com>",
6
6
  "license": "MIT",
@@ -62,12 +62,18 @@
62
62
  "dependencies": {
63
63
  "@phosphor-icons/react": "^2.1.7",
64
64
  "@radix-ui/react-accordion": "^1.2.12",
65
+ "@radix-ui/react-alert-dialog": "^1.1.15",
65
66
  "@radix-ui/react-avatar": "^1.1.3",
66
67
  "@radix-ui/react-dialog": "^1.1.14",
67
68
  "@radix-ui/react-dropdown-menu": "^2.1.3",
68
69
  "@radix-ui/react-navigation-menu": "^1.2.14",
70
+ "@radix-ui/react-popover": "^1.1.15",
71
+ "@radix-ui/react-radio-group": "^1.3.8",
69
72
  "@radix-ui/react-slot": "^1.2.3",
73
+ "@radix-ui/react-switch": "^1.2.6",
74
+ "@radix-ui/react-tabs": "^1.1.13",
70
75
  "@radix-ui/react-toast": "^1.2.15",
76
+ "@radix-ui/react-tooltip": "^1.2.8",
71
77
  "class-variance-authority": "^0.7.1",
72
78
  "clsx": "^2.1.1",
73
79
  "tailwind-merge": "^3.3.0",
@@ -0,0 +1,203 @@
1
+ 'use client'
2
+
3
+ import * as React from 'react'
4
+ import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog'
5
+ import { cn } from '../lib/utils'
6
+ import { buttonVariants } from './button'
7
+
8
+ const AlertDialog = AlertDialogPrimitive.Root
9
+
10
+ const AlertDialogTrigger = AlertDialogPrimitive.Trigger
11
+
12
+ const AlertDialogPortal = AlertDialogPrimitive.Portal
13
+
14
+ const AlertDialogOverlay = React.forwardRef<
15
+ React.ElementRef<typeof AlertDialogPrimitive.Overlay>,
16
+ React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Overlay>
17
+ >(({ className, ...props }, ref) => (
18
+ <AlertDialogPrimitive.Overlay
19
+ className={cn(
20
+ 'fixed inset-0 z-50 bg-black/60 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',
21
+ className
22
+ )}
23
+ {...props}
24
+ ref={ref}
25
+ />
26
+ ))
27
+ AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName
28
+
29
+ const AlertDialogContent = React.forwardRef<
30
+ React.ElementRef<typeof AlertDialogPrimitive.Content>,
31
+ React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Content>
32
+ >(({ className, ...props }, ref) => (
33
+ <AlertDialogPortal>
34
+ <AlertDialogOverlay />
35
+ <AlertDialogPrimitive.Content
36
+ ref={ref}
37
+ className={cn(
38
+ 'fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 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-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] rounded-sm',
39
+ className
40
+ )}
41
+ {...props}
42
+ />
43
+ </AlertDialogPortal>
44
+ ))
45
+ AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName
46
+
47
+ const AlertDialogHeader = ({
48
+ className,
49
+ ...props
50
+ }: React.HTMLAttributes<HTMLDivElement>) => (
51
+ <div
52
+ className={cn(
53
+ 'flex flex-col space-y-2 text-center sm:text-left',
54
+ className
55
+ )}
56
+ {...props}
57
+ />
58
+ )
59
+ AlertDialogHeader.displayName = 'AlertDialogHeader'
60
+
61
+ const AlertDialogFooter = ({
62
+ className,
63
+ ...props
64
+ }: React.HTMLAttributes<HTMLDivElement>) => (
65
+ <div
66
+ className={cn(
67
+ 'flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2',
68
+ className
69
+ )}
70
+ {...props}
71
+ />
72
+ )
73
+ AlertDialogFooter.displayName = 'AlertDialogFooter'
74
+
75
+ const AlertDialogTitle = React.forwardRef<
76
+ React.ElementRef<typeof AlertDialogPrimitive.Title>,
77
+ React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Title>
78
+ >(({ className, ...props }, ref) => (
79
+ <AlertDialogPrimitive.Title
80
+ ref={ref}
81
+ className={cn('text-lg font-semibold text-[var(--black)]', className)}
82
+ {...props}
83
+ />
84
+ ))
85
+ AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName
86
+
87
+ const AlertDialogDescription = React.forwardRef<
88
+ React.ElementRef<typeof AlertDialogPrimitive.Description>,
89
+ React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Description>
90
+ >(({ className, ...props }, ref) => (
91
+ <AlertDialogPrimitive.Description
92
+ ref={ref}
93
+ className={cn('text-sm text-muted-foreground', className)}
94
+ {...props}
95
+ />
96
+ ))
97
+ AlertDialogDescription.displayName =
98
+ AlertDialogPrimitive.Description.displayName
99
+
100
+ const AlertDialogAction = React.forwardRef<
101
+ React.ElementRef<typeof AlertDialogPrimitive.Action>,
102
+ React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Action>
103
+ >(({ className, ...props }, ref) => (
104
+ <AlertDialogPrimitive.Action
105
+ ref={ref}
106
+ className={cn(buttonVariants(), className)}
107
+ {...props}
108
+ />
109
+ ))
110
+ AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName
111
+
112
+ const AlertDialogCancel = React.forwardRef<
113
+ React.ElementRef<typeof AlertDialogPrimitive.Cancel>,
114
+ React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Cancel>
115
+ >(({ className, ...props }, ref) => (
116
+ <AlertDialogPrimitive.Cancel
117
+ ref={ref}
118
+ className={cn(
119
+ buttonVariants({ variant: 'outline' }),
120
+ 'mt-2 sm:mt-0',
121
+ className
122
+ )}
123
+ {...props}
124
+ />
125
+ ))
126
+ AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName
127
+
128
+ // Convenience component for common confirmation dialogs
129
+ export interface ConfirmDialogProps {
130
+ open: boolean
131
+ onOpenChange: (open: boolean) => void
132
+ title: string
133
+ description?: string
134
+ confirmText?: string
135
+ cancelText?: string
136
+ onConfirm: () => void
137
+ onCancel?: () => void
138
+ variant?: 'default' | 'destructive'
139
+ loading?: boolean
140
+ }
141
+
142
+ function ConfirmDialog({
143
+ open,
144
+ onOpenChange,
145
+ title,
146
+ description,
147
+ confirmText = 'Confirm',
148
+ cancelText = 'Cancel',
149
+ onConfirm,
150
+ onCancel,
151
+ variant = 'default',
152
+ loading = false,
153
+ }: ConfirmDialogProps) {
154
+ return (
155
+ <AlertDialog open={open} onOpenChange={onOpenChange}>
156
+ <AlertDialogContent>
157
+ <AlertDialogHeader>
158
+ <AlertDialogTitle>{title}</AlertDialogTitle>
159
+ {description && (
160
+ <AlertDialogDescription>{description}</AlertDialogDescription>
161
+ )}
162
+ </AlertDialogHeader>
163
+ <AlertDialogFooter>
164
+ <AlertDialogCancel
165
+ onClick={() => {
166
+ onCancel?.()
167
+ onOpenChange(false)
168
+ }}
169
+ >
170
+ {cancelText}
171
+ </AlertDialogCancel>
172
+ <AlertDialogAction
173
+ onClick={() => {
174
+ onConfirm()
175
+ }}
176
+ className={cn(
177
+ variant === 'destructive' &&
178
+ 'bg-destructive text-destructive-foreground hover:bg-destructive/90'
179
+ )}
180
+ disabled={loading}
181
+ >
182
+ {loading ? 'Loading...' : confirmText}
183
+ </AlertDialogAction>
184
+ </AlertDialogFooter>
185
+ </AlertDialogContent>
186
+ </AlertDialog>
187
+ )
188
+ }
189
+
190
+ export {
191
+ AlertDialog,
192
+ AlertDialogPortal,
193
+ AlertDialogOverlay,
194
+ AlertDialogTrigger,
195
+ AlertDialogContent,
196
+ AlertDialogHeader,
197
+ AlertDialogFooter,
198
+ AlertDialogTitle,
199
+ AlertDialogDescription,
200
+ AlertDialogAction,
201
+ AlertDialogCancel,
202
+ ConfirmDialog,
203
+ }
@@ -0,0 +1,160 @@
1
+ 'use client'
2
+
3
+ import * as React from 'react'
4
+ import { CaretRight, House } from '@phosphor-icons/react'
5
+ import { cn } from '../lib/utils'
6
+
7
+ export interface BreadcrumbItem {
8
+ label: string
9
+ href?: string
10
+ icon?: React.ReactNode
11
+ }
12
+
13
+ export interface BreadcrumbsProps extends React.HTMLAttributes<HTMLElement> {
14
+ items: BreadcrumbItem[]
15
+ /** Custom Link component (e.g., Next.js Link) */
16
+ LinkComponent?: React.ComponentType<{
17
+ href: string
18
+ className?: string
19
+ children?: React.ReactNode
20
+ }>
21
+ /** Separator between items */
22
+ separator?: React.ReactNode
23
+ /** Whether to show home icon for first item */
24
+ showHomeIcon?: boolean
25
+ /** Maximum items to show (will collapse middle items) */
26
+ maxItems?: number
27
+ /** Size variant */
28
+ size?: 'sm' | 'default'
29
+ }
30
+
31
+ const Breadcrumbs = React.forwardRef<HTMLElement, BreadcrumbsProps>(
32
+ (
33
+ {
34
+ items,
35
+ LinkComponent,
36
+ separator,
37
+ showHomeIcon = false,
38
+ maxItems,
39
+ size = 'default',
40
+ className,
41
+ ...props
42
+ },
43
+ ref
44
+ ) => {
45
+ const sizeStyles = {
46
+ sm: 'text-xs gap-1',
47
+ default: 'text-sm gap-1.5',
48
+ }
49
+
50
+ const iconSize = size === 'sm' ? 10 : 12
51
+
52
+ // Collapse items if maxItems is set
53
+ let displayItems = items
54
+ let isCollapsed = false
55
+ if (maxItems && items.length > maxItems) {
56
+ isCollapsed = true
57
+ displayItems = [
58
+ items[0],
59
+ { label: '...', href: undefined },
60
+ ...items.slice(-(maxItems - 2)),
61
+ ]
62
+ }
63
+
64
+ const defaultSeparator = (
65
+ <CaretRight size={iconSize} className="text-gray-400" />
66
+ )
67
+
68
+ return (
69
+ <nav
70
+ ref={ref}
71
+ aria-label="Breadcrumb"
72
+ className={cn('flex items-center text-gray-500', sizeStyles[size], className)}
73
+ {...props}
74
+ >
75
+ <ol className={cn('flex items-center', sizeStyles[size])}>
76
+ {displayItems.map((item, index) => {
77
+ const isFirst = index === 0
78
+ const isLast = index === displayItems.length - 1
79
+ const isEllipsis = item.label === '...'
80
+
81
+ return (
82
+ <li key={index} className={cn('flex items-center', sizeStyles[size])}>
83
+ {index > 0 && (
84
+ <span className="mx-1 text-gray-400">
85
+ {separator || defaultSeparator}
86
+ </span>
87
+ )}
88
+
89
+ {isEllipsis ? (
90
+ <span className="text-gray-400">{item.label}</span>
91
+ ) : item.href && !isLast ? (
92
+ LinkComponent ? (
93
+ <LinkComponent
94
+ href={item.href}
95
+ className="flex items-center gap-1 hover:text-[var(--black)] transition-colors"
96
+ >
97
+ {isFirst && showHomeIcon && (
98
+ <House size={iconSize + 2} weight="fill" />
99
+ )}
100
+ {item.icon}
101
+ <span>{item.label}</span>
102
+ </LinkComponent>
103
+ ) : (
104
+ <a
105
+ href={item.href}
106
+ className="flex items-center gap-1 hover:text-[var(--black)] transition-colors"
107
+ >
108
+ {isFirst && showHomeIcon && (
109
+ <House size={iconSize + 2} weight="fill" />
110
+ )}
111
+ {item.icon}
112
+ <span>{item.label}</span>
113
+ </a>
114
+ )
115
+ ) : (
116
+ <span
117
+ className={cn(
118
+ 'flex items-center gap-1',
119
+ isLast && 'text-[var(--black)] font-medium'
120
+ )}
121
+ aria-current={isLast ? 'page' : undefined}
122
+ >
123
+ {isFirst && showHomeIcon && (
124
+ <House size={iconSize + 2} weight="fill" />
125
+ )}
126
+ {item.icon}
127
+ <span>{item.label}</span>
128
+ </span>
129
+ )}
130
+ </li>
131
+ )
132
+ })}
133
+ </ol>
134
+ </nav>
135
+ )
136
+ }
137
+ )
138
+ Breadcrumbs.displayName = 'Breadcrumbs'
139
+
140
+ // Simple breadcrumb link component for use within the Breadcrumbs
141
+ export interface BreadcrumbLinkProps
142
+ extends React.AnchorHTMLAttributes<HTMLAnchorElement> {
143
+ asChild?: boolean
144
+ }
145
+
146
+ const BreadcrumbLink = React.forwardRef<HTMLAnchorElement, BreadcrumbLinkProps>(
147
+ ({ className, ...props }, ref) => (
148
+ <a
149
+ ref={ref}
150
+ className={cn(
151
+ 'hover:text-[var(--black)] transition-colors',
152
+ className
153
+ )}
154
+ {...props}
155
+ />
156
+ )
157
+ )
158
+ BreadcrumbLink.displayName = 'BreadcrumbLink'
159
+
160
+ export { Breadcrumbs, BreadcrumbLink }
@@ -0,0 +1,183 @@
1
+ 'use client'
2
+
3
+ import * as React from 'react'
4
+ import { cn } from '../lib/utils'
5
+ import { Button } from './button'
6
+ import { Input } from './input'
7
+ import { Label } from './label'
8
+
9
+ export type DateRangePreset = '7d' | '30d' | '90d' | 'custom' | 'today' | 'yesterday' | 'this-week' | 'this-month' | 'this-year'
10
+
11
+ export interface DateRangePickerProps {
12
+ /** Currently selected preset */
13
+ value: DateRangePreset
14
+ /** Callback when preset changes */
15
+ onChange: (value: DateRangePreset) => void
16
+ /** Custom start date (ISO string) */
17
+ customStart?: string
18
+ /** Custom end date (ISO string) */
19
+ customEnd?: string
20
+ /** Callback when custom dates change */
21
+ onCustomChange?: (start: string, end: string) => void
22
+ /** Available presets to show */
23
+ presets?: DateRangePreset[]
24
+ /** Size variant */
25
+ size?: 'sm' | 'default'
26
+ /** Additional class name */
27
+ className?: string
28
+ /** Show custom date inputs inline */
29
+ inlineCustom?: boolean
30
+ }
31
+
32
+ const presetLabels: Record<DateRangePreset, string> = {
33
+ 'today': 'Today',
34
+ 'yesterday': 'Yesterday',
35
+ '7d': 'Last 7 days',
36
+ '30d': 'Last 30 days',
37
+ '90d': 'Last 90 days',
38
+ 'this-week': 'This week',
39
+ 'this-month': 'This month',
40
+ 'this-year': 'This year',
41
+ 'custom': 'Custom',
42
+ }
43
+
44
+ const DateRangePicker = React.forwardRef<HTMLDivElement, DateRangePickerProps>(
45
+ (
46
+ {
47
+ value,
48
+ onChange,
49
+ customStart = '',
50
+ customEnd = '',
51
+ onCustomChange,
52
+ presets = ['7d', '30d', '90d', 'custom'],
53
+ size = 'default',
54
+ className,
55
+ inlineCustom = true,
56
+ },
57
+ ref
58
+ ) => {
59
+ const buttonSize = size === 'sm' ? 'sm' : 'default'
60
+
61
+ return (
62
+ <div ref={ref} className={cn('flex flex-wrap items-center gap-2', className)}>
63
+ {presets.map((preset) => (
64
+ <Button
65
+ key={preset}
66
+ variant={value === preset ? 'default' : 'outline'}
67
+ size={buttonSize}
68
+ onClick={() => onChange(preset)}
69
+ >
70
+ {presetLabels[preset]}
71
+ </Button>
72
+ ))}
73
+
74
+ {value === 'custom' && inlineCustom && onCustomChange && (
75
+ <div className="flex items-center gap-2">
76
+ <div className="space-y-1">
77
+ <Label htmlFor="start-date" className="text-xs sr-only">
78
+ Start
79
+ </Label>
80
+ <Input
81
+ id="start-date"
82
+ type="date"
83
+ value={customStart}
84
+ onChange={(e) => onCustomChange(e.target.value, customEnd)}
85
+ className={cn(size === 'sm' ? 'h-8 text-xs' : 'h-9')}
86
+ aria-label="Start date"
87
+ />
88
+ </div>
89
+ <span className="text-muted-foreground">to</span>
90
+ <div className="space-y-1">
91
+ <Label htmlFor="end-date" className="text-xs sr-only">
92
+ End
93
+ </Label>
94
+ <Input
95
+ id="end-date"
96
+ type="date"
97
+ value={customEnd}
98
+ onChange={(e) => onCustomChange(customStart, e.target.value)}
99
+ className={cn(size === 'sm' ? 'h-8 text-xs' : 'h-9')}
100
+ aria-label="End date"
101
+ />
102
+ </div>
103
+ </div>
104
+ )}
105
+ </div>
106
+ )
107
+ }
108
+ )
109
+ DateRangePicker.displayName = 'DateRangePicker'
110
+
111
+ // Compact variant with dropdown
112
+ export interface DateRangeSelectProps {
113
+ value: DateRangePreset
114
+ onChange: (value: DateRangePreset) => void
115
+ presets?: DateRangePreset[]
116
+ className?: string
117
+ }
118
+
119
+ const DateRangeSelect = React.forwardRef<HTMLSelectElement, DateRangeSelectProps>(
120
+ ({ value, onChange, presets = ['7d', '30d', '90d'], className }, ref) => {
121
+ return (
122
+ <select
123
+ ref={ref}
124
+ value={value}
125
+ onChange={(e) => onChange(e.target.value as DateRangePreset)}
126
+ className={cn(
127
+ 'h-9 rounded-sm border border-input bg-background px-3 text-sm ring-offset-background focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
128
+ className
129
+ )}
130
+ >
131
+ {presets.map((preset) => (
132
+ <option key={preset} value={preset}>
133
+ {presetLabels[preset]}
134
+ </option>
135
+ ))}
136
+ </select>
137
+ )
138
+ }
139
+ )
140
+ DateRangeSelect.displayName = 'DateRangeSelect'
141
+
142
+ // Helper to calculate date range from preset
143
+ export function getDateRangeFromPreset(preset: DateRangePreset): { start: Date; end: Date } {
144
+ const now = new Date()
145
+ const end = new Date(now)
146
+ end.setHours(23, 59, 59, 999)
147
+
148
+ let start = new Date(now)
149
+ start.setHours(0, 0, 0, 0)
150
+
151
+ switch (preset) {
152
+ case 'today':
153
+ break
154
+ case 'yesterday':
155
+ start.setDate(start.getDate() - 1)
156
+ end.setDate(end.getDate() - 1)
157
+ break
158
+ case '7d':
159
+ start.setDate(start.getDate() - 7)
160
+ break
161
+ case '30d':
162
+ start.setDate(start.getDate() - 30)
163
+ break
164
+ case '90d':
165
+ start.setDate(start.getDate() - 90)
166
+ break
167
+ case 'this-week':
168
+ start.setDate(start.getDate() - start.getDay())
169
+ break
170
+ case 'this-month':
171
+ start.setDate(1)
172
+ break
173
+ case 'this-year':
174
+ start.setMonth(0, 1)
175
+ break
176
+ default:
177
+ break
178
+ }
179
+
180
+ return { start, end }
181
+ }
182
+
183
+ export { DateRangePicker, DateRangeSelect }