Skip to content
This repository has been archived by the owner on Oct 6, 2020. It is now read-only.

Commit

Permalink
feat(Input): isFocused state and icon support (#49)
Browse files Browse the repository at this point in the history
  • Loading branch information
mathewmorris authored Aug 29, 2019
1 parent d39834c commit 2760d89
Show file tree
Hide file tree
Showing 9 changed files with 241 additions and 99 deletions.
107 changes: 91 additions & 16 deletions src/Form/Input.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { css } from 'styled-components';
import styled, { css } from 'styled-components';
import Field from './Field';
import StyledLabel from './Label';
import FormError from './FormError';
import { createEasyInput } from './EasyInput';
import { createComponent } from '../utils';
import Icon from '../Icon';
import { themeGet, createComponent } from '../utils';

const InputContainer = createComponent({
name: 'InputContainer',
Expand All @@ -17,9 +18,9 @@ const InputContainer = createComponent({
const StyledInput = createComponent({
name: 'Input',
tag: 'input',
style: ({ isFloating, size, theme, borderRadius = theme.radius }) => css`
style: ({ isFloating, theme, borderRadius = theme.radius, leftIcon, rightIcon, leftIconProps, rightIconProps }) => css`
border: 1px solid ${theme.colors.greyLight};
height: ${theme.heights[size]}px;
height: 48px;
display: block;
outline: none;
width: 100%;
Expand All @@ -28,29 +29,74 @@ const StyledInput = createComponent({
transition: 250ms all;
-webkit-appearance: none;
font-family: inherit;
font-size: ${theme.fontSizes[size]}px;
font-size: ${themeGet('typography.fontSize')}px;
color: ${theme.colors.greyDarkest};
box-sizing: border-box;
&:hover,
&:focus,
&:active {
border-color: ${theme.colors.greyDark};
}
&:focus {
border-color: ${theme.colors.primary};
}
::placeholder {
color: ${theme.colors.greyDark};
color: ${theme.colors.greyDarker};
}
&[disabled] {
opacity: 0.65;
background-color: ${theme.colors.white};
border-color: ${theme.colors.greyLight};
color: ${theme.colors.grey};
::placeholder {
color: ${theme.colors.grey};
}
}
${isFloating &&
css`
padding-bottom: 0;
line-height: 14px;
padding-top: 14px;
padding-bottom: 0px;
`};
${leftIcon &&
css`
padding-left: ${(leftIconProps.size || 16) + 12}px;
`};
${rightIcon &&
css`
padding-right: ${(rightIconProps.size || 16) + 32}px;
`};
`,
});

const StyledIcon = styled(Icon)`
position: absolute;
top: 50%;
transform: translateY(-50%);
`;

const LeftIcon = createComponent({
name: 'InputLeftIcon',
as: StyledIcon,
style: css`
left: 8px;
`,
});

const RightIcon = createComponent({
name: 'InputRightIcon',
as: StyledIcon,
style: css`
right: 8px;
`,
});

const StyledTextArea = StyledInput.withComponent('textarea');

const AutogrowShadow = createComponent({
Expand All @@ -75,7 +121,7 @@ const validateValueProp = (props, propName, componentName) => {
return null;
};

class Input extends Component {
export class Input extends Component {
static propTypes = {
value: validateValueProp,
type: PropTypes.string,
Expand All @@ -95,6 +141,10 @@ class Input extends Component {
size: PropTypes.string,
floating: PropTypes.bool,
forwardedRef: PropTypes.oneOfType([PropTypes.shape(), PropTypes.func]),
leftIcon: PropTypes.string,
leftIconProps: PropTypes.shape(),
rightIcon: PropTypes.string,
rightIconProps: PropTypes.shape(),
};

static defaultProps = {
Expand All @@ -112,6 +162,8 @@ class Input extends Component {
onBlur() {},
onChange() {},
floating: false,
leftIconProps: {},
rightIconProps: {},
};

static getDerivedStateFromProps(props, state) {
Expand Down Expand Up @@ -233,33 +285,52 @@ class Input extends Component {
autofocus,
id,
error,
value,
floating,
placeholder,
transformOnBlur,
size,
leftIcon,
leftIconProps,
rightIcon,
rightIconProps,
disabled,
...rest
} = this.props;

const isFloating = floating && value != undefined && `${value}`.trim();
const { focused, height, value } = this.state;

const isFloating = floating && value !== undefined && `${value}`.trim();

const inputProps = {
...rest,
id,
ref: this.ref,
size,
value: this.state.value,
value,
onChange: this.onChange,
onFocus: this.onFocus,
onBlur: this.onBlur,
style: multiline ? { ...style, height: this.state.height } : style,
style: multiline ? { ...style, height } : style,
placeholder,
isFloatable: floating,
isFloating,
error,
leftIcon,
leftIconProps,
rightIcon,
rightIconProps,
disabled,
};

const Label = label ? (
<StyledLabel htmlFor={id} styles={rest.styles} size={size} isFloatable={floating} isFloating={isFloating}>
<StyledLabel
htmlFor={id}
styles={rest.styles}
isFloatable={floating}
isFloating={isFloating}
isFocused={focused}
isDisabled={disabled}
error={error}>
{label}
</StyledLabel>
) : null;
Expand All @@ -271,12 +342,16 @@ class Input extends Component {
<InputContainer styles={rest.styles}>
{floating && Label}

{leftIcon && <LeftIcon styles={rest.styles} name={leftIcon} {...leftIconProps} />}

{rightIcon && <RightIcon styles={rest.styles} name={rightIcon} {...rightIconProps} />}

{multiline ? <StyledTextArea {...inputProps} /> : <StyledInput {...inputProps} />}
</InputContainer>

{autogrow && <AutogrowShadow ref={this.handleAutogrowRef} />}

{!this.state.focused && error ? <FormError styles={rest.styles}>{error}</FormError> : null}
{!focused && error ? <FormError styles={rest.styles}>{error}</FormError> : null}
</Field>
);
}
Expand Down
68 changes: 39 additions & 29 deletions src/Form/Input.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,20 @@ name: Input
---

import { Playground, PropsTable } from 'docz'
import Input from './Input'
import Input, { Input as CoreInput } from './Input'
import PhoneInput from './PhoneInput';
import DateInput from './DateInput';
import Button from '../Button'
import Formbot from './Formbot';
import Icon from '../Icon'

# Input

Form input component. Different sizes are available.

## Props

<PropsTable of={Input} />
<PropsTable of={CoreInput} />

## Examples
<Playground>
Expand All @@ -30,8 +31,8 @@ Form input component. Different sizes are available.
validations={{ value: (value) => {
if (value !== 'secretpassword') {
throw new Error('Try typing "secretpassword"')
}
}}}>
}
}}}>
{({ values, errors, onChange, onSubmit }) => (
<form onSubmit={onSubmit}>
<Input label="Input with Formbot" name="value" value={values.value} error={errors.value} onChange={onChange}/>
Expand All @@ -49,57 +50,66 @@ Form input component. Different sizes are available.

### Default Input
<Playground>
<Input placeholder="Example" label="Example" />
<Input placeholder="Example" label="Example" />
</Playground>

### Multiline Input
<Playground>
<Input multiline placeholder="Example" label="Example" value="I already have a value! If provided a value, I become controlled, so I won't update when you type here..." />
<Input multiline placeholder="Example" label="Example" value="I already have a value! If provided a value, I become controlled, so I won't update when you type here..." />
</Playground>

### Autogrow Multiline Input
<Playground>
<Input multiline autogrow placeholder="Example" label="Example" />
<Input multiline autogrow placeholder="Example" label="Example" />
</Playground>

### Floating Input
<Playground>
<Input floating placeholder="Example" label="Example" />
<Input floating placeholder="Example" label="Example" />
</Playground>

### Disabled Input
<Playground>
<Input disabled placeholder="Example" label="Example" />
<Input disabled placeholder="Example" label="Example" />
</Playground>

### Overridable Styles
### Input with Icons
<Playground>
<Input placeholder="Example" label="Example" styles={{
Input: {
background: 'beige'
}
}} />

<Input floating placeholder="Example" label="Example" styles={{
Input: {
background: 'beige'
}
}} />
<Input
placeholder="Example"
label="Example"
leftIcon="cards-spade"
rightIcon="close-circle"
rightIconProps={{
color: 'primary',
onClick: () => alert('Right icon clicked!'),
}}
/>
</Playground>

### Sizes
### Overridable Styles
<Playground>
<Input size="xs" placeholder="Example" label="Example" />
<Input size="sm" placeholder="Example" label="Example" />
<Input size="md" placeholder="Example" label="Example" />
<Input size="lg" placeholder="Example" label="Example" />
<Input size="xl" placeholder="Example" label="Example" />
<Input placeholder="Example" label="Example" styles={{
Input: {
background: 'beige'
}
}} />

<Input floating placeholder="Example" label="Example" styles={{
Input: {
background: 'beige'
}
}} />
</Playground>

## Formatted Inputs

### Phone Input
<PhoneInput placeholder="Example" label="Phone Number" value="4088675309" />
<Playground>
<PhoneInput placeholder="Example" label="Phone Number" value="4088675309" />
</Playground>

### Date Input
<DateInput placeholder="Date of Birth" label="Date of Birth" initialValue="04/01/2001" value="2001-04-01" />
<Playground>
<DateInput placeholder="Date of Birth" label="Date of Birth" initialValue="04/01/2001" value="2001-04-01" />
</Playground>
22 changes: 16 additions & 6 deletions src/Form/Label.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,35 @@
import { css } from 'styled-components';
import { createComponent } from '../utils';
import { themeGet, createComponent } from '../utils';

const Label = createComponent({
name: 'Label',
tag: 'label',
style: ({ isFloatable, isFloating, size, theme }) => css`
style: ({ isFloatable, isFloating, isFocused, isDisabled, theme }) => css`
display: block;
transition: 250ms;
font-weight: 500;
margin: 0 0 4px 4px;
font-size: ${p => p.theme.fontSizes[p.size]}px;
font-size: ${themeGet('typography.fontSize')}px;
${isFloatable &&
css`
position: absolute;
top: 2px;
top: 6px;
left: 8px;
opacity: ${isFloating ? 1 : 0};
margin: 0;
font-size: ${theme.fontSizes[size] * 0.8}px;
font-size: 12px;
line-height: 14px;
`};
${isFocused &&
css`
color: ${theme.colors.primary};
`}
${isDisabled &&
css`
color: ${theme.colors.grey};
`}
`,
});

Expand Down
Loading

0 comments on commit 2760d89

Please sign in to comment.