aboutsummaryrefslogtreecommitdiffstats
path: root/packages/ui/src
diff options
context:
space:
mode:
Diffstat (limited to 'packages/ui/src')
-rw-r--r--packages/ui/src/badge/index.tsx76
-rw-r--r--packages/ui/src/headline/index.tsx71
-rw-r--r--packages/ui/src/icon/index.tsx55
-rw-r--r--packages/ui/src/index.ts5
-rw-r--r--packages/ui/src/infobox/index.tsx192
-rw-r--r--packages/ui/src/loader/index.tsx48
-rw-r--r--packages/ui/src/typings/generic.ts10
7 files changed, 457 insertions, 0 deletions
diff --git a/packages/ui/src/badge/index.tsx b/packages/ui/src/badge/index.tsx
new file mode 100644
index 000000000..fc52ecc73
--- /dev/null
+++ b/packages/ui/src/badge/index.tsx
@@ -0,0 +1,76 @@
1import { Theme } from '@meetfranz/theme';
2import classnames from 'classnames';
3import React, { Component } from 'react';
4import injectStyle from 'react-jss';
5
6import { IWithStyle } from '../typings/generic';
7
8interface IProps extends IWithStyle {
9 type: string;
10 className?: string;
11 children: React.ReactNode;
12}
13
14const 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
29const 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
48class BadgeComponent extends Component<IProps> {
49 public static defaultProps = {
50 type: 'primary',
51 };
52
53 render() {
54 const {
55 classes,
56 children,
57 type,
58 className,
59 } = this.props;
60
61 return (
62 <div
63 className={classnames({
64 [classes.badge]: true,
65 [classes[type]]: true,
66 [`${className}`]: className,
67 })}
68 data-type="franz-badge"
69 >
70 {children}
71 </div>
72 );
73 }
74}
75
76export const Badge = injectStyle(styles)(BadgeComponent);
diff --git a/packages/ui/src/headline/index.tsx b/packages/ui/src/headline/index.tsx
new file mode 100644
index 000000000..7eabfcf80
--- /dev/null
+++ b/packages/ui/src/headline/index.tsx
@@ -0,0 +1,71 @@
1import { Theme } from '@meetfranz/theme';
2import classnames from 'classnames';
3import React, { Component } from 'react';
4import injectStyle from 'react-jss';
5
6import { IWithStyle, Omit } from '../typings/generic';
7
8interface IProps extends IWithStyle {
9 level?: number;
10 className?: string;
11 children: string | React.ReactNode;
12 id?: string;
13}
14
15const 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
38class HeadlineComponent extends Component<IProps> {
39 render() {
40 const {
41 classes,
42 level,
43 className,
44 children,
45 id,
46 } = this.props;
47
48 return React.createElement(
49 `h${level}`,
50 {
51 id,
52 className: classnames({
53 [classes.headline]: true,
54 [classes[level ? `h${level}` : 'h1']]: true,
55 [`${className}`]: className,
56 }),
57 'data-type': 'franz-headline',
58 },
59 children,
60 );
61 }
62}
63
64const Headline = injectStyle(styles)(HeadlineComponent);
65
66const createH = (level: number) => (props: Omit<IProps, 'classes' | 'theme'>) => <Headline level={level} {...props}>{props.children}</Headline>;
67
68export const H1 = createH(1);
69export const H2 = createH(2);
70export const H3 = createH(3);
71export const H4 = createH(4);
diff --git a/packages/ui/src/icon/index.tsx b/packages/ui/src/icon/index.tsx
new file mode 100644
index 000000000..e30d3396d
--- /dev/null
+++ b/packages/ui/src/icon/index.tsx
@@ -0,0 +1,55 @@
1import * as mdiIcons from '@mdi/js';
2import MdiIcon from '@mdi/react';
3import { Theme } from '@meetfranz/theme';
4import classnames from 'classnames';
5import React, { Component } from 'react';
6import injectStyle from 'react-jss';
7
8import { IWithStyle } from '../typings/generic';
9
10interface IProps extends IWithStyle {
11 icon: keyof typeof mdiIcons;
12 size?: number;
13 className?: string;
14}
15
16const styles = (theme: Theme) => ({
17 icon: {
18 fill: theme.colorText,
19 },
20});
21
22class IconComponent extends Component<IProps> {
23 public static defaultProps = {
24 size: 1,
25 };
26
27 render() {
28 const {
29 classes,
30 icon: iconName,
31 size,
32 className,
33 } = this.props;
34
35 let icon = '';
36 if (iconName && mdiIcons[iconName]) {
37 icon = mdiIcons[iconName];
38 } else if (iconName && !mdiIcons[iconName]) {
39 console.warn(`Icon '${iconName}' was not found`);
40 }
41
42 return (
43 <MdiIcon
44 path={icon}
45 size={size}
46 className={classnames({
47 [classes.icon]: true,
48 [`${className}`]: className,
49 })}
50 />
51 );
52 }
53}
54
55export const Icon = injectStyle(styles)(IconComponent);
diff --git a/packages/ui/src/index.ts b/packages/ui/src/index.ts
new file mode 100644
index 000000000..1eeec5144
--- /dev/null
+++ b/packages/ui/src/index.ts
@@ -0,0 +1,5 @@
1export { Icon } from './icon';
2export { Infobox } from './infobox';
3export * from './headline';
4export { Loader } from './loader';
5export { Badge } from './badge';
diff --git a/packages/ui/src/infobox/index.tsx b/packages/ui/src/infobox/index.tsx
new file mode 100644
index 000000000..53ed16341
--- /dev/null
+++ b/packages/ui/src/infobox/index.tsx
@@ -0,0 +1,192 @@
1import { Theme } from '@meetfranz/theme';
2import classnames from 'classnames';
3import React, { Component } from 'react';
4import injectStyle from 'react-jss';
5
6import { Icon } from '../';
7import { IWithStyle } from '../typings/generic';
8
9interface IProps extends IWithStyle {
10 icon?: string;
11 type?: string;
12 dismissable?: boolean;
13 onDismiss?: () => void;
14 ctaOnClick?: () => void;
15 ctaLabel?: string;
16 ctaLoading?: boolean;
17 children: React.ReactNode;
18}
19
20interface IState {
21 isDismissing: boolean;
22 dismissed: boolean;
23}
24
25const buttonStyles = (theme: Theme) => {
26 const styles = {};
27 Object.keys(theme.styleTypes).map((style) => {
28 Object.assign(styles, {
29 [style]: {
30 background: theme.styleTypes[style].accent,
31 color: theme.styleTypes[style].contrast,
32 border: theme.styleTypes[style].border,
33
34 '& svg': {
35 fill: theme.styleTypes[style].contrast,
36 },
37 },
38 });
39 });
40
41 return styles;
42};
43
44const styles = (theme: Theme) => ({
45 wrapper: {
46 position: 'relative',
47 overflow: 'hidden',
48 },
49 infobox: {
50 alignItems: 'center',
51 borderRadius: theme.borderRadiusSmall,
52 display: 'flex',
53 height: 'auto',
54 marginBottom: 30,
55 padding: '15px 20px',
56 top: 0,
57 transition: 'all 0.5s',
58 opacity: 1,
59 },
60 dismissing: {
61 // position: 'absolute',
62 marginTop: -100,
63 opacity: 0,
64 },
65 content: {
66 flex: 1,
67 },
68 icon: {
69 marginRight: 10,
70 },
71 close: {
72 color: (props: IProps) => theme.styleTypes[props.type ? props.type : 'primary'].contrast,
73 marginRight: -5,
74 border: 0,
75 background: 'none',
76 },
77 cta: {
78 borderColor: (props: IProps) => theme.styleTypes[props.type ? props.type : 'primary'].contrast,
79 borderRadius: theme.borderRadiusSmall,
80 borderStyle: 'solid',
81 borderWidth: 1,
82 background: 'none',
83 color: (props: IProps) => theme.styleTypes[props.type ? props.type : 'primary'].contrast,
84 marginLeft: 15,
85 padding: [4, 10],
86 fontSize: theme.uiFontSize,
87 transition: 'opacity 0.3s',
88
89 '&:hover': {
90 opacity: 0.6,
91 },
92 },
93 ...buttonStyles(theme),
94});
95
96class InfoboxComponent extends Component<IProps, IState> {
97 public static defaultProps = {
98 type: 'primary',
99 dismissable: false,
100 ctaOnClick: () => {},
101 onDismiss: () => {},
102 ctaLabel: '',
103 ctaLoading: false,
104 };
105
106 state = {
107 isDismissing: false,
108 dismissed: false,
109 };
110
111 dismiss() {
112 const {
113 onDismiss,
114 } = this.props;
115
116 this.setState({
117 isDismissing: true,
118 });
119
120 if (onDismiss) {
121 onDismiss();
122 }
123
124 setTimeout(() => {
125 this.setState({
126 dismissed: true,
127 });
128 }, 3000);
129 }
130
131 render() {
132 const {
133 classes,
134 children,
135 icon,
136 type,
137 ctaLabel,
138 ctaLoading,
139 ctaOnClick,
140 dismissable,
141 } = this.props;
142
143 const {
144 isDismissing,
145 dismissed,
146 } = this.state;
147
148 if (dismissed) {
149 return null;
150 }
151
152 return (
153 <div className={classes.wrapper}>
154 <div
155 className={classnames({
156 [classes.infobox]: true,
157 [classes[`${type}`]]: type,
158 [classes.dismissing]: isDismissing,
159 })}
160 data-type="franz-infobox"
161 >
162 {icon && (
163 <Icon icon={icon} className={classes.icon} />
164 )}
165 <div className={classes.content}>
166 {children}
167 </div>
168 {ctaLabel && (
169 <button
170 className={classes.cta}
171 onClick={ctaOnClick}
172 type="button"
173 >
174 {ctaLabel}
175 </button>
176 )}
177 {dismissable && (
178 <button
179 type="button"
180 onClick={this.dismiss.bind(this)}
181 className={classes.close}
182 >
183 <Icon icon="mdiClose" />
184 </button>
185 )}
186 </div>
187 </div>
188 );
189 }
190}
191
192export const Infobox = injectStyle(styles)(InfoboxComponent);
diff --git a/packages/ui/src/loader/index.tsx b/packages/ui/src/loader/index.tsx
new file mode 100644
index 000000000..46545c786
--- /dev/null
+++ b/packages/ui/src/loader/index.tsx
@@ -0,0 +1,48 @@
1import { Theme } from '@meetfranz/theme';
2import classnames from 'classnames';
3import React, { Component } from 'react';
4import injectStyle, { withTheme } from 'react-jss';
5import ReactLoader from 'react-loader';
6
7import { IWithStyle } from '../typings/generic';
8
9interface IProps extends IWithStyle {
10 className?: string;
11}
12
13const styles = (theme: Theme) => ({
14 container: {
15 position: 'relative',
16 height: 60,
17 },
18});
19
20class LoaderComponent extends Component<IProps> {
21 render() {
22 const {
23 classes,
24 className,
25 theme,
26 } = this.props;
27
28 return (
29 <div
30 className={classnames({
31 [classes.container]: true,
32 [`${className}`]: className,
33 })}
34 data-type="franz-loader"
35 >
36 <ReactLoader
37 loaded={false}
38 width={4}
39 scale={0.75}
40 color={theme.colorText}
41 parentClassName={classes.loader}
42 />
43 </div>
44 );
45 }
46}
47
48export const Loader = injectStyle(styles)(withTheme(LoaderComponent));
diff --git a/packages/ui/src/typings/generic.ts b/packages/ui/src/typings/generic.ts
new file mode 100644
index 000000000..d5f953b9f
--- /dev/null
+++ b/packages/ui/src/typings/generic.ts
@@ -0,0 +1,10 @@
1import { Theme } from '@meetfranz/theme/lib';
2import { Classes } from 'jss';
3
4export interface IWithStyle {
5 classes: Classes;
6 theme: Theme;
7}
8
9export type Merge<M, N> = Omit<M, Extract<keyof M, keyof N>> & N;
10export type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;