diff options
Diffstat (limited to 'src/components/ui/toggle')
-rw-r--r-- | src/components/ui/toggle/index.tsx | 125 |
1 files changed, 125 insertions, 0 deletions
diff --git a/src/components/ui/toggle/index.tsx b/src/components/ui/toggle/index.tsx new file mode 100644 index 000000000..67b6c3835 --- /dev/null +++ b/src/components/ui/toggle/index.tsx | |||
@@ -0,0 +1,125 @@ | |||
1 | import classnames from 'classnames'; | ||
2 | import { Property } from 'csstype'; | ||
3 | import { Component, InputHTMLAttributes } from 'react'; | ||
4 | import injectStyle from 'react-jss'; | ||
5 | import { Theme } from '@meetfranz/theme'; | ||
6 | |||
7 | import { IFormField, IWithStyle } from '../typings/generic'; | ||
8 | |||
9 | import { Error } from '../error'; | ||
10 | import { Label } from '../label'; | ||
11 | import { Wrapper } from '../wrapper'; | ||
12 | |||
13 | interface IProps | ||
14 | extends InputHTMLAttributes<HTMLInputElement>, | ||
15 | IFormField, | ||
16 | IWithStyle { | ||
17 | className?: string; | ||
18 | } | ||
19 | |||
20 | let buttonTransition: string = 'none'; | ||
21 | |||
22 | if (window && window.matchMedia('(prefers-reduced-motion: no-preference)')) { | ||
23 | buttonTransition = 'all .5s'; | ||
24 | } | ||
25 | |||
26 | const styles = (theme: Theme) => ({ | ||
27 | toggle: { | ||
28 | background: theme.toggleBackground, | ||
29 | borderRadius: theme.borderRadius, | ||
30 | height: theme.toggleHeight, | ||
31 | position: 'relative' as Property.Position, | ||
32 | width: theme.toggleWidth, | ||
33 | }, | ||
34 | button: { | ||
35 | background: theme.toggleButton, | ||
36 | borderRadius: '100%', | ||
37 | boxShadow: '0 1px 4px rgba(0, 0, 0, .3)', | ||
38 | width: theme.toggleHeight - 2, | ||
39 | height: theme.toggleHeight - 2, | ||
40 | left: 1, | ||
41 | top: 1, | ||
42 | position: 'absolute' as Property.Position, | ||
43 | transition: buttonTransition, | ||
44 | }, | ||
45 | buttonActive: { | ||
46 | background: theme.toggleButtonActive, | ||
47 | left: theme.toggleWidth - theme.toggleHeight + 1, | ||
48 | }, | ||
49 | input: { | ||
50 | visibility: 'hidden' as any, | ||
51 | }, | ||
52 | disabled: { | ||
53 | opacity: theme.inputDisabledOpacity, | ||
54 | }, | ||
55 | toggleLabel: { | ||
56 | display: 'flex', | ||
57 | alignItems: 'center', | ||
58 | |||
59 | '& > span': { | ||
60 | order: 1, | ||
61 | marginLeft: 15, | ||
62 | }, | ||
63 | }, | ||
64 | }); | ||
65 | |||
66 | class ToggleComponent extends Component<IProps> { | ||
67 | public static defaultProps = { | ||
68 | onChange: () => {}, | ||
69 | showLabel: true, | ||
70 | disabled: false, | ||
71 | error: '', | ||
72 | }; | ||
73 | |||
74 | render() { | ||
75 | const { | ||
76 | classes, | ||
77 | className, | ||
78 | disabled, | ||
79 | error, | ||
80 | id, | ||
81 | label, | ||
82 | showLabel, | ||
83 | checked, | ||
84 | value, | ||
85 | onChange, | ||
86 | } = this.props; | ||
87 | |||
88 | return ( | ||
89 | <Wrapper className={className} identifier="franz-toggle"> | ||
90 | <Label | ||
91 | title={label} | ||
92 | showLabel={showLabel} | ||
93 | htmlFor={id} | ||
94 | className={classes.toggleLabel} | ||
95 | > | ||
96 | <div | ||
97 | className={classnames({ | ||
98 | [`${classes.toggle}`]: true, | ||
99 | [`${classes.disabled}`]: disabled, | ||
100 | })} | ||
101 | > | ||
102 | <div | ||
103 | className={classnames({ | ||
104 | [`${classes.button}`]: true, | ||
105 | [`${classes.buttonActive}`]: checked, | ||
106 | })} | ||
107 | /> | ||
108 | <input | ||
109 | className={classes.input} | ||
110 | id={id} | ||
111 | type="checkbox" | ||
112 | checked={checked} | ||
113 | value={value} | ||
114 | onChange={onChange} | ||
115 | disabled={disabled} | ||
116 | /> | ||
117 | </div> | ||
118 | </Label> | ||
119 | {error && <Error message={error} />} | ||
120 | </Wrapper> | ||
121 | ); | ||
122 | } | ||
123 | } | ||
124 | |||
125 | export const Toggle = injectStyle(styles)(ToggleComponent); | ||