aboutsummaryrefslogtreecommitdiffstats
path: root/src/components/ui/button
diff options
context:
space:
mode:
authorLibravatar Vijay Aravamudhan <vraravam@users.noreply.github.com>2021-10-15 16:22:25 +0530
committerLibravatar GitHub <noreply@github.com>2021-10-15 16:22:25 +0530
commitec15f83b947fb2daf4ca1a72e3af527dc89512a3 (patch)
treeea049cee5184a58b5bc09505e723cd19a736c4bd /src/components/ui/button
parentchore: move 'packages/ui' into 'src' (no longer an injected package) (#2077) (diff)
downloadferdium-app-ec15f83b947fb2daf4ca1a72e3af527dc89512a3.tar.gz
ferdium-app-ec15f83b947fb2daf4ca1a72e3af527dc89512a3.tar.zst
ferdium-app-ec15f83b947fb2daf4ca1a72e3af527dc89512a3.zip
chore: move 'packages/forms' into 'src' (no longer an injected package) (#2079)
Diffstat (limited to 'src/components/ui/button')
-rw-r--r--src/components/ui/button/index.tsx265
1 files changed, 265 insertions, 0 deletions
diff --git a/src/components/ui/button/index.tsx b/src/components/ui/button/index.tsx
new file mode 100644
index 000000000..5b8927b51
--- /dev/null
+++ b/src/components/ui/button/index.tsx
@@ -0,0 +1,265 @@
1import Icon from '@mdi/react';
2import classnames from 'classnames';
3import { Property } from 'csstype';
4import { Component, MouseEvent } from 'react';
5import injectStyle, { withTheme } from 'react-jss';
6import Loader from 'react-loader';
7import { Theme } from '@meetfranz/theme';
8
9import { IFormField, IWithStyle } from '../typings/generic';
10
11type ButtonType =
12 | 'primary'
13 | 'secondary'
14 | 'success'
15 | 'danger'
16 | 'warning'
17 | 'inverted';
18
19interface IProps extends IFormField, IWithStyle {
20 className?: string;
21 disabled?: boolean;
22 id?: string;
23 type?: 'button' | 'reset' | 'submit' | undefined;
24 onClick: (
25 event: MouseEvent<HTMLButtonElement> | MouseEvent<HTMLAnchorElement>,
26 ) => void;
27 buttonType?: ButtonType;
28 stretch?: boolean;
29 loaded?: boolean;
30 busy?: boolean;
31 icon?: string;
32 href?: string;
33 target?: string;
34}
35
36let buttonTransition: string = 'none';
37let loaderContainerTransition: string = 'none';
38
39if (window && window.matchMedia('(prefers-reduced-motion: no-preference)')) {
40 buttonTransition = 'background .5s, opacity 0.3s';
41 loaderContainerTransition = 'all 0.3s';
42}
43
44const styles = (theme: Theme) => ({
45 button: {
46 borderRadius: theme.borderRadiusSmall,
47 border: 'none',
48 display: 'inline-flex',
49 position: 'relative' as Property.Position,
50 transition: buttonTransition,
51 textAlign: 'center' as Property.TextAlign,
52 outline: 'none',
53 alignItems: 'center',
54 padding: 0,
55 width: (props: IProps) =>
56 (props.stretch ? '100%' : 'auto') as Property.Width<string>,
57 fontSize: theme.uiFontSize,
58 textDecoration: 'none',
59
60 '&:hover': {
61 opacity: 0.8,
62 },
63 '&:active': {
64 opacity: 0.5,
65 transition: 'none',
66 },
67 },
68 label: {
69 margin: '10px 20px',
70 width: '100%',
71 display: 'flex',
72 alignItems: 'center',
73 justifyContent: 'center',
74 },
75 primary: {
76 background: theme.buttonPrimaryBackground,
77 color: theme.buttonPrimaryTextColor,
78
79 '& svg': {
80 fill: theme.buttonPrimaryTextColor,
81 },
82 },
83 secondary: {
84 background: theme.buttonSecondaryBackground,
85 color: theme.buttonSecondaryTextColor,
86
87 '& svg': {
88 fill: theme.buttonSecondaryTextColor,
89 },
90 },
91 success: {
92 background: theme.buttonSuccessBackground,
93 color: theme.buttonSuccessTextColor,
94
95 '& svg': {
96 fill: theme.buttonSuccessTextColor,
97 },
98 },
99 danger: {
100 background: theme.buttonDangerBackground,
101 color: theme.buttonDangerTextColor,
102
103 '& svg': {
104 fill: theme.buttonDangerTextColor,
105 },
106 },
107 warning: {
108 background: theme.buttonWarningBackground,
109 color: theme.buttonWarningTextColor,
110
111 '& svg': {
112 fill: theme.buttonWarningTextColor,
113 },
114 },
115 inverted: {
116 background: theme.buttonInvertedBackground,
117 color: theme.buttonInvertedTextColor,
118 border: theme.buttonInvertedBorder,
119
120 '& svg': {
121 fill: theme.buttonInvertedTextColor,
122 },
123 },
124 disabled: {
125 opacity: theme.inputDisabledOpacity,
126 },
127 loader: {
128 position: 'relative' as Property.Position,
129 width: 20,
130 height: 18,
131 zIndex: 9999,
132 },
133 loaderContainer: {
134 width: (props: IProps): string => (!props.busy ? '0' : '40px'),
135 height: 20,
136 overflow: 'hidden',
137 transition: loaderContainerTransition,
138 marginLeft: (props: IProps): number => (!props.busy ? 10 : 20),
139 marginRight: (props: IProps): number => (!props.busy ? -10 : -20),
140 position: (props: IProps): Property.Position =>
141 props.stretch ? 'absolute' : 'inherit',
142 },
143 icon: {
144 margin: [1, 10, 0, -5],
145 },
146});
147
148class ButtonComponent extends Component<IProps> {
149 public static defaultProps = {
150 type: 'button',
151 disabled: false,
152 onClick: () => null,
153 buttonType: 'primary' as ButtonType,
154 stretch: false,
155 busy: false,
156 };
157
158 state = {
159 busy: false,
160 };
161
162 componentWillMount() {
163 this.setState({ busy: this.props.busy });
164 }
165
166 componentWillReceiveProps(nextProps: IProps) {
167 if (nextProps.busy !== this.props.busy) {
168 if (this.props.busy) {
169 setTimeout(() => {
170 this.setState({ busy: nextProps.busy });
171 }, 300);
172 } else {
173 this.setState({ busy: nextProps.busy });
174 }
175 }
176 }
177
178 render() {
179 const {
180 classes,
181 className,
182 theme,
183 disabled,
184 id,
185 label,
186 type,
187 onClick,
188 buttonType,
189 loaded,
190 icon,
191 href,
192 target,
193 } = this.props;
194
195 const { busy } = this.state;
196
197 let showLoader = false;
198 if (loaded) {
199 showLoader = !loaded;
200 console.warn(
201 'Ferdi Button prop `loaded` will be deprecated in the future. Please use `busy` instead',
202 );
203 }
204 if (busy) {
205 showLoader = busy;
206 }
207
208 const content = (
209 <>
210 <div className={classes.loaderContainer}>
211 {showLoader && (
212 <Loader
213 loaded={false}
214 width={4}
215 scale={0.45}
216 color={theme.buttonLoaderColor[buttonType!]}
217 parentClassName={classes.loader}
218 />
219 )}
220 </div>
221 <div className={classes.label}>
222 {icon && <Icon path={icon} size={0.8} className={classes.icon} />}
223 {label}
224 </div>
225 </>
226 );
227
228 const wrapperComponent = !href ? (
229 <button
230 id={id}
231 type={type}
232 onClick={onClick}
233 className={classnames({
234 [`${classes.button}`]: true,
235 [`${classes[buttonType as ButtonType]}`]: true,
236 [`${classes.disabled}`]: disabled,
237 [`${className}`]: className,
238 })}
239 disabled={disabled}
240 data-type="franz-button"
241 >
242 {content}
243 </button>
244 ) : (
245 <a
246 href={href}
247 target={target}
248 onClick={onClick}
249 className={classnames({
250 [`${classes.button}`]: true,
251 [`${classes[buttonType as ButtonType]}`]: true,
252 [`${className}`]: className,
253 })}
254 rel={target === '_blank' ? 'noopener' : ''}
255 data-type="franz-button"
256 >
257 {content}
258 </a>
259 );
260
261 return wrapperComponent;
262 }
263}
264
265export const Button = injectStyle(styles)(withTheme(ButtonComponent));