on f1ff0cf help sign in
133 lines 3.3 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 className={className} col>
73 <Flex.Item as="label" className="f6 lh-tall" htmlFor={name}>
74 {label}
75 </Flex.Item>
76 <Flex.Item as={Flex} className="rel" row>
77 <Flex.Item
78 as="input"
79 {...rest}
80 cols={[1, accessory ? 11 : 13]}
81 className={cn(
82 'rel b b1 p3 outline-none',
83 { mono },
84 {
85 'bg-white': !disabled,
86 'bg-gray1': disabled,
87 },
88 {
89 gray4: !focused,
90 black: focused,
91 },
92 {
93 'b-green3': visiblyPassed,
94 'b-black': focused && !visiblyPassed,
95 'b-yellow3': !focused && !visiblyPassed && error,
96 'b-gray3': !focused && !error && !visiblyPassed,
97 }
98 // TODO: inputClassName ?
99 )}
100 style={{
101 ...(disabled && {
102 pointerEvents: 'none',
103 cursor: 'not-allowed',
104 }),
105 }}
106 id={name}
107 name={name}
108 onKeyPress={onKeyPress}
109 {...bind}
110 flex
111 />
112 {accessory && (
113 <div
114 className="abs"
115 style={{
116 top: 0,
117 right: 0,
118 height: '100%',
119 width: '44px',
120 overflow: 'hidden',
121 }}>
122 {accessory}
123 </div>
124 )}
125 </Flex.Item>
126 {error && (
127 <Flex.Item as={ErrorText} className="mv1">
128 {error}
129 </Flex.Item>
130 )}
131 </Flex>
132 );