on a6f399f help sign in
128 lines 3.1 kB history raw
1import React from 'react';
2import cn from 'classnames';
3import { useField } from 'react-final-form';
4import Flex from './Flex';
5import { ErrorText } from './Typography';
7export default function Input({
8 // visuals
9 type,
10 name,
11 label,
12 className,
13 accessory,
14 disabled = false,
15 mono = false,
16 obscure,
18 // callbacks
19 onEnter,
21 // state
22 config,
24 // extra
25 ...rest
26}) {
27 const {
28 input,
29 meta: {
30 active,
31 error,
32 submitError,
33 dirtySinceLastSubmit,
34 submitting,
35 submitSucceeded,
36 touched,
37 valid,
38 },
39 } = useField(name, config);
41 disabled = disabled || submitting || submitSucceeded;
43 // choose the base dom component
44 const BaseComponent = type === 'textarea' ? 'textarea' : 'input';
46 // notify parent of enter keypress iff not disabled and passing
47 // TODO: integrate this into react-final-form submission
48 // const onKeyPress = useCallback(
49 // e => !disabled && valid && e.key === 'Enter' && onEnter && onEnter(),
50 // [disabled, valid] // eslint-disable-line react-hooks/exhaustive-deps
51 // );
53 const showError = !!error;
54 const showSubmitError = !!submitError && !dirtySinceLastSubmit;
55 const indicateError = touched && !active && (showError || showSubmitError);
57 return (
58 <Flex
59 col
60 className={cn(className, 'mb1')}
61 style={{
62 ...(disabled && {
63 pointerEvents: 'none',
64 cursor: 'not-allowed',
65 }),
66 }}>
67 <Flex.Item
68 as="label"
69 className={cn('f6 lh-tall', {
70 black: !disabled,
71 gray4: disabled,
72 })}
73 htmlFor={name}>
74 {label}
75 </Flex.Item>
76 <Flex.Item as={Flex} row className="rel">
77 <Flex.Item
78 flex
79 as={BaseComponent}
80 {...rest}
81 // NOTE: 1.15 from input line-height, 24px = 12px * 2 (from p3 styling)
82 style={type === 'textarea' ? { minHeight: 'calc(1.5rem * 3)' } : {}}
83 className={cn(
84 'b b1 p3 outline-none bs-none',
85 { mono },
86 {
87 'bg-white': !disabled,
88 'bg-gray1': disabled,
89 },
90 {
91 gray4: !(active || touched),
92 black: active || touched,
93 },
94 {
95 'b-green3': valid,
96 'b-black': !valid && active,
97 'b-yellow3': !valid && !active && touched,
98 'b-gray2': !valid && !active && !touched,
99 }
100 )}
101 id={name}
102 name={name}
103 {...input}
104 type={type === 'textarea' ? undefined : type}
105 />
106 {accessory && (
107 <div
108 className="abs"
109 style={{
110 top: 0,
111 right: 0,
112 height: '100%',
113 width: 'calc(1.5em + 24px)',
114 overflow: 'hidden',
115 }}>
116 {accessory}
117 </div>
118 )}
119 </Flex.Item>
121 {indicateError && (
122 <Flex.Item as={ErrorText} className="mv1">
123 {error || submitError}
124 </Flex.Item>
125 )}
126 </Flex>
127 );