aboutsummaryrefslogtreecommitdiffstats
path: root/apps/mobile/components/ui/SearchInput/SearchInput.tsx
blob: 7e816ab65a4b69afcd7185d23b55b4d31c966296 (plain) (blame)
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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
import * as React from "react";
import { Pressable, TextInput, View } from "react-native";
import Animated, { FadeIn, FadeOut } from "react-native-reanimated";
import { TailwindResolver } from "@/components/TailwindResolver";
import { Button } from "@/components/ui/Button";
import { useColorScheme } from "@/lib/useColorScheme";
import { cn } from "@/lib/utils";
import { useAugmentedRef, useControllableState } from "@rn-primitives/hooks";
import { Icon } from "@roninoss/icons";

import type { SearchInputProps } from "./types";

const SearchInput = React.forwardRef<
  React.ElementRef<typeof TextInput>,
  SearchInputProps
>(
  (
    {
      value: valueProp,
      onChangeText: onChangeTextProp,
      placeholder = "Search...",
      containerClassName,
      iconContainerClassName,
      className,
      onCancel,
      ...props
    },
    ref,
  ) => {
    const { colors } = useColorScheme();
    const inputRef = useAugmentedRef({ ref, methods: { focus, blur, clear } });
    const [value = "", onChangeText] = useControllableState({
      prop: valueProp,
      defaultProp: valueProp ?? "",
      onChange: onChangeTextProp,
    });

    function focus() {
      inputRef.current?.focus();
    }

    function blur() {
      inputRef.current?.blur();
    }

    function clear() {
      onCancel?.();
      onChangeText("");
    }

    return (
      <Button
        variant="plain"
        className={cn(
          "android:gap-0 android:h-14 flex-row items-center rounded-full bg-card px-2",
          containerClassName,
        )}
        onPress={focus}
      >
        <View
          className={cn("p-2", iconContainerClassName)}
          pointerEvents="none"
        >
          <TailwindResolver
            className="text-muted"
            comp={(styles) => (
              <Icon
                color={styles?.color?.toString()}
                name="magnify"
                size={24}
              />
            )}
          />
        </View>

        <View className="flex-1" pointerEvents="none">
          <TextInput
            ref={inputRef}
            placeholder={placeholder}
            className={cn(
              "flex-1 rounded-r-full p-2 text-[17px] text-foreground placeholder:text-muted",
              className,
            )}
            placeholderTextColor={colors.foreground}
            value={value}
            onChangeText={onChangeText}
            role="searchbox"
            {...props}
          />
        </View>
        {!!value && (
          <Animated.View entering={FadeIn} exiting={FadeOut.duration(150)}>
            <Pressable className="p-2" onPress={clear}>
              <TailwindResolver
                className="text-muted"
                comp={(styles) => (
                  <Icon
                    name="close"
                    size={24}
                    color={styles?.color?.toString()}
                  />
                )}
              />
            </Pressable>
          </Animated.View>
        )}
      </Button>
    );
  },
);

SearchInput.displayName = "SearchInput";

export { SearchInput };