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