Skip to content
This repository has been archived by the owner on Sep 3, 2021. It is now read-only.

Commit

Permalink
Converted Input to the new Aesthetic structure. Updated Input to be a…
Browse files Browse the repository at this point in the history
…bstract and composed by other components.
  • Loading branch information
milesj committed Mar 6, 2017
1 parent 19488f7 commit 26039b1
Show file tree
Hide file tree
Showing 16 changed files with 376 additions and 232 deletions.
24 changes: 24 additions & 0 deletions src/components/Input/Checkbox.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* @copyright 2010-2017, The Titon Project
* @license http://opensource.org/licenses/BSD-3-Clause
* @link http://titon.io
* @flow
*/

import React from 'react';
import Input from './Input';
import style from '../../styler';
import { INPUT_CLASSES } from './types';

import type { InputProps } from './types';

export function ToolkitCheckbox(props: InputProps) {
return (
<Input {...props} type="checkbox" />
);
}

export default style({
...INPUT_CLASSES,
input: 'input input-checkbox',
})(ToolkitCheckbox);
59 changes: 31 additions & 28 deletions src/components/Input/Choice.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,39 +2,42 @@
* @copyright 2010-2017, The Titon Project
* @license http://opensource.org/licenses/BSD-3-Clause
* @link http://titon.io
* @flow
*/

import React, { PropTypes } from 'react';
import Component from '../../Component';
import { defaultSizeProps, sizePropTypes } from '../../propTypes';
import MODULE from './module';
import style, { classes } from '../../styler';
import { classNamesPropType, sizeDefaults, sizePropTypes } from '../../propTypes';

export default class Choice extends Component {
static module = MODULE;
import type { InputChoiceProps } from './types';

static defaultProps = {
...defaultSizeProps,
};
export function ToolkitChoice({ children, classNames, large, small, inputID }: InputChoiceProps) {
return (
<label
htmlFor={inputID}
className={classes(classNames.input, {
[classNames.input__large]: large,
[classNames.input__small]: small,
})}
>
{children}
</label>
);
}

static propTypes = {
...sizePropTypes,
children: PropTypes.node,
inputID: PropTypes.string.isRequired,
};
ToolkitChoice.propTypes = {
...sizePropTypes,
children: PropTypes.node,
classNames: classNamesPropType.isRequired,
inputID: PropTypes.string.isRequired,
};

render() {
const { children, large, small, inputID } = this.props;
ToolkitChoice.defaultProps = {
...sizeDefaults,
};

return (
<label
htmlFor={inputID}
className={this.formatChildClass('choice', {
'@large': large,
'@small': small,
})}
>
{children}
</label>
);
}
}
export default style({
input: 'input-choice',
input__large: 'input-choice--large',
input__small: 'input-choice--small',
})(ToolkitChoice);
182 changes: 90 additions & 92 deletions src/components/Input/Input.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,100 +2,125 @@
* @copyright 2010-2017, The Titon Project
* @license http://opensource.org/licenses/BSD-3-Clause
* @link http://titon.io
* @flow
*/

import React, { PropTypes } from 'react';
import Component from '../../Component';
import bind from '../../decorators/bind';
import formatInputName from '../../utility/formatInputName';
import emitEvent from '../../utility/emitEvent';
import invariant from '../../utility/invariant';
import { classes } from '../../styler';
import {
defaultInputProps, defaultSizeProps,
inputPropTypes, sizePropTypes,
classNamesPropType,
inputDefaults, sizeDefaults,
inputPropTypes, sizePropTypes,
} from '../../propTypes';
import MODULE from './module';

