diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/components/auth/Welcome.tsx (renamed from src/components/auth/Welcome.jsx) | 61 | ||||
-rw-r--r-- | src/components/ui/Link.js | 86 | ||||
-rw-r--r-- | src/components/ui/Link.tsx | 73 | ||||
-rw-r--r-- | src/components/ui/button/index.tsx | 76 | ||||
-rw-r--r-- | src/components/ui/input/index.tsx | 2 | ||||
-rw-r--r-- | src/containers/auth/WelcomeScreen.tsx | 15 | ||||
-rw-r--r-- | src/helpers/array-helpers.ts | 4 |
7 files changed, 153 insertions, 164 deletions
diff --git a/src/components/auth/Welcome.jsx b/src/components/auth/Welcome.tsx index 9e5d10126..1aa8da4d6 100644 --- a/src/components/auth/Welcome.jsx +++ b/src/components/auth/Welcome.tsx | |||
@@ -1,14 +1,14 @@ | |||
1 | /* eslint jsx-a11y/anchor-is-valid: 0 */ | 1 | import { Component, ReactElement } from 'react'; |
2 | import { Component } from 'react'; | 2 | import { observer, inject } from 'mobx-react'; |
3 | import PropTypes from 'prop-types'; | 3 | import { defineMessages, injectIntl, WrappedComponentProps } from 'react-intl'; |
4 | import { observer, PropTypes as MobxPropTypes, inject } from 'mobx-react'; | 4 | import { noop } from 'lodash'; |
5 | import { defineMessages, injectIntl } from 'react-intl'; | ||
6 | import serverlessLogin from '../../helpers/serverless-helpers'; | 5 | import serverlessLogin from '../../helpers/serverless-helpers'; |
7 | import shuffleArray from '../../helpers/array-helpers'; | 6 | import shuffleArray from '../../helpers/array-helpers'; |
8 | import { serverName } from '../../api/apiBase'; | 7 | import { serverName } from '../../api/apiBase'; |
9 | |||
10 | import Link from '../ui/Link'; | 8 | import Link from '../ui/Link'; |
11 | import { H1 } from '../ui/headline'; | 9 | import { H1 } from '../ui/headline'; |
10 | import { StoresProps } from '../../@types/ferdium-components.types'; | ||
11 | import RecipePreview from '../../models/RecipePreview'; | ||
12 | 12 | ||
13 | const messages = defineMessages({ | 13 | const messages = defineMessages({ |
14 | signupButton: { | 14 | signupButton: { |
@@ -34,24 +34,28 @@ const messages = defineMessages({ | |||
34 | }, | 34 | }, |
35 | }); | 35 | }); |
36 | 36 | ||
37 | class Welcome extends Component { | 37 | interface IProps extends Partial<StoresProps>, WrappedComponentProps { |
38 | static propTypes = { | 38 | loginRoute: string; |
39 | loginRoute: PropTypes.string.isRequired, | 39 | signupRoute: string; |
40 | signupRoute: PropTypes.string.isRequired, | 40 | changeServerRoute: string; |
41 | changeServerRoute: PropTypes.string.isRequired, | 41 | recipes: RecipePreview[]; |
42 | recipes: MobxPropTypes.arrayOrObservableArray.isRequired, | 42 | } |
43 | actions: PropTypes.object.isRequired, | ||
44 | }; | ||
45 | 43 | ||
46 | useLocalServer() { | 44 | @inject('actions') |
45 | @observer | ||
46 | class Welcome extends Component<IProps> { | ||
47 | constructor(props: IProps) { | ||
48 | super(props); | ||
49 | } | ||
50 | |||
51 | useLocalServer(): void { | ||
47 | serverlessLogin(this.props.actions); | 52 | serverlessLogin(this.props.actions); |
48 | } | 53 | } |
49 | 54 | ||
50 | render() { | 55 | render(): ReactElement { |
51 | const { intl } = this.props; | 56 | const { loginRoute, signupRoute, changeServerRoute, intl } = this.props; |
52 | const { loginRoute, signupRoute, changeServerRoute } = this.props; | ||
53 | let { recipes } = this.props; | 57 | let { recipes } = this.props; |
54 | recipes = shuffleArray(recipes); | 58 | recipes = shuffleArray<RecipePreview>(recipes); |
55 | recipes.length = 8 * 2; | 59 | recipes.length = 8 * 2; |
56 | 60 | ||
57 | let serverNameParse = serverName(); | 61 | let serverNameParse = serverName(); |
@@ -87,17 +91,22 @@ class Welcome extends Component { | |||
87 | <span>{intl.formatMessage(messages.changeServer)}</span> | 91 | <span>{intl.formatMessage(messages.changeServer)}</span> |
88 | </Link> | 92 | </Link> |
89 | </div> | 93 | </div> |
90 | <br /> | 94 | <hr |
91 | <hr /> | 95 | className="settings__hr-sections" |
92 | <br /> | 96 | style={{ marginTop: 24, marginBottom: 24, borderStyle: 'solid' }} |
93 | <a className="button" onClick={this.useLocalServer.bind(this)}> | 97 | /> |
98 | <button | ||
99 | className="button" | ||
100 | onClick={this.useLocalServer.bind(this)} | ||
101 | onKeyDown={noop} | ||
102 | > | ||
94 | {intl.formatMessage(messages.serverless)} | 103 | {intl.formatMessage(messages.serverless)} |
95 | </a> | 104 | </button> |
96 | </div> | 105 | </div> |
97 | <div className="welcome__featured-services"> | 106 | <div className="welcome__featured-services"> |
98 | {recipes.map(recipe => ( | 107 | {recipes.map(recipe => ( |
99 | <div key={recipe.id} className="welcome__featured-service"> | 108 | <div key={recipe.id} className="welcome__featured-service"> |
100 | <img key={recipe.id} src={recipe.icons.svg} alt="" /> | 109 | <img key={recipe.id} src={recipe.icons?.svg} alt="" /> |
101 | </div> | 110 | </div> |
102 | ))} | 111 | ))} |
103 | </div> | 112 | </div> |
@@ -106,4 +115,4 @@ class Welcome extends Component { | |||
106 | } | 115 | } |
107 | } | 116 | } |
108 | 117 | ||
109 | export default injectIntl(inject('actions')(observer(Welcome))); | 118 | export default injectIntl(Welcome); |
diff --git a/src/components/ui/Link.js b/src/components/ui/Link.js deleted file mode 100644 index 714fc5a68..000000000 --- a/src/components/ui/Link.js +++ /dev/null | |||
@@ -1,86 +0,0 @@ | |||
1 | import { Component } from 'react'; | ||
2 | import PropTypes from 'prop-types'; | ||
3 | import { inject, observer } from 'mobx-react'; | ||
4 | import { RouterStore } from '@superwf/mobx-react-router'; | ||
5 | import classnames from 'classnames'; | ||
6 | |||
7 | import { oneOrManyChildElements } from '../../prop-types'; | ||
8 | import matchRoute from '../../helpers/routing-helpers'; | ||
9 | import { openExternalUrl } from '../../helpers/url-helpers'; | ||
10 | |||
11 | // Should this file be converted into the coding style similar to './toggle/index.tsx'? | ||
12 | // TODO: create container component for this component | ||
13 | class Link extends Component { | ||
14 | onClick(e) { | ||
15 | if (this.props.disabled) { | ||
16 | e.preventDefault(); | ||
17 | } else if (this.props.target === '_blank') { | ||
18 | e.preventDefault(); | ||
19 | openExternalUrl(this.props.to, true); | ||
20 | } | ||
21 | // Note: if neither of the above, then let the other onClick handlers process it | ||
22 | } | ||
23 | |||
24 | render() { | ||
25 | const { | ||
26 | children, | ||
27 | stores, | ||
28 | to, | ||
29 | className, | ||
30 | activeClassName, | ||
31 | strictFilter, | ||
32 | style, | ||
33 | } = this.props; | ||
34 | const { router } = stores; | ||
35 | |||
36 | let filter = `${to}(*action)`; | ||
37 | if (strictFilter) { | ||
38 | filter = `${to}`; | ||
39 | } | ||
40 | |||
41 | const match = matchRoute(filter, router.location.pathname); | ||
42 | |||
43 | const linkClasses = classnames({ | ||
44 | [`${className}`]: true, | ||
45 | [`${activeClassName}`]: match, | ||
46 | 'is-disabled': this.props.disabled, | ||
47 | }); | ||
48 | |||
49 | return ( | ||
50 | <a | ||
51 | href={router.history.createHref(to)} | ||
52 | className={linkClasses} | ||
53 | style={style} | ||
54 | onClick={e => this.onClick(e)} | ||
55 | > | ||
56 | {children} | ||
57 | </a> | ||
58 | ); | ||
59 | } | ||
60 | } | ||
61 | |||
62 | Link.propTypes = { | ||
63 | stores: PropTypes.shape({ | ||
64 | router: PropTypes.instanceOf(RouterStore).isRequired, | ||
65 | }).isRequired, | ||
66 | children: PropTypes.oneOfType([oneOrManyChildElements, PropTypes.string]) | ||
67 | .isRequired, | ||
68 | to: PropTypes.string.isRequired, | ||
69 | className: PropTypes.string, | ||
70 | activeClassName: PropTypes.string, | ||
71 | strictFilter: PropTypes.bool, | ||
72 | target: PropTypes.string, | ||
73 | style: PropTypes.object, | ||
74 | disabled: PropTypes.bool, | ||
75 | }; | ||
76 | |||
77 | Link.defaultProps = { | ||
78 | className: '', | ||
79 | activeClassName: '', | ||
80 | strictFilter: false, | ||
81 | disabled: false, | ||
82 | target: '', | ||
83 | style: {}, | ||
84 | }; | ||
85 | |||
86 | export default inject('stores')(observer(Link)); | ||
diff --git a/src/components/ui/Link.tsx b/src/components/ui/Link.tsx new file mode 100644 index 000000000..b5890ebd1 --- /dev/null +++ b/src/components/ui/Link.tsx | |||
@@ -0,0 +1,73 @@ | |||
1 | import { Component, CSSProperties, ReactNode, MouseEvent } from 'react'; | ||
2 | import { inject, observer } from 'mobx-react'; | ||
3 | import classnames from 'classnames'; | ||
4 | import matchRoute from '../../helpers/routing-helpers'; | ||
5 | import { openExternalUrl } from '../../helpers/url-helpers'; | ||
6 | import { StoresProps } from '../../@types/ferdium-components.types'; | ||
7 | |||
8 | interface IProps extends Partial<StoresProps> { | ||
9 | children: ReactNode; | ||
10 | to: string; | ||
11 | className?: string; | ||
12 | activeClassName?: string; | ||
13 | strictFilter?: boolean; | ||
14 | target?: string; | ||
15 | style?: CSSProperties; | ||
16 | disabled?: boolean; | ||
17 | } | ||
18 | |||
19 | // TODO: create container component for this component | ||
20 | @inject('stores') | ||
21 | @observer | ||
22 | class Link extends Component<IProps> { | ||
23 | constructor(props: IProps) { | ||
24 | super(props); | ||
25 | } | ||
26 | |||
27 | onClick(e: MouseEvent<HTMLAnchorElement>): void { | ||
28 | const { disabled = false, target = '', to } = this.props; | ||
29 | if (disabled) { | ||
30 | e.preventDefault(); | ||
31 | } else if (target === '_blank') { | ||
32 | e.preventDefault(); | ||
33 | openExternalUrl(to, true); | ||
34 | } | ||
35 | // Note: if neither of the above, then let the other onClick handlers process it | ||
36 | } | ||
37 | |||
38 | render() { | ||
39 | const { | ||
40 | children, | ||
41 | stores, | ||
42 | to, | ||
43 | className = '', | ||
44 | activeClassName = '', | ||
45 | strictFilter = false, | ||
46 | disabled = false, | ||
47 | style = {}, | ||
48 | } = this.props; | ||
49 | const { router } = stores!; | ||
50 | |||
51 | const filter = strictFilter ? `${to}` : `${to}(*action)`; | ||
52 | const match = matchRoute(filter, router.location.pathname); | ||
53 | |||
54 | const linkClasses = classnames({ | ||
55 | [`${className}`]: true, | ||
56 | [`${activeClassName}`]: match, | ||
57 | 'is-disabled': disabled, | ||
58 | }); | ||
59 | |||
60 | return ( | ||
61 | <a | ||
62 | href={router.history.createHref(to)} | ||
63 | className={linkClasses} | ||
64 | style={style} | ||
65 | onClick={e => this.onClick(e)} | ||
66 | > | ||
67 | {children} | ||
68 | </a> | ||
69 | ); | ||
70 | } | ||
71 | } | ||
72 | |||
73 | export default Link; | ||
diff --git a/src/components/ui/button/index.tsx b/src/components/ui/button/index.tsx index a8bbfe730..c1e647bc0 100644 --- a/src/components/ui/button/index.tsx +++ b/src/components/ui/button/index.tsx | |||
@@ -1,10 +1,10 @@ | |||
1 | import Icon from '@mdi/react'; | 1 | import Icon from '@mdi/react'; |
2 | import classnames from 'classnames'; | 2 | import classnames from 'classnames'; |
3 | import { Property } from 'csstype'; | 3 | import { Property } from 'csstype'; |
4 | import { noop } from 'lodash'; | ||
4 | import { Component, MouseEvent } from 'react'; | 5 | import { Component, MouseEvent } from 'react'; |
5 | import withStyles, { WithStylesProps } from 'react-jss'; | 6 | import withStyles, { WithStylesProps } from 'react-jss'; |
6 | import Loader from 'react-loader'; | 7 | import Loader from 'react-loader'; |
7 | |||
8 | import { Theme } from '../../../themes'; | 8 | import { Theme } from '../../../themes'; |
9 | import { IFormField } from '../typings/generic'; | 9 | import { IFormField } from '../typings/generic'; |
10 | 10 | ||
@@ -16,24 +16,6 @@ type ButtonType = | |||
16 | | 'warning' | 16 | | 'warning' |
17 | | 'inverted'; | 17 | | 'inverted'; |
18 | 18 | ||
19 | interface IProps extends IFormField, WithStylesProps<typeof styles> { | ||
20 | className?: string; | ||
21 | label?: string; | ||
22 | disabled?: boolean; | ||
23 | id?: string; | ||
24 | type?: 'button' | 'reset' | 'submit' | undefined; | ||
25 | onClick: ( | ||
26 | event: MouseEvent<HTMLButtonElement> | MouseEvent<HTMLAnchorElement>, | ||
27 | ) => void; | ||
28 | buttonType?: ButtonType; | ||
29 | loaded?: boolean; | ||
30 | busy?: boolean; | ||
31 | icon?: string; | ||
32 | href?: string; | ||
33 | target?: string; | ||
34 | htmlForm?: string; | ||
35 | } | ||
36 | |||
37 | let buttonTransition: string = 'none'; | 19 | let buttonTransition: string = 'none'; |
38 | let loaderContainerTransition: string = 'none'; | 20 | let loaderContainerTransition: string = 'none'; |
39 | 21 | ||
@@ -148,38 +130,38 @@ const styles = (theme: Theme) => ({ | |||
148 | }, | 130 | }, |
149 | }); | 131 | }); |
150 | 132 | ||
151 | class ButtonComponent extends Component<IProps> { | 133 | interface IProps extends IFormField, WithStylesProps<typeof styles> { |
152 | customDefaultProps: { | 134 | className?: string; |
153 | disabled: boolean; | 135 | label?: string; |
154 | type: 'button' | 'reset' | 'submit' | undefined; | 136 | disabled?: boolean; |
155 | onClick: ( | 137 | id?: string; |
156 | event: MouseEvent<HTMLButtonElement> | MouseEvent<HTMLAnchorElement>, | 138 | type?: 'button' | 'reset' | 'submit' | undefined; |
157 | ) => void; | 139 | onClick: (event: MouseEvent<HTMLButtonElement | HTMLAnchorElement>) => void; |
158 | buttonType: ButtonType; | 140 | buttonType?: ButtonType; |
159 | busy: boolean; | 141 | loaded?: boolean; |
160 | } = { | 142 | busy?: boolean; |
161 | type: 'button', | 143 | icon?: string; |
162 | disabled: false, | 144 | href?: string; |
163 | onClick: () => null, | 145 | target?: string; |
164 | buttonType: 'primary' as ButtonType, | 146 | htmlForm?: string; |
165 | busy: false, | 147 | } |
166 | }; | ||
167 | 148 | ||
168 | state = { | 149 | interface IState { |
169 | busy: false, | 150 | busy: boolean; |
170 | }; | 151 | } |
171 | 152 | ||
153 | class ButtonComponent extends Component<IProps, IState> { | ||
172 | constructor(props: IProps) { | 154 | constructor(props: IProps) { |
173 | super(props); | 155 | super(props); |
174 | 156 | ||
175 | this.state = { | 157 | this.state = { |
176 | busy: props.busy || false, | 158 | busy: this.props.busy || false, |
177 | }; | 159 | }; |
178 | } | 160 | } |
179 | 161 | ||
180 | static getDerivedStateFromProps(nextProps: IProps) { | 162 | static getDerivedStateFromProps(nextProps: IProps): IState { |
181 | return { | 163 | return { |
182 | busy: nextProps.busy, | 164 | busy: nextProps.busy || false, |
183 | }; | 165 | }; |
184 | } | 166 | } |
185 | 167 | ||
@@ -188,27 +170,29 @@ class ButtonComponent extends Component<IProps> { | |||
188 | classes, | 170 | classes, |
189 | className, | 171 | className, |
190 | // theme, | 172 | // theme, |
191 | disabled, | ||
192 | id, | 173 | id, |
193 | label, | 174 | label, |
194 | type, | ||
195 | onClick, | ||
196 | buttonType, | ||
197 | loaded, | 175 | loaded, |
198 | icon, | 176 | icon, |
199 | href, | 177 | href, |
200 | target, | 178 | target, |
201 | htmlForm, | 179 | htmlForm, |
202 | } = { ...this.customDefaultProps, ...this.props }; | 180 | type = 'button', |
181 | disabled = false, | ||
182 | onClick = noop, | ||
183 | buttonType = 'primary' as ButtonType, | ||
184 | } = this.props; | ||
203 | 185 | ||
204 | const { busy } = this.state; | 186 | const { busy } = this.state; |
205 | let showLoader = false; | 187 | let showLoader = false; |
188 | |||
206 | if (loaded) { | 189 | if (loaded) { |
207 | showLoader = !loaded; | 190 | showLoader = !loaded; |
208 | console.warn( | 191 | console.warn( |
209 | 'Ferdium Button prop `loaded` will be deprecated in the future. Please use `busy` instead', | 192 | 'Ferdium Button prop `loaded` will be deprecated in the future. Please use `busy` instead', |
210 | ); | 193 | ); |
211 | } | 194 | } |
195 | |||
212 | if (busy) { | 196 | if (busy) { |
213 | showLoader = busy; | 197 | showLoader = busy; |
214 | } | 198 | } |
diff --git a/src/components/ui/input/index.tsx b/src/components/ui/input/index.tsx index b19bb4bc9..3bafc93e7 100644 --- a/src/components/ui/input/index.tsx +++ b/src/components/ui/input/index.tsx | |||
@@ -106,6 +106,7 @@ class InputComponent extends Component<IProps, IState> { | |||
106 | showPasswordToggle = false, | 106 | showPasswordToggle = false, |
107 | type = 'text', | 107 | type = 'text', |
108 | disabled = false, | 108 | disabled = false, |
109 | readOnly, | ||
109 | } = this.props; | 110 | } = this.props; |
110 | 111 | ||
111 | const { showPassword, passwordScore } = this.state; | 112 | const { showPassword, passwordScore } = this.state; |
@@ -152,6 +153,7 @@ class InputComponent extends Component<IProps, IState> { | |||
152 | min={min} | 153 | min={min} |
153 | max={max} | 154 | max={max} |
154 | step={step} | 155 | step={step} |
156 | readOnly={readOnly} | ||
155 | /> | 157 | /> |
156 | {suffix && <span className={classes.suffix}>{suffix}</span>} | 158 | {suffix && <span className={classes.suffix}>{suffix}</span>} |
157 | {showPasswordToggle && ( | 159 | {showPasswordToggle && ( |
diff --git a/src/containers/auth/WelcomeScreen.tsx b/src/containers/auth/WelcomeScreen.tsx index c03319179..561eef236 100644 --- a/src/containers/auth/WelcomeScreen.tsx +++ b/src/containers/auth/WelcomeScreen.tsx | |||
@@ -1,12 +1,19 @@ | |||
1 | import { Component, ReactElement } from 'react'; | 1 | import { Component, ReactElement } from 'react'; |
2 | import { inject, observer } from 'mobx-react'; | 2 | import { inject, observer } from 'mobx-react'; |
3 | |||
4 | import { StoresProps } from '../../@types/ferdium-components.types'; | 3 | import { StoresProps } from '../../@types/ferdium-components.types'; |
5 | import Welcome from '../../components/auth/Welcome'; | 4 | import Welcome from '../../components/auth/Welcome'; |
6 | 5 | ||
7 | class WelcomeScreen extends Component<StoresProps> { | 6 | interface IProps extends Partial<StoresProps> {} |
7 | |||
8 | @inject('stores', 'actions') | ||
9 | @observer | ||
10 | class WelcomeScreen extends Component<IProps> { | ||
11 | constructor(props: IProps) { | ||
12 | super(props); | ||
13 | } | ||
14 | |||
8 | render(): ReactElement { | 15 | render(): ReactElement { |
9 | const { user, recipePreviews } = this.props.stores; | 16 | const { user, recipePreviews } = this.props.stores!; |
10 | 17 | ||
11 | return ( | 18 | return ( |
12 | <Welcome | 19 | <Welcome |
@@ -19,4 +26,4 @@ class WelcomeScreen extends Component<StoresProps> { | |||
19 | } | 26 | } |
20 | } | 27 | } |
21 | 28 | ||
22 | export default inject('stores', 'actions')(observer(WelcomeScreen)); | 29 | export default WelcomeScreen; |
diff --git a/src/helpers/array-helpers.ts b/src/helpers/array-helpers.ts index 33be12fa7..ffb584eab 100644 --- a/src/helpers/array-helpers.ts +++ b/src/helpers/array-helpers.ts | |||
@@ -1,6 +1,6 @@ | |||
1 | export default function shuffleArray(arr: any[]): any[] { | 1 | export default function shuffleArray<T>(arr: T[]): T[] { |
2 | return arr | 2 | return arr |
3 | .map(a => [Math.random(), a]) | 3 | .map(a => [Math.random(), a] as [number, T]) |
4 | .sort((a, b) => a[0] - b[0]) | 4 | .sort((a, b) => a[0] - b[0]) |
5 | .map(a => a[1]); | 5 | .map(a => a[1]); |
6 | } | 6 | } |