@questpie/admin 0.0.1 → 1.0.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 (250) hide show
  1. package/README.md +439 -424
  2. package/dist/auth-layout-M8K8_q5R.mjs +181 -0
  3. package/dist/auth-layout-M8K8_q5R.mjs.map +1 -0
  4. package/dist/bulk-upload-dialog-h7zXD78Y.mjs +274 -0
  5. package/dist/bulk-upload-dialog-h7zXD78Y.mjs.map +1 -0
  6. package/dist/{components/ui/card.mjs → card-BKHjBQfw.mjs} +8 -8
  7. package/dist/card-BKHjBQfw.mjs.map +1 -0
  8. package/dist/client/styles/index.css +434 -0
  9. package/dist/client-BCGpkAz6.mjs +22635 -0
  10. package/dist/client-BCGpkAz6.mjs.map +1 -0
  11. package/dist/client-CcWZbkBP.d.mts +13585 -0
  12. package/dist/client-CcWZbkBP.d.mts.map +1 -0
  13. package/dist/client.d.mts +3 -0
  14. package/dist/client.mjs +14 -0
  15. package/dist/content-locales-provider-BXvuIgfg.mjs +1650 -0
  16. package/dist/content-locales-provider-BXvuIgfg.mjs.map +1 -0
  17. package/dist/dashboard-page-B4PGEdc2.mjs +2500 -0
  18. package/dist/dashboard-page-B4PGEdc2.mjs.map +1 -0
  19. package/dist/dashboard-page-CVlyR40m.mjs +6 -0
  20. package/dist/dropzone-Do3awXKd.mjs +634 -0
  21. package/dist/dropzone-Do3awXKd.mjs.map +1 -0
  22. package/dist/{views/auth/forgot-password-form.mjs → forgot-password-page-Bcp-An4Y.mjs} +87 -14
  23. package/dist/forgot-password-page-Bcp-An4Y.mjs.map +1 -0
  24. package/dist/forgot-password-page-CIILVhfo.mjs +7 -0
  25. package/dist/index-B9Xwk4hi.d.mts +2753 -0
  26. package/dist/index-B9Xwk4hi.d.mts.map +1 -0
  27. package/dist/index.d.mts +3 -0
  28. package/dist/index.mjs +14 -0
  29. package/dist/login-page-8K7fo0qK.mjs +7 -0
  30. package/dist/login-page-CP4gA-dl.mjs +298 -0
  31. package/dist/login-page-CP4gA-dl.mjs.map +1 -0
  32. package/dist/preview-utils-BKQ9-TMa.mjs +65 -0
  33. package/dist/preview-utils-BKQ9-TMa.mjs.map +1 -0
  34. package/dist/{views/auth/reset-password-form.mjs → reset-password-page-BqfDmLxA.mjs} +111 -14
  35. package/dist/reset-password-page-BqfDmLxA.mjs.map +1 -0
  36. package/dist/reset-password-page-DLATv0xQ.mjs +7 -0
  37. package/dist/runtime-6VZM878K.mjs +69 -0
  38. package/dist/runtime-6VZM878K.mjs.map +1 -0
  39. package/dist/saved-views.types-BMsz5mCy.d.mts +42 -0
  40. package/dist/saved-views.types-BMsz5mCy.d.mts.map +1 -0
  41. package/dist/server.d.mts +250 -0
  42. package/dist/server.d.mts.map +1 -0
  43. package/dist/server.mjs +832 -0
  44. package/dist/server.mjs.map +1 -0
  45. package/dist/setup-page-CMZ5P_OE.mjs +6 -0
  46. package/dist/setup-page-YAP_fzqh.mjs +264 -0
  47. package/dist/setup-page-YAP_fzqh.mjs.map +1 -0
  48. package/dist/shared.d.mts +57 -0
  49. package/dist/shared.d.mts.map +1 -0
  50. package/dist/shared.mjs +3 -0
  51. package/dist/{hooks/use-auth.mjs → use-auth-BoLmWtmU.mjs} +42 -30
  52. package/dist/use-auth-BoLmWtmU.mjs.map +1 -0
  53. package/package.json +48 -197
  54. package/.turbo/turbo-build.log +0 -108
  55. package/CHANGELOG.md +0 -10
  56. package/STATUS.md +0 -917
  57. package/VALIDATION.md +0 -602
  58. package/components.json +0 -24
  59. package/dist/__tests__/setup.mjs +0 -38
  60. package/dist/__tests__/test-utils.mjs +0 -45
  61. package/dist/__tests__/vitest.d.mjs +0 -3
  62. package/dist/components/admin-app.mjs +0 -69
  63. package/dist/components/fields/array-field.mjs +0 -190
  64. package/dist/components/fields/checkbox-field.mjs +0 -34
  65. package/dist/components/fields/custom-field.mjs +0 -32
  66. package/dist/components/fields/date-field.mjs +0 -41
  67. package/dist/components/fields/datetime-field.mjs +0 -42
  68. package/dist/components/fields/email-field.mjs +0 -37
  69. package/dist/components/fields/embedded-collection.mjs +0 -253
  70. package/dist/components/fields/field-types.mjs +0 -1
  71. package/dist/components/fields/field-utils.mjs +0 -10
  72. package/dist/components/fields/field-wrapper.mjs +0 -34
  73. package/dist/components/fields/index.mjs +0 -23
  74. package/dist/components/fields/json-field.mjs +0 -243
  75. package/dist/components/fields/locale-badge.mjs +0 -16
  76. package/dist/components/fields/number-field.mjs +0 -39
  77. package/dist/components/fields/password-field.mjs +0 -37
  78. package/dist/components/fields/relation-field.mjs +0 -104
  79. package/dist/components/fields/relation-picker.mjs +0 -229
  80. package/dist/components/fields/relation-select.mjs +0 -188
  81. package/dist/components/fields/rich-text-editor/index.mjs +0 -897
  82. package/dist/components/fields/select-field.mjs +0 -41
  83. package/dist/components/fields/switch-field.mjs +0 -34
  84. package/dist/components/fields/text-field.mjs +0 -38
  85. package/dist/components/fields/textarea-field.mjs +0 -38
  86. package/dist/components/index.mjs +0 -59
  87. package/dist/components/primitives/checkbox-input.mjs +0 -127
  88. package/dist/components/primitives/date-input.mjs +0 -303
  89. package/dist/components/primitives/index.mjs +0 -12
  90. package/dist/components/primitives/number-input.mjs +0 -104
  91. package/dist/components/primitives/select-input.mjs +0 -177
  92. package/dist/components/primitives/tag-input.mjs +0 -135
  93. package/dist/components/primitives/text-input.mjs +0 -39
  94. package/dist/components/primitives/textarea-input.mjs +0 -37
  95. package/dist/components/primitives/toggle-input.mjs +0 -31
  96. package/dist/components/primitives/types.mjs +0 -12
  97. package/dist/components/ui/accordion.mjs +0 -55
  98. package/dist/components/ui/avatar.mjs +0 -54
  99. package/dist/components/ui/badge.mjs +0 -34
  100. package/dist/components/ui/button.mjs +0 -48
  101. package/dist/components/ui/checkbox.mjs +0 -21
  102. package/dist/components/ui/combobox.mjs +0 -163
  103. package/dist/components/ui/dialog.mjs +0 -95
  104. package/dist/components/ui/dropdown-menu.mjs +0 -138
  105. package/dist/components/ui/field.mjs +0 -113
  106. package/dist/components/ui/input-group.mjs +0 -82
  107. package/dist/components/ui/input.mjs +0 -17
  108. package/dist/components/ui/label.mjs +0 -15
  109. package/dist/components/ui/popover.mjs +0 -56
  110. package/dist/components/ui/scroll-area.mjs +0 -38
  111. package/dist/components/ui/select.mjs +0 -100
  112. package/dist/components/ui/separator.mjs +0 -16
  113. package/dist/components/ui/sheet.mjs +0 -90
  114. package/dist/components/ui/sidebar.mjs +0 -387
  115. package/dist/components/ui/skeleton.mjs +0 -14
  116. package/dist/components/ui/spinner.mjs +0 -16
  117. package/dist/components/ui/switch.mjs +0 -22
  118. package/dist/components/ui/table.mjs +0 -68
  119. package/dist/components/ui/tabs.mjs +0 -48
  120. package/dist/components/ui/textarea.mjs +0 -15
  121. package/dist/components/ui/tooltip.mjs +0 -44
  122. package/dist/config/component-registry.mjs +0 -38
  123. package/dist/config/index.mjs +0 -129
  124. package/dist/hooks/admin-provider.mjs +0 -70
  125. package/dist/hooks/index.mjs +0 -7
  126. package/dist/hooks/store.mjs +0 -178
  127. package/dist/hooks/use-collection-db.mjs +0 -146
  128. package/dist/hooks/use-collection.mjs +0 -112
  129. package/dist/hooks/use-global.mjs +0 -46
  130. package/dist/hooks/use-mobile.mjs +0 -20
  131. package/dist/lib/utils.mjs +0 -10
  132. package/dist/styles/index.css +0 -336
  133. package/dist/styles/index.mjs +0 -1
  134. package/dist/utils/index.mjs +0 -9
  135. package/dist/views/auth/auth-layout.mjs +0 -52
  136. package/dist/views/auth/index.mjs +0 -6
  137. package/dist/views/auth/login-form.mjs +0 -156
  138. package/dist/views/collection/auto-form-fields.mjs +0 -525
  139. package/dist/views/collection/collection-form.mjs +0 -91
  140. package/dist/views/collection/collection-list.mjs +0 -76
  141. package/dist/views/collection/form-field.mjs +0 -42
  142. package/dist/views/collection/index.mjs +0 -6
  143. package/dist/views/common/index.mjs +0 -4
  144. package/dist/views/common/locale-switcher.mjs +0 -39
  145. package/dist/views/common/version-history.mjs +0 -272
  146. package/dist/views/index.mjs +0 -9
  147. package/dist/views/layout/admin-layout.mjs +0 -40
  148. package/dist/views/layout/admin-router.mjs +0 -95
  149. package/dist/views/layout/admin-sidebar.mjs +0 -63
  150. package/dist/views/layout/index.mjs +0 -5
  151. package/src/__tests__/setup.ts +0 -44
  152. package/src/__tests__/test-utils.tsx +0 -49
  153. package/src/__tests__/vitest.d.ts +0 -9
  154. package/src/components/admin-app.tsx +0 -221
  155. package/src/components/fields/array-field.tsx +0 -237
  156. package/src/components/fields/checkbox-field.tsx +0 -47
  157. package/src/components/fields/custom-field.tsx +0 -50
  158. package/src/components/fields/date-field.tsx +0 -65
  159. package/src/components/fields/datetime-field.tsx +0 -67
  160. package/src/components/fields/email-field.tsx +0 -51
  161. package/src/components/fields/embedded-collection.tsx +0 -315
  162. package/src/components/fields/field-types.ts +0 -162
  163. package/src/components/fields/field-utils.ts +0 -6
  164. package/src/components/fields/field-wrapper.tsx +0 -52
  165. package/src/components/fields/index.ts +0 -66
  166. package/src/components/fields/json-field.tsx +0 -440
  167. package/src/components/fields/locale-badge.tsx +0 -15
  168. package/src/components/fields/number-field.tsx +0 -57
  169. package/src/components/fields/password-field.tsx +0 -51
  170. package/src/components/fields/relation-field.tsx +0 -243
  171. package/src/components/fields/relation-picker.tsx +0 -402
  172. package/src/components/fields/relation-select.tsx +0 -327
  173. package/src/components/fields/rich-text-editor/index.tsx +0 -1337
  174. package/src/components/fields/select-field.tsx +0 -61
  175. package/src/components/fields/switch-field.tsx +0 -47
  176. package/src/components/fields/text-field.tsx +0 -55
  177. package/src/components/fields/textarea-field.tsx +0 -55
  178. package/src/components/index.ts +0 -40
  179. package/src/components/primitives/checkbox-input.tsx +0 -193
  180. package/src/components/primitives/date-input.tsx +0 -401
  181. package/src/components/primitives/index.ts +0 -24
  182. package/src/components/primitives/number-input.tsx +0 -132
  183. package/src/components/primitives/select-input.tsx +0 -296
  184. package/src/components/primitives/tag-input.tsx +0 -200
  185. package/src/components/primitives/text-input.tsx +0 -49
  186. package/src/components/primitives/textarea-input.tsx +0 -46
  187. package/src/components/primitives/toggle-input.tsx +0 -36
  188. package/src/components/primitives/types.ts +0 -235
  189. package/src/components/ui/accordion.tsx +0 -72
  190. package/src/components/ui/avatar.tsx +0 -106
  191. package/src/components/ui/badge.tsx +0 -48
  192. package/src/components/ui/button.tsx +0 -53
  193. package/src/components/ui/card.tsx +0 -94
  194. package/src/components/ui/checkbox.tsx +0 -27
  195. package/src/components/ui/combobox.tsx +0 -290
  196. package/src/components/ui/dialog.tsx +0 -151
  197. package/src/components/ui/dropdown-menu.tsx +0 -254
  198. package/src/components/ui/field.tsx +0 -227
  199. package/src/components/ui/input-group.tsx +0 -149
  200. package/src/components/ui/input.tsx +0 -20
  201. package/src/components/ui/label.tsx +0 -18
  202. package/src/components/ui/popover.tsx +0 -88
  203. package/src/components/ui/scroll-area.tsx +0 -53
  204. package/src/components/ui/select.tsx +0 -192
  205. package/src/components/ui/separator.tsx +0 -23
  206. package/src/components/ui/sheet.tsx +0 -127
  207. package/src/components/ui/sidebar.tsx +0 -723
  208. package/src/components/ui/skeleton.tsx +0 -13
  209. package/src/components/ui/spinner.tsx +0 -10
  210. package/src/components/ui/switch.tsx +0 -32
  211. package/src/components/ui/table.tsx +0 -99
  212. package/src/components/ui/tabs.tsx +0 -82
  213. package/src/components/ui/textarea.tsx +0 -18
  214. package/src/components/ui/tooltip.tsx +0 -70
  215. package/src/config/component-registry.ts +0 -190
  216. package/src/config/index.ts +0 -1099
  217. package/src/hooks/README.md +0 -269
  218. package/src/hooks/admin-provider.tsx +0 -110
  219. package/src/hooks/index.ts +0 -41
  220. package/src/hooks/store.ts +0 -248
  221. package/src/hooks/use-auth.ts +0 -168
  222. package/src/hooks/use-collection-db.ts +0 -209
  223. package/src/hooks/use-collection.ts +0 -156
  224. package/src/hooks/use-global.ts +0 -69
  225. package/src/hooks/use-mobile.ts +0 -21
  226. package/src/lib/utils.ts +0 -6
  227. package/src/styles/index.css +0 -340
  228. package/src/utils/index.ts +0 -6
  229. package/src/views/auth/auth-layout.tsx +0 -77
  230. package/src/views/auth/forgot-password-form.tsx +0 -192
  231. package/src/views/auth/index.ts +0 -21
  232. package/src/views/auth/login-form.tsx +0 -229
  233. package/src/views/auth/reset-password-form.tsx +0 -232
  234. package/src/views/collection/auto-form-fields.tsx +0 -982
  235. package/src/views/collection/collection-form.tsx +0 -186
  236. package/src/views/collection/collection-list.tsx +0 -223
  237. package/src/views/collection/form-field.tsx +0 -52
  238. package/src/views/collection/index.ts +0 -15
  239. package/src/views/common/index.ts +0 -8
  240. package/src/views/common/locale-switcher.tsx +0 -45
  241. package/src/views/common/version-history.tsx +0 -406
  242. package/src/views/index.ts +0 -25
  243. package/src/views/layout/admin-layout.tsx +0 -117
  244. package/src/views/layout/admin-router.tsx +0 -206
  245. package/src/views/layout/admin-sidebar.tsx +0 -185
  246. package/src/views/layout/index.ts +0 -12
  247. package/tsconfig.json +0 -13
  248. package/tsconfig.tsbuildinfo +0 -1
  249. package/tsdown.config.ts +0 -13
  250. package/vitest.config.ts +0 -29
