Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 30 additions & 31 deletions core/components/ui/counter/counter.tsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,23 @@
import { cva } from 'class-variance-authority';
import { ChevronDown, ChevronUp } from 'lucide-react';
import { ComponentPropsWithRef, ElementRef, forwardRef, useRef, useState } from 'react';
import {
ComponentPropsWithRef,
ElementRef,
forwardRef,
useImperativeHandle,
useRef,
useState,
} from 'react';

import { cn } from '~/lib/utils';

const inputVariants = cva(
'peer/input w-full border-2 border-gray-200 px-12 py-2.5 text-center text-base placeholder:text-gray-500 hover:border-primary focus-visible:border-primary focus-visible:outline-none focus-visible:ring-4 focus-visible:ring-primary/20 disabled:bg-gray-100 disabled:hover:border-gray-200 peer-hover/down:border-primary peer-hover/up:border-primary peer-hover/down:disabled:border-gray-200 peer-hover/up:disabled:border-gray-200 [&::-webkit-inner-spin-button]:appearance-none',
{
variants: {
variant: {
success:
'border-success-secondary focus-visible:border-success-secondary focus-visible:ring-success-secondary/20 disabled:border-gray-200 hover:border-success peer-hover/down:border-success peer-hover/up:border-success peer-hover/down:disabled:border-gray-200 peer-hover/up:disabled:border-gray-200',
error:
'border-error-secondary focus-visible:border-error-secondary focus-visible:ring-error-secondary/20 disabled:border-gray-200 hover:border-error peer-hover/down:border-error peer-hover/up:border-error peer-hover/down:disabled:border-gray-200 peer-hover/up:disabled:border-gray-200',
},
},
},
);

type VariantTypes = 'success' | 'error';

interface CounterProps extends Omit<ComponentPropsWithRef<'input'>, 'onChange'> {
interface Props extends Omit<ComponentPropsWithRef<'input'>, 'onChange'> {
defaultValue?: number | '';
isInteger?: boolean;
max?: number;
min?: number;
step?: number;
value?: number | '';
variant?: VariantTypes;
variant?: 'success' | 'error';
onChange?: (value: number | '') => void;
}

Expand All @@ -43,7 +33,9 @@ const getDefaultValue = (defaultValue: number | '', min: number, max: number) =>
return defaultValue;
};

export const Counter = forwardRef<ElementRef<'div'>, CounterProps>(
type CounterRef = ElementRef<'input'> | null;

const Counter = forwardRef<ElementRef<'input'>, Props>(
(
{
children,
Expand All @@ -63,7 +55,10 @@ export const Counter = forwardRef<ElementRef<'div'>, CounterProps>(
ref,
) => {
const [value, setValue] = useState<number | ''>(getDefaultValue(defaultValue, min, max));
const inputRef = useRef<ElementRef<'input'>>(null);
const inputRef = useRef<CounterRef>(null);

useImperativeHandle<CounterRef, CounterRef>(ref, () => inputRef.current);

const currValue = valueProp ?? value;

const updateValue = (newValue: number | '') => {
Expand Down Expand Up @@ -103,13 +98,11 @@ export const Counter = forwardRef<ElementRef<'div'>, CounterProps>(
};

return (
<div className={cn('relative')} ref={ref}>
<div className={cn('relative', className)}>
<button
aria-hidden="true"
aria-label="Decrease count"
className={cn(
'peer/down absolute start-0 top-0 flex h-full w-12 items-center justify-center focus-visible:outline-none disabled:text-gray-200',
)}
className="peer/down absolute start-0 top-0 flex h-full w-12 items-center justify-center focus-visible:outline-none disabled:text-gray-200"
disabled={!canDecrement()}
onClick={() => {
decrement();
Expand All @@ -125,9 +118,7 @@ export const Counter = forwardRef<ElementRef<'div'>, CounterProps>(
<button
aria-hidden="true"
aria-label="Increase count"
className={cn(
'peer/up absolute end-0 top-0 flex h-full w-12 items-center justify-center focus-visible:outline-none disabled:text-gray-200',
)}
className="peer/up absolute end-0 top-0 flex h-full w-12 items-center justify-center focus-visible:outline-none disabled:text-gray-200"
disabled={!canIncrement()}
onClick={() => {
increment();
Expand All @@ -141,7 +132,13 @@ export const Counter = forwardRef<ElementRef<'div'>, CounterProps>(
</button>

<input
className={cn(inputVariants({ variant, className }))}
className={cn(
'peer/input w-full border-2 border-gray-200 px-12 py-2.5 text-center text-base placeholder:text-gray-500 hover:border-primary focus-visible:border-primary focus-visible:outline-none focus-visible:ring-4 focus-visible:ring-primary/20 disabled:bg-gray-100 disabled:hover:border-gray-200 peer-hover/down:border-primary peer-hover/up:border-primary peer-hover/down:disabled:border-gray-200 peer-hover/up:disabled:border-gray-200 [&::-webkit-inner-spin-button]:appearance-none',
variant === 'success' &&
'border-success-secondary hover:border-success focus-visible:border-success-secondary focus-visible:ring-success-secondary/20 disabled:border-gray-200 peer-hover/down:border-success peer-hover/up:border-success peer-hover/down:disabled:border-gray-200 peer-hover/up:disabled:border-gray-200',
variant === 'error' &&
'border-error-secondary hover:border-error focus-visible:border-error-secondary focus-visible:ring-error-secondary/20 disabled:border-gray-200 peer-hover/down:border-error peer-hover/up:border-error peer-hover/down:disabled:border-gray-200 peer-hover/up:disabled:border-gray-200',
)}
disabled={disabled}
max={max}
min={min}
Expand All @@ -168,15 +165,17 @@ export const Counter = forwardRef<ElementRef<'div'>, CounterProps>(

updateValue(Number.isNaN(valueAsNumber) ? '' : valueAsNumber);
}}
ref={inputRef}
step={step}
type="number"
value={currValue}
{...props}
ref={inputRef}
/>
</div>
);
},
);

Counter.displayName = 'Counter';

export { Counter };