1"use client";
2
3import { NumberField as NumberFieldPrimitive } from "@base-ui/react/number-field";
4import { MinusIcon, PlusIcon } from "lucide-react";
5import * as React from "react";
6import { cn } from "~/lib/utils";
7
8export function NumberField({
9 id,
10 className,
11 size = "default",
12 ...props
13}: NumberFieldPrimitive.Root.Props & {
14 size?: "sm" | "default" | "lg";
15}): React.ReactElement {
16 const generatedId = React.useId();
17 const fieldId = id ?? generatedId;
18 return (
19 <NumberFieldPrimitive.Root
20 className={cn("flex w-full flex-col items-start gap-2", className)}
21 data-size={size}
22 data-slot="number-field"
23 id={fieldId}
24 {...props}
25 />
26 );
27}
28
29export function NumberFieldGroup({
30 className,
31 ...props
32}: NumberFieldPrimitive.Group.Props): React.ReactElement {
33 return (
34 <NumberFieldPrimitive.Group
35 className={cn(
36 "relative flex w-full justify-between rounded-lg border border-input bg-background not-dark:bg-clip-padding text-base text-foreground shadow-xs/5 ring-ring/24 transition-shadow before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-lg)-1px)] not-data-disabled:not-focus-within:not-aria-invalid:before:shadow-[0_1px_--theme(--color-black/4%)] focus-within:border-ring focus-within:ring-[3px] focus-within:ring-inset has-aria-invalid:border-destructive/36 has-autofill:bg-foreground/4 focus-within:has-aria-invalid:border-destructive/64 focus-within:has-aria-invalid:ring-destructive/48 data-disabled:pointer-events-none data-disabled:opacity-64 sm:text-sm dark:bg-input/32 dark:has-autofill:bg-foreground/8 dark:has-aria-invalid:ring-destructive/24 dark:not-data-disabled:not-focus-within:not-aria-invalid:before:shadow-[0_-1px_--theme(--color-white/6%)] [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0 [[data-disabled],:focus-within,[aria-invalid]]:shadow-none",
37 className,
38 )}
39 data-slot="number-field-group"
40 {...props}
41 />
42 );
43}
44
45export function NumberFieldDecrement({
46 className,
47 ...props
48}: NumberFieldPrimitive.Decrement.Props): React.ReactElement {
49 return (
50 <NumberFieldPrimitive.Decrement
51 className={cn(
52 "relative flex shrink-0 cursor-pointer items-center justify-center rounded-s-[calc(var(--radius-lg)-1px)] in-data-[size=sm]:px-[calc(--spacing(2.5)-1px)] px-[calc(--spacing(3)-1px)] transition-colors pointer-coarse:after:absolute pointer-coarse:after:size-full pointer-coarse:after:min-h-11 pointer-coarse:after:min-w-11 hover:bg-accent",
53 className,
54 )}
55 data-slot="number-field-decrement"
56 {...props}
57 >
58 <MinusIcon />
59 </NumberFieldPrimitive.Decrement>
60 );
61}
62
63export function NumberFieldIncrement({
64 className,
65 ...props
66}: NumberFieldPrimitive.Increment.Props): React.ReactElement {
67 return (
68 <NumberFieldPrimitive.Increment
69 className={cn(
70 "relative flex shrink-0 cursor-pointer items-center justify-center rounded-e-[calc(var(--radius-lg)-1px)] in-data-[size=sm]:px-[calc(--spacing(2.5)-1px)] px-[calc(--spacing(3)-1px)] transition-colors pointer-coarse:after:absolute pointer-coarse:after:size-full pointer-coarse:after:min-h-11 pointer-coarse:after:min-w-11 hover:bg-accent",
71 className,
72 )}
73 data-slot="number-field-increment"
74 {...props}
75 >
76 <PlusIcon />
77 </NumberFieldPrimitive.Increment>
78 );
79}
80
81export function NumberFieldInput({
82 className,
83 ...props
84}: NumberFieldPrimitive.Input.Props): React.ReactElement {
85 return (
86 <NumberFieldPrimitive.Input
87 className={cn(
88 "h-8.5 in-data-[size=lg]:h-9.5 in-data-[size=sm]:h-7.5 w-full min-w-0 grow bg-transparent in-data-[size=sm]:px-[calc(--spacing(2.5)-1px)] px-[calc(--spacing(3)-1px)] text-center tabular-nums in-data-[size=lg]:leading-9.5 in-data-[size=sm]:leading-7.5 leading-8.5 outline-none [transition:background-color_5000000s_ease-in-out_0s] sm:h-7.5 sm:in-data-[size=lg]:h-8.5 sm:in-data-[size=sm]:h-6.5 sm:in-data-[size=lg]:leading-8.5 sm:in-data-[size=sm]:leading-8.5 sm:leading-7.5",
89 className,
90 )}
91 data-slot="number-field-input"
92 {...props}
93 />
94 );
95}
96
97export { NumberFieldPrimitive };