export default class Input extends Component {
static module = MODULE;
import type { InputProps, InputState } from './types';
import type { PropsMap } from '../../types';

static defaultProps = {
...defaultInputProps,
...defaultSizeProps,
type: 'text',
};
// Private
export default class BaseInput extends React.Component {
props: InputProps;
state: InputState;
uid: string;

static propTypes = {
...inputPropTypes,
...sizePropTypes,
children: PropTypes.node,
type: PropTypes.string,
classNames: classNamesPropType.isRequired,
type: PropTypes.string.isRequired,
};

constructor(props) {
static defaultProps = {
...inputDefaults,
...sizeDefaults,
};

constructor(props: InputProps) {
super();

const compName = this.constructor.name.toLowerCase();
let defaultValue = props.defaultValue;
let defaultChecked = props.defaultChecked;

// Select
if (compName === 'select') {
if (props.type === 'select') {
if (props.multiple) {
defaultValue = Array.isArray(defaultValue) ? defaultValue : [defaultValue];
} else {
defaultValue = String(defaultValue);
}

// Checkbox, Switch
} else if (compName === 'switch' || compName === 'checkbox' || props.type === 'checkbox') {
} else if (props.type === 'checkbox') {
if (props.multiple) {
invariant(defaultValue,
'A default value is required when using `multiple` checkboxes.');
invariant(defaultValue, 'A default value is required when using `multiple` checkboxes.');
} else {
defaultValue = defaultValue || '1';
}

// Radio
} else if (compName === 'radio' || props.type === 'radio') {
} else if (props.type === 'radio') {
invariant(defaultValue, 'A default value is required when using radios.');

if (typeof defaultChecked === 'string') {
defaultChecked = (defaultValue === defaultChecked);
}
}

this.uid = props.id || formatInputName(props.name);
this.state = {
checked: Boolean(defaultChecked),
type: (compName === 'input') ? props.type : compName,
value: defaultValue,
};
}

shouldComponentUpdate(nextProps, nextState) {
shouldComponentUpdate(nextProps: InputProps, nextState: InputState) {
return (nextState.value !== this.state.value || nextState.checked !== this.state.checked);
}

componentWillUpdate(nextProps, nextState) {
componentWillUpdate(nextProps: InputProps, nextState: InputState) {
const { checked, value } = nextState;
const args = this.isChoiceType() ? [checked, value] : [value, this.state.value];

emitEvent(this, 'onChanging', ...args);
emitEvent(this, 'input', 'onChanging', ...args);
}

componentDidUpdate(prevProps, prevState) {
componentDidUpdate(prevProps: InputProps, prevState: InputState) {
const { checked, value } = this.state;
const args = this.isChoiceType() ? [checked, value] : [value, prevState.value];

emitEvent(this, 'onChanged', ...args);
emitEvent(this, 'input', 'onChanged', ...args);
}

isChoiceType(): boolean {
return ['checkbox', 'radio'].includes(this.props.type);
}

gatherProps(native = true) {
const props = this.props;
handleOnChange = ({ target }: Event) => {
const { type, multiple } = this.props;
const newState = {};

if (this.isChoiceType()) {
newState.checked = !this.state.checked;
} else {
newState.value = target.value;

if (type === 'select' && multiple) {
newState.value = Array.from(target.selectedOptions).map(option => option.value);
}
}

this.setState(newState);
};

render() {
const { children, classNames, ...props } = this.props;
const state = this.state;
let inputProps = {
const inputProps: PropsMap = {
disabled: props.disabled,
id: props.id || formatInputName(props.name),
id: this.uid,
name: props.name,
onChange: this.handleOnChange,
readOnly: props.readOnly,
Expand All @@ -105,81 +130,54 @@ export default class Input extends Component {

// Native elements inherit more base functionality
// Custom elements define their own classes and props
if (native) {
inputProps = {
...inputProps,
className: this.formatClass({
'@large': props.large,
'@small': props.small,
[`@${state.type}`]: true,
...this.gatherStateClasses(),
}),
};
if (props.native) {
inputProps.className = classes(classNames.input, {
[classNames.input__small]: props.small,
[classNames.input__large]: props.large,
[classNames.input__checked]: state.checked,
[classNames.input__disabled]: props.disabled,
[classNames.input__invalid]: props.invalid,
[classNames.input__multiple]: props.multiple,
[classNames.input__readOnly]: props.readOnly,
[classNames.input__required]: props.required,
});
}

switch (state.type) {
// Add specific props and append a value to the ID
switch (props.type) {
case 'select':
inputProps.multiple = props.multiple;

return (
<select {...inputProps}>
{children}
</select>
);

case 'textarea':
return (
<textarea {...inputProps} />
);

case 'checkbox':
case 'radio':
case 'switch':
inputProps.type = state.type;
inputProps.checked = state.checked;
inputProps.multiple = props.multiple;

if (!props.id && (props.multiple || state.type === 'radio')) {
if (
!props.id &&
(props.multiple || props.type === 'radio') &&
typeof state.value === 'string'
) {
inputProps.id += `-${state.value}`;
}
break;

// These aren't native HTML inputs but we need to catch them
case 'select':
inputProps.multiple = props.multiple;
break;

case 'textarea':
break;

// Only include the type on true input elements
// eslint-disable-next-line no-fallthrough
default:
inputProps.type = props.type;
break;
}

return inputProps;
}

gatherStateClasses() {
const { disabled, multiple, readOnly, required } = this.props;

return {
'is-checked': this.state.checked,
'is-disabled': disabled,
'is-multiple': multiple,
'is-read-only': readOnly,
'is-required': required,
};
}

isChoiceType() {
return ['checkbox', 'radio', 'switch'].includes(this.state.type);
}

@bind
handleOnChange(e) {
const newState = {};

if (this.isChoiceType()) {
newState.checked = !this.state.checked;
} else {
newState.value = e.target.value;
return (
<input {...inputProps} />
);
}

this.setState(newState);
}

render() {
return (
<input {...this.gatherProps()} />
);
}
}
24 changes: 24 additions & 0 deletions src/components/Input/Radio.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* @copyright 2010-2017, The Titon Project
* @license http://opensource.org/licenses/BSD-3-Clause
* @link http://titon.io
* @flow
*/

import React from 'react';
import Input from './Input';
import style from '../../styler';
import { INPUT_CLASSES } from './types';

import type { InputProps } from './types';

export function ToolkitRadio(props: InputProps) {
return (
<Input {...props} type="radio" />
);
}

export default style({
...INPUT_CLASSES,
input: 'input input-radio',
})(ToolkitRadio);
Loading

0 comments on commit 26039b1

Please sign in to comment.