@page-speed/forms 0.4.1 → 0.4.2

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/dist/inputs.d.cts CHANGED
@@ -131,6 +131,10 @@ interface CheckboxProps extends Omit<InputProps<boolean>, "onChange" | "placehol
131
131
  * Optional description text below the label (secondary text)
132
132
  */
133
133
  description?: React.ReactNode;
134
+ /**
135
+ * Layout variant
136
+ */
137
+ checkboxVariant?: "boxed" | "inline";
134
138
  /**
135
139
  * Additional native input attributes
136
140
  */
@@ -178,7 +182,7 @@ interface CheckboxProps extends Omit<InputProps<boolean>, "onChange" | "placehol
178
182
  *
179
183
  * @see https://opensite.ai/developers/page-speed/forms/checkbox
180
184
  */
181
- declare function Checkbox({ name, value, onChange, onBlur, disabled, required, error, className, indeterminate, label, description, ...props }: CheckboxProps): React.JSX.Element;
185
+ declare function Checkbox({ name, value, onChange, onBlur, disabled, required, error, className, indeterminate, label, description, checkboxVariant, ...props }: CheckboxProps): React.JSX.Element;
182
186
  declare namespace Checkbox {
183
187
  var displayName: string;
184
188
  }
@@ -363,6 +367,13 @@ interface RadioProps extends Omit<InputProps<string>, "onChange" | "placeholder"
363
367
  * @default "stacked"
364
368
  */
365
369
  layout?: "inline" | "stacked";
370
+ /**
371
+ * Visual variant for radio options
372
+ * - "boxed": bordered card with ring on hover/selected, radio circle on right
373
+ * - "inline": minimal style, radio circle on left
374
+ * @default "inline"
375
+ */
376
+ radioVariant?: "boxed" | "inline";
366
377
  /**
367
378
  * Group-level label
368
379
  */
@@ -424,7 +435,7 @@ interface RadioProps extends Omit<InputProps<string>, "onChange" | "placeholder"
424
435
  *
425
436
  * @see https://opensite.ai/developers/page-speed/forms/radio
426
437
  */
427
- declare function Radio({ name, value, onChange, onBlur, disabled, required, error, className, layout, label, options, ...props }: RadioProps): React.JSX.Element;
438
+ declare function Radio({ name, value, onChange, onBlur, disabled, required, error, className, layout, radioVariant, label, options, ...props }: RadioProps): React.JSX.Element;
428
439
  declare namespace Radio {
429
440
  var displayName: string;
430
441
  }
package/dist/inputs.d.ts CHANGED
@@ -131,6 +131,10 @@ interface CheckboxProps extends Omit<InputProps<boolean>, "onChange" | "placehol
131
131
  * Optional description text below the label (secondary text)
132
132
  */
133
133
  description?: React.ReactNode;
134
+ /**
135
+ * Layout variant
136
+ */
137
+ checkboxVariant?: "boxed" | "inline";
134
138
  /**
135
139
  * Additional native input attributes
136
140
  */
@@ -178,7 +182,7 @@ interface CheckboxProps extends Omit<InputProps<boolean>, "onChange" | "placehol
178
182
  *
179
183
  * @see https://opensite.ai/developers/page-speed/forms/checkbox
180
184
  */
181
- declare function Checkbox({ name, value, onChange, onBlur, disabled, required, error, className, indeterminate, label, description, ...props }: CheckboxProps): React.JSX.Element;
185
+ declare function Checkbox({ name, value, onChange, onBlur, disabled, required, error, className, indeterminate, label, description, checkboxVariant, ...props }: CheckboxProps): React.JSX.Element;
182
186
  declare namespace Checkbox {
183
187
  var displayName: string;
184
188
  }
@@ -363,6 +367,13 @@ interface RadioProps extends Omit<InputProps<string>, "onChange" | "placeholder"
363
367
  * @default "stacked"
364
368
  */
365
369
  layout?: "inline" | "stacked";
370
+ /**
371
+ * Visual variant for radio options
372
+ * - "boxed": bordered card with ring on hover/selected, radio circle on right
373
+ * - "inline": minimal style, radio circle on left
374
+ * @default "inline"
375
+ */
376
+ radioVariant?: "boxed" | "inline";
366
377
  /**
367
378
  * Group-level label
368
379
  */
@@ -424,7 +435,7 @@ interface RadioProps extends Omit<InputProps<string>, "onChange" | "placeholder"
424
435
  *
