1 | import { h, Component } from 'preact'
|
2 | const noOp = val => val
|
3 |
|
4 | class Input extends Component {
|
5 | constructor (props) {
|
6 | super(props)
|
7 | if (props.getSetter) {
|
8 | this.setter = props.getSetter(props.name)
|
9 | }
|
10 | this.state = {
|
11 | hasChanged: false,
|
12 | valid: this.isValid(props.value)
|
13 | }
|
14 | }
|
15 |
|
16 | isValid (val) {
|
17 | if (!this.props.test) {
|
18 | return true
|
19 | }
|
20 | return this.props.test(val)
|
21 | }
|
22 |
|
23 | componentDidMount () {
|
24 | const { value } = this.props
|
25 | if (value) {
|
26 | this.updateValue(value)
|
27 | } else {
|
28 | const result = this.isValid(value)
|
29 | this.setState({valid: result})
|
30 | this.setter && this.setter({validity: result})
|
31 | }
|
32 | }
|
33 |
|
34 | updateValue (val) {
|
35 | const { type, clean, test } = this.props
|
36 | if (type === 'number' && val && typeof val === 'string') {
|
37 | val = Number(val)
|
38 | }
|
39 | if (clean) {
|
40 | val = clean(val)
|
41 | }
|
42 | const update = {
|
43 | value: val
|
44 | }
|
45 | if (test) {
|
46 | const result = this.isValid(val)
|
47 | this.setState({valid: result})
|
48 | update.validity = result
|
49 | }
|
50 | if (this.setter) {
|
51 | this.setter(update)
|
52 | }
|
53 | }
|
54 |
|
55 | render ({
|
56 | name,
|
57 | autoFocus = false,
|
58 | label,
|
59 | subLabel,
|
60 | helpText,
|
61 | value,
|
62 | containerClasses = 'mv2',
|
63 | onFocus = noOp,
|
64 | placeholder,
|
65 | disabled = false,
|
66 | className = 'pa2 input-reset br2 bg-transparent hover-bg w-100 ba',
|
67 | getSetter = null,
|
68 | clean = noOp,
|
69 | test = null,
|
70 | min,
|
71 | max,
|
72 | inputmode,
|
73 | pattern,
|
74 | type = 'text'},
|
75 | {
|
76 | valid,
|
77 | hasChanged
|
78 | }) {
|
79 | const isCheckbox = type === 'checkbox'
|
80 | const isTextarea = type === 'textarea'
|
81 |
|
82 | if (isCheckbox) {
|
83 | className = 'mr2'
|
84 | }
|
85 |
|
86 | if (type === 'number' && !inputmode) {
|
87 | inputmode = 'numeric'
|
88 | }
|
89 |
|
90 | const onInput = e => {
|
91 | if (!this.state.hasChanged) {
|
92 | this.setState({hasChanged: true})
|
93 | }
|
94 | this.updateValue(e.target.value)
|
95 | }
|
96 |
|
97 | const colorClasses = (hasChanged && !valid) ? 'b--red' : 'b--black-40'
|
98 | const inputClasses = className + ' ' + colorClasses
|
99 |
|
100 | return (
|
101 | h('div', {
|
102 | key: name,
|
103 | className: containerClasses
|
104 | }, [
|
105 | (label || subLabel) && !isCheckbox && (
|
106 | h('label', {
|
107 | className: 'db fw9 lh-copy f6',
|
108 | htmlFor: name
|
109 | }, [
|
110 | label,
|
111 | ' ',
|
112 | subLabel && h('span', { className: 'f6 fw5' }, [subLabel])
|
113 | ])
|
114 | ),
|
115 | !isTextarea && !isCheckbox && (
|
116 | h('input', {
|
117 | className: inputClasses,
|
118 | autoFocus,
|
119 | name,
|
120 | type,
|
121 | placeholder,
|
122 | onFocus,
|
123 | onInput,
|
124 | value,
|
125 | disabled,
|
126 | id: name,
|
127 | min,
|
128 | max,
|
129 | inputmode,
|
130 | pattern,
|
131 | 'data-e2e': `${name}Input`
|
132 | })
|
133 | ),
|
134 | isTextarea && (
|
135 | h('textarea', {
|
136 | className: inputClasses,
|
137 | autoFocus,
|
138 | name,
|
139 | placeholder,
|
140 | onFocus,
|
141 | onInput,
|
142 | value,
|
143 | disabled,
|
144 | id: name,
|
145 | 'data-e2e': `${name}Input`
|
146 | }, [placeholder])
|
147 | ),
|
148 | type === 'checkbox' && (
|
149 | h('div', {className: 'flex items-center'}, [
|
150 | h('input', {
|
151 | className: inputClasses,
|
152 | autoFocus,
|
153 | name,
|
154 | type: 'checkbox',
|
155 | onFocus,
|
156 | onChange: e => {
|
157 | this.updateValue(e.target.checked)
|
158 | },
|
159 | value,
|
160 | disabled,
|
161 | id: name,
|
162 | 'data-e2e': `${name}Input`
|
163 | }),
|
164 | h('label', {
|
165 | className: 'fw6 lh-copy f6',
|
166 | htmlFor: name
|
167 | }, [
|
168 | label,
|
169 | ' ',
|
170 | subLabel && h('span', {className: 'f6 fw5'}, [subLabel])
|
171 | ])
|
172 | ])
|
173 | ),
|
174 | helpText && h('span', {className: 'f6 fw5 lh-copy'}, [helpText])
|
175 | ])
|
176 | )
|
177 | }
|
178 | }
|
179 |
|
180 | export default Input
|