@srcroot/ui 0.0.55 → 0.0.56

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 (107) hide show
  1. package/README.md +151 -151
  2. package/dist/index.d.ts +0 -0
  3. package/dist/index.js +33 -17
  4. package/package.json +7 -2
  5. package/src/registry/analytics/google-analytics.tsx +36 -39
  6. package/src/registry/analytics/google-tag-manager.tsx +62 -65
  7. package/src/registry/analytics/meta-pixel.tsx +44 -47
  8. package/src/registry/analytics/microsoft-clarity.tsx +31 -34
  9. package/src/registry/analytics/tiktok-pixel.tsx +34 -37
  10. package/src/registry/lib/utils.ts +0 -0
  11. package/src/registry/themes/v3/blue.css +157 -157
  12. package/src/registry/themes/v3/glass.css +153 -153
  13. package/src/registry/themes/v3/gray.css +157 -157
  14. package/src/registry/themes/v3/green.css +157 -157
  15. package/src/registry/themes/v3/neutral.css +157 -157
  16. package/src/registry/themes/v3/orange.css +157 -157
  17. package/src/registry/themes/v3/rose.css +157 -157
  18. package/src/registry/themes/v3/slate.css +157 -157
  19. package/src/registry/themes/v3/stone.css +157 -157
  20. package/src/registry/themes/v3/violet.css +186 -186
  21. package/src/registry/themes/v3/zinc.css +157 -157
  22. package/src/registry/themes/v4/blue.css +184 -184
  23. package/src/registry/themes/v4/glass.css +180 -180
  24. package/src/registry/themes/v4/gray.css +184 -184
  25. package/src/registry/themes/v4/green.css +184 -184
  26. package/src/registry/themes/v4/neutral.css +184 -184
  27. package/src/registry/themes/v4/orange.css +184 -184
  28. package/src/registry/themes/v4/rose.css +184 -184
  29. package/src/registry/themes/v4/slate.css +184 -184
  30. package/src/registry/themes/v4/stone.css +184 -184
  31. package/src/registry/themes/v4/violet.css +184 -184
  32. package/src/registry/themes/v4/zinc.css +184 -184
  33. package/src/registry/ui/accordion.tsx +164 -165
  34. package/src/registry/ui/alert-dialog.tsx +213 -214
  35. package/src/registry/ui/alert.tsx +73 -76
  36. package/src/registry/ui/aspect-ratio.tsx +44 -47
  37. package/src/registry/ui/avatar.tsx +96 -97
  38. package/src/registry/ui/badge.tsx +52 -55
  39. package/src/registry/ui/breadcrumb.tsx +147 -150
  40. package/src/registry/ui/button-group.tsx +64 -67
  41. package/src/registry/ui/button.tsx +71 -72
  42. package/src/registry/ui/calendar.tsx +514 -515
  43. package/src/registry/ui/card.tsx +88 -91
  44. package/src/registry/ui/carousel.tsx +214 -214
  45. package/src/registry/ui/chart.tsx +373 -373
  46. package/src/registry/ui/chatbot.tsx +86 -13
  47. package/src/registry/ui/checkbox.tsx +93 -94
  48. package/src/registry/ui/collapsible.tsx +107 -108
  49. package/src/registry/ui/combobox.tsx +171 -171
  50. package/src/registry/ui/command.tsx +300 -300
  51. package/src/registry/ui/container.tsx +44 -47
  52. package/src/registry/ui/context-menu.tsx +221 -221
  53. package/src/registry/ui/date-picker.tsx +228 -228
  54. package/src/registry/ui/dialog.tsx +269 -270
  55. package/src/registry/ui/drawer.tsx +10 -4
  56. package/src/registry/ui/dropdown-menu.tsx +529 -530
  57. package/src/registry/ui/empty-state.tsx +0 -2
  58. package/src/registry/ui/file-upload.tsx +0 -0
  59. package/src/registry/ui/floating-dock.tsx +0 -0
  60. package/src/registry/ui/form-field.tsx +91 -94
  61. package/src/registry/ui/google-analytics.tsx +38 -0
  62. package/src/registry/ui/google-tag-manager.tsx +64 -0
  63. package/src/registry/ui/hover-card.tsx +223 -223
  64. package/src/registry/ui/image.tsx +144 -147
  65. package/src/registry/ui/input-group.tsx +82 -85
  66. package/src/registry/ui/input.tsx +125 -125
  67. package/src/registry/ui/kbd.tsx +60 -63
  68. package/src/registry/ui/label.tsx +36 -37
  69. package/src/registry/ui/loading-spinner.tsx +108 -111
  70. package/src/registry/ui/map.tsx +0 -0
  71. package/src/registry/ui/marquee.tsx +2 -0
  72. package/src/registry/ui/menubar.tsx +246 -246
  73. package/src/registry/ui/meta-pixel.tsx +46 -0
  74. package/src/registry/ui/microsoft-clarity.tsx +33 -0
  75. package/src/registry/ui/native-select.tsx +49 -52
  76. package/src/registry/ui/otp-input.tsx +152 -155
  77. package/src/registry/ui/pagination.tsx +149 -152
  78. package/src/registry/ui/patterns.tsx +28 -0
  79. package/src/registry/ui/popover.tsx +226 -227
  80. package/src/registry/ui/progress.tsx +51 -52
  81. package/src/registry/ui/radio.tsx +99 -102
  82. package/src/registry/ui/resizable.tsx +314 -314
  83. package/src/registry/ui/scroll-animation.tsx +45 -0
  84. package/src/registry/ui/scroll-area.tsx +121 -122
  85. package/src/registry/ui/scroll-to-top.tsx +0 -0
  86. package/src/registry/ui/search.tsx +147 -150
  87. package/src/registry/ui/select.tsx +292 -293
  88. package/src/registry/ui/separator.tsx +46 -47
  89. package/src/registry/ui/sheet.tsx +6 -3
  90. package/src/registry/ui/sidebar.tsx +628 -628
  91. package/src/registry/ui/skeleton.tsx +26 -29
  92. package/src/registry/ui/slider.tsx +196 -197
  93. package/src/registry/ui/slot.tsx +69 -72
  94. package/src/registry/ui/star-rating.tsx +131 -134
  95. package/src/registry/ui/switch.tsx +72 -73
  96. package/src/registry/ui/table-of-contents.tsx +96 -96
  97. package/src/registry/ui/table.tsx +138 -139
  98. package/src/registry/ui/tabs.tsx +124 -125
  99. package/src/registry/ui/text.tsx +61 -64
  100. package/src/registry/ui/textarea.tsx +41 -42
  101. package/src/registry/ui/theme-switcher.tsx +66 -66
  102. package/src/registry/ui/tiktok-pixel.tsx +36 -0
  103. package/src/registry/ui/toast.tsx +97 -98
  104. package/src/registry/ui/toggle-group.tsx +129 -129
  105. package/src/registry/ui/toggle.tsx +72 -72
  106. package/src/registry/ui/tooltip.tsx +143 -144
  107. package/src/registry/ui/whatsapp.tsx +0 -0
