on d27f157 help sign in
126 lines 3.0 kB history raw
1import React, { useCallback } from 'react';
2import cn from 'classnames';
4import Flex from './Flex';
5import { ErrorText } from './Typography';
7export default React.memo(function Input({
8 // visuals
9 type,
10 name,
11 label,
12 className,
13 accessory,
14 mono = false,
16 // callbacks
17 onEnter,
19 // state from hook
20 focused,
21 pass,
22 syncPass,
23 visiblyPassed,
24 error,
25 hintError,
26 data,
27 bind,
28 autoFocus,
29 disabled,
30 touched,
32 // ignored
33 initialValue,
34 validators,
35 transformers,
37 // extra
38 textarea = false,
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 return (
53 <Flex
54 className={cn(className, 'mb1')}
55 col
56 style={{
57 ...(disabled && {
58 pointerEvents: 'none',
59 cursor: 'not-allowed',
60 }),
61 }}>
62 <Flex.Item
63 as="label"
64 className={cn('f6 lh-tall', {
65 black: !disabled,
66 gray4: disabled,
67 })}
68 htmlFor={name}>
69 {label}
70 </Flex.Item>
71 <Flex.Item as={Flex} className="rel" row>
72 <Flex.Item
73 as={type === 'textarea' ? 'textarea' : 'input'}
74 type={type === 'textarea' ? undefined : type}
75 {...rest}
76 // NOTE: 1.15 from input line-height, 24px = 12px * 2 (from p3 styling)
77 style={
78 type === 'textarea' ? { minHeight: 'calc(1.15rem * 2 + 24px)' } : {}
79 }
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 && !touched,
89 black: focused || touched,
90 },
91 {
92 'b-green3': visiblyPassed,
93 'b-black': focused && !visiblyPassed,
94 'b-yellow3': !focused && hintError,
95 'b-gray2': !focused && !hintError && !visiblyPassed,
96 }
97 // TODO: inputClassName ?
98 )}
99 id={name}
100 name={name}
101 onKeyPress={onKeyPress}
102 {...bind}
103 flex
104 />
105 {accessory && (
106 <div
107 className="abs"
108 style={{
109 top: 0,
110 right: 0,
111 height: '100%',
112 width: 'calc(1.5em + 24px)',
113 overflow: 'hidden',
114 }}>
115 {accessory}
116 </div>
117 )}
118 </Flex.Item>
119 {error && (
120 <Flex.Item as={ErrorText} className="mv1">
121 {error}
122 </Flex.Item>
123 )}
124 </Flex>
125 );
126});