diff options
author | 2021-10-15 15:25:41 +0530 | |
---|---|---|
committer | 2021-10-15 15:25:41 +0530 | |
commit | 0ad7444fb1dc2cdb82830df4ef241d75a6bfd82d (patch) | |
tree | 5a994fb8e0620aa5d2542ddd9c8561ef9861a9b5 /src/components | |
parent | chore: refresh lock file to fix vulnerabilities (#2075) (diff) | |
download | ferdium-app-0ad7444fb1dc2cdb82830df4ef241d75a6bfd82d.tar.gz ferdium-app-0ad7444fb1dc2cdb82830df4ef241d75a6bfd82d.tar.zst ferdium-app-0ad7444fb1dc2cdb82830df4ef241d75a6bfd82d.zip |
chore: move 'packages/ui' into 'src' (no longer an injected package) (#2077)
Diffstat (limited to 'src/components')
-rw-r--r-- | src/components/auth/SetupAssistant.js | 2 | ||||
-rw-r--r-- | src/components/services/content/ConnectionLostBanner.js | 3 | ||||
-rw-r--r-- | src/components/settings/account/AccountDashboard.js | 2 | ||||
-rw-r--r-- | src/components/settings/recipes/RecipesDashboard.js | 3 | ||||
-rw-r--r-- | src/components/ui/Modal/index.tsx | 4 | ||||
-rw-r--r-- | src/components/ui/badge/ProBadge.tsx | 64 | ||||
-rw-r--r-- | src/components/ui/badge/index.tsx | 71 | ||||
-rw-r--r-- | src/components/ui/headline/index.tsx | 70 | ||||
-rw-r--r-- | src/components/ui/icon/index.tsx | 46 | ||||
-rw-r--r-- | src/components/ui/infobox/index.tsx | 205 | ||||
-rw-r--r-- | src/components/ui/loader/index.tsx | 44 | ||||
-rw-r--r-- | src/components/ui/typings/generic.ts | 11 |
12 files changed, 518 insertions, 7 deletions
diff --git a/src/components/auth/SetupAssistant.js b/src/components/auth/SetupAssistant.js index d0efd6361..d009a2878 100644 --- a/src/components/auth/SetupAssistant.js +++ b/src/components/auth/SetupAssistant.js | |||
@@ -6,7 +6,7 @@ import injectSheet from 'react-jss'; | |||
6 | import classnames from 'classnames'; | 6 | import classnames from 'classnames'; |
7 | 7 | ||
8 | import { Input, Button } from '@meetfranz/forms'; | 8 | import { Input, Button } from '@meetfranz/forms'; |
9 | import { Badge } from '@meetfranz/ui'; | 9 | import { Badge } from '../ui/badge'; |
10 | import Modal from '../ui/Modal'; | 10 | import Modal from '../ui/Modal'; |
11 | import Infobox from '../ui/Infobox'; | 11 | import Infobox from '../ui/Infobox'; |
12 | import Appear from '../ui/effects/Appear'; | 12 | import Appear from '../ui/effects/Appear'; |
diff --git a/src/components/services/content/ConnectionLostBanner.js b/src/components/services/content/ConnectionLostBanner.js index 236d2739b..5111a081a 100644 --- a/src/components/services/content/ConnectionLostBanner.js +++ b/src/components/services/content/ConnectionLostBanner.js | |||
@@ -2,12 +2,11 @@ import { createRef, Component } from 'react'; | |||
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { observer } from 'mobx-react'; | 3 | import { observer } from 'mobx-react'; |
4 | import injectSheet from 'react-jss'; | 4 | import injectSheet from 'react-jss'; |
5 | import { Icon } from '@meetfranz/ui'; | ||
6 | import { defineMessages, injectIntl } from 'react-intl'; | 5 | import { defineMessages, injectIntl } from 'react-intl'; |
7 | 6 | ||
8 | import { mdiAlert } from '@mdi/js'; | 7 | import { mdiAlert } from '@mdi/js'; |
9 | import { LIVE_API_FERDI_WEBSITE } from '../../../config'; | 8 | import { LIVE_API_FERDI_WEBSITE } from '../../../config'; |
10 | // import { Button } from '@meetfranz/forms'; | 9 | import { Icon } from '../../ui/icon'; |
11 | 10 | ||
12 | const messages = defineMessages({ | 11 | const messages = defineMessages({ |
13 | text: { | 12 | text: { |
diff --git a/src/components/settings/account/AccountDashboard.js b/src/components/settings/account/AccountDashboard.js index 686501267..6c489e64b 100644 --- a/src/components/settings/account/AccountDashboard.js +++ b/src/components/settings/account/AccountDashboard.js | |||
@@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; | |||
3 | import { observer, PropTypes as MobxPropTypes } from 'mobx-react'; | 3 | import { observer, PropTypes as MobxPropTypes } from 'mobx-react'; |
4 | import { defineMessages, injectIntl } from 'react-intl'; | 4 | import { defineMessages, injectIntl } from 'react-intl'; |
5 | import ReactTooltip from 'react-tooltip'; | 5 | import ReactTooltip from 'react-tooltip'; |
6 | import { H1, H2 } from '@meetfranz/ui'; | 6 | import { H1, H2 } from '../../ui/headline'; |
7 | 7 | ||
8 | import Loader from '../../ui/Loader'; | 8 | import Loader from '../../ui/Loader'; |
9 | import Button from '../../ui/Button'; | 9 | import Button from '../../ui/Button'; |
diff --git a/src/components/settings/recipes/RecipesDashboard.js b/src/components/settings/recipes/RecipesDashboard.js index 0c521319a..8ab726eb3 100644 --- a/src/components/settings/recipes/RecipesDashboard.js +++ b/src/components/settings/recipes/RecipesDashboard.js | |||
@@ -6,7 +6,8 @@ import { Link } from 'react-router'; | |||
6 | 6 | ||
7 | import { Button, Input } from '@meetfranz/forms'; | 7 | import { Button, Input } from '@meetfranz/forms'; |
8 | import injectSheet from 'react-jss'; | 8 | import injectSheet from 'react-jss'; |
9 | import { H3, H2 } from '@meetfranz/ui'; | 9 | |
10 | import { H3, H2 } from '../../ui/headline'; | ||
10 | import SearchInput from '../../ui/SearchInput'; | 11 | import SearchInput from '../../ui/SearchInput'; |
11 | import Infobox from '../../ui/Infobox'; | 12 | import Infobox from '../../ui/Infobox'; |
12 | import RecipeItem from './RecipeItem'; | 13 | import RecipeItem from './RecipeItem'; |
diff --git a/src/components/ui/Modal/index.tsx b/src/components/ui/Modal/index.tsx index 0bb0aaa61..f2f4461b8 100644 --- a/src/components/ui/Modal/index.tsx +++ b/src/components/ui/Modal/index.tsx | |||
@@ -2,9 +2,9 @@ import { Component, ReactChildren } from 'react'; | |||
2 | import ReactModal from 'react-modal'; | 2 | import ReactModal from 'react-modal'; |
3 | import classnames from 'classnames'; | 3 | import classnames from 'classnames'; |
4 | import injectCSS from 'react-jss'; | 4 | import injectCSS from 'react-jss'; |
5 | import { Icon } from '@meetfranz/ui'; | ||
6 | |||
7 | import { mdiClose } from '@mdi/js'; | 5 | import { mdiClose } from '@mdi/js'; |
6 | |||
7 | import { Icon } from '../icon'; | ||
8 | import styles from './styles'; | 8 | import styles from './styles'; |
9 | 9 | ||
10 | type Props = { | 10 | type Props = { |
diff --git a/src/components/ui/badge/ProBadge.tsx b/src/components/ui/badge/ProBadge.tsx new file mode 100644 index 000000000..cb6bc4c98 --- /dev/null +++ b/src/components/ui/badge/ProBadge.tsx | |||
@@ -0,0 +1,64 @@ | |||
1 | import { mdiStar } from '@mdi/js'; | ||
2 | import classnames from 'classnames'; | ||
3 | import { Component } from 'react'; | ||
4 | import injectStyle from 'react-jss'; | ||
5 | |||
6 | import { Theme } from '@meetfranz/theme'; | ||
7 | import { Icon } from '../icon'; | ||
8 | import { Badge } from './index'; | ||
9 | import { IWithStyle } from '../typings/generic'; | ||
10 | |||
11 | interface IProps extends IWithStyle { | ||
12 | badgeClasses?: string; | ||
13 | iconClasses?: string; | ||
14 | inverted?: boolean; | ||
15 | className?: string; | ||
16 | } | ||
17 | |||
18 | const styles = (theme: Theme) => ({ | ||
19 | badge: { | ||
20 | height: 'auto', | ||
21 | padding: [4, 6, 2, 7], | ||
22 | borderRadius: theme.borderRadiusSmall, | ||
23 | }, | ||
24 | invertedBadge: { | ||
25 | background: theme.styleTypes.primary.contrast, | ||
26 | color: theme.styleTypes.primary.accent, | ||
27 | }, | ||
28 | icon: { | ||
29 | fill: theme.styleTypes.primary.contrast, | ||
30 | }, | ||
31 | invertedIcon: { | ||
32 | fill: theme.styleTypes.primary.accent, | ||
33 | }, | ||
34 | }); | ||
35 | |||
36 | class ProBadgeComponent extends Component<IProps> { | ||
37 | render() { | ||
38 | const { classes, badgeClasses, iconClasses, inverted, className } = | ||
39 | this.props; | ||
40 | |||
41 | return ( | ||
42 | <Badge | ||
43 | type="primary" | ||
44 | className={classnames([ | ||
45 | classes.badge, | ||
46 | inverted && classes.invertedBadge, | ||
47 | badgeClasses, | ||
48 | className, | ||
49 | ])} | ||
50 | > | ||
51 | <Icon | ||
52 | icon={mdiStar} | ||
53 | className={classnames([ | ||
54 | classes.icon, | ||
55 | inverted && classes.invertedIcon, | ||
56 | iconClasses, | ||
57 | ])} | ||
58 | /> | ||
59 | </Badge> | ||
60 | ); | ||
61 | } | ||
62 | } | ||
63 | |||
64 | export const ProBadge = injectStyle(styles)(ProBadgeComponent); | ||
diff --git a/src/components/ui/badge/index.tsx b/src/components/ui/badge/index.tsx new file mode 100644 index 000000000..6495036ff --- /dev/null +++ b/src/components/ui/badge/index.tsx | |||
@@ -0,0 +1,71 @@ | |||
1 | import classnames from 'classnames'; | ||
2 | import { Component, ReactNode } from 'react'; | ||
3 | import injectStyle from 'react-jss'; | ||
4 | |||
5 | import { Theme } from '@meetfranz/theme'; | ||
6 | import { IWithStyle } from '../typings/generic'; | ||
7 | |||
8 | interface IProps extends IWithStyle { | ||
9 | type: string; | ||
10 | className?: string; | ||
11 | children: ReactNode; | ||
12 | } | ||
13 | |||
14 | const badgeStyles = (theme: Theme) => { | ||
15 | const styles = {}; | ||
16 | Object.keys(theme.styleTypes).map(style => { | ||
17 | Object.assign(styles, { | ||
18 | [style]: { | ||
19 | background: theme.styleTypes[style].accent, | ||
20 | color: theme.styleTypes[style].contrast, | ||
21 | border: theme.styleTypes[style].border, | ||
22 | }, | ||
23 | }); | ||
24 | }); | ||
25 | |||
26 | return styles; | ||
27 | }; | ||
28 | |||
29 | const styles = (theme: Theme) => ({ | ||
30 | badge: { | ||
31 | display: 'inline-block', | ||
32 | padding: [3, 8, 4], | ||
33 | fontSize: theme.badgeFontSize, | ||
34 | borderRadius: theme.badgeBorderRadius, | ||
35 | margin: [0, 4], | ||
36 | |||
37 | '&:first-child': { | ||
38 | marginLeft: 0, | ||
39 | }, | ||
40 | |||
41 | '&:last-child': { | ||
42 | marginRight: 0, | ||
43 | }, | ||
44 | }, | ||
45 | ...badgeStyles(theme), | ||
46 | }); | ||
47 | |||
48 | class BadgeComponent extends Component<IProps> { | ||
49 | public static defaultProps = { | ||
50 | type: 'primary', | ||
51 | }; | ||
52 | |||
53 | render() { | ||
54 | const { classes, children, type, className } = this.props; | ||
55 | |||
56 | return ( | ||
57 | <div | ||
58 | className={classnames({ | ||
59 | [classes.badge]: true, | ||
60 | [classes[type]]: true, | ||
61 | [`${className}`]: className, | ||
62 | })} | ||
63 | data-type="franz-badge" | ||
64 | > | ||
65 | {children} | ||
66 | </div> | ||
67 | ); | ||
68 | } | ||
69 | } | ||
70 | |||
71 | export const Badge = injectStyle(styles)(BadgeComponent); | ||
diff --git a/src/components/ui/headline/index.tsx b/src/components/ui/headline/index.tsx new file mode 100644 index 000000000..3a3654f02 --- /dev/null +++ b/src/components/ui/headline/index.tsx | |||
@@ -0,0 +1,70 @@ | |||
1 | import classnames from 'classnames'; | ||
2 | import { Component, createElement, ReactNode } from 'react'; | ||
3 | import injectStyle from 'react-jss'; | ||
4 | |||
5 | import { Theme } from '@meetfranz/theme'; | ||
6 | import { IWithStyle, Omit } from '../typings/generic'; | ||
7 | |||
8 | interface IProps extends IWithStyle { | ||
9 | level?: number; | ||
10 | className?: string; | ||
11 | children: string | ReactNode; | ||
12 | id?: string; | ||
13 | } | ||
14 | |||
15 | const styles = (theme: Theme) => ({ | ||
16 | headline: { | ||
17 | fontWeight: 'lighter', | ||
18 | color: theme.colorText, | ||
19 | marginTop: 0, | ||
20 | marginBottom: 10, | ||
21 | textAlign: 'left', | ||
22 | }, | ||
23 | h1: { | ||
24 | fontSize: 30, | ||
25 | marginTop: 0, | ||
26 | }, | ||
27 | h2: { | ||
28 | fontSize: 20, | ||
29 | }, | ||
30 | h3: { | ||
31 | fontSize: 18, | ||
32 | }, | ||
33 | h4: { | ||
34 | fontSize: theme.uiFontSize, | ||
35 | }, | ||
36 | }); | ||
37 | |||
38 | class HeadlineComponent extends Component<IProps> { | ||
39 | render() { | ||
40 | const { classes, level, className, children, id } = this.props; | ||
41 | |||
42 | return createElement( | ||
43 | `h${level}`, | ||
44 | { | ||
45 | id, | ||
46 | className: classnames({ | ||
47 | [classes.headline]: true, | ||
48 | [classes[level ? `h${level}` : 'h1']]: true, | ||
49 | [`${className}`]: className, | ||
50 | }), | ||
51 | 'data-type': 'franz-headline', | ||
52 | }, | ||
53 | children, | ||
54 | ); | ||
55 | } | ||
56 | } | ||
57 | |||
58 | const Headline = injectStyle(styles)(HeadlineComponent); | ||
59 | |||
60 | const createH = (level: number) => (props: Omit<IProps, 'classes' | 'theme'>) => | ||
61 | ( | ||
62 | <Headline level={level} {...props}> | ||
63 | {props.children} | ||
64 | </Headline> | ||
65 | ); | ||
66 | |||
67 | export const H1 = createH(1); | ||
68 | export const H2 = createH(2); | ||
69 | export const H3 = createH(3); | ||
70 | export const H4 = createH(4); | ||
diff --git a/src/components/ui/icon/index.tsx b/src/components/ui/icon/index.tsx new file mode 100644 index 000000000..fdc48d14a --- /dev/null +++ b/src/components/ui/icon/index.tsx | |||
@@ -0,0 +1,46 @@ | |||
1 | import MdiIcon from '@mdi/react'; | ||
2 | import classnames from 'classnames'; | ||
3 | import { Component } from 'react'; | ||
4 | import injectStyle from 'react-jss'; | ||
5 | |||
6 | import { Theme } from '@meetfranz/theme'; | ||
7 | import { IWithStyle } from '../typings/generic'; | ||
8 | |||
9 | interface IProps extends IWithStyle { | ||
10 | icon: string; | ||
11 | size?: number; | ||
12 | className?: string; | ||
13 | } | ||
14 | |||
15 | const styles = (theme: Theme) => ({ | ||
16 | icon: { | ||
17 | fill: theme.colorText, | ||
18 | }, | ||
19 | }); | ||
20 | |||
21 | class IconComponent extends Component<IProps> { | ||
22 | public static defaultProps = { | ||
23 | size: 1, | ||
24 | }; | ||
25 | |||
26 | render() { | ||
27 | const { classes, icon, size, className } = this.props; | ||
28 | |||
29 | if (!icon) { | ||
30 | console.warn('No Icon specified'); | ||
31 | } | ||
32 | |||
33 | return ( | ||
34 | <MdiIcon | ||
35 | path={icon} | ||
36 | size={size} | ||
37 | className={classnames({ | ||
38 | [classes.icon]: true, | ||
39 | [`${className}`]: className, | ||
40 | })} | ||
41 | /> | ||
42 | ); | ||
43 | } | ||
44 | } | ||
45 | |||
46 | export const Icon = injectStyle(styles)(IconComponent); | ||
diff --git a/src/components/ui/infobox/index.tsx b/src/components/ui/infobox/index.tsx new file mode 100644 index 000000000..e6be83556 --- /dev/null +++ b/src/components/ui/infobox/index.tsx | |||
@@ -0,0 +1,205 @@ | |||
1 | import { mdiClose } from '@mdi/js'; | ||
2 | import classnames from 'classnames'; | ||
3 | import { Component, ReactNode } from 'react'; | ||
4 | import injectStyle from 'react-jss'; | ||
5 | |||
6 | import { Theme } from '@meetfranz/theme'; | ||
7 | import { Icon } from '../icon'; | ||
8 | import { IWithStyle } from '../typings/generic'; | ||
9 | |||
10 | interface IProps extends IWithStyle { | ||
11 | icon?: string; | ||
12 | type?: string; | ||
13 | dismissable?: boolean; | ||
14 | onDismiss?: () => void; | ||
15 | onUnmount?: () => void; | ||
16 | ctaOnClick?: () => void; | ||
17 | ctaLabel?: string; | ||
18 | ctaLoading?: boolean; | ||
19 | children: ReactNode; | ||
20 | className: string; | ||
21 | } | ||
22 | |||
23 | interface IState { | ||
24 | isDismissing: boolean; | ||
25 | dismissed: boolean; | ||
26 | } | ||
27 | |||
28 | const buttonStyles = (theme: Theme) => { | ||
29 | const styles = {}; | ||
30 | Object.keys(theme.styleTypes).map(style => { | ||
31 | Object.assign(styles, { | ||
32 | [style]: { | ||
33 | background: theme.styleTypes[style].accent, | ||
34 | color: theme.styleTypes[style].contrast, | ||
35 | border: theme.styleTypes[style].border, | ||
36 | |||
37 | '& svg': { | ||
38 | fill: theme.styleTypes[style].contrast, | ||
39 | }, | ||
40 | }, | ||
41 | }); | ||
42 | }); | ||
43 | |||
44 | return styles; | ||
45 | }; | ||
46 | |||
47 | const infoBoxTransition: string = 'none'; | ||
48 | const ctaTransition: string = 'none'; | ||
49 | |||
50 | // TODO: Not sure why, but this location alone, the `dinwo` is not defined - and it throws an error thus aborting the startup sequence of ferdi | ||
51 | // if (window && window.matchMedia('(prefers-reduced-motion: no-preference)')) { | ||
52 | // infoBoxTransition = 'all 0.5s'; | ||
53 | // ctaTransition = 'opacity 0.3s'; | ||
54 | // } | ||
55 | |||
56 | const styles = (theme: Theme) => ({ | ||
57 | wrapper: { | ||
58 | position: 'relative', | ||
59 | overflow: 'hidden', | ||
60 | height: 'auto', | ||
61 | marginBottom: 30, | ||
62 | }, | ||
63 | infobox: { | ||
64 | alignItems: 'center', | ||
65 | borderRadius: theme.borderRadiusSmall, | ||
66 | display: 'flex', | ||
67 | height: 'auto', | ||
68 | padding: '15px 20px', | ||
69 | top: 0, | ||
70 | transition: infoBoxTransition, | ||
71 | opacity: 1, | ||
72 | }, | ||
73 | dismissing: { | ||
74 | // position: 'absolute', | ||
75 | marginTop: -100, | ||
76 | opacity: 0, | ||
77 | }, | ||
78 | content: { | ||
79 | flex: 1, | ||
80 | }, | ||
81 | icon: { | ||
82 | marginRight: 10, | ||
83 | }, | ||
84 | close: { | ||
85 | color: (props: IProps) => | ||
86 | theme.styleTypes[props.type ? props.type : 'primary'].contrast, | ||
87 | marginRight: -5, | ||
88 | border: 0, | ||
89 | background: 'none', | ||
90 | }, | ||
91 | cta: { | ||
92 | borderColor: (props: IProps) => | ||
93 | theme.styleTypes[props.type ? props.type : 'primary'].contrast, | ||
94 | borderRadius: theme.borderRadiusSmall, | ||
95 | borderStyle: 'solid', | ||
96 | borderWidth: 1, | ||
97 | background: 'none', | ||
98 | color: (props: IProps) => | ||
99 | theme.styleTypes[props.type ? props.type : 'primary'].contrast, | ||
100 | marginLeft: 15, | ||
101 | padding: [4, 10], | ||
102 | fontSize: theme.uiFontSize, | ||
103 | transition: ctaTransition, | ||
104 | |||
105 | '&:hover': { | ||
106 | opacity: 0.6, | ||
107 | }, | ||
108 | }, | ||
109 | ...buttonStyles(theme), | ||
110 | }); | ||
111 | |||
112 | class InfoboxComponent extends Component<IProps, IState> { | ||
113 | public static defaultProps = { | ||
114 | type: 'primary', | ||
115 | dismissable: false, | ||
116 | ctaOnClick: () => {}, | ||
117 | onDismiss: () => {}, | ||
118 | ctaLabel: '', | ||
119 | ctaLoading: false, | ||
120 | }; | ||
121 | |||
122 | state = { | ||
123 | isDismissing: false, | ||
124 | dismissed: false, | ||
125 | }; | ||
126 | |||
127 | dismiss() { | ||
128 | const { onDismiss } = this.props; | ||
129 | |||
130 | this.setState({ | ||
131 | isDismissing: true, | ||
132 | }); | ||
133 | |||
134 | if (onDismiss) { | ||
135 | onDismiss(); | ||
136 | } | ||
137 | |||
138 | setTimeout(() => { | ||
139 | this.setState({ | ||
140 | dismissed: true, | ||
141 | }); | ||
142 | }, 3000); | ||
143 | } | ||
144 | |||
145 | componentWillUnmount(): void { | ||
146 | const { onUnmount } = this.props; | ||
147 | if (onUnmount) onUnmount(); | ||
148 | } | ||
149 | |||
150 | render() { | ||
151 | const { | ||
152 | classes, | ||
153 | children, | ||
154 | icon, | ||
155 | type, | ||
156 | ctaLabel, | ||
157 | ctaOnClick, | ||
158 | dismissable, | ||
159 | className, | ||
160 | } = this.props; | ||
161 | |||
162 | const { isDismissing, dismissed } = this.state; | ||
163 | |||
164 | if (dismissed) { | ||
165 | return null; | ||
166 | } | ||
167 | |||
168 | return ( | ||
169 | <div | ||
170 | className={classnames({ | ||
171 | [classes.wrapper]: true, | ||
172 | [`${className}`]: className, | ||
173 | })} | ||
174 | > | ||
175 | <div | ||
176 | className={classnames({ | ||
177 | [classes.infobox]: true, | ||
178 | [classes[`${type}`]]: type, | ||
179 | [classes.dismissing]: isDismissing, | ||
180 | })} | ||
181 | data-type="franz-infobox" | ||
182 | > | ||
183 | {icon && <Icon icon={icon} className={classes.icon} />} | ||
184 | <div className={classes.content}>{children}</div> | ||
185 | {ctaLabel && ( | ||
186 | <button className={classes.cta} onClick={ctaOnClick} type="button"> | ||
187 | {ctaLabel} | ||
188 | </button> | ||
189 | )} | ||
190 | {dismissable && ( | ||
191 | <button | ||
192 | type="button" | ||
193 | onClick={this.dismiss.bind(this)} | ||
194 | className={classes.close} | ||
195 | > | ||
196 | <Icon icon={mdiClose} /> | ||
197 | </button> | ||
198 | )} | ||
199 | </div> | ||
200 | </div> | ||
201 | ); | ||
202 | } | ||
203 | } | ||
204 | |||
205 | export const Infobox = injectStyle(styles)(InfoboxComponent); | ||
diff --git a/src/components/ui/loader/index.tsx b/src/components/ui/loader/index.tsx new file mode 100644 index 000000000..0607bd48b --- /dev/null +++ b/src/components/ui/loader/index.tsx | |||
@@ -0,0 +1,44 @@ | |||
1 | import classnames from 'classnames'; | ||
2 | import { Component } from 'react'; | ||
3 | import injectStyle, { withTheme } from 'react-jss'; | ||
4 | import ReactLoader from 'react-loader'; | ||
5 | |||
6 | import { IWithStyle } from '../typings/generic'; | ||
7 | |||
8 | interface IProps extends IWithStyle { | ||
9 | className?: string; | ||
10 | color?: string; | ||
11 | } | ||
12 | |||
13 | const styles = () => ({ | ||
14 | container: { | ||
15 | position: 'relative', | ||
16 | height: 60, | ||
17 | }, | ||
18 | }); | ||
19 | |||
20 | class LoaderComponent extends Component<IProps> { | ||
21 | render() { | ||
22 | const { classes, className, color, theme } = this.props; | ||
23 | |||
24 | return ( | ||
25 | <div | ||
26 | className={classnames({ | ||
27 | [classes.container]: true, | ||
28 | [`${className}`]: className, | ||
29 | })} | ||
30 | data-type="franz-loader" | ||
31 | > | ||
32 | <ReactLoader | ||
33 | loaded={false} | ||
34 | width={4} | ||
35 | scale={0.75} | ||
36 | color={color || theme.colorText} | ||
37 | parentClassName={classes.loader} | ||
38 | /> | ||
39 | </div> | ||
40 | ); | ||
41 | } | ||
42 | } | ||
43 | |||
44 | export const Loader = injectStyle(styles)(withTheme(LoaderComponent)); | ||
diff --git a/src/components/ui/typings/generic.ts b/src/components/ui/typings/generic.ts new file mode 100644 index 000000000..ddce3f7c7 --- /dev/null +++ b/src/components/ui/typings/generic.ts | |||
@@ -0,0 +1,11 @@ | |||
1 | import { Classes } from 'jss'; | ||
2 | |||
3 | import { Theme } from '@meetfranz/theme'; | ||
4 | |||
5 | export interface IWithStyle { | ||
6 | classes: Classes; | ||
7 | theme: Theme; | ||
8 | } | ||
9 | |||
10 | export type Merge<M, N> = Omit<M, Extract<keyof M, keyof N>> & N; | ||
11 | export type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>; | ||