aboutsummaryrefslogtreecommitdiffstats
path: root/packages/forms/src
diff options
context:
space:
mode:
Diffstat (limited to 'packages/forms/src')
-rw-r--r--packages/forms/src/button/index.tsx70
-rw-r--r--packages/forms/src/error/index.tsx13
-rw-r--r--packages/forms/src/error/styles.ts2
-rw-r--r--packages/forms/src/input/index.tsx63
-rw-r--r--packages/forms/src/input/scorePassword.ts6
-rw-r--r--packages/forms/src/input/styles.ts9
-rw-r--r--packages/forms/src/label/index.tsx13
-rw-r--r--packages/forms/src/label/styles.ts2
-rw-r--r--packages/forms/src/select/index.tsx146
-rw-r--r--packages/forms/src/textarea/index.tsx24
-rw-r--r--packages/forms/src/textarea/styles.ts7
-rw-r--r--packages/forms/src/toggle/index.tsx18
-rw-r--r--packages/forms/src/typings/generic.ts3
-rw-r--r--packages/forms/src/wrapper/index.tsx9
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 @@
1import Icon from '@mdi/react'; 1import Icon from '@mdi/react';
2import { Theme } from '@meetfranz/theme';
3import classnames from 'classnames'; 2import classnames from 'classnames';
4import CSS from 'csstype'; 3import { Property } from 'csstype';
5import React, { Component } from 'react'; 4import React, { Component } from 'react';
6import injectStyle, { withTheme } from 'react-jss'; 5import injectStyle, { withTheme } from 'react-jss';
7import Loader from 'react-loader'; 6import Loader from 'react-loader';
8 7
9import { IFormField, IWithStyle } from '../typings/generic'; 8import { IFormField, IWithStyle } from '../typings/generic';
9import { Theme } from '../../../theme';
10 10
11type ButtonType = 'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'inverted'; 11type ButtonType =
12 | 'primary'
13 | 'secondary'
14 | 'success'
15 | 'danger'
16 | 'warning'
17 | 'inverted';
12 18
13interface IProps extends IFormField, IWithStyle { 19interface 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
28interface IState { 38let buttonTransition: string = 'none';
29 busy: boolean; 39let loaderContainerTransition: string = 'none';
40
41if (window && window.matchMedia('(prefers-reduced-motion: no-preference)')) {
42 buttonTransition = 'background .5s, opacity 0.3s';
43 loaderContainerTransition = 'all 0.3s';
30} 44}
31 45
32const styles = (theme: Theme) => ({ 46const 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
12class ErrorComponent extends Component<IProps> { 12class 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 @@
1import { Theme } from '../../../theme/lib'; 1import { Theme } from '../../../theme';
2 2
3export default (theme: Theme) => ({ 3export 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
20interface IProps extends React.InputHTMLAttributes<HTMLInputElement>, IFormField, IWithStyle { 20interface 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
13export function scorePasswordFunc(password: string): number { 13export 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 @@
1import { Theme } from '@meetfranz/theme'; 1import { Property } from 'csstype';
2import CSS from 'csstype'; 2
3import { Theme } from '../../../theme';
3 4
4const prefixStyles = (theme: Theme) => ({ 5const 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
8import styles from './styles'; 8import styles from './styles';
9 9
10interface ILabel extends IFormField, React.LabelHTMLAttributes<HTMLLabelElement> { 10interface 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 @@
1import { Theme } from '../../../theme/lib'; 1import { Theme } from '../../../theme';
2 2
3export default (theme: Theme) => ({ 3export 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 @@
1import { mdiArrowRightDropCircleOutline, mdiCloseCircle, mdiMagnify } from '@mdi/js'; 1import {
2 mdiArrowRightDropCircleOutline,
3 mdiCloseCircle,
4 mdiMagnify,
5} from '@mdi/js';
2import Icon from '@mdi/react'; 6import Icon from '@mdi/react';
3import { Theme } from '@meetfranz/theme';
4import classnames from 'classnames'; 7import classnames from 'classnames';
5import React, { Component, createRef } from 'react'; 8import React, { Component, createRef } from 'react';
6import injectStyle from 'react-jss'; 9import injectStyle from 'react-jss';
7 10
8import { IFormField, IWithStyle } from '../typings/generic'; 11import { IFormField, IWithStyle } from '../typings/generic';
12import { Theme } from '../../../theme';
9 13
10import { NONAME } from 'dns';
11import { Error } from '../error'; 14import { Error } from '../error';
12import { Label } from '../label'; 15import { Label } from '../label';
13import { Wrapper } from '../wrapper'; 16import { Wrapper } from '../wrapper';
@@ -43,6 +46,14 @@ interface IState {
43 options: IOptions; 46 options: IOptions;
44} 47}
45 48
49let popupTransition: string = 'none';
50let toggleTransition: string = 'none';
51
52if (window && window.matchMedia('(prefers-reduced-motion: no-preference)')) {
53 popupTransition = 'all 0.3s';
54 toggleTransition = 'transform 0.3s';
55}
56
46const styles = (theme: Theme) => ({ 57const 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 @@
1import { mdiEye, mdiEyeOff } from '@mdi/js';
2import Icon from '@mdi/react';
3import classnames from 'classnames'; 1import classnames from 'classnames';
4import React, { Component, createRef } from 'react'; 2import React, { Component, createRef } from 'react';
5import injectSheet from 'react-jss'; 3import injectSheet from 'react-jss';
@@ -16,7 +14,10 @@ interface IData {
16 [index: string]: string; 14 [index: string]: string;
17} 15}
18 16
19interface IProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement>, IFormField, IWithStyle { 17interface 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 @@
1import { Theme } from '@meetfranz/theme'; 1import { Property } from 'csstype';
2import CSS from 'csstype'; 2
3import { Theme } from '../../../theme';
3 4
4export default (theme: Theme) => ({ 5export 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 @@
1import { Theme } from '@meetfranz/theme';
2import classnames from 'classnames'; 1import classnames from 'classnames';
3import CSS from 'csstype'; 2import { Property } from 'csstype';
4import React, { Component } from 'react'; 3import React, { Component } from 'react';
5import injectStyle from 'react-jss'; 4import injectStyle from 'react-jss';
6 5
7import { IFormField, IWithStyle, Omit } from '../typings/generic'; 6import { IFormField, IWithStyle } from '../typings/generic';
7import { Theme } from '../../../theme';
8 8
9import { Error } from '../error'; 9import { Error } from '../error';
10import { Label } from '../label'; 10import { Label } from '../label';
@@ -17,12 +17,18 @@ interface IProps
17 className?: string; 17 className?: string;
18} 18}
19 19
20let buttonTransition: string = 'none';
21
22if (window && window.matchMedia('(prefers-reduced-motion: no-preference)')) {
23 buttonTransition = 'all .5s';
24}
25
20const styles = (theme: Theme) => ({ 26const 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 @@
1import { Theme } from '@meetfranz/theme/lib';
2import { Classes } from 'jss'; 1import { Classes } from 'jss';
3 2
3import { Theme } from '../../../theme';
4
4export interface IFormField { 5export 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
13const styles = { 13const 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
19class WrapperComponent extends Component<IProps> { 19class 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