on cd91628 help sign in
135 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,
36 setValue,
38 // extra
39 ...rest
40}) {
41 // NB(shrugs): we disable exhaustive deps because we don't want the callbacks
42 // (whose identity might change between renders) to trigger a re-notify of
43 // state that we already know
44 // this also happens to prevent render loops when rendering a map of inputs
46 // notify parent of enter keypress iff not disabled and passing
47 const onKeyPress = useCallback(
48 e => !disabled && pass && e.key === 'Enter' && onEnter && onEnter(),
49 [disabled, pass] // eslint-disable-line react-hooks/exhaustive-deps
50 );
52 // notify parent of value only when passing
53 useEffect(() => {
54 pass && onValue && onValue(data);
55 }, [pass, data]); // eslint-disable-line react-hooks/exhaustive-deps
57 // notify parent of pass
58 useEffect(() => {
59 onPass && onPass(pass);
60 }, [pass]); // eslint-disable-line react-hooks/exhaustive-deps
62 // notify parent of error whenever error changes
63 useEffect(() => {
64 onError && onError(error);
65 }, [error]); // eslint-disable-line react-hooks/exhaustive-deps
67 // notify parent of focus
68 useEffect(() => {
69 onFocus && onFocus(focused);
70 }, [focused]); // eslint-disable-line react-hooks/exhaustive-deps
72 return (
73 <Flex
74 className={cn(className, 'mb1')}
75 col
76 style={{
77 ...(disabled && {
78 pointerEvents: 'none',
79 cursor: 'not-allowed',
80 }),
81 }}>
82 <Flex.Item as="label" className="f6 lh-tall" htmlFor={name}>
83 {label}
84 </Flex.Item>
85 <Flex.Item as={Flex} className="rel" row>
86 <Flex.Item
87 as="input"
88 {...rest}
89 className={cn(
90 'b b1 p3 outline-none',
91 { mono },
92 {
93 'bg-white': !disabled,
94 'bg-gray1': disabled,
95 },
96 {
97 gray4: !focused,
98 black: focused,
99 },
100 {
101 'b-green3': visiblyPassed,
102 'b-black': focused && !visiblyPassed,
103 'b-yellow3': !focused && error,
104 'b-gray3': !focused && !error && !visiblyPassed,
105 }
106 // TODO: inputClassName ?
107 )}
108 id={name}
109 name={name}
110 onKeyPress={onKeyPress}
111 {...bind}
112 flex
113 />
114 {accessory && (
115 <div
116 className="abs"
117 style={{
118 top: 0,
119 right: 0,
120 height: '100%',
121 width: '44px',
122 overflow: 'hidden',
123 }}>
124 {accessory}
125 </div>
126 )}
127 </Flex.Item>
128 {error && (
129 <Flex.Item as={ErrorText} className="mv1">
130 {error}
131 </Flex.Item>
132 )}
133 </Flex>
134 );