@@ -1,52 +1,49 @@
1
- "use client"
2
-
3
- import * as React from "react"
4
- import { cn } from "@/lib/utils"
5
-
6
- export interface NativeSelectProps extends React.SelectHTMLAttributes<HTMLSelectElement> { }
7
-
8
- /**
9
- * NativeSelect - Styled browser-native select element
10
- *
11
- * Uses the browser's native <select> for accessibility and mobile UX,
12
- * with custom styling to match the design system.
13
- */
14
- const NativeSelect = React.forwardRef<HTMLSelectElement, NativeSelectProps>(
15
- ({ className, children, ...props }, ref) => {
16
- return (
17
- <div className="relative">
18
- <select
19
- ref={ref}
20
- className={cn(
21
- "flex h-10 w-full appearance-none rounded-md border border-input bg-background px-3 py-2 pr-8 text-sm ring-offset-background",
22
- "focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
23
- "disabled:cursor-not-allowed disabled:opacity-50",
24
- className
25
- )}
26
- {...props}
27
- >
28
- {children}
29
- </select>
30
- {/* Custom chevron icon */}
31
- <div className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2">
32
- <svg
33
- className="h-4 w-4 opacity-50"
34
- xmlns="http://www.w3.org/2000/svg"
35
- viewBox="0 0 24 24"
36
- fill="none"
37
- stroke="currentColor"
38
- strokeWidth="2"
39
- strokeLinecap="round"
40
- strokeLinejoin="round"
41
- >
42
- <path d="m6 9 6 6 6-6" />
43
- </svg>
44
- </div>
45
- </div>
46
- )
47
- }
48
- )
49
- NativeSelect.displayName = "NativeSelect"
50
-
51
- export { NativeSelect }
52
-
1
+ import * as React from "react"
2
+ import { cn } from "@/lib/utils"
3
+
4
+ export interface NativeSelectProps extends React.SelectHTMLAttributes<HTMLSelectElement> { }
5
+
6
+ /**
7
+ * NativeSelect - Styled browser-native select element
8
+ *
9
+ * Uses the browser's native <select> for accessibility and mobile UX,
10
+ * with custom styling to match the design system.
11
+ */
12
+ const NativeSelect = React.forwardRef<HTMLSelectElement, NativeSelectProps>(
13
+ ({ className, children, ...props }, ref) => {
14
+ return (
15
+ <div className="relative">
16
+ <select
17
+ ref={ref}
18
+ className={cn(
19
+ "flex h-10 w-full appearance-none rounded-md border border-input bg-background px-3 py-2 pr-8 text-sm ring-offset-background",
20
+ "focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
21
+ "disabled:cursor-not-allowed disabled:opacity-50",
22
+ className
23
+ )}
24
+ {...props}
25
+ >
26
+ {children}
27
+ </select>
28
+ {/* Custom chevron icon */}
29
+ <div className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2">
30
+ <svg
31
+ className="h-4 w-4 opacity-50"
32
+ xmlns="http://www.w3.org/2000/svg"
33
+ viewBox="0 0 24 24"
34
+ fill="none"
35
+ stroke="currentColor"
36
+ strokeWidth="2"
37
+ strokeLinecap="round"
38
+ strokeLinejoin="round"
39
+ >
40
+ <path d="m6 9 6 6 6-6" />
41
+ </svg>
42
+ </div>
43
+ </div>
44
+ )
45
+ }
46
+ )
47
+ NativeSelect.displayName = "NativeSelect"
48
+
49
+ export { NativeSelect }
@@ -1,155 +1,152 @@
1
- "use client"
2
-
3
- import * as React from "react"
4
- import { cn } from "@/lib/utils"
5
-
6
- interface OtpInputProps {
7
- /** Number of OTP digits */
8
- length?: number
9
- /** Callback when OTP is complete */
10
- onComplete?: (otp: string) => void
11
- /** Callback when value changes */
12
- onChange?: (value: string) => void
13
- /** Current value */
14
- value?: string
15
- /** Whether input is disabled */
16
- disabled?: boolean
17
- /** Auto focus first input */
18
- autoFocus?: boolean
19
- /** Input type (number or password for hidden) */
20
- type?: "number" | "password"
21
- className?: string
22
- }
23
-
24
- /**
25
- * OTP Input component with auto-advance
26
- *
27
- * @example
28
- * const [otp, setOtp] = useState("")
29
- * <OtpInput
30
- * length={6}
31
- * value={otp}
32
- * onChange={setOtp}
33
- * onComplete={(code) => verifyOtp(code)}
34
- * />
35
- */
36
- const OtpInput = React.forwardRef<HTMLDivElement, OtpInputProps>(
37
- (
38
- {
39
- length = 6,
40
- onComplete,
41
- onChange,
42
- value = "",
43
- disabled,
44
- autoFocus,
45
- type = "number",
46
- className,
47
- },
48
- ref
49
- ) => {
50
- const inputRefs = React.useRef<(HTMLInputElement | null)[]>([])
51
- const [values, setValues] = React.useState<string[]>(
52
- value.split("").concat(Array(length - value.length).fill(""))
53
- )
54
-
55
- React.useEffect(() => {
56
- const newValues = value.split("").concat(Array(length - value.length).fill(""))
57
- setValues(newValues.slice(0, length))
58
- }, [value, length])
59
-
60
- const focusInput = (index: number) => {
61
- if (index >= 0 && index < length) {
62
- inputRefs.current[index]?.focus()
63
- }
64
- }
65
-
66
- const handleChange = (index: number, inputValue: string) => {
67
- if (disabled) return
68
-
69
- // Only allow single digit
70
- const digit = inputValue.slice(-1)
71
- if (type === "number" && digit && !/^\d$/.test(digit)) return
72
-
73
- const newValues = [...values]
74
- newValues[index] = digit
75
- setValues(newValues)
76
-
77
- const newOtp = newValues.join("")
78
- onChange?.(newOtp)
79
-
80
- // Auto-advance to next input
81
- if (digit && index < length - 1) {
82
- focusInput(index + 1)
83
- }
84
-
85
- // Check if complete
86
- if (newOtp.length === length && !newOtp.includes("")) {
87
- onComplete?.(newOtp)
88
- }
89
- }
90
-
91
- const handleKeyDown = (index: number, e: React.KeyboardEvent<HTMLInputElement>) => {
92
- if (e.key === "Backspace") {
93
- if (!values[index] && index > 0) {
94
- focusInput(index - 1)
95
- }
96
- } else if (e.key === "ArrowLeft") {
97
- e.preventDefault()
98
- focusInput(index - 1)
99
- } else if (e.key === "ArrowRight") {
100
- e.preventDefault()
101
- focusInput(index + 1)
102
- }
103
- }
104
-
105
- const handlePaste = (e: React.ClipboardEvent) => {
106
- e.preventDefault()
107
- const pastedData = e.clipboardData.getData("text").slice(0, length)
108
-
109
- if (type === "number" && !/^\d+$/.test(pastedData)) return
110
-
111
- const newValues = pastedData.split("").concat(Array(length - pastedData.length).fill(""))
112
- setValues(newValues.slice(0, length))
113
-
114
- const newOtp = newValues.slice(0, length).join("")
115
- onChange?.(newOtp)
116
-
117
- if (newOtp.length === length) {
118
- onComplete?.(newOtp)
119
- }
120
-
121
- focusInput(Math.min(pastedData.length, length - 1))
122
- }
123
-
124
- return (
125
- <div ref={ref} className={cn("flex gap-2", className)}>
126
- {Array.from({ length }).map((_, index) => (
127
- <input
128
- key={index}
129
- ref={(el) => { inputRefs.current[index] = el }}
130
- type={type === "password" ? "password" : "text"}
131
- inputMode="numeric"
132
- maxLength={1}
133
- value={values[index] || ""}
134
- onChange={(e) => handleChange(index, e.target.value)}
135
- onKeyDown={(e) => handleKeyDown(index, e)}
136
- onPaste={handlePaste}
137
- onFocus={(e) => e.target.select()}
138
- disabled={disabled}
139
- autoFocus={autoFocus && index === 0}
140
- className={cn(
141
- "h-12 w-12 rounded-md border border-input bg-transparent text-center text-lg font-semibold shadow-sm transition-colors",
142
- "focus:outline-none focus:ring-2 focus:ring-ring focus:border-transparent",
143
- "disabled:cursor-not-allowed disabled:opacity-50"
144
- )}
145
- aria-label={`Digit ${index + 1} of ${length}`}
146
- />
147
- ))}
148
- </div>
149
- )
150
- }
151
- )
152
- OtpInput.displayName = "OtpInput"
153
-
154
- export { OtpInput }
155
-
1
+ import * as React from "react"
2
+ import { cn } from "@/lib/utils"
3
+
4
+ interface OtpInputProps {
5
+ /** Number of OTP digits */
6
+ length?: number
7
+ /** Callback when OTP is complete */
8
+ onComplete?: (otp: string) => void
9
+ /** Callback when value changes */
10
+ onChange?: (value: string) => void
11
+ /** Current value */
12
+ value?: string
13
+ /** Whether input is disabled */
14
+ disabled?: boolean
15
+ /** Auto focus first input */
16
+ autoFocus?: boolean
17
+ /** Input type (number or password for hidden) */
18
+ type?: "number" | "password"
19
+ className?: string
20
+ }
21
+
22
+ /**
23
+ * OTP Input component with auto-advance
24
+ *
25
+ * @example
26
+ * const [otp, setOtp] = useState("")
27
+ * <OtpInput
28
+ * length={6}
29
+ * value={otp}
30
+ * onChange={setOtp}
31
+ * onComplete={(code) => verifyOtp(code)}
32
+ * />
33
+ */
34
+ const OtpInput = React.forwardRef<HTMLDivElement, OtpInputProps>(
35
+ (
36
+ {
37
+ length = 6,
38
+ onComplete,
39
+ onChange,
40
+ value = "",
41
+ disabled,
42
+ autoFocus,
43
+ type = "number",
44
+ className,
45
+ },
46
+ ref
47
+ ) => {
48
+ const inputRefs = React.useRef<(HTMLInputElement | null)[]>([])
49
+ const [values, setValues] = React.useState<string[]>(
50
+ value.split("").concat(Array(length - value.length).fill(""))
51
+ )
52
+
53
+ React.useEffect(() => {
54
+ const newValues = value.split("").concat(Array(length - value.length).fill(""))
55
+ setValues(newValues.slice(0, length))
56
+ }, [value, length])
57
+
58
+ const focusInput = (index: number) => {
59
+ if (index >= 0 && index < length) {
60
+ inputRefs.current[index]?.focus()
61
+ }
62
+ }
63
+
64
+ const handleChange = (index: number, inputValue: string) => {
65
+ if (disabled) return
66
+
67
+ // Only allow single digit
68
+ const digit = inputValue.slice(-1)
69
+ if (type === "number" && digit && !/^\d$/.test(digit)) return
70
+
71
+ const newValues = [...values]
72
+ newValues[index] = digit
73
+ setValues(newValues)
74
+
75
+ const newOtp = newValues.join("")
76
+ onChange?.(newOtp)
77
+
78
+ // Auto-advance to next input
79
+ if (digit && index < length - 1) {
80
+ focusInput(index + 1)
81
+ }
82
+
83
+ // Check if complete
84
+ if (newOtp.length === length && !newOtp.includes("")) {
85
+ onComplete?.(newOtp)
86
+ }
87
+ }
88
+
89
+ const handleKeyDown = (index: number, e: React.KeyboardEvent<HTMLInputElement>) => {
90
+ if (e.key === "Backspace") {
91
+ if (!values[index] && index > 0) {
92
+ focusInput(index - 1)
93
+ }
94
+ } else if (e.key === "ArrowLeft") {
95
+ e.preventDefault()
96
+ focusInput(index - 1)
97
+ } else if (e.key === "ArrowRight") {
98
+ e.preventDefault()
99
+ focusInput(index + 1)
100
+ }
101
+ }
102
+
103
+ const handlePaste = (e: React.ClipboardEvent) => {
104
+ e.preventDefault()
105
+ const pastedData = e.clipboardData.getData("text").slice(0, length)
106
+
107
+ if (type === "number" && !/^\d+$/.test(pastedData)) return
108
+
109
+ const newValues = pastedData.split("").concat(Array(length - pastedData.length).fill(""))
110
+ setValues(newValues.slice(0, length))
111
+
112
+ const newOtp = newValues.slice(0, length).join("")
113
+ onChange?.(newOtp)
114
+
115
+ if (newOtp.length === length) {
116
+ onComplete?.(newOtp)
117
+ }
118
+
119
+ focusInput(Math.min(pastedData.length, length - 1))
120
+ }
121
+
122
+ return (
123
+ <div ref={ref} className={cn("flex gap-2", className)}>
124
+ {Array.from({ length }).map((_, index) => (
125
+ <input
126
+ key={index}
127
+ ref={(el) => { inputRefs.current[index] = el }}
128
+ type={type === "password" ? "password" : "text"}
129
+ inputMode="numeric"
130
+ maxLength={1}
131
+ value={values[index] || ""}
132
+ onChange={(e) => handleChange(index, e.target.value)}
133
+ onKeyDown={(e) => handleKeyDown(index, e)}
134
+ onPaste={handlePaste}
135
+ onFocus={(e) => e.target.select()}
136
+ disabled={disabled}
137
+ autoFocus={autoFocus && index === 0}
138
+ className={cn(
139
+ "h-12 w-12 rounded-md border border-input bg-transparent text-center text-lg font-semibold shadow-sm transition-colors",
140
+ "focus:outline-none focus:ring-2 focus:ring-ring focus:border-transparent",
141
+ "disabled:cursor-not-allowed disabled:opacity-50"
142
+ )}
143
+ aria-label={`Digit ${index + 1} of ${length}`}
144
+ />
145
+ ))}
146
+ </div>
147
+ )
148
+ }
149
+ )
150
+ OtpInput.displayName = "OtpInput"
151
+
152
+ export { OtpInput }