diff options
author | 2019-01-28 11:35:25 +0100 | |
---|---|---|
committer | 2019-01-28 11:35:25 +0100 | |
commit | 9a5b313ea12bdb9dc3e3873ca3a2639bd7483e46 (patch) | |
tree | 038dc5e0a209d06e1c15c1e3c4740d5bdda96f8a /packages/forms | |
parent | Add href and type to button component (diff) | |
download | ferdium-app-9a5b313ea12bdb9dc3e3873ca3a2639bd7483e46.tar.gz ferdium-app-9a5b313ea12bdb9dc3e3873ca3a2639bd7483e46.tar.zst ferdium-app-9a5b313ea12bdb9dc3e3873ca3a2639bd7483e46.zip |
Update packages
Diffstat (limited to 'packages/forms')
-rw-r--r-- | packages/forms/package-lock.json | 2 | ||||
-rw-r--r-- | packages/forms/package.json | 2 | ||||
-rw-r--r-- | packages/forms/src/button/index.tsx | 2 | ||||
-rw-r--r-- | packages/forms/src/error/index.tsx | 4 | ||||
-rw-r--r-- | packages/forms/src/index.ts | 1 | ||||
-rw-r--r-- | packages/forms/src/input/index.tsx | 36 | ||||
-rw-r--r-- | packages/forms/src/input/scorePassword.ts | 2 | ||||
-rw-r--r-- | packages/forms/src/label/index.tsx | 4 | ||||
-rw-r--r-- | packages/forms/src/select/index.tsx | 422 | ||||
-rw-r--r-- | packages/forms/src/toggle/index.tsx | 15 | ||||
-rw-r--r-- | packages/forms/src/wrapper/index.tsx | 12 |
11 files changed, 476 insertions, 26 deletions
diff --git a/packages/forms/package-lock.json b/packages/forms/package-lock.json index cdfab23b5..834db8d33 100644 --- a/packages/forms/package-lock.json +++ b/packages/forms/package-lock.json | |||
@@ -1,6 +1,6 @@ | |||
1 | { | 1 | { |
2 | "name": "@meetfranz/forms", | 2 | "name": "@meetfranz/forms", |
3 | "version": "1.0.1", | 3 | "version": "1.0.4", |
4 | "lockfileVersion": 1, | 4 | "lockfileVersion": 1, |
5 | "requires": true, | 5 | "requires": true, |
6 | "dependencies": { | 6 | "dependencies": { |
diff --git a/packages/forms/package.json b/packages/forms/package.json index 8dc9920c8..32b81eed2 100644 --- a/packages/forms/package.json +++ b/packages/forms/package.json | |||
@@ -25,7 +25,7 @@ | |||
25 | "dependencies": { | 25 | "dependencies": { |
26 | "@mdi/js": "^3.3.92", | 26 | "@mdi/js": "^3.3.92", |
27 | "@mdi/react": "^1.1.0", | 27 | "@mdi/react": "^1.1.0", |
28 | "@meetfranz/theme": "^1.0.4", | 28 | "@meetfranz/theme": "file:../theme", |
29 | "react-html-attributes": "^1.4.3", | 29 | "react-html-attributes": "^1.4.3", |
30 | "react-loader": "^2.4.5" | 30 | "react-loader": "^2.4.5" |
31 | }, | 31 | }, |
diff --git a/packages/forms/src/button/index.tsx b/packages/forms/src/button/index.tsx index 92d69ae0e..b7cca7fa4 100644 --- a/packages/forms/src/button/index.tsx +++ b/packages/forms/src/button/index.tsx | |||
@@ -8,7 +8,7 @@ import React, { Component } from 'react'; | |||
8 | import injectStyle from 'react-jss'; | 8 | import injectStyle from 'react-jss'; |
9 | import Loader from 'react-loader'; | 9 | import Loader from 'react-loader'; |
10 | 10 | ||
11 | import { IFormField, IWithStyle, Omit } from '../typings/generic'; | 11 | import { IFormField, IWithStyle } from '../typings/generic'; |
12 | 12 | ||
13 | type ButtonType = 'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'inverted'; | 13 | type ButtonType = 'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'inverted'; |
14 | 14 | ||
diff --git a/packages/forms/src/error/index.tsx b/packages/forms/src/error/index.tsx index 3feaef7f6..9d26e086d 100644 --- a/packages/forms/src/error/index.tsx +++ b/packages/forms/src/error/index.tsx | |||
@@ -12,7 +12,7 @@ interface IProps { | |||
12 | } | 12 | } |
13 | 13 | ||
14 | @observer | 14 | @observer |
15 | class Error extends Component<IProps> { | 15 | class ErrorComponent extends Component<IProps> { |
16 | render() { | 16 | render() { |
17 | const { | 17 | const { |
18 | classes, | 18 | classes, |
@@ -29,4 +29,4 @@ class Error extends Component<IProps> { | |||
29 | } | 29 | } |
30 | } | 30 | } |
31 | 31 | ||
32 | export default injectSheet(styles)(Error); | 32 | export const Error = injectSheet(styles)(ErrorComponent); |
diff --git a/packages/forms/src/index.ts b/packages/forms/src/index.ts index fbeb7e3d3..ea47fe25e 100644 --- a/packages/forms/src/index.ts +++ b/packages/forms/src/index.ts | |||
@@ -1,3 +1,4 @@ | |||
1 | export { Input } from './input'; | 1 | export { Input } from './input'; |
2 | export { Toggle } from './toggle'; | 2 | export { Toggle } from './toggle'; |
3 | export { Button } from './button'; | 3 | export { Button } from './button'; |
4 | export { Select } from './select'; | ||
diff --git a/packages/forms/src/input/index.tsx b/packages/forms/src/input/index.tsx index 9fcf48010..cd6da3778 100644 --- a/packages/forms/src/input/index.tsx +++ b/packages/forms/src/input/index.tsx | |||
@@ -7,19 +7,25 @@ import injectSheet from 'react-jss'; | |||
7 | 7 | ||
8 | import { IFormField, IWithStyle } from '../typings/generic'; | 8 | import { IFormField, IWithStyle } from '../typings/generic'; |
9 | 9 | ||
10 | import Error from '../error'; | 10 | import { Error } from '../error'; |
11 | import Label from '../label'; | 11 | import { Label } from '../label'; |
12 | import Wrapper from '../wrapper'; | 12 | import { Wrapper } from '../wrapper'; |
13 | import scorePasswordFunc from './scorePassword'; | 13 | import { scorePasswordFunc } from './scorePassword'; |
14 | 14 | ||
15 | import styles from './styles'; | 15 | import styles from './styles'; |
16 | 16 | ||
17 | interface IData { | ||
18 | [index: string]: string; | ||
19 | } | ||
20 | |||
17 | interface IProps extends React.InputHTMLAttributes<HTMLInputElement>, IFormField, IWithStyle { | 21 | interface IProps extends React.InputHTMLAttributes<HTMLInputElement>, IFormField, IWithStyle { |
18 | focus?: boolean; | 22 | focus?: boolean; |
19 | prefix?: string; | 23 | prefix?: string; |
20 | suffix?: string; | 24 | suffix?: string; |
21 | scorePassword?: boolean; | 25 | scorePassword?: boolean; |
22 | showPasswordToggle?: boolean; | 26 | showPasswordToggle?: boolean; |
27 | data: IData; | ||
28 | inputClassName?: string; | ||
23 | } | 29 | } |
24 | 30 | ||
25 | interface IState { | 31 | interface IState { |
@@ -48,10 +54,16 @@ class InputComponent extends Component<IProps, IState> { | |||
48 | private inputRef = createRef<HTMLInputElement>(); | 54 | private inputRef = createRef<HTMLInputElement>(); |
49 | 55 | ||
50 | componentDidMount() { | 56 | componentDidMount() { |
51 | const { focus } = this.props; | 57 | const { focus, data } = this.props; |
58 | |||
59 | if (this.inputRef && this.inputRef.current) { | ||
60 | if (focus) { | ||
61 | this.inputRef.current.focus(); | ||
62 | } | ||
52 | 63 | ||
53 | if (focus && this.inputRef && this.inputRef.current) { | 64 | if (data) { |
54 | this.inputRef.current.focus(); | 65 | Object.keys(data).map(key => this.inputRef.current!.dataset[key] = data[key]); |
66 | } | ||
55 | } | 67 | } |
56 | } | 68 | } |
57 | 69 | ||
@@ -77,6 +89,7 @@ class InputComponent extends Component<IProps, IState> { | |||
77 | disabled, | 89 | disabled, |
78 | error, | 90 | error, |
79 | id, | 91 | id, |
92 | inputClassName, | ||
80 | label, | 93 | label, |
81 | prefix, | 94 | prefix, |
82 | scorePassword, | 95 | scorePassword, |
@@ -99,15 +112,17 @@ class InputComponent extends Component<IProps, IState> { | |||
99 | const inputType = type === 'password' && showPassword ? 'text' : type; | 112 | const inputType = type === 'password' && showPassword ? 'text' : type; |
100 | 113 | ||
101 | return ( | 114 | return ( |
102 | <Wrapper> | 115 | <Wrapper |
116 | className={className} | ||
117 | > | ||
103 | <Label | 118 | <Label |
104 | title={label} | 119 | title={label} |
105 | showLabel={showLabel} | 120 | showLabel={showLabel} |
106 | htmlFor={id} | 121 | htmlFor={id} |
107 | className={className} | ||
108 | > | 122 | > |
109 | <div | 123 | <div |
110 | className={classnames({ | 124 | className={classnames({ |
125 | [`${inputClassName}`]: inputClassName, | ||
111 | [`${classes.hasPasswordScore}`]: scorePassword, | 126 | [`${classes.hasPasswordScore}`]: scorePassword, |
112 | [`${classes.wrapper}`]: true, | 127 | [`${classes.wrapper}`]: true, |
113 | [`${classes.disabled}`]: disabled, | 128 | [`${classes.disabled}`]: disabled, |
@@ -122,13 +137,14 @@ class InputComponent extends Component<IProps, IState> { | |||
122 | id={id} | 137 | id={id} |
123 | type={inputType} | 138 | type={inputType} |
124 | name={name} | 139 | name={name} |
125 | value={value} | 140 | defaultValue={value as string} |
126 | placeholder={placeholder} | 141 | placeholder={placeholder} |
127 | spellCheck={spellCheck} | 142 | spellCheck={spellCheck} |
128 | className={classes.input} | 143 | className={classes.input} |
129 | ref={this.inputRef} | 144 | ref={this.inputRef} |
130 | onChange={this.onChange.bind(this)} | 145 | onChange={this.onChange.bind(this)} |
131 | onBlur={onBlur} | 146 | onBlur={onBlur} |
147 | disabled={disabled} | ||
132 | /> | 148 | /> |
133 | {suffix && ( | 149 | {suffix && ( |
134 | <span className={classes.suffix}> | 150 | <span className={classes.suffix}> |
diff --git a/packages/forms/src/input/scorePassword.ts b/packages/forms/src/input/scorePassword.ts index bdad7aa28..0b7719ec1 100644 --- a/packages/forms/src/input/scorePassword.ts +++ b/packages/forms/src/input/scorePassword.ts | |||
@@ -10,7 +10,7 @@ interface IVariations { | |||
10 | upper: boolean; | 10 | upper: boolean; |
11 | } | 11 | } |
12 | 12 | ||
13 | export default function scorePasswordFunc(password: string): number { | 13 | export function scorePasswordFunc(password: string): number { |
14 | let score: number = 0; | 14 | let score: number = 0; |
15 | if (!password) { | 15 | if (!password) { |
16 | return score; | 16 | return score; |
diff --git a/packages/forms/src/label/index.tsx b/packages/forms/src/label/index.tsx index 348b820c5..ee3268b16 100644 --- a/packages/forms/src/label/index.tsx +++ b/packages/forms/src/label/index.tsx | |||
@@ -13,7 +13,7 @@ interface ILabel extends IFormField, React.LabelHTMLAttributes<HTMLLabelElement> | |||
13 | } | 13 | } |
14 | 14 | ||
15 | @observer | 15 | @observer |
16 | class Label extends Component<ILabel> { | 16 | class LabelComponent extends Component<ILabel> { |
17 | static defaultProps = { | 17 | static defaultProps = { |
18 | showLabel: true, | 18 | showLabel: true, |
19 | }; | 19 | }; |
@@ -46,4 +46,4 @@ class Label extends Component<ILabel> { | |||
46 | } | 46 | } |
47 | } | 47 | } |
48 | 48 | ||
49 | export default injectSheet(styles)(Label); | 49 | export const Label = injectSheet(styles)(LabelComponent); |
diff --git a/packages/forms/src/select/index.tsx b/packages/forms/src/select/index.tsx new file mode 100644 index 000000000..58bb7317a --- /dev/null +++ b/packages/forms/src/select/index.tsx | |||
@@ -0,0 +1,422 @@ | |||
1 | import { mdiArrowRightDropCircleOutline, mdiCloseCircle, mdiMagnify } from '@mdi/js'; | ||
2 | import Icon from '@mdi/react'; | ||
3 | import { Theme } from '@meetfranz/theme'; | ||
4 | import classnames from 'classnames'; | ||
5 | import debounce from 'lodash/debounce'; | ||
6 | import { observer } from 'mobx-react'; | ||
7 | import React, { Component, createRef } from 'react'; | ||
8 | import injectStyle from 'react-jss'; | ||
9 | |||
10 | import { IFormField, IWithStyle } from '../typings/generic'; | ||
11 | |||
12 | import { NONAME } from 'dns'; | ||
13 | import { Error } from '../error'; | ||
14 | import { Label } from '../label'; | ||
15 | import { Wrapper } from '../wrapper'; | ||
16 | |||
17 | interface IOptions { | ||
18 | [index: string]: string; | ||
19 | } | ||
20 | |||
21 | interface IData { | ||
22 | [index: string]: string; | ||
23 | } | ||
24 | |||
25 | interface IProps extends IFormField, IWithStyle { | ||
26 | actionText: string; | ||
27 | className?: string; | ||
28 | inputClassName?: string; | ||
29 | defaultValue?: string; | ||
30 | disabled?: boolean; | ||
31 | id?: string; | ||
32 | name: string; | ||
33 | options: IOptions; | ||
34 | value: string; | ||
35 | onChange: (event: React.ChangeEvent<HTMLInputElement>) => void; | ||
36 | showSearch: boolean; | ||
37 | data: IData; | ||
38 | } | ||
39 | |||
40 | interface IState { | ||
41 | open: boolean; | ||
42 | value: string; | ||
43 | needle: string; | ||
44 | selected: number; | ||
45 | options: IOptions; | ||
46 | } | ||
47 | |||
48 | const styles = (theme: Theme) => ({ | ||
49 | select: { | ||
50 | background: theme.selectBackground, | ||
51 | border: theme.selectBorder, | ||
52 | borderRadius: theme.borderRadiusSmall, | ||
53 | height: theme.selectHeight, | ||
54 | fontSize: theme.uiFontSize, | ||
55 | width: '100%', | ||
56 | display: 'flex', | ||
57 | alignItems: 'center', | ||
58 | textAlign: 'left', | ||
59 | color: theme.selectColor, | ||
60 | }, | ||
61 | popup: { | ||
62 | opacity: 0, | ||
63 | height: 0, | ||
64 | overflowX: 'scroll', | ||
65 | border: theme.selectBorder, | ||
66 | borderTop: 0, | ||
67 | transition: 'all 0.3s', | ||
68 | }, | ||
69 | open: { | ||
70 | opacity: 1, | ||
71 | height: 350, | ||
72 | background: theme.selectPopupBackground, | ||
73 | }, | ||
74 | option: { | ||
75 | padding: 10, | ||
76 | borderBottom: theme.selectOptionBorder, | ||
77 | color: theme.selectOptionColor, | ||
78 | |||
79 | '&:hover': { | ||
80 | background: theme.selectOptionItemHover, | ||
81 | color: theme.selectOptionItemHoverColor, | ||
82 | }, | ||
83 | '&:active': { | ||
84 | background: theme.selectOptionItemActive, | ||
85 | color: theme.selectOptionItemActiveColor, | ||
86 | }, | ||
87 | }, | ||
88 | selected: { | ||
89 | fontWeight: 'bold', | ||
90 | }, | ||
91 | toggle: { | ||
92 | marginLeft: 'auto', | ||
93 | fill: theme.selectToggleColor, | ||
94 | transition: 'transform 0.3s', | ||
95 | }, | ||
96 | toggleOpened: { | ||
97 | transform: 'rotateZ(90deg)', | ||
98 | }, | ||
99 | searchContainer: { | ||
100 | display: 'flex', | ||
101 | background: theme.selectSearchBackground, | ||
102 | alignItems: 'center', | ||
103 | paddingLeft: 10, | ||
104 | color: theme.selectColor, | ||
105 | |||
106 | '& svg': { | ||
107 | fill: theme.selectSearchColor, | ||
108 | }, | ||
109 | }, | ||
110 | search: { | ||
111 | border: 0, | ||
112 | width: '100%', | ||
113 | fontSize: theme.uiFontSize, | ||
114 | background: 'none', | ||
115 | marginLeft: 10, | ||
116 | padding: [10, 0], | ||
117 | color: theme.selectSearchColor, | ||
118 | }, | ||
119 | clearNeedle: { | ||
120 | background: 'none', | ||
121 | border: 0, | ||
122 | }, | ||
123 | focused: { | ||
124 | fontWeight: 'bold', | ||
125 | background: theme.selectOptionItemHover, | ||
126 | color: theme.selectOptionItemHoverColor, | ||
127 | }, | ||
128 | hasError: { | ||
129 | borderColor: theme.brandDanger, | ||
130 | }, | ||
131 | disabled: { | ||
132 | opacity: theme.selectDisabledOpacity, | ||
133 | }, | ||
134 | }); | ||
135 | |||
136 | @observer | ||
137 | class SelectComponent extends Component<IProps> { | ||
138 | public static defaultProps = { | ||
139 | onChange: () => {}, | ||
140 | showLabel: true, | ||
141 | disabled: false, | ||
142 | error: '', | ||
143 | }; | ||
144 | |||
145 | state = { | ||
146 | open: false, | ||
147 | value: '', | ||
148 | needle: '', | ||
149 | selected: 0, | ||
150 | options: null, | ||
151 | }; | ||
152 | |||
153 | private componentRef = createRef<HTMLDivElement>(); | ||
154 | private inputRef = createRef<HTMLInputElement>(); | ||
155 | private searchInputRef = createRef<HTMLInputElement>(); | ||
156 | private scrollContainerRef = createRef<HTMLDivElement>(); | ||
157 | private activeOptionRef = createRef<HTMLDivElement>(); | ||
158 | |||
159 | private keyListener: any; | ||
160 | |||
161 | componentWillReceiveProps(nextProps: IProps) { | ||
162 | if (nextProps.value && nextProps.value !== this.props.value) { | ||
163 | this.setState({ | ||
164 | value: nextProps.value, | ||
165 | }); | ||
166 | } | ||
167 | } | ||
168 | |||
169 | componentDidUpdate() { | ||
170 | const { | ||
171 | open, | ||
172 | } = this.state; | ||
173 | |||
174 | if (this.searchInputRef && this.searchInputRef.current) { | ||
175 | if (open) { | ||
176 | this.searchInputRef.current.focus(); | ||
177 | } | ||
178 | } | ||
179 | } | ||
180 | |||
181 | componentDidMount() { | ||
182 | if (this.componentRef && this.componentRef.current) { | ||
183 | this.keyListener = this.componentRef.current.addEventListener('keydown', debounce((e) => { | ||
184 | const { | ||
185 | selected, | ||
186 | open, | ||
187 | options, | ||
188 | } = this.state; | ||
189 | |||
190 | if (!open) return; | ||
191 | |||
192 | if (e.keyCode === 38 && selected > 0) { | ||
193 | this.setState((state: IState) => ({ | ||
194 | selected: state.selected - 1, | ||
195 | })); | ||
196 | } else if (e.keyCode === 40 && selected < Object.keys(options!).length - 1) { | ||
197 | this.setState((state: IState) => ({ | ||
198 | selected: state.selected + 1, | ||
199 | })); | ||
200 | } else if (e.keyCode === 13) { | ||
201 | this.select(Object.keys(options!)[selected]); | ||
202 | } | ||
203 | |||
204 | if (this.activeOptionRef && this.activeOptionRef.current && this.scrollContainerRef && this.scrollContainerRef.current) { | ||
205 | const containerTopOffset = this.scrollContainerRef.current.offsetTop; | ||
206 | const optionTopOffset = this.activeOptionRef.current.offsetTop; | ||
207 | |||
208 | const topOffset = optionTopOffset - containerTopOffset; | ||
209 | |||
210 | this.scrollContainerRef.current.scrollTop = topOffset - 35; | ||
211 | } | ||
212 | }, 10, { | ||
213 | leading: true, | ||
214 | })); | ||
215 | } | ||
216 | |||
217 | if (this.inputRef && this.inputRef.current) { | ||
218 | const { | ||
219 | data, | ||
220 | } = this.props; | ||
221 | |||
222 | if (data) { | ||
223 | Object.keys(data).map(key => this.inputRef.current!.dataset[key] = data[key]); | ||
224 | } | ||
225 | } | ||
226 | } | ||
227 | |||
228 | componentWillMount() { | ||
229 | const { value } = this.props; | ||
230 | |||
231 | if (this.componentRef && this.componentRef.current) { | ||
232 | this.componentRef.current.removeEventListener('keydown', this.keyListener); | ||
233 | } | ||
234 | |||
235 | if (value) { | ||
236 | this.setState({ | ||
237 | value, | ||
238 | }); | ||
239 | } | ||
240 | |||
241 | this.setFilter(); | ||
242 | } | ||
243 | |||
244 | setFilter(needle: string = '') { | ||
245 | const { options } = this.props; | ||
246 | |||
247 | let filteredOptions = {}; | ||
248 | if (needle) { | ||
249 | Object.keys(options).map((key) => { | ||
250 | if (key.toLocaleLowerCase().startsWith(needle.toLocaleLowerCase()) || options[key].toLocaleLowerCase().startsWith(needle.toLocaleLowerCase())) { | ||
251 | Object.assign(filteredOptions, { | ||
252 | [`${key}`]: options[key], | ||
253 | }); | ||
254 | } | ||
255 | }); | ||
256 | } else { | ||
257 | filteredOptions = options; | ||
258 | } | ||
259 | |||
260 | this.setState({ | ||
261 | needle, | ||
262 | options: filteredOptions, | ||
263 | selected: 0, | ||
264 | }); | ||
265 | } | ||
266 | |||
267 | select(key: string) { | ||
268 | this.setState((state: IState) => ({ | ||
269 | value: key, | ||
270 | open: false, | ||
271 | })); | ||
272 | |||
273 | this.setFilter(); | ||
274 | |||
275 | if (this.props.onChange) { | ||
276 | this.props.onChange(key as any); | ||
277 | } | ||
278 | } | ||
279 | |||
280 | render() { | ||
281 | const { | ||
282 | actionText, | ||
283 | classes, | ||
284 | className, | ||
285 | defaultValue, | ||
286 | disabled, | ||
287 | error, | ||
288 | id, | ||
289 | inputClassName, | ||
290 | name, | ||
291 | label, | ||
292 | showLabel, | ||
293 | showSearch, | ||
294 | onChange, | ||
295 | } = this.props; | ||
296 | |||
297 | const { | ||
298 | open, | ||
299 | needle, | ||
300 | value, | ||
301 | selected, | ||
302 | options, | ||
303 | } = this.state; | ||
304 | |||
305 | let selection = ''; | ||
306 | if (!value && defaultValue && options![defaultValue]) { | ||
307 | selection = options![defaultValue]; | ||
308 | } else if (value && options![value]) { | ||
309 | selection = options![value]; | ||
310 | } else { | ||
311 | selection = actionText; | ||
312 | } | ||
313 | |||
314 | return ( | ||
315 | <Wrapper | ||
316 | className={className} | ||
317 | > | ||
318 | <Label | ||
319 | title={label} | ||
320 | showLabel={showLabel} | ||
321 | htmlFor={id} | ||
322 | > | ||
323 | <div | ||
324 | className={classnames({ | ||
325 | [`${classes.hasError}`]: error, | ||
326 | [`${classes.disabled}`]: disabled, | ||
327 | })} | ||
328 | ref={this.componentRef} | ||
329 | > | ||
330 | <button | ||
331 | type="button" | ||
332 | className={classnames({ | ||
333 | [`${inputClassName}`]: inputClassName, | ||
334 | [`${classes.select}`]: true, | ||
335 | [`${classes.hasError}`]: error, | ||
336 | })} | ||
337 | onClick= {!disabled ? () => this.setState((state: IState) => ({ | ||
338 | open: !state.open, | ||
339 | })) : () => {}} | ||
340 | > | ||
341 | {selection} | ||
342 | <Icon | ||
343 | path={mdiArrowRightDropCircleOutline} | ||
344 | size={0.8} | ||
345 | className={classnames({ | ||
346 | [`${classes.toggle}`]: true, | ||
347 | [`${classes.toggleOpened}`]: open, | ||
348 | })} | ||
349 | /> | ||
350 | </button> | ||
351 | {showSearch && open && ( | ||
352 | <div className={classes.searchContainer}> | ||
353 | <Icon | ||
354 | path={mdiMagnify} | ||
355 | size={0.8} | ||
356 | /> | ||
357 | <input | ||
358 | type="text" | ||
359 | value={needle} | ||
360 | onChange={e => this.setFilter(e.currentTarget.value)} | ||
361 | placeholder="Search" | ||
362 | className={classes.search} | ||
363 | ref={this.searchInputRef} | ||
364 | /> | ||
365 | {needle && ( | ||
366 | <button | ||
367 | type="button" | ||
368 | className={classes.clearNeedle} | ||
369 | onClick={() => this.setState({ needle: '', selected: -1 })} | ||
370 | > | ||
371 | <Icon | ||
372 | path={mdiCloseCircle} | ||
373 | size={0.7} | ||
374 | /> | ||
375 | </button> | ||
376 | )} | ||
377 | </div> | ||
378 | )} | ||
379 | <div | ||
380 | className={classnames({ | ||
381 | [`${classes.popup}`]: true, | ||
382 | [`${classes.open}`]: open, | ||
383 | })} | ||
384 | ref={this.scrollContainerRef} | ||
385 | > | ||
386 | {Object.keys(options!).map(((key, i) => ( | ||
387 | <div | ||
388 | key={key} | ||
389 | onClick={() => this.select(key)} | ||
390 | className={classnames({ | ||
391 | [`${classes.option}`]: true, | ||
392 | [`${classes.selected}`]: options![key] === selection, | ||
393 | [`${classes.focused}`]: selected === i, | ||
394 | })} | ||
395 | onMouseOver={() => this.setState({ selected: i })} | ||
396 | ref={selected === i ? this.activeOptionRef : null} | ||
397 | > | ||
398 | {options![key]} | ||
399 | </div> | ||
400 | )))} | ||
401 | </div> | ||
402 | </div> | ||
403 | <input | ||
404 | className={classes.input} | ||
405 | id={id} | ||
406 | name={name} | ||
407 | type="hidden" | ||
408 | defaultValue={value} | ||
409 | onChange={onChange} | ||
410 | disabled={disabled} | ||
411 | ref={this.inputRef} | ||
412 | /> | ||
413 | </Label> | ||
414 | {error && ( | ||
415 | <Error message={error} /> | ||
416 | )} | ||
417 | </Wrapper> | ||
418 | ); | ||
419 | } | ||
420 | } | ||
421 | |||
422 | export const Select = injectStyle(styles)(SelectComponent); | ||
diff --git a/packages/forms/src/toggle/index.tsx b/packages/forms/src/toggle/index.tsx index a1cd7f1a4..4f446ab1a 100644 --- a/packages/forms/src/toggle/index.tsx +++ b/packages/forms/src/toggle/index.tsx | |||
@@ -7,11 +7,13 @@ import injectStyle from 'react-jss'; | |||
7 | 7 | ||
8 | import { IFormField, IWithStyle, Omit } from '../typings/generic'; | 8 | import { IFormField, IWithStyle, Omit } from '../typings/generic'; |
9 | 9 | ||
10 | import Error from '../error'; | 10 | import { Error } from '../error'; |
11 | import Label from '../label'; | 11 | import { Label } from '../label'; |
12 | import Wrapper from '../wrapper'; | 12 | import { Wrapper } from '../wrapper'; |
13 | 13 | ||
14 | interface IProps extends React.InputHTMLAttributes<HTMLInputElement>, IFormField, IWithStyle {} | 14 | interface IProps extends React.InputHTMLAttributes<HTMLInputElement>, IFormField, IWithStyle { |
15 | className?: string; | ||
16 | } | ||
15 | 17 | ||
16 | const styles = (theme: Theme) => ({ | 18 | const styles = (theme: Theme) => ({ |
17 | toggle: { | 19 | toggle: { |
@@ -65,6 +67,7 @@ class ToggleComponent extends Component<IProps> { | |||
65 | render() { | 67 | render() { |
66 | const { | 68 | const { |
67 | classes, | 69 | classes, |
70 | className, | ||
68 | disabled, | 71 | disabled, |
69 | error, | 72 | error, |
70 | id, | 73 | id, |
@@ -76,7 +79,9 @@ class ToggleComponent extends Component<IProps> { | |||
76 | } = this.props; | 79 | } = this.props; |
77 | 80 | ||
78 | return ( | 81 | return ( |
79 | <Wrapper> | 82 | <Wrapper |
83 | className={className} | ||
84 | > | ||
80 | <Label | 85 | <Label |
81 | title={label} | 86 | title={label} |
82 | showLabel={showLabel} | 87 | showLabel={showLabel} |
diff --git a/packages/forms/src/wrapper/index.tsx b/packages/forms/src/wrapper/index.tsx index 633cc4c99..87e2c6513 100644 --- a/packages/forms/src/wrapper/index.tsx +++ b/packages/forms/src/wrapper/index.tsx | |||
@@ -1,3 +1,4 @@ | |||
1 | import classnames from 'classnames'; | ||
1 | import { observer } from 'mobx-react'; | 2 | import { observer } from 'mobx-react'; |
2 | import React, { Component } from 'react'; | 3 | import React, { Component } from 'react'; |
3 | import injectStyle from 'react-jss'; | 4 | import injectStyle from 'react-jss'; |
@@ -7,22 +8,27 @@ import styles from './styles'; | |||
7 | 8 | ||
8 | interface IProps extends IWithStyle { | 9 | interface IProps extends IWithStyle { |
9 | children: React.ReactNode; | 10 | children: React.ReactNode; |
11 | className?: string; | ||
10 | } | 12 | } |
11 | 13 | ||
12 | @observer | 14 | @observer |
13 | class Wrapper extends Component<IProps> { | 15 | class WrapperComponent extends Component<IProps> { |
14 | render() { | 16 | render() { |
15 | const { | 17 | const { |
16 | children, | 18 | children, |
17 | classes, | 19 | classes, |
20 | className, | ||
18 | } = this.props; | 21 | } = this.props; |
19 | 22 | ||
20 | return ( | 23 | return ( |
21 | <div className={classes.container}> | 24 | <div className={classnames({ |
25 | [`${classes.container}`]: true, | ||
26 | [`${className}`]: className, | ||
27 | })}> | ||
22 | {children} | 28 | {children} |
23 | </div> | 29 | </div> |
24 | ); | 30 | ); |
25 | } | 31 | } |
26 | } | 32 | } |
27 | 33 | ||
28 | export default injectStyle(styles)(Wrapper); | 34 | export const Wrapper = injectStyle(styles)(WrapperComponent); |