on 7ac8cfe help sign in
134 lines 3.2 kB history raw
1import React, { useCallback, useEffect } from 'react';
2import cn from 'classnames';
4import Flex from './Flex';
5import { ErrorText } from './Typography';
7export default function Input({
8 // visuals
9 name,
10 label,
11 className,
12 accessory,
13 mono = false,
15 // callbacks
16 onValue,
17 onPass,
18 onError,
19 onFocus,
20 onEnter,
22 // state from hook
23 focused,
24 pass,
25 visiblyPassed,
26 error,
27 data,
28 bind,
29 autoFocus,
30 disabled,
32 // ignored
33 initialValue,
34 validators,
35 transformers,
37 // extra
38 ...rest
39}) {
40 // NB(shrugs): we disable exhaustive deps because we don't want the callbacks
41 // (whose identity might change between renders) to trigger a re-notify of
42 // state that we already know
43 // this also happens to prevent render loops when rendering a map of inputs
45 // notify parent of enter keypress iff not disabled and passing
46 const onKeyPress = useCallback(
47 e => !disabled && pass && e.key === 'Enter' && onEnter && onEnter(),
48 [disabled, pass] // eslint-disable-line react-hooks/exhaustive-deps
49 );
51 // notify parent of value only when passing
52 useEffect(() => {
53 pass && onValue && onValue(data);
54 }, [pass, data]); // eslint-disable-line react-hooks/exhaustive-deps
56 // notify parent of pass
57 useEffect(() => {
58 onPass && onPass(pass);
59 }, [pass]); // eslint-disable-line react-hooks/exhaustive-deps
61 // notify parent of error whenever error changes
62 useEffect(() => {
63 onError && onError(error);
64 }, [error]); // eslint-disable-line react-hooks/exhaustive-deps
66 // notify parent of focus
67 useEffect(() => {
68 onFocus && onFocus(focused);
69 }, [focused]); // eslint-disable-line react-hooks/exhaustive-deps
71 return (
72 <Flex
73 className={className}
74 col
75 style={{
76 ...(disabled && {
77 pointerEvents: 'none',
78 cursor: 'not-allowed',
79 }),
80 }}>
81 <Flex.Item as="label" className="f6 lh-tall" htmlFor={name}>
82 {label}
83 </Flex.Item>
84 <Flex.Item as={Flex} className="rel" row>
85 <Flex.Item
86 as="input"
87 {...rest}
88 className={cn(
89 'b b1 p3 outline-none',
90 { mono },
91 {
92 'bg-white': !disabled,
93 'bg-gray1': disabled,
94 },
95 {
96 gray4: !focused,
97 black: focused,
98 },
99 {
100 'b-green3': visiblyPassed,
101 'b-black': focused && !visiblyPassed,
102 'b-yellow3': !focused && error,
103 'b-gray3': !focused && !error && !visiblyPassed,
104 }
105 // TODO: inputClassName ?
106 )}
107 id={name}
108 name={name}
109 onKeyPress={onKeyPress}
110 {...bind}
111 flex
112 />
113 {accessory && (
114 <div
115 className="abs"
116 style={{
117 top: 0,
118 right: 0,
119 height: '100%',
120 width: '44px',
121 overflow: 'hidden',
122 }}>
123 {accessory}
124 </div>
125 )}
126 </Flex.Item>
127 {error && (
128 <Flex.Item as={ErrorText} className="mv1">
129 {error}
130 </Flex.Item>
131 )}
132 </Flex>
133 );