diff options
Diffstat (limited to 'packages/forms/src')
-rw-r--r-- | packages/forms/src/button/index.tsx | 70 | ||||
-rw-r--r-- | packages/forms/src/error/index.tsx | 13 | ||||
-rw-r--r-- | packages/forms/src/error/styles.ts | 2 | ||||
-rw-r--r-- | packages/forms/src/input/index.tsx | 63 | ||||
-rw-r--r-- | packages/forms/src/input/scorePassword.ts | 6 | ||||
-rw-r--r-- | packages/forms/src/input/styles.ts | 9 | ||||
-rw-r--r-- | packages/forms/src/label/index.tsx | 13 | ||||
-rw-r--r-- | packages/forms/src/label/styles.ts | 2 | ||||
-rw-r--r-- | packages/forms/src/select/index.tsx | 146 | ||||
-rw-r--r-- | packages/forms/src/textarea/index.tsx | 24 | ||||
-rw-r--r-- | packages/forms/src/textarea/styles.ts | 7 | ||||
-rw-r--r-- | packages/forms/src/toggle/index.tsx | 18 | ||||
-rw-r--r-- | packages/forms/src/typings/generic.ts | 3 | ||||
-rw-r--r-- | packages/forms/src/wrapper/index.tsx | 9 |
14 files changed, 200 insertions, 185 deletions
diff --git a/packages/forms/src/button/index.tsx b/packages/forms/src/button/index.tsx index b81154a43..48fb61635 100644 --- a/packages/forms/src/button/index.tsx +++ b/packages/forms/src/button/index.tsx | |||
@@ -1,21 +1,31 @@ | |||
1 | import Icon from '@mdi/react'; | 1 | import Icon from '@mdi/react'; |
2 | import { Theme } from '@meetfranz/theme'; | ||
3 | import classnames from 'classnames'; | 2 | import classnames from 'classnames'; |
4 | import CSS from 'csstype'; | 3 | import { Property } from 'csstype'; |
5 | import React, { Component } from 'react'; | 4 | import React, { Component } from 'react'; |
6 | import injectStyle, { withTheme } from 'react-jss'; | 5 | import injectStyle, { withTheme } from 'react-jss'; |
7 | import Loader from 'react-loader'; | 6 | import Loader from 'react-loader'; |
8 | 7 | ||
9 | import { IFormField, IWithStyle } from '../typings/generic'; | 8 | import { IFormField, IWithStyle } from '../typings/generic'; |
9 | import { Theme } from '../../../theme'; | ||
10 | 10 | ||
11 | type ButtonType = 'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'inverted'; | 11 | type ButtonType = |
12 | | 'primary' | ||
13 | | 'secondary' | ||
14 | | 'success' | ||
15 | | 'danger' | ||
16 | | 'warning' | ||
17 | | 'inverted'; | ||
12 | 18 | ||
13 | interface IProps extends IFormField, IWithStyle { | 19 | interface IProps extends IFormField, IWithStyle { |
14 | className?: string; | 20 | className?: string; |
15 | disabled?: boolean; | 21 | disabled?: boolean; |
16 | id?: string; | 22 | id?: string; |
17 | type?: "button" | "reset" | "submit" | undefined; | 23 | type?: 'button' | 'reset' | 'submit' | undefined; |
18 | onClick: (event: React.MouseEvent<HTMLButtonElement> | React.MouseEvent<HTMLAnchorElement>) => void; | 24 | onClick: ( |
25 | event: | ||
26 | | React.MouseEvent<HTMLButtonElement> | ||
27 | | React.MouseEvent<HTMLAnchorElement>, | ||
28 | ) => void; | ||
19 | buttonType?: ButtonType; | 29 | buttonType?: ButtonType; |
20 | stretch?: boolean; | 30 | stretch?: boolean; |
21 | loaded?: boolean; | 31 | loaded?: boolean; |
@@ -25,8 +35,12 @@ interface IProps extends IFormField, IWithStyle { | |||
25 | target?: string; | 35 | target?: string; |
26 | } | 36 | } |
27 | 37 | ||
28 | interface IState { | 38 | let buttonTransition: string = 'none'; |
29 | busy: boolean; | 39 | let loaderContainerTransition: string = 'none'; |
40 | |||
41 | if (window && window.matchMedia('(prefers-reduced-motion: no-preference)')) { | ||
42 | buttonTransition = 'background .5s, opacity 0.3s'; | ||
43 | loaderContainerTransition = 'all 0.3s'; | ||
30 | } | 44 | } |
31 | 45 | ||
32 | const styles = (theme: Theme) => ({ | 46 | const styles = (theme: Theme) => ({ |
@@ -34,16 +48,16 @@ const styles = (theme: Theme) => ({ | |||
34 | borderRadius: theme.borderRadiusSmall, | 48 | borderRadius: theme.borderRadiusSmall, |
35 | border: 'none', | 49 | border: 'none', |
36 | display: 'inline-flex', | 50 | display: 'inline-flex', |
37 | position: 'relative' as CSS.PositionProperty, | 51 | position: 'relative' as Property.Position, |
38 | transition: 'background .5s, opacity 0.3s', | 52 | transition: buttonTransition, |
39 | textAlign: 'center' as CSS.TextAlignProperty, | 53 | textAlign: 'center' as Property.TextAlign, |
40 | outline: 'none', | 54 | outline: 'none', |
41 | alignItems: 'center', | 55 | alignItems: 'center', |
42 | padding: 0, | 56 | padding: 0, |
43 | width: (props: IProps) => (props.stretch ? '100%' : 'auto') as CSS.WidthProperty<string>, | 57 | width: (props: IProps) => |
58 | (props.stretch ? '100%' : 'auto') as Property.Width<string>, | ||
44 | fontSize: theme.uiFontSize, | 59 | fontSize: theme.uiFontSize, |
45 | textDecoration: 'none', | 60 | textDecoration: 'none', |
46 | // height: theme.buttonHeight, | ||
47 | 61 | ||
48 | '&:hover': { | 62 | '&:hover': { |
49 | opacity: 0.8, | 63 | opacity: 0.8, |
@@ -113,7 +127,7 @@ const styles = (theme: Theme) => ({ | |||
113 | opacity: theme.inputDisabledOpacity, | 127 | opacity: theme.inputDisabledOpacity, |
114 | }, | 128 | }, |
115 | loader: { | 129 | loader: { |
116 | position: 'relative' as CSS.PositionProperty, | 130 | position: 'relative' as Property.Position, |
117 | width: 20, | 131 | width: 20, |
118 | height: 18, | 132 | height: 18, |
119 | zIndex: 9999, | 133 | zIndex: 9999, |
@@ -122,10 +136,11 @@ const styles = (theme: Theme) => ({ | |||
122 | width: (props: IProps): string => (!props.busy ? '0' : '40px'), | 136 | width: (props: IProps): string => (!props.busy ? '0' : '40px'), |
123 | height: 20, | 137 | height: 20, |
124 | overflow: 'hidden', | 138 | overflow: 'hidden', |
125 | transition: 'all 0.3s', | 139 | transition: loaderContainerTransition, |
126 | marginLeft: (props: IProps): number => !props.busy ? 10 : 20, | 140 | marginLeft: (props: IProps): number => (!props.busy ? 10 : 20), |
127 | marginRight: (props: IProps): number => !props.busy ? -10 : -20, | 141 | marginRight: (props: IProps): number => (!props.busy ? -10 : -20), |
128 | position: (props: IProps): CSS.PositionProperty => props.stretch ? 'absolute' : 'inherit', | 142 | position: (props: IProps): Property.Position => |
143 | props.stretch ? 'absolute' : 'inherit', | ||
129 | }, | 144 | }, |
130 | icon: { | 145 | icon: { |
131 | margin: [1, 10, 0, -5], | 146 | margin: [1, 10, 0, -5], |
@@ -155,7 +170,7 @@ class ButtonComponent extends Component<IProps> { | |||
155 | if (this.props.busy) { | 170 | if (this.props.busy) { |
156 | setTimeout(() => { | 171 | setTimeout(() => { |
157 | this.setState({ busy: nextProps.busy }); | 172 | this.setState({ busy: nextProps.busy }); |
158 | }, 300); | 173 | }, 300); |
159 | } else { | 174 | } else { |
160 | this.setState({ busy: nextProps.busy }); | 175 | this.setState({ busy: nextProps.busy }); |
161 | } | 176 | } |
@@ -175,19 +190,18 @@ class ButtonComponent extends Component<IProps> { | |||
175 | buttonType, | 190 | buttonType, |
176 | loaded, | 191 | loaded, |
177 | icon, | 192 | icon, |
178 | busy: busyProp, | ||
179 | href, | 193 | href, |
180 | target, | 194 | target, |
181 | } = this.props; | 195 | } = this.props; |
182 | 196 | ||
183 | const { | 197 | const { busy } = this.state; |
184 | busy, | ||
185 | } = this.state; | ||
186 | 198 | ||
187 | let showLoader = false; | 199 | let showLoader = false; |
188 | if (loaded) { | 200 | if (loaded) { |
189 | showLoader = !loaded; | 201 | showLoader = !loaded; |
190 | console.warn('Ferdi Button prop `loaded` will be deprecated in the future. Please use `busy` instead'); | 202 | console.warn( |
203 | 'Ferdi Button prop `loaded` will be deprecated in the future. Please use `busy` instead', | ||
204 | ); | ||
191 | } | 205 | } |
192 | if (busy) { | 206 | if (busy) { |
193 | showLoader = busy; | 207 | showLoader = busy; |
@@ -207,19 +221,13 @@ class ButtonComponent extends Component<IProps> { | |||
207 | )} | 221 | )} |
208 | </div> | 222 | </div> |
209 | <div className={classes.label}> | 223 | <div className={classes.label}> |
210 | {icon && ( | 224 | {icon && <Icon path={icon} size={0.8} className={classes.icon} />} |
211 | <Icon | ||
212 | path={icon} | ||
213 | size={0.8} | ||
214 | className={classes.icon} | ||
215 | /> | ||
216 | )} | ||
217 | {label} | 225 | {label} |
218 | </div> | 226 | </div> |
219 | </> | 227 | </> |
220 | ); | 228 | ); |
221 | 229 | ||
222 | let wrapperComponent = null; | 230 | let wrapperComponent: JSX.Element; |
223 | 231 | ||
224 | if (!href) { | 232 | if (!href) { |
225 | wrapperComponent = ( | 233 | wrapperComponent = ( |
diff --git a/packages/forms/src/error/index.tsx b/packages/forms/src/error/index.tsx index a487bb281..243321d97 100644 --- a/packages/forms/src/error/index.tsx +++ b/packages/forms/src/error/index.tsx | |||
@@ -11,18 +11,9 @@ interface IProps { | |||
11 | 11 | ||
12 | class ErrorComponent extends Component<IProps> { | 12 | class ErrorComponent extends Component<IProps> { |
13 | render() { | 13 | render() { |
14 | const { | 14 | const { classes, message } = this.props; |
15 | classes, | ||
16 | message, | ||
17 | } = this.props; | ||
18 | 15 | ||
19 | return ( | 16 | return <p className={classes.message}>{message}</p>; |
20 | <p | ||
21 | className={classes.message} | ||
22 | > | ||
23 | {message} | ||
24 | </p> | ||
25 | ); | ||
26 | } | 17 | } |
27 | } | 18 | } |
28 | 19 | ||
diff --git a/packages/forms/src/error/styles.ts b/packages/forms/src/error/styles.ts index 5104838a5..4c0c7e2c0 100644 --- a/packages/forms/src/error/styles.ts +++ b/packages/forms/src/error/styles.ts | |||
@@ -1,4 +1,4 @@ | |||
1 | import { Theme } from '../../../theme/lib'; | 1 | import { Theme } from '../../../theme'; |
2 | 2 | ||
3 | export default (theme: Theme) => ({ | 3 | export default (theme: Theme) => ({ |
4 | message: { | 4 | message: { |
diff --git a/packages/forms/src/input/index.tsx b/packages/forms/src/input/index.tsx index b96dbc12d..41751710a 100644 --- a/packages/forms/src/input/index.tsx +++ b/packages/forms/src/input/index.tsx | |||
@@ -17,7 +17,10 @@ interface IData { | |||
17 | [index: string]: string; | 17 | [index: string]: string; |
18 | } | 18 | } |
19 | 19 | ||
20 | interface IProps extends React.InputHTMLAttributes<HTMLInputElement>, IFormField, IWithStyle { | 20 | interface IProps |
21 | extends React.InputHTMLAttributes<HTMLInputElement>, | ||
22 | IFormField, | ||
23 | IWithStyle { | ||
21 | focus?: boolean; | 24 | focus?: boolean; |
22 | prefix?: string; | 25 | prefix?: string; |
23 | suffix?: string; | 26 | suffix?: string; |
@@ -62,23 +65,24 @@ class InputComponent extends Component<IProps, IState> { | |||
62 | } | 65 | } |
63 | 66 | ||
64 | if (data) { | 67 | if (data) { |
65 | Object.keys(data).map(key => this.inputRef.current!.dataset[key] = data[key]); | 68 | Object.keys(data).map( |
69 | key => (this.inputRef.current!.dataset[key] = data[key]), | ||
70 | ); | ||
66 | } | 71 | } |
67 | } | 72 | } |
68 | } | 73 | } |
69 | 74 | ||
70 | onChange(e: React.ChangeEvent<HTMLInputElement>) { | 75 | onChange(e: React.ChangeEvent<HTMLInputElement>) { |
71 | const { | 76 | const { scorePassword, onChange } = this.props; |
72 | scorePassword, | ||
73 | onChange, | ||
74 | } = this.props; | ||
75 | 77 | ||
76 | if (onChange) { | 78 | if (onChange) { |
77 | onChange(e); | 79 | onChange(e); |
78 | } | 80 | } |
79 | 81 | ||
80 | if (this.inputRef && this.inputRef.current && scorePassword) { | 82 | if (this.inputRef && this.inputRef.current && scorePassword) { |
81 | this.setState({ passwordScore: scorePasswordFunc(this.inputRef.current.value) }); | 83 | this.setState({ |
84 | passwordScore: scorePasswordFunc(this.inputRef.current.value), | ||
85 | }); | ||
82 | } | 86 | } |
83 | } | 87 | } |
84 | 88 | ||
@@ -117,10 +121,7 @@ class InputComponent extends Component<IProps, IState> { | |||
117 | noMargin, | 121 | noMargin, |
118 | } = this.props; | 122 | } = this.props; |
119 | 123 | ||
120 | const { | 124 | const { showPassword, passwordScore } = this.state; |
121 | showPassword, | ||
122 | passwordScore, | ||
123 | } = this.state; | ||
124 | 125 | ||
125 | const inputType = type === 'password' && showPassword ? 'text' : type; | 126 | const inputType = type === 'password' && showPassword ? 'text' : type; |
126 | 127 | ||
@@ -144,12 +145,9 @@ class InputComponent extends Component<IProps, IState> { | |||
144 | [`${classes.wrapper}`]: true, | 145 | [`${classes.wrapper}`]: true, |
145 | [`${classes.disabled}`]: disabled, | 146 | [`${classes.disabled}`]: disabled, |
146 | [`${classes.hasError}`]: error, | 147 | [`${classes.hasError}`]: error, |
147 | })}> | 148 | })} |
148 | {prefix && ( | 149 | > |
149 | <span className={classes.prefix}> | 150 | {prefix && <span className={classes.prefix}>{prefix}</span>} |
150 | {prefix} | ||
151 | </span> | ||
152 | )} | ||
153 | <input | 151 | <input |
154 | id={id} | 152 | id={id} |
155 | type={inputType} | 153 | type={inputType} |
@@ -168,30 +166,29 @@ class InputComponent extends Component<IProps, IState> { | |||
168 | max={max} | 166 | max={max} |
169 | step={step} | 167 | step={step} |
170 | /> | 168 | /> |
171 | {suffix && ( | 169 | {suffix && <span className={classes.suffix}>{suffix}</span>} |
172 | <span className={classes.suffix}> | ||
173 | {suffix} | ||
174 | </span> | ||
175 | )} | ||
176 | {showPasswordToggle && ( | 170 | {showPasswordToggle && ( |
177 | <button | 171 | <button |
178 | type="button" | 172 | type="button" |
179 | className={classes.formModifier} | 173 | className={classes.formModifier} |
180 | onClick={() => this.setState(prevState => ({ showPassword: !prevState.showPassword }))} | 174 | onClick={() => |
175 | this.setState(prevState => ({ | ||
176 | showPassword: !prevState.showPassword, | ||
177 | })) | ||
178 | } | ||
181 | tabIndex={-1} | 179 | tabIndex={-1} |
182 | > | 180 | > |
183 | <Icon | 181 | <Icon path={!showPassword ? mdiEye : mdiEyeOff} size={1} /> |
184 | path={!showPassword ? mdiEye : mdiEyeOff} | ||
185 | size={1} | ||
186 | /> | ||
187 | </button> | 182 | </button> |
188 | )} | 183 | )} |
189 | </div> | 184 | </div> |
190 | {scorePassword && ( | 185 | {scorePassword && ( |
191 | <div className={classnames({ | 186 | <div |
192 | [`${classes.passwordScore}`]: true, | 187 | className={classnames({ |
193 | [`${classes.hasError}`]: error, | 188 | [`${classes.passwordScore}`]: true, |
194 | })}> | 189 | [`${classes.hasError}`]: error, |
190 | })} | ||
191 | > | ||
195 | <meter | 192 | <meter |
196 | value={passwordScore < 5 ? 5 : passwordScore} | 193 | value={passwordScore < 5 ? 5 : passwordScore} |
197 | low={30} | 194 | low={30} |
@@ -202,9 +199,7 @@ class InputComponent extends Component<IProps, IState> { | |||
202 | </div> | 199 | </div> |
203 | )} | 200 | )} |
204 | </Label> | 201 | </Label> |
205 | {error && ( | 202 | {error && <Error message={error} />} |
206 | <Error message={error} /> | ||
207 | )} | ||
208 | </Wrapper> | 203 | </Wrapper> |
209 | ); | 204 | ); |
210 | } | 205 | } |
diff --git a/packages/forms/src/input/scorePassword.ts b/packages/forms/src/input/scorePassword.ts index 0b7719ec1..bc30de4b8 100644 --- a/packages/forms/src/input/scorePassword.ts +++ b/packages/forms/src/input/scorePassword.ts | |||
@@ -11,7 +11,7 @@ interface IVariations { | |||
11 | } | 11 | } |
12 | 12 | ||
13 | export function scorePasswordFunc(password: string): number { | 13 | export function scorePasswordFunc(password: string): number { |
14 | let score: number = 0; | 14 | let score = 0; |
15 | if (!password) { | 15 | if (!password) { |
16 | return score; | 16 | return score; |
17 | } | 17 | } |
@@ -32,8 +32,8 @@ export function scorePasswordFunc(password: string): number { | |||
32 | }; | 32 | }; |
33 | 33 | ||
34 | let variationCount = 0; | 34 | let variationCount = 0; |
35 | Object.keys(variations).forEach((key) => { | 35 | Object.keys(variations).forEach(key => { |
36 | variationCount += (variations[key] === true) ? 1 : 0; | 36 | variationCount += variations[key] === true ? 1 : 0; |
37 | }); | 37 | }); |
38 | 38 | ||
39 | score += (variationCount - 1) * 10; | 39 | score += (variationCount - 1) * 10; |
diff --git a/packages/forms/src/input/styles.ts b/packages/forms/src/input/styles.ts index e2ab30a4f..6d56e93b3 100644 --- a/packages/forms/src/input/styles.ts +++ b/packages/forms/src/input/styles.ts | |||
@@ -1,5 +1,6 @@ | |||
1 | import { Theme } from '@meetfranz/theme'; | 1 | import { Property } from 'csstype'; |
2 | import CSS from 'csstype'; | 2 | |
3 | import { Theme } from '../../../theme'; | ||
3 | 4 | ||
4 | const prefixStyles = (theme: Theme) => ({ | 5 | const prefixStyles = (theme: Theme) => ({ |
5 | background: theme.inputPrefixBackground, | 6 | background: theme.inputPrefixBackground, |
@@ -13,7 +14,7 @@ export default (theme: Theme) => ({ | |||
13 | label: { | 14 | label: { |
14 | '& > div': { | 15 | '& > div': { |
15 | marginTop: 5, | 16 | marginTop: 5, |
16 | } | 17 | }, |
17 | }, | 18 | }, |
18 | disabled: { | 19 | disabled: { |
19 | opacity: theme.inputDisabledOpacity, | 20 | opacity: theme.inputDisabledOpacity, |
@@ -85,7 +86,7 @@ export default (theme: Theme) => ({ | |||
85 | background: theme.inputBackground, | 86 | background: theme.inputBackground, |
86 | border: theme.inputBorder, | 87 | border: theme.inputBorder, |
87 | borderRadius: theme.borderRadiusSmall, | 88 | borderRadius: theme.borderRadiusSmall, |
88 | boxSizing: 'border-box' as CSS.BoxSizingProperty, | 89 | boxSizing: 'border-box' as Property.BoxSizing, |
89 | display: 'flex', | 90 | display: 'flex', |
90 | height: theme.inputHeight, | 91 | height: theme.inputHeight, |
91 | order: 1, | 92 | order: 1, |
diff --git a/packages/forms/src/label/index.tsx b/packages/forms/src/label/index.tsx index 1b33ba22c..ad503b785 100644 --- a/packages/forms/src/label/index.tsx +++ b/packages/forms/src/label/index.tsx | |||
@@ -7,7 +7,9 @@ import { IFormField } from '../typings/generic'; | |||
7 | 7 | ||
8 | import styles from './styles'; | 8 | import styles from './styles'; |
9 | 9 | ||
10 | interface ILabel extends IFormField, React.LabelHTMLAttributes<HTMLLabelElement> { | 10 | interface ILabel |
11 | extends IFormField, | ||
12 | React.LabelHTMLAttributes<HTMLLabelElement> { | ||
11 | classes: Classes; | 13 | classes: Classes; |
12 | isRequired: boolean; | 14 | isRequired: boolean; |
13 | } | 15 | } |
@@ -38,11 +40,12 @@ class LabelComponent extends Component<ILabel> { | |||
38 | htmlFor={htmlFor} | 40 | htmlFor={htmlFor} |
39 | > | 41 | > |
40 | {showLabel && ( | 42 | {showLabel && ( |
41 | <span className={classes.label}>{title}{isRequired && ' *'}</span> | 43 | <span className={classes.label}> |
44 | {title} | ||
45 | {isRequired && ' *'} | ||
46 | </span> | ||
42 | )} | 47 | )} |
43 | <div className={classes.content}> | 48 | <div className={classes.content}>{children}</div> |
44 | {children} | ||
45 | </div> | ||
46 | </label> | 49 | </label> |
47 | ); | 50 | ); |
48 | } | 51 | } |
diff --git a/packages/forms/src/label/styles.ts b/packages/forms/src/label/styles.ts index c64c9b285..7c62b6b4c 100644 --- a/packages/forms/src/label/styles.ts +++ b/packages/forms/src/label/styles.ts | |||
@@ -1,4 +1,4 @@ | |||
1 | import { Theme } from '../../../theme/lib'; | 1 | import { Theme } from '../../../theme'; |
2 | 2 | ||
3 | export default (theme: Theme) => ({ | 3 | export default (theme: Theme) => ({ |
4 | content: {}, | 4 | content: {}, |
diff --git a/packages/forms/src/select/index.tsx b/packages/forms/src/select/index.tsx index 0e5ded176..d7479f63e 100644 --- a/packages/forms/src/select/index.tsx +++ b/packages/forms/src/select/index.tsx | |||
@@ -1,13 +1,16 @@ | |||
1 | import { mdiArrowRightDropCircleOutline, mdiCloseCircle, mdiMagnify } from '@mdi/js'; | 1 | import { |
2 | mdiArrowRightDropCircleOutline, | ||
3 | mdiCloseCircle, | ||
4 | mdiMagnify, | ||
5 | } from '@mdi/js'; | ||
2 | import Icon from '@mdi/react'; | 6 | import Icon from '@mdi/react'; |
3 | import { Theme } from '@meetfranz/theme'; | ||
4 | import classnames from 'classnames'; | 7 | import classnames from 'classnames'; |
5 | import React, { Component, createRef } from 'react'; | 8 | import React, { Component, createRef } from 'react'; |
6 | import injectStyle from 'react-jss'; | 9 | import injectStyle from 'react-jss'; |
7 | 10 | ||
8 | import { IFormField, IWithStyle } from '../typings/generic'; | 11 | import { IFormField, IWithStyle } from '../typings/generic'; |
12 | import { Theme } from '../../../theme'; | ||
9 | 13 | ||
10 | import { NONAME } from 'dns'; | ||
11 | import { Error } from '../error'; | 14 | import { Error } from '../error'; |
12 | import { Label } from '../label'; | 15 | import { Label } from '../label'; |
13 | import { Wrapper } from '../wrapper'; | 16 | import { Wrapper } from '../wrapper'; |
@@ -43,6 +46,14 @@ interface IState { | |||
43 | options: IOptions; | 46 | options: IOptions; |
44 | } | 47 | } |
45 | 48 | ||
49 | let popupTransition: string = 'none'; | ||
50 | let toggleTransition: string = 'none'; | ||
51 | |||
52 | if (window && window.matchMedia('(prefers-reduced-motion: no-preference)')) { | ||
53 | popupTransition = 'all 0.3s'; | ||
54 | toggleTransition = 'transform 0.3s'; | ||
55 | } | ||
56 | |||
46 | const styles = (theme: Theme) => ({ | 57 | const styles = (theme: Theme) => ({ |
47 | select: { | 58 | select: { |
48 | background: theme.selectBackground, | 59 | background: theme.selectBackground, |
@@ -59,7 +70,7 @@ const styles = (theme: Theme) => ({ | |||
59 | label: { | 70 | label: { |
60 | '& > div': { | 71 | '& > div': { |
61 | marginTop: 5, | 72 | marginTop: 5, |
62 | } | 73 | }, |
63 | }, | 74 | }, |
64 | popup: { | 75 | popup: { |
65 | opacity: 0, | 76 | opacity: 0, |
@@ -67,7 +78,7 @@ const styles = (theme: Theme) => ({ | |||
67 | overflowX: 'scroll', | 78 | overflowX: 'scroll', |
68 | border: theme.selectBorder, | 79 | border: theme.selectBorder, |
69 | borderTop: 0, | 80 | borderTop: 0, |
70 | transition: 'all 0.3s', | 81 | transition: popupTransition, |
71 | }, | 82 | }, |
72 | open: { | 83 | open: { |
73 | opacity: 1, | 84 | opacity: 1, |
@@ -95,7 +106,7 @@ const styles = (theme: Theme) => ({ | |||
95 | toggle: { | 106 | toggle: { |
96 | marginLeft: 'auto', | 107 | marginLeft: 'auto', |
97 | fill: theme.selectToggleColor, | 108 | fill: theme.selectToggleColor, |
98 | transition: 'transform 0.3s', | 109 | transition: toggleTransition, |
99 | }, | 110 | }, |
100 | toggleOpened: { | 111 | toggleOpened: { |
101 | transform: 'rotateZ(90deg)', | 112 | transform: 'rotateZ(90deg)', |
@@ -154,9 +165,13 @@ class SelectComponent extends Component<IProps> { | |||
154 | }; | 165 | }; |
155 | 166 | ||
156 | private componentRef = createRef<HTMLDivElement>(); | 167 | private componentRef = createRef<HTMLDivElement>(); |
168 | |||
157 | private inputRef = createRef<HTMLInputElement>(); | 169 | private inputRef = createRef<HTMLInputElement>(); |
170 | |||
158 | private searchInputRef = createRef<HTMLInputElement>(); | 171 | private searchInputRef = createRef<HTMLInputElement>(); |
172 | |||
159 | private scrollContainerRef = createRef<HTMLDivElement>(); | 173 | private scrollContainerRef = createRef<HTMLDivElement>(); |
174 | |||
160 | private activeOptionRef = createRef<HTMLDivElement>(); | 175 | private activeOptionRef = createRef<HTMLDivElement>(); |
161 | 176 | ||
162 | private keyListener: any; | 177 | private keyListener: any; |
@@ -169,10 +184,8 @@ class SelectComponent extends Component<IProps> { | |||
169 | } | 184 | } |
170 | } | 185 | } |
171 | 186 | ||
172 | componentDidUpdate(prevProps: IProps, prevState: IState) { | 187 | componentDidUpdate() { |
173 | const { | 188 | const { open } = this.state; |
174 | open, | ||
175 | } = this.state; | ||
176 | 189 | ||
177 | if (this.searchInputRef && this.searchInputRef.current) { | 190 | if (this.searchInputRef && this.searchInputRef.current) { |
178 | if (open) { | 191 | if (open) { |
@@ -183,12 +196,12 @@ class SelectComponent extends Component<IProps> { | |||
183 | 196 | ||
184 | componentDidMount() { | 197 | componentDidMount() { |
185 | if (this.inputRef && this.inputRef.current) { | 198 | if (this.inputRef && this.inputRef.current) { |
186 | const { | 199 | const { data } = this.props; |
187 | data, | ||
188 | } = this.props; | ||
189 | 200 | ||
190 | if (data) { | 201 | if (data) { |
191 | Object.keys(data).map(key => this.inputRef.current!.dataset[key] = data[key]); | 202 | Object.keys(data).map( |
203 | key => (this.inputRef.current!.dataset[key] = data[key]), | ||
204 | ); | ||
192 | } | 205 | } |
193 | } | 206 | } |
194 | 207 | ||
@@ -196,12 +209,13 @@ class SelectComponent extends Component<IProps> { | |||
196 | } | 209 | } |
197 | 210 | ||
198 | componentWillMount() { | 211 | componentWillMount() { |
199 | const { | 212 | const { value } = this.props; |
200 | value, | ||
201 | } = this.props; | ||
202 | 213 | ||
203 | if (this.componentRef && this.componentRef.current) { | 214 | if (this.componentRef && this.componentRef.current) { |
204 | this.componentRef.current.removeEventListener('keydown', this.keyListener); | 215 | this.componentRef.current.removeEventListener( |
216 | 'keydown', | ||
217 | this.keyListener, | ||
218 | ); | ||
205 | } | 219 | } |
206 | 220 | ||
207 | if (value) { | 221 | if (value) { |
@@ -217,13 +231,18 @@ class SelectComponent extends Component<IProps> { | |||
217 | window.removeEventListener('keydown', this.arrowKeysHandler.bind(this)); | 231 | window.removeEventListener('keydown', this.arrowKeysHandler.bind(this)); |
218 | } | 232 | } |
219 | 233 | ||
220 | setFilter(needle: string = '') { | 234 | setFilter(needle = '') { |
221 | const { options } = this.props; | 235 | const { options } = this.props; |
222 | 236 | ||
223 | let filteredOptions = {}; | 237 | let filteredOptions = {}; |
224 | if (needle) { | 238 | if (needle) { |
225 | Object.keys(options).map((key) => { | 239 | Object.keys(options).map(key => { |
226 | if (key.toLocaleLowerCase().startsWith(needle.toLocaleLowerCase()) || options[key].toLocaleLowerCase().startsWith(needle.toLocaleLowerCase())) { | 240 | if ( |
241 | key.toLocaleLowerCase().startsWith(needle.toLocaleLowerCase()) || | ||
242 | options[key] | ||
243 | .toLocaleLowerCase() | ||
244 | .startsWith(needle.toLocaleLowerCase()) | ||
245 | ) { | ||
227 | Object.assign(filteredOptions, { | 246 | Object.assign(filteredOptions, { |
228 | [`${key}`]: options[key], | 247 | [`${key}`]: options[key], |
229 | }); | 248 | }); |
@@ -241,7 +260,7 @@ class SelectComponent extends Component<IProps> { | |||
241 | } | 260 | } |
242 | 261 | ||
243 | select(key: string) { | 262 | select(key: string) { |
244 | this.setState((state: IState) => ({ | 263 | this.setState(() => ({ |
245 | value: key, | 264 | value: key, |
246 | open: false, | 265 | open: false, |
247 | })); | 266 | })); |
@@ -254,11 +273,7 @@ class SelectComponent extends Component<IProps> { | |||
254 | } | 273 | } |
255 | 274 | ||
256 | arrowKeysHandler(e: KeyboardEvent) { | 275 | arrowKeysHandler(e: KeyboardEvent) { |
257 | const { | 276 | const { selected, open, options } = this.state; |
258 | selected, | ||
259 | open, | ||
260 | options, | ||
261 | } = this.state; | ||
262 | 277 | ||
263 | if (!open) return; | 278 | if (!open) return; |
264 | 279 | ||
@@ -271,7 +286,10 @@ class SelectComponent extends Component<IProps> { | |||
271 | this.setState((state: IState) => ({ | 286 | this.setState((state: IState) => ({ |
272 | selected: state.selected - 1, | 287 | selected: state.selected - 1, |
273 | })); | 288 | })); |
274 | } else if (e.keyCode === 40 && selected < Object.keys(options!).length - 1) { | 289 | } else if ( |
290 | e.keyCode === 40 && | ||
291 | selected < Object.keys(options!).length - 1 | ||
292 | ) { | ||
275 | this.setState((state: IState) => ({ | 293 | this.setState((state: IState) => ({ |
276 | selected: state.selected + 1, | 294 | selected: state.selected + 1, |
277 | })); | 295 | })); |
@@ -279,7 +297,12 @@ class SelectComponent extends Component<IProps> { | |||
279 | this.select(Object.keys(options!)[selected]); | 297 | this.select(Object.keys(options!)[selected]); |
280 | } | 298 | } |
281 | 299 | ||
282 | if (this.activeOptionRef && this.activeOptionRef.current && this.scrollContainerRef && this.scrollContainerRef.current) { | 300 | if ( |
301 | this.activeOptionRef && | ||
302 | this.activeOptionRef.current && | ||
303 | this.scrollContainerRef && | ||
304 | this.scrollContainerRef.current | ||
305 | ) { | ||
283 | const containerTopOffset = this.scrollContainerRef.current.offsetTop; | 306 | const containerTopOffset = this.scrollContainerRef.current.offsetTop; |
284 | const optionTopOffset = this.activeOptionRef.current.offsetTop; | 307 | const optionTopOffset = this.activeOptionRef.current.offsetTop; |
285 | 308 | ||
@@ -289,10 +312,15 @@ class SelectComponent extends Component<IProps> { | |||
289 | } | 312 | } |
290 | } | 313 | } |
291 | 314 | ||
292 | switch (e.keyCode){ | 315 | switch (e.keyCode) { |
293 | case 37: case 39: case 38: case 40: // Arrow keys | 316 | case 37: |
294 | case 32: break; // Space | 317 | case 39: |
295 | default: break; // do not block other keys | 318 | case 38: |
319 | case 40: // Arrow keys | ||
320 | case 32: | ||
321 | break; // Space | ||
322 | default: | ||
323 | break; // do not block other keys | ||
296 | } | 324 | } |
297 | } | 325 | } |
298 | 326 | ||
@@ -314,13 +342,7 @@ class SelectComponent extends Component<IProps> { | |||
314 | required, | 342 | required, |
315 | } = this.props; | 343 | } = this.props; |
316 | 344 | ||
317 | const { | 345 | const { open, needle, value, selected, options } = this.state; |
318 | open, | ||
319 | needle, | ||
320 | value, | ||
321 | selected, | ||
322 | options, | ||
323 | } = this.state; | ||
324 | 346 | ||
325 | let selection = ''; | 347 | let selection = ''; |
326 | if (!value && defaultValue && options![defaultValue]) { | 348 | if (!value && defaultValue && options![defaultValue]) { |
@@ -332,10 +354,7 @@ class SelectComponent extends Component<IProps> { | |||
332 | } | 354 | } |
333 | 355 | ||
334 | return ( | 356 | return ( |
335 | <Wrapper | 357 | <Wrapper className={className} identifier="franz-select"> |
336 | className={className} | ||
337 | identifier="franz-select" | ||
338 | > | ||
339 | <Label | 358 | <Label |
340 | title={label} | 359 | title={label} |
341 | showLabel={showLabel} | 360 | showLabel={showLabel} |
@@ -352,14 +371,19 @@ class SelectComponent extends Component<IProps> { | |||
352 | > | 371 | > |
353 | <button | 372 | <button |
354 | type="button" | 373 | type="button" |
355 | className={classnames({ | 374 | className={classnames({ |
356 | [`${inputClassName}`]: inputClassName, | 375 | [`${inputClassName}`]: inputClassName, |
357 | [`${classes.select}`]: true, | 376 | [`${classes.select}`]: true, |
358 | [`${classes.hasError}`]: error, | 377 | [`${classes.hasError}`]: error, |
359 | })} | 378 | })} |
360 | onClick= {!disabled ? () => this.setState((state: IState) => ({ | 379 | onClick={ |
361 | open: !state.open, | 380 | !disabled |
362 | })) : () => {}} | 381 | ? () => |
382 | this.setState((state: IState) => ({ | ||
383 | open: !state.open, | ||
384 | })) | ||
385 | : () => {} | ||
386 | } | ||
363 | > | 387 | > |
364 | {selection} | 388 | {selection} |
365 | <Icon | 389 | <Icon |
@@ -373,10 +397,7 @@ class SelectComponent extends Component<IProps> { | |||
373 | </button> | 397 | </button> |
374 | {showSearch && open && ( | 398 | {showSearch && open && ( |
375 | <div className={classes.searchContainer}> | 399 | <div className={classes.searchContainer}> |
376 | <Icon | 400 | <Icon path={mdiMagnify} size={0.8} /> |
377 | path={mdiMagnify} | ||
378 | size={0.8} | ||
379 | /> | ||
380 | <input | 401 | <input |
381 | type="text" | 402 | type="text" |
382 | value={needle} | 403 | value={needle} |
@@ -391,10 +412,7 @@ class SelectComponent extends Component<IProps> { | |||
391 | className={classes.clearNeedle} | 412 | className={classes.clearNeedle} |
392 | onClick={() => this.setFilter()} | 413 | onClick={() => this.setFilter()} |
393 | > | 414 | > |
394 | <Icon | 415 | <Icon path={mdiCloseCircle} size={0.7} /> |
395 | path={mdiCloseCircle} | ||
396 | size={0.7} | ||
397 | /> | ||
398 | </button> | 416 | </button> |
399 | )} | 417 | )} |
400 | </div> | 418 | </div> |
@@ -406,7 +424,7 @@ class SelectComponent extends Component<IProps> { | |||
406 | })} | 424 | })} |
407 | ref={this.scrollContainerRef} | 425 | ref={this.scrollContainerRef} |
408 | > | 426 | > |
409 | {Object.keys(options!).map(((key, i) => ( | 427 | {Object.keys(options!).map((key, i) => ( |
410 | <div | 428 | <div |
411 | key={key} | 429 | key={key} |
412 | onClick={() => this.select(key)} | 430 | onClick={() => this.select(key)} |
@@ -420,7 +438,7 @@ class SelectComponent extends Component<IProps> { | |||
420 | > | 438 | > |
421 | {options![key]} | 439 | {options![key]} |
422 | </div> | 440 | </div> |
423 | )))} | 441 | ))} |
424 | </div> | 442 | </div> |
425 | </div> | 443 | </div> |
426 | <input | 444 | <input |
@@ -434,9 +452,7 @@ class SelectComponent extends Component<IProps> { | |||
434 | ref={this.inputRef} | 452 | ref={this.inputRef} |
435 | /> | 453 | /> |
436 | </Label> | 454 | </Label> |
437 | {error && ( | 455 | {error && <Error message={error} />} |
438 | <Error message={error} /> | ||
439 | )} | ||
440 | </Wrapper> | 456 | </Wrapper> |
441 | ); | 457 | ); |
442 | } | 458 | } |
diff --git a/packages/forms/src/textarea/index.tsx b/packages/forms/src/textarea/index.tsx index cd691a507..2d89d1c9f 100644 --- a/packages/forms/src/textarea/index.tsx +++ b/packages/forms/src/textarea/index.tsx | |||
@@ -1,5 +1,3 @@ | |||
1 | import { mdiEye, mdiEyeOff } from '@mdi/js'; | ||
2 | import Icon from '@mdi/react'; | ||
3 | import classnames from 'classnames'; | 1 | import classnames from 'classnames'; |
4 | import React, { Component, createRef } from 'react'; | 2 | import React, { Component, createRef } from 'react'; |
5 | import injectSheet from 'react-jss'; | 3 | import injectSheet from 'react-jss'; |
@@ -16,7 +14,10 @@ interface IData { | |||
16 | [index: string]: string; | 14 | [index: string]: string; |
17 | } | 15 | } |
18 | 16 | ||
19 | interface IProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement>, IFormField, IWithStyle { | 17 | interface IProps |
18 | extends React.TextareaHTMLAttributes<HTMLTextAreaElement>, | ||
19 | IFormField, | ||
20 | IWithStyle { | ||
20 | focus?: boolean; | 21 | focus?: boolean; |
21 | data: IData; | 22 | data: IData; |
22 | textareaClassName?: string; | 23 | textareaClassName?: string; |
@@ -39,14 +40,14 @@ class TextareaComponent extends Component<IProps> { | |||
39 | const { data } = this.props; | 40 | const { data } = this.props; |
40 | 41 | ||
41 | if (this.textareaRef && this.textareaRef.current && data) { | 42 | if (this.textareaRef && this.textareaRef.current && data) { |
42 | Object.keys(data).map(key => this.textareaRef.current!.dataset[key] = data[key]); | 43 | Object.keys(data).map( |
44 | key => (this.textareaRef.current!.dataset[key] = data[key]), | ||
45 | ); | ||
43 | } | 46 | } |
44 | } | 47 | } |
45 | 48 | ||
46 | onChange(e: React.ChangeEvent<HTMLTextAreaElement>) { | 49 | onChange(e: React.ChangeEvent<HTMLTextAreaElement>) { |
47 | const { | 50 | const { onChange } = this.props; |
48 | onChange, | ||
49 | } = this.props; | ||
50 | 51 | ||
51 | if (onChange) { | 52 | if (onChange) { |
52 | onChange(e); | 53 | onChange(e); |
@@ -59,7 +60,6 @@ class TextareaComponent extends Component<IProps> { | |||
59 | className, | 60 | className, |
60 | disabled, | 61 | disabled, |
61 | error, | 62 | error, |
62 | focus, | ||
63 | id, | 63 | id, |
64 | textareaClassName, | 64 | textareaClassName, |
65 | label, | 65 | label, |
@@ -96,9 +96,9 @@ class TextareaComponent extends Component<IProps> { | |||
96 | [`${classes.wrapper}`]: true, | 96 | [`${classes.wrapper}`]: true, |
97 | [`${classes.disabled}`]: disabled, | 97 | [`${classes.disabled}`]: disabled, |
98 | [`${classes.hasError}`]: error, | 98 | [`${classes.hasError}`]: error, |
99 | })}> | 99 | })} |
100 | > | ||
100 | <textarea | 101 | <textarea |
101 | autoFocus={focus} | ||
102 | id={id} | 102 | id={id} |
103 | name={name} | 103 | name={name} |
104 | placeholder={placeholder} | 104 | placeholder={placeholder} |
@@ -117,9 +117,7 @@ class TextareaComponent extends Component<IProps> { | |||
117 | </textarea> | 117 | </textarea> |
118 | </div> | 118 | </div> |
119 | </Label> | 119 | </Label> |
120 | {error && ( | 120 | {error && <Error message={error} />} |
121 | <Error message={error} /> | ||
122 | )} | ||
123 | </Wrapper> | 121 | </Wrapper> |
124 | ); | 122 | ); |
125 | } | 123 | } |
diff --git a/packages/forms/src/textarea/styles.ts b/packages/forms/src/textarea/styles.ts index c1cbd76a5..ff3a3202b 100644 --- a/packages/forms/src/textarea/styles.ts +++ b/packages/forms/src/textarea/styles.ts | |||
@@ -1,5 +1,6 @@ | |||
1 | import { Theme } from '@meetfranz/theme'; | 1 | import { Property } from 'csstype'; |
2 | import CSS from 'csstype'; | 2 | |
3 | import { Theme } from '../../../theme'; | ||
3 | 4 | ||
4 | export default (theme: Theme) => ({ | 5 | export default (theme: Theme) => ({ |
5 | label: { | 6 | label: { |
@@ -42,7 +43,7 @@ export default (theme: Theme) => ({ | |||
42 | background: theme.inputBackground, | 43 | background: theme.inputBackground, |
43 | border: theme.inputBorder, | 44 | border: theme.inputBorder, |
44 | borderRadius: theme.borderRadiusSmall, | 45 | borderRadius: theme.borderRadiusSmall, |
45 | boxSizing: 'border-box' as CSS.BoxSizingProperty, | 46 | boxSizing: 'border-box' as Property.BoxSizing, |
46 | display: 'flex', | 47 | display: 'flex', |
47 | order: 1, | 48 | order: 1, |
48 | width: '100%', | 49 | width: '100%', |
diff --git a/packages/forms/src/toggle/index.tsx b/packages/forms/src/toggle/index.tsx index ea335f281..bdf1d22cf 100644 --- a/packages/forms/src/toggle/index.tsx +++ b/packages/forms/src/toggle/index.tsx | |||
@@ -1,10 +1,10 @@ | |||
1 | import { Theme } from '@meetfranz/theme'; | ||
2 | import classnames from 'classnames'; | 1 | import classnames from 'classnames'; |
3 | import CSS from 'csstype'; | 2 | import { Property } from 'csstype'; |
4 | import React, { Component } from 'react'; | 3 | import React, { Component } from 'react'; |
5 | import injectStyle from 'react-jss'; | 4 | import injectStyle from 'react-jss'; |
6 | 5 | ||
7 | import { IFormField, IWithStyle, Omit } from '../typings/generic'; | 6 | import { IFormField, IWithStyle } from '../typings/generic'; |
7 | import { Theme } from '../../../theme'; | ||
8 | 8 | ||
9 | import { Error } from '../error'; | 9 | import { Error } from '../error'; |
10 | import { Label } from '../label'; | 10 | import { Label } from '../label'; |
@@ -17,12 +17,18 @@ interface IProps | |||
17 | className?: string; | 17 | className?: string; |
18 | } | 18 | } |
19 | 19 | ||
20 | let buttonTransition: string = 'none'; | ||
21 | |||
22 | if (window && window.matchMedia('(prefers-reduced-motion: no-preference)')) { | ||
23 | buttonTransition = 'all .5s'; | ||
24 | } | ||
25 | |||
20 | const styles = (theme: Theme) => ({ | 26 | const styles = (theme: Theme) => ({ |
21 | toggle: { | 27 | toggle: { |
22 | background: theme.toggleBackground, | 28 | background: theme.toggleBackground, |
23 | borderRadius: theme.borderRadius, | 29 | borderRadius: theme.borderRadius, |
24 | height: theme.toggleHeight, | 30 | height: theme.toggleHeight, |
25 | position: 'relative' as CSS.PositionProperty, | 31 | position: 'relative' as Property.Position, |
26 | width: theme.toggleWidth, | 32 | width: theme.toggleWidth, |
27 | }, | 33 | }, |
28 | button: { | 34 | button: { |
@@ -33,8 +39,8 @@ const styles = (theme: Theme) => ({ | |||
33 | height: theme.toggleHeight - 2, | 39 | height: theme.toggleHeight - 2, |
34 | left: 1, | 40 | left: 1, |
35 | top: 1, | 41 | top: 1, |
36 | position: 'absolute' as CSS.PositionProperty, | 42 | position: 'absolute' as Property.Position, |
37 | transition: 'all .5s', | 43 | transition: buttonTransition, |
38 | }, | 44 | }, |
39 | buttonActive: { | 45 | buttonActive: { |
40 | background: theme.toggleButtonActive, | 46 | background: theme.toggleButtonActive, |
diff --git a/packages/forms/src/typings/generic.ts b/packages/forms/src/typings/generic.ts index 29136dfed..0fd0cdbf3 100644 --- a/packages/forms/src/typings/generic.ts +++ b/packages/forms/src/typings/generic.ts | |||
@@ -1,6 +1,7 @@ | |||
1 | import { Theme } from '@meetfranz/theme/lib'; | ||
2 | import { Classes } from 'jss'; | 1 | import { Classes } from 'jss'; |
3 | 2 | ||
3 | import { Theme } from '../../../theme'; | ||
4 | |||
4 | export interface IFormField { | 5 | export interface IFormField { |
5 | showLabel?: boolean; | 6 | showLabel?: boolean; |
6 | label?: string; | 7 | label?: string; |
diff --git a/packages/forms/src/wrapper/index.tsx b/packages/forms/src/wrapper/index.tsx index cf179bc5e..3ae551e2c 100644 --- a/packages/forms/src/wrapper/index.tsx +++ b/packages/forms/src/wrapper/index.tsx | |||
@@ -12,18 +12,13 @@ interface IProps extends IWithStyle { | |||
12 | 12 | ||
13 | const styles = { | 13 | const styles = { |
14 | container: { | 14 | container: { |
15 | marginBottom: (props: IProps) => props.noMargin ? 0 : 20, | 15 | marginBottom: (props: IProps) => (props.noMargin ? 0 : 20), |
16 | }, | 16 | }, |
17 | }; | 17 | }; |
18 | 18 | ||
19 | class WrapperComponent extends Component<IProps> { | 19 | class WrapperComponent extends Component<IProps> { |
20 | render() { | 20 | render() { |
21 | const { | 21 | const { children, classes, className, identifier } = this.props; |
22 | children, | ||
23 | classes, | ||
24 | className, | ||
25 | identifier, | ||
26 | } = this.props; | ||
27 | 22 | ||
28 | return ( | 23 | return ( |
29 | <div | 24 | <div |