File size: 2,107 Bytes
cd40c70
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
import * as React from 'react';
import { Search, XCircle } from 'lucide-react';
import { Input } from './input';
import { cn } from '../../lib/utils';

type InputProps = React.ComponentProps<typeof Input>;
export type SearchInputProps = Omit<InputProps, 'type' | 'size'> & {
  value: string;
  onValueChange: (value: string) => void;
  /** Size: sm (h-9), default (h-10), lg (h-11) */
  size?: 'sm' | 'default' | 'lg';
};

const SearchInput = React.forwardRef<HTMLInputElement, SearchInputProps>(
  (
    {
      value,
      onValueChange,
      placeholder,
      'aria-label': ariaLabel,
      className,
      size = 'default',
      ...props
    },
    ref
  ) => {
    const sizeClasses = {
      sm: 'h-9 pl-9 pr-9',
      default: 'h-10 pl-10 pr-10',
      lg: 'h-11 pl-11 pr-11 text-base',
    };
    const iconSizes = {
      sm: 'h-4 w-4',
      default: 'h-4 w-4',
      lg: 'h-5 w-5',
    };
    const hasValue = value.length > 0;

    return (
      <div className={cn('relative w-full', className)}>
        <Search
          className={cn(
            'absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground pointer-events-none',
            iconSizes[size]
          )}
          aria-hidden
        />
        <Input
          ref={ref}
          type="search"
          value={value}
          onChange={(e) => onValueChange(e.target.value)}
          placeholder={placeholder}
          aria-label={ariaLabel}
          className={cn(sizeClasses[size])}
          {...props}
        />
        {hasValue && (
          <button
            type="button"
            onClick={() => onValueChange('')}
            className="absolute right-2 top-1/2 -translate-y-1/2 rounded-full p-1 text-muted-foreground hover:bg-muted hover:text-foreground focus:outline-none focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-2"
            aria-label="Clear search"
          >
            <XCircle className={cn(iconSizes[size])} strokeWidth={2} />
          </button>
        )}
      </div>
    );
  }
);
SearchInput.displayName = 'SearchInput';

export { SearchInput };