@exxatdesignux/ui 0.0.5

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 (59) hide show
  1. package/package.json +72 -0
  2. package/src/components/ui/avatar.tsx +384 -0
  3. package/src/components/ui/badge.tsx +49 -0
  4. package/src/components/ui/banner.tsx +364 -0
  5. package/src/components/ui/breadcrumb.tsx +120 -0
  6. package/src/components/ui/button.tsx +66 -0
  7. package/src/components/ui/calendar.tsx +220 -0
  8. package/src/components/ui/card.tsx +136 -0
  9. package/src/components/ui/chart.tsx +378 -0
  10. package/src/components/ui/checkbox.tsx +160 -0
  11. package/src/components/ui/coach-mark.tsx +361 -0
  12. package/src/components/ui/collapsible.tsx +33 -0
  13. package/src/components/ui/command.tsx +232 -0
  14. package/src/components/ui/date-picker-field.tsx +186 -0
  15. package/src/components/ui/dialog.tsx +171 -0
  16. package/src/components/ui/drag-handle-grip.tsx +10 -0
  17. package/src/components/ui/drawer.tsx +134 -0
  18. package/src/components/ui/dropdown-menu.tsx +422 -0
  19. package/src/components/ui/field.tsx +238 -0
  20. package/src/components/ui/form.tsx +137 -0
  21. package/src/components/ui/input-group.tsx +156 -0
  22. package/src/components/ui/input-mask.tsx +135 -0
  23. package/src/components/ui/input.tsx +22 -0
  24. package/src/components/ui/kbd.tsx +55 -0
  25. package/src/components/ui/label.tsx +25 -0
  26. package/src/components/ui/payment-card-fields.tsx +65 -0
  27. package/src/components/ui/popover.tsx +46 -0
  28. package/src/components/ui/radio-group.tsx +217 -0
  29. package/src/components/ui/select.tsx +191 -0
  30. package/src/components/ui/selection-tile-grid.tsx +246 -0
  31. package/src/components/ui/separator.tsx +28 -0
  32. package/src/components/ui/sheet.tsx +147 -0
  33. package/src/components/ui/sidebar.tsx +716 -0
  34. package/src/components/ui/skeleton.tsx +13 -0
  35. package/src/components/ui/sonner.tsx +39 -0
  36. package/src/components/ui/status-badge.tsx +109 -0
  37. package/src/components/ui/table.tsx +117 -0
  38. package/src/components/ui/tabs.tsx +90 -0
  39. package/src/components/ui/textarea.tsx +18 -0
  40. package/src/components/ui/tip.tsx +21 -0
  41. package/src/components/ui/toggle-group.tsx +89 -0
  42. package/src/components/ui/toggle-switch.tsx +31 -0
  43. package/src/components/ui/toggle.tsx +48 -0
  44. package/src/components/ui/tooltip.tsx +59 -0
  45. package/src/components/ui/view-segmented-control.tsx +160 -0
  46. package/src/globals.css +1795 -0
  47. package/src/hooks/.gitkeep +0 -0
  48. package/src/hooks/use-app-theme.ts +172 -0
  49. package/src/hooks/use-coach-mark.ts +342 -0
  50. package/src/hooks/use-mobile.ts +31 -0
  51. package/src/hooks/use-mod-key-label.ts +29 -0
  52. package/src/index.ts +55 -0
  53. package/src/lib/compose-refs.ts +15 -0
  54. package/src/lib/date-filter.ts +67 -0
  55. package/src/lib/utils.ts +6 -0
  56. package/src/theme/apply-windows-contrast-theme.ts +29 -0
  57. package/src/theme/windows-contrast-theme.json +147 -0
  58. package/src/theme.css +1130 -0
  59. package/src/types/react-payment-inputs.d.ts +20 -0
