-
Notifications
You must be signed in to change notification settings - Fork 5.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(input-number): update docs #1818
base: main
Are you sure you want to change the base?
Conversation
@dinogit is attempting to deploy a commit to the shadcn-pro Team on Vercel. A member of the Team first needs to authorize it. |
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
This looks great. Thank you. Some quick notes: I wonder if we can make the component composable. Something like: <NumberField>
<NumberFieldInput />
<NumberFieldIncrement />
<NumberFieldDecrement />
</NumberField> |
sure, will give it a try |
Hey guys! I've recently been working on a Registrazione.schermo.2023-11-14.alle.14.37.25.movI’m sharing the full source code here in case it could be helpful in any way. Usage: <NumberInput
label="% Example"
defaultValue={0.1}
minValue={0}
formatOptions={{
style: "percent",
notation: "compact",
}}
/> Source: "use client";
import * as React from "react";
import { useNumberFieldState } from "react-stately";
import {
type AriaNumberFieldProps,
useLocale,
useNumberField,
useButton,
type AriaButtonOptions,
} from "react-aria";
import { ChevronUp, ChevronDown } from "lucide-react";
import { Input } from "./input";
import { Label } from "./label";
import { cn } from "./utils/cn";
export const NumberInput = ({
className,
...props
}: {
className?: string;
} & AriaNumberFieldProps) => {
const { locale } = useLocale();
const state = useNumberFieldState({ ...props, locale });
const inputRef = React.useRef(null);
const {
labelProps,
groupProps,
inputProps,
incrementButtonProps,
decrementButtonProps,
} = useNumberField(props, state, inputRef);
return (
<div className={className}>
<Label {...labelProps}>{props.label}</Label>
<div className="grid h-10 grid-cols-[auto_2.3rem]" {...groupProps}>
<Input
{...inputProps}
className={cn(
inputProps.className,
"row-span-2 h-full rounded-r-none border-r-0",
)}
ref={inputRef}
/>
<AriaButton
className="rounded-tr-md border px-2 hover:bg-border"
{...incrementButtonProps}
>
<ChevronUp className="mx-auto" size="1em" />
</AriaButton>
<AriaButton
className="rounded-br-md border-x border-b px-2 hover:bg-border"
{...decrementButtonProps}
>
<ChevronDown className="mx-auto" size="1em" />
</AriaButton>
</div>
</div>
);
};
const AriaButton = ({
className,
children,
...props
}: {
className?: string;
children: React.ReactNode;
} & AriaButtonOptions<"button">) => {
const ref = React.useRef(null);
const { buttonProps } = useButton(props, ref);
return (
<button
{...buttonProps}
className={cn(buttonProps.className, className)}
ref={ref}
>
{children}
</button>
);
}; |
Hey everyone! I've been working on a solution for a personal project and thought I'd share. Source: 'use client'
import * as React from 'react'
import { useLocale } from 'react-aria'
import { ChevronDownIcon, ChevronUpIcon } from '@radix-ui/react-icons'
import { type NumberFieldState, useNumberFieldState, NumberFieldStateOptions } from 'react-stately'
import { Input, InputProps } from '@/src/components/ui/input'
import { Button, ButtonProps } from '@/src/components/ui/button'
import { cn } from '@/src/lib/utils'
import { ControllerRenderProps } from 'react-hook-form'
type NumberFieldContextValue = NumberFieldState
const NumberFieldContext = React.createContext<NumberFieldContextValue>(
{} as NumberFieldContextValue
)
const useNumberField = () => {
const numberFieldContext = React.useContext(NumberFieldContext)
if (!numberFieldContext) {
throw new Error('useNumberField should be used within <NumberField>')
}
return numberFieldContext
}
type NumberFieldProps = Partial<NumberFieldStateOptions> & ControllerRenderProps
const NumberField = React.forwardRef<HTMLDivElement, React.PropsWithChildren<NumberFieldProps>>(
({ children, ...props }, ref) => {
const { locale } = useLocale()
const state = useNumberFieldState({ ...props, locale })
return (
<NumberFieldContext.Provider value={state}>
<div ref={ref} className={cn('flex rounded-md gap-1')}>
{children}
</div>
</NumberFieldContext.Provider>
)
}
)
NumberField.displayName = 'NumberField'
const NumberFieldIncrement = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, ...props }, ref) => {
const state = useNumberField()
return (
<Button
variant={'outline'}
size={'icon'}
type="button"
className={cn('aspect-square', className)}
onClick={state.increment}
ref={ref}
{...props}
>
<ChevronUpIcon />
</Button>
)
}
)
NumberFieldIncrement.displayName = 'NumberFieldIncrement'
const NumberFieldDecrement = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, ...props }, ref) => {
const state = useNumberField()
return (
<Button
variant={'outline'}
size={'icon'}
type="button"
className={cn('aspect-square', className)}
onClick={state.decrement}
ref={ref}
{...props}
>
<ChevronDownIcon />
</Button>
)
}
)
NumberFieldDecrement.displayName = 'NumberFieldDecrement'
const NumberFieldInput = React.forwardRef<HTMLInputElement, InputProps>(
({ className, ...props }, ref) => {
const state = useNumberField()
return (
<Input
ref={ref}
type="number"
className={cn(
'[appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none',
className
)}
value={state.inputValue}
onChange={(e) => state.setInputValue(e.target.value)}
{...props}
/>
)
}
)
NumberFieldInput.displayName = 'NumberFieldInput'
export { NumberField, NumberFieldInput, NumberFieldDecrement, NumberFieldIncrement } Usage: // ...
<FormField
control={form.control}
name="exampleNumberField"
render={({ field }) => (
<FormItem>
<FormLabel>Example number Field</FormLabel>
<FormControl>
<NumberField {...field}>
<NumberFieldInput />
<NumberFieldDecrement />
<NumberFieldIncrement />
</NumberField>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
// ... Example: Screen.Recording.2024-01-16.at.8.22.34.PM.mov |
Input Number component wit min, max and steps