-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathInput.tsx
125 lines (115 loc) · 3.18 KB
/
Input.tsx
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
115
116
117
118
119
120
121
122
123
124
125
import * as React from 'react'
import clsx from 'clsx'
import {
InputProps,
DefaultSize,
DefaultVariant,
SizeClassesTable,
VariantClassesTable,
IconStaticClasses,
ResetStaticClasses,
ResultStaticClass,
StaticClasses,
inputClasses,
StaticInputClasses,
} from '@cypress-design/constants-input'
import {
IconObjectMagnifyingGlass,
IconActionDeleteLarge,
} from '@cypress-design/react-icon'
export interface InputPropsJsx extends InputProps {
id: string
value?: string
className?: string
}
type ReactInputProps = InputPropsJsx & React.HTMLProps<HTMLInputElement>
// tbd: what is a better way of allowing the <input/>
// to propagate its focus state to siblings?
const usePropagateFocusProps = () => {
const [hasFocus, setHasFocus] = React.useState(false)
const enableFocusStyle = React.useCallback(() => setHasFocus(true), [])
const disableFocusStyle = React.useCallback(() => setHasFocus(false), [])
const propagateFocusProps = React.useMemo(() => {
return {
onFocus: enableFocusStyle,
onMouseEnter: enableFocusStyle,
onMouseOver: enableFocusStyle,
onBlur: disableFocusStyle,
onMouseLeave: disableFocusStyle,
onMouseOut: disableFocusStyle,
}
}, [enableFocusStyle, disableFocusStyle])
return {
propagateFocusProps,
hasFocus,
}
}
export const Input: React.FC<ReactInputProps> = ({
id,
className,
customIcon,
isSearch,
searchResults,
size = DefaultSize,
variant = DefaultVariant,
onReset,
onChange,
placeholder,
disabled,
value,
...rest
}) => {
const { propagateFocusProps, hasFocus } = usePropagateFocusProps()
const finalIsDisabled = disabled || variant === 'disabled'
const finalVariant = finalIsDisabled ? 'disabled' : variant
const variantClasses = inputClasses[finalVariant] ?? {}
const iconStrokeColor =
finalVariant !== 'default'
? variantClasses.icon
: hasFocus
? inputClasses.active.icon
: inputClasses.default.icon
const Icon = customIcon ?? isSearch ? IconObjectMagnifyingGlass : null
return (
<div
{...rest}
id={id}
className={clsx(
StaticClasses,
VariantClassesTable[finalVariant],
SizeClassesTable[size],
className
)}
>
{Icon && (
<Icon
className={IconStaticClasses}
strokeColor={iconStrokeColor}
data-cy="text-input--search-icon"
/>
)}
<input
// unclear how to prevent the native [X] from showing
// with tailwind, so only using "text" for now:
type="text" // tbd: support "search"
value={value}
disabled={finalIsDisabled}
onChange={onChange}
placeholder={placeholder}
className={StaticInputClasses}
{...propagateFocusProps}
/>
{onReset && (
<button type="button" className={ResetStaticClasses} onClick={onReset}>
<IconActionDeleteLarge strokeColor={iconStrokeColor} />
</button>
)}
{searchResults && (
<p data-cy="text-input--search-results" className={ResultStaticClass}>
{searchResults.match} of {searchResults.total} {searchResults.entity}
</p>
)}
</div>
)
}
export default Input