@@ -0,0 +1,186 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+
5
+ import { cn } from "../../lib/utils"
6
+ import { formatDateUS } from "../../lib/date-filter"
7
+ import { Button } from "./button"
8
+ import { Calendar } from "./calendar"
9
+ import {
10
+ InputGroup,
11
+ InputGroupAddon,
12
+ InputGroupButton,
13
+ } from "./input-group"
14
+ import { MaskedInput, exxatMaskPatterns } from "./input-mask"
15
+ import {
16
+ Popover,
17
+ PopoverContent,
18
+ PopoverTrigger,
19
+ } from "./popover"
20
+
21
+ export const DATE_PICKER_ICON_CLASS = "fa-light fa-calendar"
22
+
23
+ export interface DatePickerFieldProps {
24
+ value: Date | undefined
25
+ onChange: (d: Date | undefined) => void
26
+ id?: string
27
+ disabled?: boolean
28
+ /** Passed to the trigger `Button` (e.g. `h-8 text-sm` in compact drawers). */
29
+ triggerClassName?: string
30
+ fromYear?: number
31
+ toYear?: number
32
+ popoverAlign?: "start" | "center" | "end"
33
+ popoverClassName?: string
34
+ }
35
+
36
+ /**
37
+ * Calendar + popover trigger — same pattern as New Placement schedule dates (WCAG: button exposes label, not raw text input).
38
+ */
39
+ export function DatePickerField({
40
+ value,
41
+ onChange,
42
+ id,
43
+ disabled,
44
+ triggerClassName,
45
+ fromYear = 2020,
46
+ toYear = 2032,
47
+ popoverAlign = "start",
48
+ popoverClassName,
49
+ }: DatePickerFieldProps) {
50
+ return (
51
+ <Popover>
52
+ <PopoverTrigger asChild>
53
+ <Button
54
+ id={id}
55
+ type="button"
56
+ variant="outline"
57
+ disabled={disabled}
58
+ className={cn(
59
+ "w-full justify-start text-left font-normal",
60
+ triggerClassName,
61
+ )}
62
+ aria-label={value ? formatDateUS(value.toISOString()) : "Pick a date"}
63
+ >
64
+ <i className={cn(DATE_PICKER_ICON_CLASS, "mr-2 shrink-0 text-muted-foreground")} aria-hidden="true" />
65
+ <span className={cn(!value && "text-muted-foreground")}>
66
+ {value ? formatDateUS(value.toISOString()) : "MM/DD/YYYY"}
67
+ </span>
68
+ </Button>
69
+ </PopoverTrigger>
70
+ <PopoverContent className={cn("z-[80] w-auto p-0", popoverClassName)} align={popoverAlign}>
71
+ <Calendar
72
+ mode="single"
73
+ selected={value}
74
+ onSelect={onChange}
75
+ initialFocus
76
+ fromYear={fromYear}
77
+ toYear={toYear}
78
+ captionLayout="dropdown"
79
+ />
80
+ </PopoverContent>
81
+ </Popover>
82
+ )
83
+ }
84
+
85
+ export interface DateTextInputFieldProps {
86
+ value: string
87
+ onValueChange: (value: string) => void
88
+ "aria-label": string
89
+ id?: string
90
+ placeholder?: string
91
+ className?: string
92
+ inputClassName?: string
93
+ autoFocus?: boolean
94
+ disabled?: boolean
95
+ fromYear?: number
96
+ toYear?: number
97
+ iconButtonVariant?: "ghost" | "outline"
98
+ popoverAlign?: "start" | "center" | "end"
99
+ popoverClassName?: string
100
+ }
101
+
102
+ function parseMdyToLocalDate(raw: string): Date | undefined {
103
+ const match = raw.trim().match(/^(\d{2})\/(\d{2})\/(\d{4})$/)
104
+ if (!match) return undefined
105
+ const month = Number(match[1])
106
+ const day = Number(match[2])
107
+ const year = Number(match[3])
108
+ if (month < 1 || month > 12 || day < 1 || day > 31) return undefined
109
+ const date = new Date(year, month - 1, day, 12, 0, 0, 0)
110
+ if (date.getFullYear() !== year || date.getMonth() !== month - 1 || date.getDate() !== day) return undefined
111
+ return date
112
+ }
113
+
114
+ function formatLocalDateToMdy(date: Date): string {
115
+ const month = String(date.getMonth() + 1).padStart(2, "0")
116
+ const day = String(date.getDate()).padStart(2, "0")
117
+ const year = String(date.getFullYear())
118
+ return `${month}/${day}/${year}`
119
+ }
120
+
121
+ export function DateTextInputField({
122
+ value,
123
+ onValueChange,
124
+ "aria-label": ariaLabel,
125
+ id,
126
+ placeholder = "MM/DD/YYYY",
127
+ className,
128
+ inputClassName,
129
+ autoFocus,
130
+ disabled,
131
+ fromYear = 2020,
132
+ toYear = 2032,
133
+ iconButtonVariant = "ghost",
134
+ popoverAlign = "end",
135
+ popoverClassName,
136
+ }: DateTextInputFieldProps) {
137
+ return (
138
+ <InputGroup className={className}>
139
+ <MaskedInput
140
+ data-slot="input-group-control"
141
+ id={id}
142
+ mask={exxatMaskPatterns.dateMDY}
143
+ aria-label={ariaLabel}
144
+ placeholder={placeholder}
145
+ value={value}
146
+ onChange={(event) => onValueChange(event.target.value)}
147
+ className={cn(
148
+ "flex-1 rounded-none border-0 bg-transparent shadow-none ring-0 focus-visible:ring-0 disabled:bg-transparent aria-invalid:ring-0 dark:bg-transparent dark:disabled:bg-transparent",
149
+ inputClassName,
150
+ )}
151
+ autoFocus={autoFocus}
152
+ disabled={disabled}
153
+ />
154
+ <InputGroupAddon align="inline-end">
155
+ <Popover>
156
+ <PopoverTrigger asChild>
157
+ <InputGroupButton
158
+ type="button"
159
+ variant={iconButtonVariant}
160
+ size="icon-xs"
161
+ disabled={disabled}
162
+ aria-label={`${ariaLabel} pick date`}
163
+ >
164
+ <i className={DATE_PICKER_ICON_CLASS} aria-hidden="true" />
165
+ </InputGroupButton>
166
+ </PopoverTrigger>
167
+ <PopoverContent
168
+ className={cn("z-[80] w-auto p-0", popoverClassName)}
169
+ align={popoverAlign}
170
+ sideOffset={6}
171
+ >
172
+ <Calendar
173
+ mode="single"
174
+ selected={parseMdyToLocalDate(value)}
175
+ onSelect={(next) => onValueChange(next ? formatLocalDateToMdy(next) : "")}
176
+ initialFocus
177
+ fromYear={fromYear}
178
+ toYear={toYear}
179
+ captionLayout="dropdown"
180
+ />
181
+ </PopoverContent>
182
+ </Popover>
183
+ </InputGroupAddon>
184
+ </InputGroup>
185
+ )
186
+ }
@@ -0,0 +1,171 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { Dialog as DialogPrimitive } from "radix-ui"
5
+
6
+ import { cn } from "../../lib/utils"
7
+ import { Button } from "./button"
8
+ import { XIcon } from "lucide-react"
9
+
10
+ function Dialog({
11
+ ...props
12
+ }: React.ComponentProps<typeof DialogPrimitive.Root>) {
13
+ return <DialogPrimitive.Root data-slot="dialog" {...props} />
14
+ }
15
+
16
+ function DialogTrigger({
17
+ ...props
18
+ }: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
19
+ return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />
20
+ }
21
+
22
+ function DialogPortal({
23
+ ...props
24
+ }: React.ComponentProps<typeof DialogPrimitive.Portal>) {
25
+ return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />
26
+ }
27
+
28
+ function DialogClose({
29
+ ...props
30
+ }: React.ComponentProps<typeof DialogPrimitive.Close>) {
31
+ return <DialogPrimitive.Close data-slot="dialog-close" {...props} />
32
+ }
33
+
34
+ function DialogOverlay({
35
+ className,
36
+ ...props
37
+ }: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
38
+ return (
39
+ <DialogPrimitive.Overlay
40
+ data-slot="dialog-overlay"
41
+ className={cn(
42
+ "fixed inset-0 isolate z-50 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0",
43
+ className
44
+ )}
45
+ {...props}
46
+ />
47
+ )
48
+ }
49
+
50
+ function DialogContent({
51
+ className,
52
+ children,
53
+ showCloseButton = true,
54
+ overlayClassName,
55
+ ...props
56
+ }: React.ComponentProps<typeof DialogPrimitive.Content> & {
57
+ showCloseButton?: boolean
58
+ /** Merged onto `DialogOverlay` (e.g. `bg-transparent` for a palette with no dimmed backdrop). */
59
+ overlayClassName?: string
60
+ }) {
61
+ return (
62
+ <DialogPortal>
63
+ <DialogOverlay className={overlayClassName ?? undefined} />
64
+ <DialogPrimitive.Content
65
+ data-slot="dialog-content"
66
+ className={cn(
67
+ "fixed top-1/2 start-1/2 z-50 grid w-full max-w-[calc(100%-2rem)] -translate-x-1/2 rtl:translate-x-1/2 -translate-y-1/2 gap-4 rounded-xl bg-popover p-4 text-sm text-popover-foreground ring-1 ring-foreground/10 duration-100 outline-none sm:max-w-sm data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
68
+ className
69
+ )}
70
+ {...props}
71
+ >
72
+ {children}
73
+ {showCloseButton && (
74
+ <DialogPrimitive.Close data-slot="dialog-close" asChild>
75
+ <Button
76
+ variant="ghost"
77
+ className="absolute top-2 end-2"
78
+ size="icon-sm"
79
+ >
80
+ <XIcon
81
+ />
82
+ <span className="sr-only">Close</span>
83
+ </Button>
84
+ </DialogPrimitive.Close>
85
+ )}
86
+ </DialogPrimitive.Content>
87
+ </DialogPortal>
88
+ )
89
+ }
90
+
91
+ function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
92
+ return (
93
+ <div
94
+ data-slot="dialog-header"
95
+ className={cn("flex flex-col gap-2", className)}
96
+ {...props}
97
+ />
98
+ )
99
+ }
100
+
101
+ function DialogFooter({
102
+ className,
103
+ showCloseButton = false,
104
+ children,
105
+ ...props
106
+ }: React.ComponentProps<"div"> & {
107
+ showCloseButton?: boolean
108
+ }) {
109
+ return (
110
+ <div
111
+ data-slot="dialog-footer"
112
+ className={cn(
113
+ "-mx-4 -mb-4 flex flex-col-reverse gap-2 rounded-b-xl border-t bg-muted/50 p-4 sm:flex-row sm:justify-end",
114
+ className
115
+ )}
116
+ {...props}
117
+ >
118
+ {children}
119
+ {showCloseButton && (
120
+ <DialogPrimitive.Close asChild>
121
+ <Button variant="outline">Close</Button>
122
+ </DialogPrimitive.Close>
123
+ )}
124
+ </div>
125
+ )
126
+ }
127
+
128
+ function DialogTitle({
129
+ className,
130
+ ...props
131
+ }: React.ComponentProps<typeof DialogPrimitive.Title>) {
132
+ return (
133
+ <DialogPrimitive.Title
134
+ data-slot="dialog-title"
135
+ className={cn(
136
+ "font-heading text-base leading-none font-medium",
137
+ className
138
+ )}
139
+ {...props}
140
+ />
141
+ )
142
+ }
143
+
144
+ function DialogDescription({
145
+ className,
146
+ ...props
147
+ }: React.ComponentProps<typeof DialogPrimitive.Description>) {
148
+ return (
149
+ <DialogPrimitive.Description
150
+ data-slot="dialog-description"
151
+ className={cn(
152
+ "text-sm text-muted-foreground *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground",
153
+ className
154
+ )}
155
+ {...props}
156
+ />
157
+ )
158
+ }
159
+
160
+ export {
161
+ Dialog,
162
+ DialogClose,
163
+ DialogContent,
164
+ DialogDescription,
165
+ DialogFooter,
166
+ DialogHeader,
167
+ DialogOverlay,
168
+ DialogPortal,
169
+ DialogTitle,
170
+ DialogTrigger,
171
+ }
@@ -0,0 +1,10 @@
1
+ import { cn } from "../../lib/utils"
2
+
3
+ /**
4
+ * Solid grip icon for drag handles — use anywhere rows/cards reorder (dashboard, Properties, etc.).
5
+ */
6
+ export function DragHandleGripIcon({ className }: { className?: string }) {
7
+ return (
8
+ <i className={cn("fa-solid fa-grip-dots-vertical shrink-0", className)} aria-hidden="true" />
9
+ )
10
+ }
@@ -0,0 +1,134 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { Drawer as DrawerPrimitive } from "vaul"
5
+
6
+ import { cn } from "../../lib/utils"
7
+
8
+ function Drawer({
9
+ ...props
10
+ }: React.ComponentProps<typeof DrawerPrimitive.Root>) {
11
+ return <DrawerPrimitive.Root data-slot="drawer" {...props} />
12
+ }
13
+
14
+ function DrawerTrigger({
15
+ ...props
16
+ }: React.ComponentProps<typeof DrawerPrimitive.Trigger>) {
17
+ return <DrawerPrimitive.Trigger data-slot="drawer-trigger" {...props} />
18
+ }
19
+
20
+ function DrawerPortal({
21
+ ...props
22
+ }: React.ComponentProps<typeof DrawerPrimitive.Portal>) {
23
+ return <DrawerPrimitive.Portal data-slot="drawer-portal" {...props} />
24
+ }
25
+
26
+ function DrawerClose({
27
+ ...props
28
+ }: React.ComponentProps<typeof DrawerPrimitive.Close>) {
29
+ return <DrawerPrimitive.Close data-slot="drawer-close" {...props} />
30
+ }
31
+
32
+ function DrawerOverlay({
33
+ className,
34
+ ...props
35
+ }: React.ComponentProps<typeof DrawerPrimitive.Overlay>) {
36
+ return (
37
+ <DrawerPrimitive.Overlay
38
+ data-slot="drawer-overlay"
39
+ className={cn(
40
+ "fixed inset-0 z-50 bg-overlay duration-300 ease-out supports-backdrop-filter:backdrop-blur-xs data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0",
41
+ className
42
+ )}
43
+ {...props}
44
+ />
45
+ )
46
+ }
47
+
48
+ function DrawerContent({
49
+ className,
50
+ children,
51
+ ...props
52
+ }: React.ComponentProps<typeof DrawerPrimitive.Content>) {
53
+ return (
54
+ <DrawerPortal data-slot="drawer-portal">
55
+ <DrawerOverlay />
56
+ <DrawerPrimitive.Content
57
+ data-slot="drawer-content"
58
+ className={cn(
59
+ "group/drawer-content fixed z-50 flex h-auto flex-col bg-background text-sm duration-300 ease-out outline-none data-[vaul-drawer-direction=bottom]:inset-x-0 data-[vaul-drawer-direction=bottom]:bottom-0 data-[vaul-drawer-direction=bottom]:mt-24 data-[vaul-drawer-direction=bottom]:max-h-[80vh] data-[vaul-drawer-direction=bottom]:rounded-t-xl data-[vaul-drawer-direction=bottom]:border-t data-[vaul-drawer-direction=left]:inset-y-0 data-[vaul-drawer-direction=left]:start-0 data-[vaul-drawer-direction=left]:w-3/4 data-[vaul-drawer-direction=left]:rounded-e-xl data-[vaul-drawer-direction=left]:border-e data-[vaul-drawer-direction=right]:inset-y-0 data-[vaul-drawer-direction=right]:end-0 data-[vaul-drawer-direction=right]:w-3/4 data-[vaul-drawer-direction=right]:rounded-s-xl data-[vaul-drawer-direction=right]:border-s data-[vaul-drawer-direction=top]:inset-x-0 data-[vaul-drawer-direction=top]:top-0 data-[vaul-drawer-direction=top]:mb-24 data-[vaul-drawer-direction=top]:max-h-[80vh] data-[vaul-drawer-direction=top]:rounded-b-xl data-[vaul-drawer-direction=top]:border-b data-[vaul-drawer-direction=left]:sm:max-w-sm data-[vaul-drawer-direction=right]:sm:max-w-sm",
60
+ className
61
+ )}
62
+ {...props}
63
+ >
64
+ <div className="mx-auto mt-4 hidden h-1 w-[100px] shrink-0 rounded-full bg-muted group-data-[vaul-drawer-direction=bottom]/drawer-content:block" />
65
+ {children}
66
+ </DrawerPrimitive.Content>
67
+ </DrawerPortal>
68
+ )
69
+ }
70
+
71
+ function DrawerHeader({ className, ...props }: React.ComponentProps<"div">) {
72
+ return (
73
+ <div
74
+ data-slot="drawer-header"
75
+ className={cn(
76
+ "flex flex-col gap-0.5 p-4 group-data-[vaul-drawer-direction=bottom]/drawer-content:text-center group-data-[vaul-drawer-direction=top]/drawer-content:text-center md:gap-0.5 md:text-start",
77
+ className
78
+ )}
79
+ {...props}
80
+ />
81
+ )
82
+ }
83
+
84
+ function DrawerFooter({ className, ...props }: React.ComponentProps<"div">) {
85
+ return (
86
+ <div
87
+ data-slot="drawer-footer"
88
+ className={cn("mt-auto flex flex-col gap-2 p-4", className)}
89
+ {...props}
90
+ />
91
+ )
92
+ }
93
+
94
+ function DrawerTitle({
95
+ className,
96
+ ...props
97
+ }: React.ComponentProps<typeof DrawerPrimitive.Title>) {
98
+ return (
99
+ <DrawerPrimitive.Title
100
+ data-slot="drawer-title"
101
+ className={cn(
102
+ "font-heading text-base font-medium text-foreground",
103
+ className
104
+ )}
105
+ {...props}
106
+ />
107
+ )
108
+ }
109
+
110
+ function DrawerDescription({
111
+ className,
112
+ ...props
113
+ }: React.ComponentProps<typeof DrawerPrimitive.Description>) {
114
+ return (
115
+ <DrawerPrimitive.Description
116
+ data-slot="drawer-description"
117
+ className={cn("text-sm text-muted-foreground", className)}
118
+ {...props}
119
+ />
120
+ )
121
+ }
122
+
123
+ export {
124
+ Drawer,
125
+ DrawerPortal,
126
+ DrawerOverlay,
127
+ DrawerTrigger,
128
+ DrawerClose,
129
+ DrawerContent,
130
+ DrawerHeader,
131
+ DrawerFooter,
132
+ DrawerTitle,
133
+ DrawerDescription,
134
+ }