From 8b4231e3109d4b29e4d90f4553f718a1d7867bc5 Mon Sep 17 00:00:00 2001 From: Stefan Malzner Date: Mon, 14 Jan 2019 22:26:06 +0100 Subject: Add buttons --- packages/forms/src/button/index.tsx | 172 ++++++++++++++++++++++++++++++++++ packages/forms/src/error/styles.ts | 1 + packages/forms/src/index.ts | 1 + packages/forms/src/input/index.tsx | 34 ++++--- packages/forms/src/input/styles.ts | 6 +- packages/forms/src/label/styles.ts | 1 + packages/forms/src/toggle/index.tsx | 8 +- packages/forms/src/typings/generic.ts | 2 + packages/forms/src/wrapper/index.tsx | 2 +- 9 files changed, 201 insertions(+), 26 deletions(-) create mode 100644 packages/forms/src/button/index.tsx (limited to 'packages/forms/src') diff --git a/packages/forms/src/button/index.tsx b/packages/forms/src/button/index.tsx new file mode 100644 index 000000000..c4a138b16 --- /dev/null +++ b/packages/forms/src/button/index.tsx @@ -0,0 +1,172 @@ +import { Theme } from '@meetfranz/theme'; +import classnames from 'classnames'; +import CSS from 'csstype'; +import { observer } from 'mobx-react'; +import React, { Component } from 'react'; +import injectStyle from 'react-jss'; +import Loader from 'react-loader'; + +import { IFormField, IWithStyle } from '../typings/generic'; + +type ButtonType = 'primary' | 'secondary' | 'danger' | 'warning' | 'inverted'; + +interface IProps extends React.InputHTMLAttributes, IFormField, IWithStyle { + buttonType?: ButtonType; + stretch?: boolean; + loaded?: boolean; + busy?: boolean; +} + +interface IState { + busy: boolean; +} + +const styles = (theme: Theme) => ({ + button: { + borderRadius: theme.borderRadiusSmall, + border: 'none', + display: 'flex', + position: 'relative' as CSS.PositionProperty, + transition: 'background .5s', + textAlign: 'center' as CSS.TextAlignProperty, + outline: 'none', + alignItems: 'center', + padding: 0, + width: (props: IProps) => (props.stretch ? '100%' : 'auto') as CSS.WidthProperty, + fontSize: theme.uiFontSize, + }, + label: { + margin: '10px 20px', + width: '100%', + }, + primary: { + background: theme.buttonPrimaryBackground, + color: theme.buttonPrimaryTextColor, + }, + secondary: { + background: theme.buttonSecondaryBackground, + color: theme.buttonSecondaryTextColor, + }, + danger: { + background: theme.buttonDangerBackground, + color: theme.buttonDangerTextColor, + }, + warning: { + background: theme.buttonWarningBackground, + color: theme.buttonWarningTextColor, + }, + inverted: { + background: theme.buttonInvertedBackground, + color: theme.buttonInvertedTextColor, + border: theme.buttonInvertedBorder, + }, + disabled: { + opacity: theme.inputDisabledOpacity, + }, + loader: { + position: 'relative' as CSS.PositionProperty, + width: 20, + height: 18, + zIndex: 9999, + }, + loaderContainer: { + width: (props: IProps): string => (!props.busy ? '0' : '40px'), + height: 20, + overflow: 'hidden', + transition: 'all 0.3s', + marginLeft: (props: IProps): number => !props.busy ? 10 : 20, + marginRight: (props: IProps): number => !props.busy ? -10 : -20, + position: (props: IProps): CSS.PositionProperty => props.stretch ? 'absolute' : 'inherit', + }, +}); + +@observer +class ButtonComponent extends Component { + public static defaultProps = { + type: 'button', + disabled: false, + onClick: () => null, + buttonType: 'primary' as ButtonType, + stretch: false, + busy: false, + }; + + state = { + busy: false, + }; + + componentWillMount() { + this.setState({ busy: this.props.busy }); + } + + componentWillReceiveProps(nextProps: IProps) { + if (nextProps.busy !== this.props.busy) { + if (this.props.busy) { + setTimeout(() => { + this.setState({ busy: nextProps.busy }); + }, 300); + } else { + this.setState({ busy: nextProps.busy }); + } + } + } + + render() { + const { + classes, + theme, + disabled, + id, + label, + type, + onClick, + buttonType, + loaded, + busy: busyProp, + } = this.props; + + const { + busy, + } = this.state; + + let showLoader = false; + if (loaded) { + showLoader = !loaded; + console.warn('Franz Button prop `loaded` will be deprecated in the future. Please use `busy` instead'); + } + if (busy) { + showLoader = busy; + } + + return ( + + ); + } +} + +export const Button = injectStyle(styles)(ButtonComponent); diff --git a/packages/forms/src/error/styles.ts b/packages/forms/src/error/styles.ts index 36a2b24e7..5104838a5 100644 --- a/packages/forms/src/error/styles.ts +++ b/packages/forms/src/error/styles.ts @@ -4,5 +4,6 @@ export default (theme: Theme) => ({ message: { color: theme.brandDanger, margin: '5px 0 0', + fontSize: theme.uiFontSize, }, }); diff --git a/packages/forms/src/index.ts b/packages/forms/src/index.ts index a506de111..fbeb7e3d3 100644 --- a/packages/forms/src/index.ts +++ b/packages/forms/src/index.ts @@ -1,2 +1,3 @@ export { Input } from './input'; export { Toggle } from './toggle'; +export { Button } from './button'; diff --git a/packages/forms/src/input/index.tsx b/packages/forms/src/input/index.tsx index d82ee5fe3..8f17ba2f9 100644 --- a/packages/forms/src/input/index.tsx +++ b/packages/forms/src/input/index.tsx @@ -34,6 +34,7 @@ class InputComponent extends Component { public static defaultProps = { focus: false, onChange: () => {}, + onBlur: () => {}, scorePassword: false, showLabel: true, showPasswordToggle: false, @@ -74,6 +75,7 @@ class InputComponent extends Component { render() { const { classes, + className, disabled, error, id, @@ -84,6 +86,11 @@ class InputComponent extends Component { showLabel, showPasswordToggle, type, + value, + name, + placeholder, + spellCheck, + onBlur, } = this.props; const { @@ -91,18 +98,7 @@ class InputComponent extends Component { passwordScore, } = this.state; - const inputProps = pick(this.props, htmlElementAttributes['input']); - - if (type === 'password' && showPassword) { - inputProps.type = 'text'; - } - - inputProps.onChange = this.onChange.bind(this); - - const cssClasses = classnames({ - [`${inputProps.className}`]: inputProps.className, - [`${classes.input}`]: true, - }); + const inputType = type === 'password' && showPassword ? 'text' : type; return ( @@ -110,10 +106,11 @@ class InputComponent extends Component { title={label} showLabel={showLabel} htmlFor={id} + className={className} >
{ )} {suffix && ( diff --git a/packages/forms/src/input/styles.ts b/packages/forms/src/input/styles.ts index 2b34e92c3..a64d2c340 100644 --- a/packages/forms/src/input/styles.ts +++ b/packages/forms/src/input/styles.ts @@ -6,12 +6,10 @@ const prefixStyles = (theme: Theme) => ({ color: theme.inputPrefixColor, lineHeight: theme.inputHeight, padding: '0 10px', + fontSize: theme.uiFontSize, }); export default (theme: Theme) => ({ - container: { - // display: 'flex', - }, disabled: { opacity: theme.inputDisabledOpacity, }, @@ -33,7 +31,7 @@ export default (theme: Theme) => ({ input: { background: 'none', border: 0, - fontSize: theme.inputFontSize, + fontSize: theme.uiFontSize, outline: 'none', padding: 8, width: '100%', diff --git a/packages/forms/src/label/styles.ts b/packages/forms/src/label/styles.ts index 64011a93d..f3998de04 100644 --- a/packages/forms/src/label/styles.ts +++ b/packages/forms/src/label/styles.ts @@ -6,6 +6,7 @@ export default (theme: Theme) => ({ }, label: { color: theme.labelColor, + fontSize: theme.uiFontSize, }, hasError: { color: theme.brandDanger, diff --git a/packages/forms/src/toggle/index.tsx b/packages/forms/src/toggle/index.tsx index 759694dc8..a1cd7f1a4 100644 --- a/packages/forms/src/toggle/index.tsx +++ b/packages/forms/src/toggle/index.tsx @@ -11,9 +11,7 @@ import Error from '../error'; import Label from '../label'; import Wrapper from '../wrapper'; -interface IProps extends React.InputHTMLAttributes, IFormField, IWithStyle { - // field: any; -} +interface IProps extends React.InputHTMLAttributes, IFormField, IWithStyle {} const styles = (theme: Theme) => ({ toggle: { @@ -42,7 +40,7 @@ const styles = (theme: Theme) => ({ visibility: 'hidden' as any, }, disabled: { - opacity: 0.5, + opacity: theme.inputDisabledOpacity, }, toggleLabel: { display: 'flex', @@ -77,8 +75,6 @@ class ToggleComponent extends Component { onChange, } = this.props; - console.log('props', this.props); - return (