@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.
- package/package.json +72 -0
- package/src/components/ui/avatar.tsx +384 -0
- package/src/components/ui/badge.tsx +49 -0
- package/src/components/ui/banner.tsx +364 -0
- package/src/components/ui/breadcrumb.tsx +120 -0
- package/src/components/ui/button.tsx +66 -0
- package/src/components/ui/calendar.tsx +220 -0
- package/src/components/ui/card.tsx +136 -0
- package/src/components/ui/chart.tsx +378 -0
- package/src/components/ui/checkbox.tsx +160 -0
- package/src/components/ui/coach-mark.tsx +361 -0
- package/src/components/ui/collapsible.tsx +33 -0
- package/src/components/ui/command.tsx +232 -0
- package/src/components/ui/date-picker-field.tsx +186 -0
- package/src/components/ui/dialog.tsx +171 -0
- package/src/components/ui/drag-handle-grip.tsx +10 -0
- package/src/components/ui/drawer.tsx +134 -0
- package/src/components/ui/dropdown-menu.tsx +422 -0
- package/src/components/ui/field.tsx +238 -0
- package/src/components/ui/form.tsx +137 -0
- package/src/components/ui/input-group.tsx +156 -0
- package/src/components/ui/input-mask.tsx +135 -0
- package/src/components/ui/input.tsx +22 -0
- package/src/components/ui/kbd.tsx +55 -0
- package/src/components/ui/label.tsx +25 -0
- package/src/components/ui/payment-card-fields.tsx +65 -0
- package/src/components/ui/popover.tsx +46 -0
- package/src/components/ui/radio-group.tsx +217 -0
- package/src/components/ui/select.tsx +191 -0
- package/src/components/ui/selection-tile-grid.tsx +246 -0
- package/src/components/ui/separator.tsx +28 -0
- package/src/components/ui/sheet.tsx +147 -0
- package/src/components/ui/sidebar.tsx +716 -0
- package/src/components/ui/skeleton.tsx +13 -0
- package/src/components/ui/sonner.tsx +39 -0
- package/src/components/ui/status-badge.tsx +109 -0
- package/src/components/ui/table.tsx +117 -0
- package/src/components/ui/tabs.tsx +90 -0
- package/src/components/ui/textarea.tsx +18 -0
- package/src/components/ui/tip.tsx +21 -0
- package/src/components/ui/toggle-group.tsx +89 -0
- package/src/components/ui/toggle-switch.tsx +31 -0
- package/src/components/ui/toggle.tsx +48 -0
- package/src/components/ui/tooltip.tsx +59 -0
- package/src/components/ui/view-segmented-control.tsx +160 -0
- package/src/globals.css +1795 -0
- package/src/hooks/.gitkeep +0 -0
- package/src/hooks/use-app-theme.ts +172 -0
- package/src/hooks/use-coach-mark.ts +342 -0
- package/src/hooks/use-mobile.ts +31 -0
- package/src/hooks/use-mod-key-label.ts +29 -0
- package/src/index.ts +55 -0
- package/src/lib/compose-refs.ts +15 -0
- package/src/lib/date-filter.ts +67 -0
- package/src/lib/utils.ts +6 -0
- package/src/theme/apply-windows-contrast-theme.ts +29 -0
- package/src/theme/windows-contrast-theme.json +147 -0
- package/src/theme.css +1130 -0
- package/src/types/react-payment-inputs.d.ts +20 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Format any date string (ISO, MM/DD/YYYY, "Mar 15 2026", etc.) into the
|
|
3
|
+
* app-wide display format: MM/DD/YYYY.
|
|
4
|
+
* Returns "—" for empty / unparseable values.
|
|
5
|
+
*/
|
|
6
|
+
export function formatDateUS(raw: string | null | undefined): string {
|
|
7
|
+
if (!raw || raw.trim() === "—" || raw.trim() === "-") return "—"
|
|
8
|
+
// Already MM/DD/YYYY — return as-is
|
|
9
|
+
if (/^\d{2}\/\d{2}\/\d{4}$/.test(raw.trim())) return raw.trim()
|
|
10
|
+
const d = new Date(raw)
|
|
11
|
+
if (Number.isNaN(d.getTime())) return raw
|
|
12
|
+
const m = String(d.getMonth() + 1).padStart(2, "0")
|
|
13
|
+
const day = String(d.getDate()).padStart(2, "0")
|
|
14
|
+
const y = d.getFullYear()
|
|
15
|
+
return `${m}/${day}/${y}`
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Format a Date (or ISO string) into "MM/DD/YYYY hh:mm AM/PM EST".
|
|
20
|
+
* Time zone label is always appended as the literal string "EST" (display only).
|
|
21
|
+
*/
|
|
22
|
+
export function formatDateTimeUS(raw: Date | string | null | undefined): string {
|
|
23
|
+
if (!raw) return "—"
|
|
24
|
+
const d = raw instanceof Date ? raw : new Date(raw)
|
|
25
|
+
if (Number.isNaN(d.getTime())) return String(raw)
|
|
26
|
+
const m = String(d.getMonth() + 1).padStart(2, "0")
|
|
27
|
+
const day = String(d.getDate()).padStart(2, "0")
|
|
28
|
+
const y = d.getFullYear()
|
|
29
|
+
let h = d.getHours()
|
|
30
|
+
const min = String(d.getMinutes()).padStart(2, "0")
|
|
31
|
+
const ampm = h >= 12 ? "PM" : "AM"
|
|
32
|
+
h = h % 12 || 12
|
|
33
|
+
return `${m}/${day}/${y} ${String(h).padStart(2, "0")}:${min} ${ampm} EST`
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Parse a human-readable date string into YYYY-MM-DD for comparison (local timezone). */
|
|
37
|
+
export function parseRowDateToYmd(raw: string): string | null {
|
|
38
|
+
const t = raw.trim()
|
|
39
|
+
if (!t || t === "—" || t === "-") return null
|
|
40
|
+
const d = new Date(t)
|
|
41
|
+
if (Number.isNaN(d.getTime())) return null
|
|
42
|
+
const y = d.getFullYear()
|
|
43
|
+
const m = String(d.getMonth() + 1).padStart(2, "0")
|
|
44
|
+
const day = String(d.getDate()).padStart(2, "0")
|
|
45
|
+
return `${y}-${m}-${day}`
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Format YYYY-MM-DD for compact filter chip label. */
|
|
49
|
+
export function formatYmdForDisplay(ymd: string): string {
|
|
50
|
+
const d = new Date(`${ymd}T12:00:00`)
|
|
51
|
+
if (Number.isNaN(d.getTime())) return ymd
|
|
52
|
+
return d.toLocaleDateString(undefined, { month: "short", day: "numeric", year: "numeric" })
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Local noon to avoid timezone shifting the calendar day. */
|
|
56
|
+
export function ymdToLocalDate(ymd: string | undefined): Date | undefined {
|
|
57
|
+
if (!ymd || !/^\d{4}-\d{2}-\d{2}$/.test(ymd)) return undefined
|
|
58
|
+
const [y, m, d] = ymd.split("-").map(Number)
|
|
59
|
+
return new Date(y, m - 1, d, 12, 0, 0, 0)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function localDateToYmd(d: Date): string {
|
|
63
|
+
const y = d.getFullYear()
|
|
64
|
+
const m = String(d.getMonth() + 1).padStart(2, "0")
|
|
65
|
+
const day = String(d.getDate()).padStart(2, "0")
|
|
66
|
+
return `${y}-${m}-${day}`
|
|
67
|
+
}
|
package/src/lib/utils.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import windowsContrastTheme from "./windows-contrast-theme.json"
|
|
2
|
+
|
|
3
|
+
type WindowsContrastFile = {
|
|
4
|
+
light: Record<string, string>
|
|
5
|
+
dark: Record<string, string>
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const file = windowsContrastTheme as WindowsContrastFile
|
|
9
|
+
|
|
10
|
+
const WINDOWS_VAR_KEYS = new Set([
|
|
11
|
+
...Object.keys(file.light),
|
|
12
|
+
...Object.keys(file.dark),
|
|
13
|
+
])
|
|
14
|
+
|
|
15
|
+
export function applyWindowsContrastTheme() {
|
|
16
|
+
const el = document.documentElement
|
|
17
|
+
const isDark = el.classList.contains("dark")
|
|
18
|
+
const map = isDark ? file.dark : file.light
|
|
19
|
+
for (const [prop, val] of Object.entries(map)) {
|
|
20
|
+
el.style.setProperty(prop, val)
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function clearWindowsContrastTheme() {
|
|
25
|
+
const el = document.documentElement
|
|
26
|
+
for (const key of WINDOWS_VAR_KEYS) {
|
|
27
|
+
el.style.removeProperty(key)
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$comment": "In-app “Windows (JSON)” contrast mode. Edit hex or oklch() strings to match a Windows Contrast theme (Settings → Accessibility → Contrast themes) or an exported .theme file. Keys are Exxat semantic CSS variables on <html>. Light/dark follow the app’s light/dark mode (next-themes).",
|
|
3
|
+
"windowsSettingsLabels": {
|
|
4
|
+
"Background": "Canvas — maps to --background, surfaces, sidebar base",
|
|
5
|
+
"Text": "Text — maps to --foreground and readable copy tokens",
|
|
6
|
+
"Hyperlink": "Hyperlinks — mapped to --primary / focus via --ring where useful",
|
|
7
|
+
"Selected text": "Selected text — maps to --accent and --accent-foreground",
|
|
8
|
+
"Button text": "Button text — maps to --primary / --primary-foreground pair",
|
|
9
|
+
"Disabled text": "Disabled text — maps to --muted / --muted-foreground"
|
|
10
|
+
},
|
|
11
|
+
"light": {
|
|
12
|
+
"--hc-highlight": "#0000ff",
|
|
13
|
+
"--hc-highlight-text": "#ffffff",
|
|
14
|
+
"--background": "#ffffff",
|
|
15
|
+
"--foreground": "#000000",
|
|
16
|
+
"--card": "#ffffff",
|
|
17
|
+
"--card-foreground": "#000000",
|
|
18
|
+
"--popover": "#ffffff",
|
|
19
|
+
"--popover-foreground": "#000000",
|
|
20
|
+
"--primary": "#0000ff",
|
|
21
|
+
"--primary-foreground": "#ffffff",
|
|
22
|
+
"--secondary": "#ffffff",
|
|
23
|
+
"--secondary-foreground": "#000000",
|
|
24
|
+
"--muted": "#e8e8e8",
|
|
25
|
+
"--muted-foreground": "#3b3b3b",
|
|
26
|
+
"--accent": "#0000ff",
|
|
27
|
+
"--accent-foreground": "#ffffff",
|
|
28
|
+
"--destructive": "#b5200d",
|
|
29
|
+
"--destructive-foreground": "#ffffff",
|
|
30
|
+
"--border": "#000000",
|
|
31
|
+
"--border-control": "#000000",
|
|
32
|
+
"--border-control-3": "#000000",
|
|
33
|
+
"--border-control-35": "#000000",
|
|
34
|
+
"--control-border": "#000000",
|
|
35
|
+
"--input": "#000000",
|
|
36
|
+
"--input-background": "#ffffff",
|
|
37
|
+
"--ring": "#0000ff",
|
|
38
|
+
"--sidebar": "#ffffff",
|
|
39
|
+
"--sidebar-foreground": "#000000",
|
|
40
|
+
"--sidebar-primary": "#000000",
|
|
41
|
+
"--sidebar-primary-foreground": "#ffffff",
|
|
42
|
+
"--sidebar-accent": "#d9d9d9",
|
|
43
|
+
"--sidebar-accent-foreground": "#000000",
|
|
44
|
+
"--sidebar-border": "#000000",
|
|
45
|
+
"--sidebar-ring": "#0000ff",
|
|
46
|
+
"--sidebar-section-label-foreground": "#515151",
|
|
47
|
+
"--brand-tint": "#f0f0f0",
|
|
48
|
+
"--brand-tint-light": "#f7f7f7",
|
|
49
|
+
"--brand-tint-subtle": "#fafafa",
|
|
50
|
+
"--chart-1": "#000000",
|
|
51
|
+
"--chart-2": "#3b3b3b",
|
|
52
|
+
"--chart-3": "#6e6e6e",
|
|
53
|
+
"--chart-4": "#a0a0a0",
|
|
54
|
+
"--chart-5": "#d1d1d1",
|
|
55
|
+
"--chip-1": "#000000",
|
|
56
|
+
"--chip-2": "#000000",
|
|
57
|
+
"--chip-3": "#000000",
|
|
58
|
+
"--chip-4": "#000000",
|
|
59
|
+
"--chip-5": "#000000",
|
|
60
|
+
"--chip-destructive": "#b5200d",
|
|
61
|
+
"--interactive-hover": "#e0e0e0",
|
|
62
|
+
"--interactive-hover-foreground": "#000000",
|
|
63
|
+
"--interactive-hover-subtle": "#ebebeb",
|
|
64
|
+
"--interactive-hover-soft": "#ebebeb",
|
|
65
|
+
"--interactive-hover-medium": "#d5d5d5",
|
|
66
|
+
"--interactive-hover-strong": "#cccccc",
|
|
67
|
+
"--interactive-hover-row": "#e0e0e0",
|
|
68
|
+
"--overlay": "color-mix(in srgb, #000000 18%, transparent)",
|
|
69
|
+
"--dt-row-bg": "#ffffff",
|
|
70
|
+
"--dt-row-hover": "#ebebeb",
|
|
71
|
+
"--dt-row-selected": "#0000ff",
|
|
72
|
+
"--dt-row-selected-fg": "#ffffff",
|
|
73
|
+
"--dt-header-bg": "#ffffff",
|
|
74
|
+
"--dt-group-bg": "#ebebeb",
|
|
75
|
+
"--dt-new-row-bg": "#ffffff",
|
|
76
|
+
"--dt-new-row-border": "#000000",
|
|
77
|
+
"--theme-color-chrome": "#ffffff"
|
|
78
|
+
},
|
|
79
|
+
"dark": {
|
|
80
|
+
"--hc-highlight": "#ffff00",
|
|
81
|
+
"--hc-highlight-text": "#000000",
|
|
82
|
+
"--background": "#000000",
|
|
83
|
+
"--foreground": "#ffffff",
|
|
84
|
+
"--card": "#000000",
|
|
85
|
+
"--card-foreground": "#ffffff",
|
|
86
|
+
"--popover": "#0a0a0a",
|
|
87
|
+
"--popover-foreground": "#ffffff",
|
|
88
|
+
"--primary": "#00ffff",
|
|
89
|
+
"--primary-foreground": "#000000",
|
|
90
|
+
"--secondary": "#000000",
|
|
91
|
+
"--secondary-foreground": "#ffffff",
|
|
92
|
+
"--muted": "#1a1a1a",
|
|
93
|
+
"--muted-foreground": "#ffffff",
|
|
94
|
+
"--accent": "#ffff00",
|
|
95
|
+
"--accent-foreground": "#000000",
|
|
96
|
+
"--destructive": "#ff8080",
|
|
97
|
+
"--destructive-foreground": "#000000",
|
|
98
|
+
"--border": "#ffffff",
|
|
99
|
+
"--border-control": "#ffffff",
|
|
100
|
+
"--border-control-3": "#ffffff",
|
|
101
|
+
"--border-control-35": "#ffffff",
|
|
102
|
+
"--control-border": "#ffffff",
|
|
103
|
+
"--input": "#ffffff",
|
|
104
|
+
"--input-background": "#000000",
|
|
105
|
+
"--ring": "#ffff00",
|
|
106
|
+
"--sidebar": "#000000",
|
|
107
|
+
"--sidebar-foreground": "#ffffff",
|
|
108
|
+
"--sidebar-primary": "#ffffff",
|
|
109
|
+
"--sidebar-primary-foreground": "#000000",
|
|
110
|
+
"--sidebar-accent": "#1a1a1a",
|
|
111
|
+
"--sidebar-accent-foreground": "#ffffff",
|
|
112
|
+
"--sidebar-border": "#ffffff",
|
|
113
|
+
"--sidebar-ring": "#ffff00",
|
|
114
|
+
"--sidebar-section-label-foreground": "#c8c8c8",
|
|
115
|
+
"--brand-tint": "#1a1a1a",
|
|
116
|
+
"--brand-tint-light": "#141414",
|
|
117
|
+
"--brand-tint-subtle": "#101010",
|
|
118
|
+
"--chart-1": "#ffffff",
|
|
119
|
+
"--chart-2": "#c8c8c8",
|
|
120
|
+
"--chart-3": "#8e8e8e",
|
|
121
|
+
"--chart-4": "#5a5a5a",
|
|
122
|
+
"--chart-5": "#2e2e2e",
|
|
123
|
+
"--chip-1": "#ffffff",
|
|
124
|
+
"--chip-2": "#ffffff",
|
|
125
|
+
"--chip-3": "#ffffff",
|
|
126
|
+
"--chip-4": "#ffffff",
|
|
127
|
+
"--chip-5": "#ffffff",
|
|
128
|
+
"--chip-destructive": "#ff8080",
|
|
129
|
+
"--interactive-hover": "#262626",
|
|
130
|
+
"--interactive-hover-foreground": "#ffffff",
|
|
131
|
+
"--interactive-hover-subtle": "#1f1f1f",
|
|
132
|
+
"--interactive-hover-soft": "#1f1f1f",
|
|
133
|
+
"--interactive-hover-medium": "#2e2e2e",
|
|
134
|
+
"--interactive-hover-strong": "#383838",
|
|
135
|
+
"--interactive-hover-row": "#262626",
|
|
136
|
+
"--overlay": "color-mix(in srgb, #ffffff 22%, transparent)",
|
|
137
|
+
"--dt-row-bg": "#000000",
|
|
138
|
+
"--dt-row-hover": "#1a1a1a",
|
|
139
|
+
"--dt-row-selected": "#ffff00",
|
|
140
|
+
"--dt-row-selected-fg": "#000000",
|
|
141
|
+
"--dt-header-bg": "#000000",
|
|
142
|
+
"--dt-group-bg": "#1a1a1a",
|
|
143
|
+
"--dt-new-row-bg": "#000000",
|
|
144
|
+
"--dt-new-row-border": "#ffffff",
|
|
145
|
+
"--theme-color-chrome": "#000000"
|
|
146
|
+
}
|
|
147
|
+
}
|