@@ -1,401 +0,0 @@
1
- "use client";
2
-
3
- import { useState } from "react";
4
- import { DayPicker } from "react-day-picker";
5
- import { format } from "date-fns";
6
- import { CalendarBlank, X } from "@phosphor-icons/react";
7
- import {
8
- Popover,
9
- PopoverContent,
10
- PopoverTrigger,
11
- } from "../ui/popover";
12
- import { cn } from "../../lib/utils";
13
- import type {
14
- DateInputProps,
15
- DateTimeInputProps,
16
- DateRangeInputProps,
17
- } from "./types";
18
-
19
- /**
20
- * Date Input Primitive
21
- *
22
- * A date picker with popover calendar.
23
- * Uses react-day-picker under the hood.
24
- *
25
- * @example
26
- * ```tsx
27
- * <DateInput
28
- * value={selectedDate}
29
- * onChange={setSelectedDate}
30
- * placeholder="Select date"
31
- * />
32
- * ```
33
- */
34
- export function DateInput({
35
- value,
36
- onChange,
37
- minDate,
38
- maxDate,
39
- format: dateFormat = "PP",
40
- placeholder = "Select date",
41
- disabled,
42
- className,
43
- id,
44
- "aria-invalid": ariaInvalid,
45
- }: DateInputProps) {
46
- const [open, setOpen] = useState(false);
47
-
48
- const handleSelect = (date: Date | undefined) => {
49
- onChange(date ?? null);
50
- setOpen(false);
51
- };
52
-
53
- const handleClear = (e: React.MouseEvent) => {
54
- e.stopPropagation();
55
- onChange(null);
56
- };
57
-
58
- return (
59
- <Popover open={open} onOpenChange={setOpen}>
60
- <PopoverTrigger
61
- id={id}
62
- disabled={disabled}
63
- aria-invalid={ariaInvalid}
64
- className={cn(
65
- "flex h-9 w-full items-center justify-start gap-2 border border-input bg-background px-3 py-2 text-sm",
66
- "hover:bg-accent hover:text-accent-foreground",
67
- "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
68
- "disabled:cursor-not-allowed disabled:opacity-50",
69
- !value && "text-muted-foreground",
70
- className,
71
- )}
72
- >
73
- <CalendarBlank className="size-4" />
74
- <span className="flex-1 text-left">
75
- {value ? format(value, dateFormat) : placeholder}
76
- </span>
77
- {value && !disabled && (
78
- <X
79
- className="size-4 opacity-50 hover:opacity-100"
80
- onClick={handleClear}
81
- />
82
- )}
83
- </PopoverTrigger>
84
- <PopoverContent className="w-auto p-0" align="start">
85
- <DayPicker
86
- mode="single"
87
- selected={value ?? undefined}
88
- onSelect={handleSelect}
89
- disabled={(date) => {
90
- if (minDate && date < minDate) return true;
91
- if (maxDate && date > maxDate) return true;
92
- return false;
93
- }}
94
- className="p-3"
95
- classNames={{
96
- months: "flex flex-col sm:flex-row gap-2",
97
- month: "flex flex-col gap-4",
98
- month_caption:
99
- "flex justify-center pt-1 relative items-center w-full",
100
- caption_label: "text-sm font-medium",
101
- nav: "flex items-center gap-1",
102
- button_previous:
103
- "absolute left-1 size-7 bg-transparent p-0 opacity-50 hover:opacity-100",
104
- button_next:
105
- "absolute right-1 size-7 bg-transparent p-0 opacity-50 hover:opacity-100",
106
- month_grid: "w-full border-collapse",
107
- weekdays: "flex",
108
- weekday: "text-muted-foreground w-8 font-normal text-[0.8rem]",
109
- week: "flex w-full mt-2",
110
- day: "relative p-0 text-center text-sm focus-within:relative focus-within:z-20 [&:has([aria-selected])]:bg-accent [&:has([aria-selected].day-outside)]:bg-accent/50",
111
- day_button: cn(
112
- "size-8 p-0 font-normal",
113
- "hover:bg-accent hover:text-accent-foreground",
114
- "focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
115
- ),
116
- selected:
117
- "bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground",
118
- today: "bg-accent text-accent-foreground",
119
- outside: "text-muted-foreground opacity-50",
120
- disabled: "text-muted-foreground opacity-50",
121
- hidden: "invisible",
122
- }}
123
- />
124
- </PopoverContent>
125
- </Popover>
126
- );
127
- }
128
-
129
- /**
130
- * DateTime Input Primitive
131
- *
132
- * A date and time picker.
133
- *
134
- * @example
135
- * ```tsx
136
- * <DateTimeInput
137
- * value={scheduledAt}
138
- * onChange={setScheduledAt}
139
- * precision="minute"
140
- * />
141
- * ```
142
- */
143
- export function DateTimeInput({
144
- value,
145
- onChange,
146
- minDate,
147
- maxDate,
148
- format: dateFormat = "PPp",
149
- precision = "minute",
150
- placeholder = "Select date and time",
151
- disabled,
152
- className,
153
- id,
154
- "aria-invalid": ariaInvalid,
155
- }: DateTimeInputProps) {
156
- const [open, setOpen] = useState(false);
157
- const [timeValue, setTimeValue] = useState(() => {
158
- if (!value) return "";
159
- return precision === "second"
160
- ? format(value, "HH:mm:ss")
161
- : format(value, "HH:mm");
162
- });
163
-
164
- const handleDateSelect = (date: Date | undefined) => {
165
- if (!date) {
166
- onChange(null);
167
- return;
168
- }
169
-
170
- // Preserve existing time if available
171
- if (timeValue) {
172
- const [hours, minutes, seconds] = timeValue.split(":").map(Number);
173
- date.setHours(hours || 0, minutes || 0, seconds || 0);
174
- }
175
- onChange(date);
176
- };
177
-
178
- const handleTimeChange = (e: React.ChangeEvent<HTMLInputElement>) => {
179
- const time = e.target.value;
180
- setTimeValue(time);
181
-
182
- if (value && time) {
183
- const [hours, minutes, seconds] = time.split(":").map(Number);
184
- const newDate = new Date(value);
185
- newDate.setHours(hours || 0, minutes || 0, seconds || 0);
186
- onChange(newDate);
187
- }
188
- };
189
-
190
- const handleClear = (e: React.MouseEvent) => {
191
- e.stopPropagation();
192
- onChange(null);
193
- setTimeValue("");
194
- };
195
-
196
- return (
197
- <Popover open={open} onOpenChange={setOpen}>
198
- <PopoverTrigger
199
- id={id}
200
- disabled={disabled}
201
- aria-invalid={ariaInvalid}
202
- className={cn(
203
- "flex h-9 w-full items-center justify-start gap-2 border border-input bg-background px-3 py-2 text-sm",
204
- "hover:bg-accent hover:text-accent-foreground",
205
- "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
206
- "disabled:cursor-not-allowed disabled:opacity-50",
207
- !value && "text-muted-foreground",
208
- className,
209
- )}
210
- >
211
- <CalendarBlank className="size-4" />
212
- <span className="flex-1 text-left">
213
- {value ? format(value, dateFormat) : placeholder}
214
- </span>
215
- {value && !disabled && (
216
- <X
217
- className="size-4 opacity-50 hover:opacity-100"
218
- onClick={handleClear}
219
- />
220
- )}
221
- </PopoverTrigger>
222
- <PopoverContent className="w-auto p-0" align="start">
223
- <DayPicker
224
- mode="single"
225
- selected={value ?? undefined}
226
- onSelect={handleDateSelect}
227
- disabled={(date) => {
228
- if (minDate && date < minDate) return true;
229
- if (maxDate && date > maxDate) return true;
230
- return false;
231
- }}
232
- className="p-3"
233
- classNames={{
234
- months: "flex flex-col sm:flex-row gap-2",
235
- month: "flex flex-col gap-4",
236
- month_caption:
237
- "flex justify-center pt-1 relative items-center w-full",
238
- caption_label: "text-sm font-medium",
239
- nav: "flex items-center gap-1",
240
- button_previous:
241
- "absolute left-1 size-7 bg-transparent p-0 opacity-50 hover:opacity-100",
242
- button_next:
243
- "absolute right-1 size-7 bg-transparent p-0 opacity-50 hover:opacity-100",
244
- month_grid: "w-full border-collapse",
245
- weekdays: "flex",
246
- weekday: "text-muted-foreground w-8 font-normal text-[0.8rem]",
247
- week: "flex w-full mt-2",
248
- day: "relative p-0 text-center text-sm focus-within:relative focus-within:z-20 [&:has([aria-selected])]:bg-accent [&:has([aria-selected].day-outside)]:bg-accent/50",
249
- day_button: cn(
250
- "size-8 p-0 font-normal",
251
- "hover:bg-accent hover:text-accent-foreground",
252
- "focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
253
- ),
254
- selected:
255
- "bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground",
256
- today: "bg-accent text-accent-foreground",
257
- outside: "text-muted-foreground opacity-50",
258
- disabled: "text-muted-foreground opacity-50",
259
- hidden: "invisible",
260
- }}
261
- />
262
- <div className="border-t border-border p-3">
263
- <input
264
- type="time"
265
- step={precision === "second" ? 1 : 60}
266
- value={timeValue}
267
- onChange={handleTimeChange}
268
- className={cn(
269
- "flex h-9 w-full border border-input bg-background px-3 py-2 text-sm",
270
- "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
271
- )}
272
- />
273
- </div>
274
- </PopoverContent>
275
- </Popover>
276
- );
277
- }
278
-
279
- /**
280
- * Date Range Input Primitive
281
- *
282
- * A date range picker for selecting start and end dates.
283
- *
284
- * @example
285
- * ```tsx
286
- * <DateRangeInput
287
- * value={{ start: startDate, end: endDate }}
288
- * onChange={setDateRange}
289
- * />
290
- * ```
291
- */
292
- export function DateRangeInput({
293
- value,
294
- onChange,
295
- minDate,
296
- maxDate,
297
- placeholder = "Select date range",
298
- disabled,
299
- className,
300
- id,
301
- "aria-invalid": ariaInvalid,
302
- }: DateRangeInputProps) {
303
- const [open, setOpen] = useState(false);
304
-
305
- const handleSelect = (range: { from?: Date; to?: Date } | undefined) => {
306
- onChange({
307
- start: range?.from ?? null,
308
- end: range?.to ?? null,
309
- });
310
- };
311
-
312
- const handleClear = (e: React.MouseEvent) => {
313
- e.stopPropagation();
314
- onChange({ start: null, end: null });
315
- };
316
-
317
- const displayValue = () => {
318
- if (!value.start && !value.end) return placeholder;
319
- if (value.start && value.end) {
320
- return `${format(value.start, "PP")} - ${format(value.end, "PP")}`;
321
- }
322
- if (value.start) return `${format(value.start, "PP")} - ...`;
323
- return placeholder;
324
- };
325
-
326
- return (
327
- <Popover open={open} onOpenChange={setOpen}>
328
- <PopoverTrigger
329
- id={id}
330
- disabled={disabled}
331
- aria-invalid={ariaInvalid}
332
- className={cn(
333
- "flex h-9 w-full items-center justify-start gap-2 border border-input bg-background px-3 py-2 text-sm",
334
- "hover:bg-accent hover:text-accent-foreground",
335
- "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
336
- "disabled:cursor-not-allowed disabled:opacity-50",
337
- !value.start && !value.end && "text-muted-foreground",
338
- className,
339
- )}
340
- >
341
- <CalendarBlank className="size-4" />
342
- <span className="flex-1 text-left">{displayValue()}</span>
343
- {(value.start || value.end) && !disabled && (
344
- <X
345
- className="size-4 opacity-50 hover:opacity-100"
346
- onClick={handleClear}
347
- />
348
- )}
349
- </PopoverTrigger>
350
- <PopoverContent className="w-auto p-0" align="start">
351
- <DayPicker
352
- mode="range"
353
- selected={
354
- value.start || value.end
355
- ? { from: value.start ?? undefined, to: value.end ?? undefined }
356
- : undefined
357
- }
358
- onSelect={handleSelect}
359
- numberOfMonths={2}
360
- disabled={(date) => {
361
- if (minDate && date < minDate) return true;
362
- if (maxDate && date > maxDate) return true;
363
- return false;
364
- }}
365
- className="p-3"
366
- classNames={{
367
- months: "flex flex-col sm:flex-row gap-2",
368
- month: "flex flex-col gap-4",
369
- month_caption:
370
- "flex justify-center pt-1 relative items-center w-full",
371
- caption_label: "text-sm font-medium",
372
- nav: "flex items-center gap-1",
373
- button_previous:
374
- "absolute left-1 size-7 bg-transparent p-0 opacity-50 hover:opacity-100",
375
- button_next:
376
- "absolute right-1 size-7 bg-transparent p-0 opacity-50 hover:opacity-100",
377
- month_grid: "w-full border-collapse",
378
- weekdays: "flex",
379
- weekday: "text-muted-foreground w-8 font-normal text-[0.8rem]",
380
- week: "flex w-full mt-2",
381
- day: "relative p-0 text-center text-sm focus-within:relative focus-within:z-20 [&:has([aria-selected])]:bg-accent [&:has([aria-selected].day-outside)]:bg-accent/50",
382
- day_button: cn(
383
- "size-8 p-0 font-normal",
384
- "hover:bg-accent hover:text-accent-foreground",
385
- "focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
386
- ),
387
- selected:
388
- "bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground",
389
- range_start: "rounded-l-md",
390
- range_end: "rounded-r-md",
391
- range_middle: "bg-accent",
392
- today: "bg-accent text-accent-foreground",
393
- outside: "text-muted-foreground opacity-50",
394
- disabled: "text-muted-foreground opacity-50",
395
- hidden: "invisible",
396
- }}
397
- />
398
- </PopoverContent>
399
- </Popover>
400
- );
401
- }
@@ -1,24 +0,0 @@
1
- // =============================================================================
2
- // Primitives - Generic UI components with value/onChange pattern
3
- // =============================================================================
4
-
5
- // Types
6
- export * from "./types";
7
-
8
- // Text inputs
9
- export { TextInput } from "./text-input";
10
- export { NumberInput } from "./number-input";
11
- export { TextareaInput } from "./textarea-input";
12
-
13
- // Select inputs
14
- export { SelectInput } from "./select-input";
15
-
16
- // Boolean inputs
17
- export { ToggleInput } from "./toggle-input";
18
- export { CheckboxInput, CheckboxGroup, RadioGroup } from "./checkbox-input";
19
-
20
- // Date inputs
21
- export { DateInput, DateTimeInput, DateRangeInput } from "./date-input";
22
-
23
- // Special inputs
24
- export { TagInput } from "./tag-input";
@@ -1,132 +0,0 @@
1
- import { Minus, Plus } from "@phosphor-icons/react";
2
- import { Input } from "../ui/input";
3
- import { Button } from "../ui/button";
4
- import { cn } from "../../lib/utils";
5
- import type { NumberInputProps } from "./types";
6
-
7
- /**
8
- * Number Input Primitive
9
- *
10
- * A number input with optional increment/decrement buttons.
11
- *
12
- * @example
13
- * ```tsx
14
- * <NumberInput
15
- * value={count}
16
- * onChange={setCount}
17
- * min={0}
18
- * max={100}
19
- * step={1}
20
- * showButtons
21
- * />
22
- * ```
23
- */
24
- export function NumberInput({
25
- value,
26
- onChange,
27
- min,
28
- max,
29
- step = 1,
30
- showButtons = false,
31
- placeholder,
32
- disabled,
33
- readOnly,
34
- className,
35
- id,
36
- "aria-invalid": ariaInvalid,
37
- }: NumberInputProps) {
38
- const handleChange = (newValue: number | null) => {
39
- if (newValue === null) {
40
- onChange(null);
41
- return;
42
- }
43
-
44
- let clampedValue = newValue;
45
- if (min !== undefined && clampedValue < min) clampedValue = min;
46
- if (max !== undefined && clampedValue > max) clampedValue = max;
47
-
48
- onChange(clampedValue);
49
- };
50
-
51
- const increment = () => {
52
- const current = value ?? 0;
53
- handleChange(current + step);
54
- };
55
-
56
- const decrement = () => {
57
- const current = value ?? 0;
58
- handleChange(current - step);
59
- };
60
-
61
- if (showButtons) {
62
- return (
63
- <div className={cn("flex items-center gap-1", className)}>
64
- <Button
65
- type="button"
66
- variant="outline"
67
- size="icon-sm"
68
- onClick={decrement}
69
- disabled={disabled || (min !== undefined && (value ?? 0) <= min)}
70
- tabIndex={-1}
71
- >
72
- <Minus className="size-3" />
73
- </Button>
74
- <Input
75
- id={id}
76
- type="number"
77
- value={value ?? ""}
78
- onChange={(e) => {
79
- const val = e.target.value;
80
- if (val === "") {
81
- handleChange(null);
82
- } else {
83
- handleChange(Number(val));
84
- }
85
- }}
86
- placeholder={placeholder}
87
- disabled={disabled}
88
- readOnly={readOnly}
89
- min={min}
90
- max={max}
91
- step={step}
92
- aria-invalid={ariaInvalid}
93
- className="text-center [appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none"
94
- />
95
- <Button
96
- type="button"
97
- variant="outline"
98
- size="icon-sm"
99
- onClick={increment}
100
- disabled={disabled || (max !== undefined && (value ?? 0) >= max)}
101
- tabIndex={-1}
102
- >
103
- <Plus className="size-3" />
104
- </Button>
105
- </div>
106
- );
107
- }
108
-
109
- return (
110
- <Input
111
- id={id}
112
- type="number"
113
- value={value ?? ""}
114
- onChange={(e) => {
115
- const val = e.target.value;
116
- if (val === "") {
117
- handleChange(null);
118
- } else {
119
- handleChange(Number(val));
120
- }
121
- }}
122
- placeholder={placeholder}
123
- disabled={disabled}
124
- readOnly={readOnly}
125
- min={min}
126
- max={max}
127
- step={step}
128
- aria-invalid={ariaInvalid}
129
- className={cn(className)}
130
- />
131
- );
132
- }