425
436
  * @see https://opensite.ai/developers/page-speed/forms/radio
426
437
  */
427
- declare function Radio({ name, value, onChange, onBlur, disabled, required, error, className, layout, label, options, ...props }: RadioProps): React.JSX.Element;
438
+ declare function Radio({ name, value, onChange, onBlur, disabled, required, error, className, layout, radioVariant, label, options, ...props }: RadioProps): React.JSX.Element;
428
439
  declare namespace Radio {
429
440
  var displayName: string;
430
441
  }
package/dist/inputs.js CHANGED
@@ -1,4 +1,6 @@
1
1
  import * as React7 from 'react';
2
+ import { clsx } from 'clsx';
3
+ import { twMerge } from 'tailwind-merge';
2
4
 
3
5
  // src/inputs/TextInput.tsx
4
6
  function TextInput({
@@ -95,6 +97,11 @@ function TextArea({
95
97
  );
96
98
  }
97
99
  TextArea.displayName = "TextArea";
100
+ function cn(...inputs) {
101
+ return twMerge(clsx(inputs));
102
+ }
103
+
104
+ // src/inputs/Checkbox.tsx
98
105
  function Checkbox({
99
106
  name,
100
107
  value,
@@ -107,6 +114,7 @@ function Checkbox({
107
114
  indeterminate = false,
108
115
  label,
109
116
  description,
117
+ checkboxVariant = "boxed",
110
118
  ...props
111
119
  }) {
112
120
  const inputRef = React7.useRef(null);
@@ -122,36 +130,92 @@ function Checkbox({
122
130
  const handleBlur = () => {
123
131
  onBlur?.();
124
132
  };
125
- const checkbox = /* @__PURE__ */ React7.createElement("div", { className: "relative inline-flex" }, /* @__PURE__ */ React7.createElement(
126
- "input",
133
+ const isActive = value || indeterminate && !value;
134
+ const checkbox = /* @__PURE__ */ React7.createElement(
135
+ "div",
127
136
  {
128
- ref: inputRef,
129
- type: "checkbox",
130
- id: checkboxId,
131
- name,
132
- checked: value,
133
- onChange: handleChange,
134
- onBlur: handleBlur,
135
- disabled,
136
- required,
137
- className: `peer relative flex size-4 shrink-0 appearance-none items-center justify-center rounded-lg border border-input bg-transparent outline-none transition-colors focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 ${error ? "border-destructive ring-3 ring-destructive/20" : ""} ${value ? "bg-primary border-primary" : ""} ${className}`,
138
- "aria-invalid": error || props["aria-invalid"],
139
- "aria-describedby": description ? `${checkboxId}-description` : props["aria-describedby"],
140
- "aria-required": required || props["aria-required"],
141
- ...props
142
- }
143
- ), value && /* @__PURE__ */ React7.createElement("span", { className: "pointer-events-none absolute top-1/2 left-1/2 flex -translate-x-1/2 -translate-y-1/2 items-center justify-center text-primary-foreground" }, /* @__PURE__ */ React7.createElement("svg", { className: "size-3", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "3" }, /* @__PURE__ */ React7.createElement("polyline", { points: "20 6 9 17 4 12" }))), indeterminate && !value && /* @__PURE__ */ React7.createElement("span", { className: "pointer-events-none absolute top-1/2 left-1/2 flex -translate-x-1/2 -translate-y-1/2 items-center justify-center text-primary" }, /* @__PURE__ */ React7.createElement("svg", { className: "size-3", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "3" }, /* @__PURE__ */ React7.createElement("line", { x1: "5", y1: "12", x2: "19", y2: "12" }))));
137
+ className: cn(
138
+ "relative inline-flex items-center justify-center",
139
+ !label && className
140
+ )
141
+ },
142
+ /* @__PURE__ */ React7.createElement(
143
+ "input",
144
+ {
145
+ ref: inputRef,
146
+ type: "checkbox",
147
+ id: checkboxId,
148
+ name,
149
+ checked: value,
150
+ onChange: handleChange,
151
+ onBlur: handleBlur,
152
+ disabled,
153
+ required,
154
+ className: "peer sr-only",
155
+ "aria-invalid": error || props["aria-invalid"],
156
+ "aria-describedby": description ? `${checkboxId}-description` : props["aria-describedby"],
157
+ "aria-required": required || props["aria-required"],
158
+ ...props
159
+ }
160
+ ),
161
+ /* @__PURE__ */ React7.createElement(
162
+ "div",
163
+ {
164
+ className: cn(
165
+ "flex shrink-0 items-center justify-center rounded-full border-2 transition-colors size-6",
166
+ !error && isActive && "border-primary bg-primary text-primary-foreground",
167
+ !error && !isActive && "border-input bg-transparent",
168
+ error && isActive && "border-destructive bg-destructive text-destructive-foreground",
169
+ error && !isActive && "border-destructive bg-transparent",
170
+ disabled && "opacity-50",
171
+ "peer-focus-visible:ring-2 peer-focus-visible:ring-ring/50 peer-focus-visible:ring-offset-1"
172
+ )
173
+ },
174
+ value && /* @__PURE__ */ React7.createElement(
175
+ "svg",
176
+ {
177
+ className: "size-3.5",
178
+ viewBox: "0 0 24 24",
179
+ fill: "none",
180
+ stroke: "currentColor",
181
+ strokeWidth: "3",
182
+ strokeLinecap: "round",
183
+ strokeLinejoin: "round"
184
+ },
185
+ /* @__PURE__ */ React7.createElement("polyline", { points: "20 6 9 17 4 12" })
186
+ ),
187
+ indeterminate && !value && /* @__PURE__ */ React7.createElement(
188
+ "svg",
189
+ {
190
+ className: "size-3.5",
191
+ viewBox: "0 0 24 24",
192
+ fill: "none",
193
+ stroke: "currentColor",
194
+ strokeWidth: "3",
195
+ strokeLinecap: "round",
196
+ strokeLinejoin: "round"
197
+ },
198
+ /* @__PURE__ */ React7.createElement("line", { x1: "5", y1: "12", x2: "19", y2: "12" })
199
+ )
200
+ )
201
+ );
144
202
  if (label) {
145
203
  return /* @__PURE__ */ React7.createElement(
146
204
  "label",
147
205
  {
148
- className: `flex w-fit gap-2 items-center ${disabled ? "opacity-50 cursor-not-allowed" : "cursor-pointer"}`,
206
+ className: cn(
207
+ "w-full h-full flex gap-3 p-3 duration-200",
208
+ checkboxVariant === "boxed" && "border rounded-lg hover:ring-2",
209
+ checkboxVariant === "boxed" && value && "ring-2",
210
+ disabled ? "opacity-50 cursor-not-allowed hover:ring-0" : "cursor-pointer",
211
+ className
212
+ ),
149
213
  htmlFor: checkboxId
150
214
  },
151
- /* @__PURE__ */ React7.createElement("div", { className: "flex w-full flex-row items-center gap-2" }, checkbox, /* @__PURE__ */ React7.createElement("div", { className: "flex flex-1 flex-col gap-0.5" }, /* @__PURE__ */ React7.createElement("div", { className: "flex items-center gap-2 text-sm font-medium" }, label), description && /* @__PURE__ */ React7.createElement(
215
+ /* @__PURE__ */ React7.createElement("div", { className: "flex w-full flex-row gap-2" }, checkbox, /* @__PURE__ */ React7.createElement("div", { className: "flex flex-col gap-0.5" }, /* @__PURE__ */ React7.createElement("div", { className: "text-sm font-medium" }, label), description && /* @__PURE__ */ React7.createElement(
152
216
  "p",
153
217
  {
154
- className: "text-muted-foreground text-sm",
218
+ className: "text-xs opacity-75",
155
219
  id: `${checkboxId}-description`
156
220
  },
157
221
  description
@@ -189,6 +253,18 @@ function CheckboxGroup({
189
253
  ).length;
190
254
  const allSelected = selectedEnabledCount === enabledOptions.length;
191
255
  const someSelected = selectedEnabledCount > 0 && !allSelected;
256
+ const checkboxVariant = React7.useMemo(() => {
257
+ if (options.some((opt) => opt.description)) {
258
+ return "boxed";
259
+ }
260
+ return "inline";
261
+ }, [options]);
262
+ const countableValue = React7.useMemo(() => {
263
+ if (value?.length > 0) {
264
+ return value.length;
265
+ }
266
+ return 0;
267
+ }, [value]);
192
268
  const handleChange = (optionValue, checked) => {
193
269
  const newValues = checked ? [...value, optionValue] : value.filter((v) => v !== optionValue);
194
270
  if (maxSelections && checked && newValues.length > maxSelections) {
@@ -207,9 +283,14 @@ function CheckboxGroup({
207
283
  const handleBlur = () => {
208
284
  onBlur?.();
209
285
  };
210
- const layoutClass = layout === "inline" ? "flex flex-row flex-wrap gap-4" : layout === "grid" ? `grid gap-3` : "flex flex-col gap-3";
211
- const containerClass = `w-full ${layoutClass} ${className}`.trim();
212
- const maxReached = Boolean(maxSelections && value.length >= maxSelections);
286
+ const maxReached = Boolean(maxSelections && countableValue >= maxSelections);
287
+ const containerClass = cn(
288
+ "w-full gap-3",
289
+ layout === "stacked" && "flex flex-col",
290
+ layout === "inline" && "flex flex-row flex-wrap",
291
+ layout === "grid" && "grid",
292
+ className
293
+ );
213
294
  return /* @__PURE__ */ React7.createElement(
214
295
  "div",
215
296
  {
@@ -226,65 +307,53 @@ function CheckboxGroup({
226
307
  label && /* @__PURE__ */ React7.createElement("div", { className: "text-sm font-medium" }, label),
227
308
  description && /* @__PURE__ */ React7.createElement("div", { className: "text-muted-foreground text-sm" }, description),
228
309
  showSelectAll && enabledOptions.length > 0 && /* @__PURE__ */ React7.createElement(
229
- "label",
310
+ Checkbox,
230
311
  {
231
- className: `flex w-fit gap-2 items-center ${disabled ? "opacity-50 cursor-not-allowed" : "cursor-pointer"}`
232
- },
233
- /* @__PURE__ */ React7.createElement("div", { className: "flex w-full flex-row items-center gap-2" }, /* @__PURE__ */ React7.createElement("div", { className: "relative inline-flex" }, /* @__PURE__ */ React7.createElement(
234
- "input",
235
- {
236
- type: "checkbox",
237
- checked: allSelected,
238
- ref: (input) => {
239
- if (input) {
240
- input.indeterminate = someSelected;
241
- }
242
- },
243
- onChange: (e) => handleSelectAll(e.target.checked),
244
- onBlur: handleBlur,
245
- disabled,
246
- className: "peer relative flex size-4 shrink-0 appearance-none items-center justify-center rounded-lg border border-input bg-transparent outline-none transition-colors focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50",
247
- "aria-label": selectAllLabel
248
- }
249
- ), allSelected && /* @__PURE__ */ React7.createElement("span", { className: "pointer-events-none absolute top-1/2 left-1/2 flex -translate-x-1/2 -translate-y-1/2 items-center justify-center text-primary-foreground" }, /* @__PURE__ */ React7.createElement("svg", { className: "size-3", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "3" }, /* @__PURE__ */ React7.createElement("polyline", { points: "20 6 9 17 4 12" }))), someSelected && !allSelected && /* @__PURE__ */ React7.createElement("span", { className: "pointer-events-none absolute top-1/2 left-1/2 flex -translate-x-1/2 -translate-y-1/2 items-center justify-center text-primary" }, /* @__PURE__ */ React7.createElement("svg", { className: "size-3", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "3" }, /* @__PURE__ */ React7.createElement("line", { x1: "5", y1: "12", x2: "19", y2: "12" })))), /* @__PURE__ */ React7.createElement("span", { className: "text-sm font-medium" }, selectAllLabel))
312
+ name: `${name}-select-all`,
313
+ id: `${name}-select-all`,
314
+ value: allSelected,
315
+ onChange: handleSelectAll,
316
+ onBlur: handleBlur,
317
+ indeterminate: someSelected,
318
+ label: selectAllLabel,
319
+ checkboxVariant: "inline",
320
+ disabled,
321
+ "aria-label": selectAllLabel
322
+ }
250
323
  ),
251
324
  options.map((option) => {
252
325
  const isChecked = value.includes(option.value);
253
326
  const isDisabled = disabled || option.disabled || maxReached && !isChecked;
254
- const checkboxId = `${name}-${option.value}`;
255
327
  return /* @__PURE__ */ React7.createElement(
256
- "label",
328
+ Checkbox,
257
329
  {
258
330
  key: option.value,
259
- className: `flex w-fit gap-2 items-center ${isDisabled ? "opacity-50 cursor-not-allowed" : "cursor-pointer"}`,
260
- htmlFor: checkboxId
261
- },
262
- /* @__PURE__ */ React7.createElement("div", { className: "flex w-full flex-row items-center gap-2" }, /* @__PURE__ */ React7.createElement("div", { className: "relative inline-flex" }, /* @__PURE__ */ React7.createElement(
263
- "input",
264
- {
265
- type: "checkbox",
266
- id: checkboxId,
267
- name,
268
- value: option.value,
269
- checked: isChecked,
270
- onChange: (e) => handleChange(option.value, e.target.checked),
271
- onBlur: handleBlur,
272
- disabled: isDisabled,
273
- required: required && minSelections ? value.length < minSelections : false,
274
- className: `peer relative flex size-4 shrink-0 appearance-none items-center justify-center rounded-lg border border-input bg-transparent outline-none transition-colors focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 ${error ? "border-destructive ring-3 ring-destructive/20" : ""} ${isChecked ? "bg-primary border-primary" : ""}`,
275
- "aria-describedby": option.description ? `${checkboxId}-description` : props["aria-describedby"]
276
- }
277
- ), isChecked && /* @__PURE__ */ React7.createElement("span", { className: "pointer-events-none absolute top-1/2 left-1/2 flex -translate-x-1/2 -translate-y-1/2 items-center justify-center text-primary-foreground" }, /* @__PURE__ */ React7.createElement("svg", { className: "size-3", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "3" }, /* @__PURE__ */ React7.createElement("polyline", { points: "20 6 9 17 4 12" })))), /* @__PURE__ */ React7.createElement("div", { className: "flex flex-1 flex-col gap-0.5" }, renderOption ? renderOption(option) : /* @__PURE__ */ React7.createElement(React7.Fragment, null, /* @__PURE__ */ React7.createElement("div", { className: "flex items-center gap-2 text-sm font-medium" }, option.label), option.description && /* @__PURE__ */ React7.createElement(
278
- "p",
279
- {
280
- className: "text-muted-foreground text-sm",
281
- id: `${checkboxId}-description`
282
- },
283
- option.description
284
- ))))
331
+ name,
332
+ id: `${name}-${option.value}`,
333
+ value: isChecked,
334
+ onChange: (checked) => handleChange(option.value, checked),
335
+ onBlur: handleBlur,
336
+ disabled: isDisabled,
337
+ required: required && minSelections ? value.length < minSelections : false,
338
+ error,
339
+ label: renderOption ? renderOption(option) : option.label,
340
+ description: renderOption ? void 0 : option.description,
341
+ checkboxVariant
342
+ }
285
343
  );
286
344
  }),
287
- (minSelections || maxSelections) && /* @__PURE__ */ React7.createElement("div", { className: "text-sm text-muted-foreground mt-2", "aria-live": "polite" }, minSelections && value.length < minSelections && /* @__PURE__ */ React7.createElement("span", { className: "text-destructive" }, "Select at least ", minSelections, " option", minSelections !== 1 ? "s" : ""), maxSelections && /* @__PURE__ */ React7.createElement("span", null, value.length, "/", maxSelections, " selected"))
345
+ (minSelections || maxSelections) && /* @__PURE__ */ React7.createElement(
346
+ "div",
347
+ {
348
+ className: cn(
349
+ "text-sm p-2 rounded-lg border font-semibold mt-2",
350
+ minSelections && countableValue < minSelections ? "border-destructive bg-destructive/80 text-destructive-foreground" : "border-border bg-card text-card-foreground"
351
+ ),
352
+ "aria-live": "polite"
353
+ },
354
+ minSelections && countableValue < minSelections && /* @__PURE__ */ React7.createElement("span", null, "Select at least ", minSelections, " option", minSelections !== 1 ? "s" : ""),
355
+ maxSelections && /* @__PURE__ */ React7.createElement("span", null, countableValue, "/", maxSelections, " selected")
356
+ )
288
357
  );
289
358
  }
290
359
  CheckboxGroup.displayName = "CheckboxGroup";
@@ -298,6 +367,7 @@ function Radio({
298
367
  error = false,
299
368
  className = "",
300
369
  layout = "stacked",
370
+ radioVariant = "inline",
301
371
  label,
302
372
  options,
303
373
  ...props
@@ -333,8 +403,12 @@ function Radio({
333
403
  const handleBlur = () => {
334
404
  onBlur?.();
335
405
  };
336
- const layoutClass = layout === "inline" ? "flex flex-row flex-wrap gap-4" : "grid w-full gap-2";
337
- const containerClass = `${layoutClass} ${className}`.trim();
406
+ const containerClass = cn(
407
+ "w-full gap-3",
408
+ layout === "stacked" && "flex flex-col",
409
+ layout === "inline" && "flex flex-row flex-wrap",
410
+ className
411
+ );
338
412
  return /* @__PURE__ */ React7.createElement(
339
413
  "div",
340
414
  {
@@ -350,38 +424,60 @@ function Radio({
350
424
  const isChecked = value === option.value;
351
425
  const isDisabled = disabled || option.disabled;
352
426
  const radioId = `${name}-${option.value}`;
427
+ const hasDescription = option.description != null && option.description !== "";
428
+ const radioIndicator = /* @__PURE__ */ React7.createElement("div", { className: "relative inline-flex items-center justify-center" }, /* @__PURE__ */ React7.createElement(
429
+ "input",
430
+ {
431
+ type: "radio",
432
+ id: radioId,
433
+ name,
434
+ value: option.value,
435
+ checked: isChecked,
436
+ onChange: (e) => handleChange(e.target.value),
437
+ onBlur: handleBlur,
438
+ disabled: isDisabled,
439
+ required,
440
+ className: "peer sr-only",
441
+ "aria-describedby": hasDescription ? `${radioId}-description` : props["aria-describedby"]
442
+ }
443
+ ), /* @__PURE__ */ React7.createElement(
444
+ "div",
445
+ {
446
+ className: cn(
447
+ "flex shrink-0 items-center justify-center rounded-full border-2 transition-colors size-6",
448
+ !error && isChecked && "border-primary bg-transparent",
449
+ !error && !isChecked && "border-input bg-transparent",
450
+ error && isChecked && "border-destructive bg-transparent",
451
+ error && !isChecked && "border-destructive bg-transparent",
452
+ isDisabled && "opacity-50",
453
+ "peer-focus-visible:ring-2 peer-focus-visible:ring-ring/50 peer-focus-visible:ring-offset-1"
454
+ )
455
+ },
456
+ isChecked && /* @__PURE__ */ React7.createElement("div", { className: "size-3 rounded-full bg-primary" })
457
+ ));
458
+ const labelContent = /* @__PURE__ */ React7.createElement("div", { className: "flex flex-col gap-0.5" }, /* @__PURE__ */ React7.createElement("div", { className: "text-sm font-medium" }, option.label), hasDescription && /* @__PURE__ */ React7.createElement(
459
+ "p",
460
+ {
461
+ className: "text-xs opacity-75",
462
+ id: `${radioId}-description`
463
+ },
464
+ option.description
465
+ ));
353
466
  return /* @__PURE__ */ React7.createElement(
354
467
  "label",
355
468
  {
356
469
  key: option.value,
357
- className: `flex w-fit gap-2 items-center ${isDisabled ? "opacity-50 cursor-not-allowed" : "cursor-pointer"}`,
470
+ className: cn(
471
+ "w-full h-full flex gap-3 p-3 duration-200",
472
+ radioVariant === "boxed" && "border rounded-lg hover:ring-2",
473
+ radioVariant === "boxed" && isChecked && "ring-2",
474
+ isDisabled ? "opacity-50 cursor-not-allowed hover:ring-0" : "cursor-pointer"
475
+ ),
358
476
  htmlFor: radioId,
359
477
  onKeyDown: (e) => handleKeyDown(e, index),
360
478
  tabIndex: isDisabled ? -1 : 0
361
479
  },
362
- /* @__PURE__ */ React7.createElement("div", { className: "flex w-full flex-row items-center gap-2" }, /* @__PURE__ */ React7.createElement("div", { className: "flex flex-1 flex-col gap-0.5" }, option.description ? /* @__PURE__ */ React7.createElement(React7.Fragment, null, /* @__PURE__ */ React7.createElement("div", { className: "flex items-center gap-2 text-sm font-medium" }, option.label), /* @__PURE__ */ React7.createElement(
363
- "p",
364
- {
365
- className: "text-muted-foreground text-sm",
366
- id: `${radioId}-description`
367
- },
368
- option.description
369
- )) : /* @__PURE__ */ React7.createElement("span", { className: "text-sm font-medium" }, option.label)), /* @__PURE__ */ React7.createElement("div", { className: "relative" }, /* @__PURE__ */ React7.createElement(
370
- "input",
371
- {
372
- type: "radio",
373
- id: radioId,
374
- name,
375
- value: option.value,
376
- checked: isChecked,
377
- onChange: (e) => handleChange(e.target.value),
378
- onBlur: handleBlur,
379
- disabled: isDisabled,
380
- required,
381
- className: `peer relative flex aspect-square size-4 shrink-0 appearance-none rounded-full border border-input outline-none transition-colors focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 ${error ? "border-destructive" : ""} ${isChecked ? "border-primary bg-primary" : "bg-transparent"}`,
382
- "aria-describedby": option.description ? `${radioId}-description` : props["aria-describedby"]
383
- }
384
- ), isChecked && /* @__PURE__ */ React7.createElement("span", { className: "pointer-events-none absolute top-1/2 left-1/2 flex size-4 -translate-x-1/2 -translate-y-1/2 items-center justify-center text-primary-foreground" }, /* @__PURE__ */ React7.createElement("svg", { className: "size-2 fill-current", viewBox: "0 0 24 24" }, /* @__PURE__ */ React7.createElement("circle", { cx: "12", cy: "12", r: "10" })))))
480
+ /* @__PURE__ */ React7.createElement("div", { className: "flex w-full flex-row items-center gap-2" }, radioVariant === "inline" && radioIndicator, /* @__PURE__ */ React7.createElement("div", { className: "flex flex-1 flex-col gap-0.5" }, labelContent), radioVariant === "boxed" && radioIndicator)
385
481
  );
386
482
  })
387
483
  );
@@ -1231,7 +1327,9 @@ function DatePicker({
1231
1327
  }) {
1232
1328
  const [isOpen, setIsOpen] = React7.useState(false);
1233
1329
  const [inputValue, setInputValue] = React7.useState("");
1234
- const [selectedMonth, setSelectedMonth] = React7.useState(value || /* @__PURE__ */ new Date());
1330
+ const [selectedMonth, setSelectedMonth] = React7.useState(
1331
+ value || /* @__PURE__ */ new Date()
1332
+ );
1235
1333
  const containerRef = React7.useRef(null);
1236
1334
  const inputRef = React7.useRef(null);
1237
1335
  React7.useEffect(() => {
@@ -1322,7 +1420,7 @@ function DatePicker({
1322
1420
  "button",
1323
1421
  {
1324
1422
  type: "button",
1325
- className: "flex items-center justify-center h-8 w-8 rounded border-none bg-transparent hover:bg-accent cursor-pointer",
1423
+ className: "flex items-center justify-center h-8 w-8 rounded border-none bg-transparent hover:bg-primary hover:text-primary-foreground cursor-pointer",
1326
1424
  onClick: handlePrevMonth,
1327
1425
  "aria-label": "Previous month"
1328
1426
  },
@@ -1331,12 +1429,19 @@ function DatePicker({
1331
1429
  "button",
1332
1430
  {
1333
1431
  type: "button",
1334
- className: "flex items-center justify-center h-8 w-8 rounded border-none bg-transparent hover:bg-accent cursor-pointer",
1432
+ className: "flex items-center justify-center h-8 w-8 rounded border-none bg-transparent hover:bg-primary hover:text-primary-foreground cursor-pointer",
1335
1433
  onClick: handleNextMonth,
1336
1434
  "aria-label": "Next month"
1337
1435
  },
1338
1436
  "\u2192"
1339
- )), /* @__PURE__ */ React7.createElement("div", { className: "grid grid-cols-7 gap-1 mt-2" }, ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"].map((day) => /* @__PURE__ */ React7.createElement("div", { key: day, className: "flex items-center justify-center h-8 w-full text-xs text-muted-foreground font-medium" }, day))), /* @__PURE__ */ React7.createElement("div", { className: "grid grid-cols-7 gap-1" }, days.map((date, index) => {
1437
+ )), /* @__PURE__ */ React7.createElement("div", { className: "grid grid-cols-7 gap-1 mt-2" }, ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"].map((day) => /* @__PURE__ */ React7.createElement(
1438
+ "div",
1439
+ {
1440
+ key: day,
1441
+ className: "flex items-center justify-center h-8 w-full text-xs font-medium"
1442
+ },
1443
+ day
1444
+ ))), /* @__PURE__ */ React7.createElement("div", { className: "grid grid-cols-7 gap-1" }, days.map((date, index) => {
1340
1445
  if (!date) {
1341
1446
  return /* @__PURE__ */ React7.createElement("div", { key: `empty-${index}` });
1342
1447
  }
@@ -1348,7 +1453,7 @@ function DatePicker({
1348
1453
  {
1349
1454
  key: date.toISOString(),
1350
1455
  type: "button",
1351
- className: `flex items-center justify-center h-8 w-full rounded border-none bg-transparent cursor-pointer text-sm transition-colors hover:bg-accent hover:text-accent-foreground ${isSelected ? "bg-primary text-primary-foreground font-semibold" : ""} ${isToday ? "border border-primary" : ""} ${disabled2 ? "cursor-not-allowed opacity-50 pointer-events-none" : ""}`,
1456
+ className: `flex items-center justify-center h-8 w-full rounded border-none bg-transparent cursor-pointer text-sm transition-colors hover:bg-primary hover:text-primary-foreground ${isSelected ? "bg-primary text-primary-foreground font-semibold" : ""} ${isToday ? "border border-primary" : ""} ${disabled2 ? "cursor-not-allowed opacity-50 pointer-events-none" : ""}`,
1352
1457
  onClick: () => !disabled2 && handleDateSelect(date),
1353
1458
  disabled: disabled2,
1354
1459
  "aria-label": formatDate(date, format)
@@ -1365,26 +1470,33 @@ function DatePicker({
1365
1470
  name,
1366
1471
  value: value ? value.toISOString() : ""
1367
1472
  }
1368
- ), /* @__PURE__ */ React7.createElement("div", { className: "relative" }, showIcon && /* @__PURE__ */ React7.createElement("span", { className: "absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground pointer-events-none", "aria-hidden": "true" }, /* @__PURE__ */ React7.createElement(
1369
- "svg",
1473
+ ), /* @__PURE__ */ React7.createElement("div", { className: "relative" }, showIcon && /* @__PURE__ */ React7.createElement(
1474
+ "span",
1370
1475
  {
1371
- xmlns: "http://www.w3.org/2000/svg",
1372
- width: "18",
1373
- height: "18",
1374
- viewBox: "0 0 24 24",
1375
- fill: "none",
1376
- stroke: "currentColor",
1377
- strokeLinecap: "round",
1378
- strokeLinejoin: "round",
1379
- strokeWidth: "2"
1476
+ className: "absolute left-3 top-1/2 -translate-y-1/2 pointer-events-none",
1477
+ "aria-hidden": "true"
1380
1478
  },
1381
- /* @__PURE__ */ React7.createElement("path", { d: "M8 2v4m8-4v4m5 8V6a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h8M3 10h18m-5 10l2 2l4-4" })
1382
- )), /* @__PURE__ */ React7.createElement(
1479
+ /* @__PURE__ */ React7.createElement(
1480
+ "svg",
1481
+ {
1482
+ xmlns: "http://www.w3.org/2000/svg",
1483
+ width: "18",
1484
+ height: "18",
1485
+ viewBox: "0 0 24 24",
1486
+ fill: "none",
1487
+ stroke: "currentColor",
1488
+ strokeLinecap: "round",
1489
+ strokeLinejoin: "round",
1490
+ strokeWidth: "2"
1491
+ },
1492
+ /* @__PURE__ */ React7.createElement("path", { d: "M8 2v4m8-4v4m5 8V6a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h8M3 10h18m-5 10l2 2l4-4" })
1493
+ )
1494
+ ), /* @__PURE__ */ React7.createElement(
1383
1495
  "input",
1384
1496
  {
1385
1497
  ref: inputRef,
1386
1498
  type: "text",
1387
- className: `flex h-9 w-full rounded-md border border-input bg-transparent ${showIcon ? "pl-10" : "pl-3"} ${clearable && value ? "pr-10" : "pr-3"} py-1 text-base shadow-sm transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm ${error ? "border-red-500 ring-1 ring-red-500" : ""}`,
1499
+ className: `flex h-9 w-full rounded-md border border-input bg-transparent ${showIcon ? "pl-10" : "pl-3"} ${clearable && value ? "pr-10" : "pr-3"} py-1 text-base shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm ${error ? "border-red-500 ring-1 ring-red-500" : ""}`,
1388
1500
  value: inputValue,
1389
1501
  onChange: handleInputChange,
1390
1502
  onClick: handleToggle,
@@ -1401,7 +1513,7 @@ function DatePicker({
1401
1513
  "button",
1402
1514
  {
1403
1515
  type: "button",
1404
- className: "absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground transition-colors",
1516
+ className: "absolute right-3 top-1/2 -translate-y-1/2 transition-colors",
1405
1517
  onClick: handleClear,
1406
1518
  "aria-label": "Clear date",
1407
1519
  tabIndex: -1