diff --git a/.storybook/preview-head.html b/.storybook/preview-head.html index cae05f5..a766be0 100644 --- a/.storybook/preview-head.html +++ b/.storybook/preview-head.html @@ -1,6 +1,7 @@ + \ No newline at end of file + diff --git a/src/ChipInput.js b/src/ChipInput.js index e23c965..6f33980 100644 --- a/src/ChipInput.js +++ b/src/ChipInput.js @@ -8,6 +8,7 @@ import PropTypes from 'prop-types' import Input from '@material-ui/core/Input' import FilledInput from '@material-ui/core/FilledInput/FilledInput' import OutlinedInput from '@material-ui/core/OutlinedInput' +import InputAdornment from '@material-ui/core/InputAdornment' import InputLabel from '@material-ui/core/InputLabel' import Chip from '@material-ui/core/Chip' import withStyles from '@material-ui/core/styles/withStyles' @@ -35,10 +36,29 @@ const styles = (theme) => { boxSizing: 'border-box' }, '&$outlined': { - paddingTop: 14 + paddingTop: 8 }, '&$filled': { - paddingTop: 28 + paddingTop: 22, + '&$hasChips': { + paddingTop: 16 + } + }, + + '&:not($adornmentRoot)': { + flexGrow: 1, + display: 'flex', + alignContent: 'center', + flexWrap: 'wrap', + '& > *': { + '&:not($chip)': { + flex: '1 0' + } + }, + '& > div + input': { + paddingBottom: '7px', + minWidth: 88 + } } }, input: { @@ -46,14 +66,29 @@ const styles = (theme) => { appearance: 'none', // Remove border in Safari, doesn't seem to break anything in other browsers WebkitTapHighlightColor: 'rgba(0,0,0,0)', // Remove mobile color flashing (deprecated style). float: 'left', - '&:not($standard)': { - paddingTop: 0 + minWidth: 'min-content', + paddingTop: 0, + '&$outlined': { + paddingTop: 6, + paddingBottom: 15, + '&$hasChips': { + paddingTop: 0, + paddingBottom: 7 + } + }, + '&$filled': { + paddingBottom: 15, + '&$hasChips': { + paddingBottom: 10 + } } }, + hasChips: {}, chipContainer: { cursor: 'text', marginBottom: -2, minHeight: 40, + display: 'flex', '&$labeled&$standard': { marginTop: 18 } @@ -65,14 +100,30 @@ const styles = (theme) => { label: { top: 4, '&$outlined&:not($labelShrink)': { - top: -4 + top: -6 }, '&$filled&:not($labelShrink)': { top: 0 + }, + '&$adornedStart': { + left: 24 } }, labelShrink: { - top: 0 + top: 0, + '&$filled': { + marginTop: -8, + '&$adornedStart': { + marginLeft: -8 + } + }, + '&$adornedStart': { + left: 0, + '&$outlined': { + marginLeft: -8, + marginTop: -4 + } + } }, helperText: { marginBottom: -20 @@ -137,6 +188,35 @@ const styles = (theme) => { chip: { margin: '0 8px 8px 0', float: 'left' + }, + adornedStart: {}, + adornedEnd: {}, + adornmentRoot: { + '&$standard': { + marginTop: '8px' + }, + '&$outlined': { + paddingTop: '14px', + paddingBottom: 0 + }, + '&$filled': { + paddingTop: '22px' + }, + '& > *:first-child': { + marginTop: '-3.5px' + } + }, + inputAdornedStart: { + '&:not($standard)': { + paddingLeft: 34, + marginLeft: -38 + } + }, + inputAdornedEnd: { + '&:not($standard)': { + paddingRight: 30, + marginRight: -38 + } } } } @@ -412,12 +492,14 @@ class ChipInput extends React.Component { chipRenderer = defaultChipRenderer, classes, className, + color, clearInputValueOnChange, defaultValue, dataSource, dataSourceConfig, disabled, disableUnderline, + endAdornment, error, filter, FormHelperTextProps, @@ -426,6 +508,7 @@ class ChipInput extends React.Component { helperText, id, InputProps = {}, + inputAdornmentRenderer = defaultInputAdornmentRenderer, inputRef, InputLabelProps = {}, inputValue, @@ -444,15 +527,17 @@ class ChipInput extends React.Component { placeholder, required, rootRef, + startAdornment, value, variant, ...other } = this.props const chips = value || this.state.chips - const actualInputValue = inputValue != null ? inputValue : this.state.inputValue + const actualInputValue = inputValue || this.state.inputValue - const hasInput = (this.props.value || actualInputValue).length > 0 || actualInputValue.length > 0 + const hasInput = (value || chips).length > 0 || actualInputValue.length > 0 + const hasChips = chips.length > 0 const shrinkFloatingLabel = InputLabelProps.shrink != null ? InputLabelProps.shrink : (label != null && (hasInput || this.state.isFocused)) @@ -468,7 +553,8 @@ class ChipInput extends React.Component { isFocused: this.state.focusedChip === i, handleClick: () => this.setState({ focusedChip: i }), handleDelete: () => this.handleDeleteChip(value, i), - className: classes.chip + className: classes.chip, + color: color }, i ) @@ -485,15 +571,27 @@ class ChipInput extends React.Component { 0 } - if (variant !== 'standard') { - InputMore.startAdornment = ( - {chipComponents} - ) - } else { + if (variant === 'standard') { InputProps.disableUnderline = true } + InputMore.startAdornment = ( + {chipComponents} + ) + InputMore.endAdornment = null const InputComponent = variantComponent[variant] + const hasAdornment = !!InputProps.startAdornment || startAdornment || InputProps.endAdornment || endAdornment + const adornmentClasses = { + root: cx( + classes.inputRoot, + classes[variant], + { + [classes.adornmentRoot]: hasAdornment + } + ) + } + const renderedStartAdornment = (InputProps.startAdornment || startAdornment) ? inputAdornmentRenderer(InputProps.startAdornment || startAdornment, adornmentClasses) : null + const renderedEndAdornment = (InputProps.endAdornment || endAdornment) ? inputAdornmentRenderer(InputProps.endAdornment || endAdornment, adornmentClasses) : null return ( - {variant === 'standard' && chipComponents} + {renderedStartAdornment} + {renderedEndAdornment} {helperText && ( node` that returns an Input Adornment to render within the input. This can be used to overwrite the default element used to wrap the input adornment or to overwrite how the styles are applied to the input adornment. */ + inputAdornmentRenderer: PropTypes.func, /** Props to pass through to the `InputLabel`. */ InputLabelProps: PropTypes.object, /** Props to pass through to the `Input`. */ @@ -625,6 +747,8 @@ ChipInput.propTypes = { onUpdateInput: PropTypes.func, /** A placeholder that is displayed if the input has no values. */ placeholder: PropTypes.string, + /** Start `InputAdornment` for this component. */ + startAdornment: PropTypes.node, /** The chips to display (enables controlled mode if set). */ value: PropTypes.array, /** The variant of the Input component */ @@ -635,6 +759,7 @@ ChipInput.defaultProps = { allowDuplicates: false, blurBehavior: 'clear', clearInputValueOnChange: false, + color: 'default', disableUnderline: false, newChipKeyCodes: [13], variant: 'standard' @@ -642,7 +767,7 @@ ChipInput.defaultProps = { export default withStyles(styles)(ChipInput) -export const defaultChipRenderer = ({ value, text, isFocused, isDisabled, handleClick, handleDelete, className }, key) => ( +export const defaultChipRenderer = ({ value, text, isFocused, isDisabled, handleClick, handleDelete, className, color }, key) => ( ) + +export const defaultInputAdornmentRenderer = (inputAdornment, additionalClasses) => { + const classes = inputAdornment.props.classes || {} + const rootClass = cx(additionalClasses.root, classes.root) + const filled = cx(additionalClasses.filled, classes.filled) + const positionStart = cx(additionalClasses.positionStart, classes.positionStart) + const positionEnd = cx(additionalClasses.positionEnd, classes.positionEnd) + return ( + + ) +} diff --git a/src/ChipInput.spec.js b/src/ChipInput.spec.js index dfbfedc..18301cf 100644 --- a/src/ChipInput.spec.js +++ b/src/ChipInput.spec.js @@ -345,6 +345,7 @@ describe('custom chips', () => { text: 'a', chip: 'a', className: expect.any(String), + color: 'default', isDisabled: false, isFocused: false, handleClick: expect.any(Function), diff --git a/src/__snapshots__/ChipInput.spec.js.snap b/src/__snapshots__/ChipInput.spec.js.snap index a618bf3..3476083 100644 --- a/src/__snapshots__/ChipInput.spec.js.snap +++ b/src/__snapshots__/ChipInput.spec.js.snap @@ -14,26 +14,33 @@ exports[`uncontrolled mode matches the snapshot 1`] = ` blurBehavior="clear" classes={ Object { - "chip": "ChipInput-chip-17", - "chipContainer": "ChipInput-chipContainer-4", - "disabled": "ChipInput-disabled-14", - "error": "ChipInput-error-16", - "filled": "ChipInput-filled-7", - "focused": "ChipInput-focused-13", - "helperText": "ChipInput-helperText-11", - "inkbar": "ChipInput-inkbar-12", + "adornedEnd": "ChipInput-adornedEnd-20", + "adornedStart": "ChipInput-adornedStart-19", + "adornmentRoot": "ChipInput-adornmentRoot-21", + "chip": "ChipInput-chip-18", + "chipContainer": "ChipInput-chipContainer-5", + "disabled": "ChipInput-disabled-15", + "error": "ChipInput-error-17", + "filled": "ChipInput-filled-8", + "focused": "ChipInput-focused-14", + "hasChips": "ChipInput-hasChips-4", + "helperText": "ChipInput-helperText-12", + "inkbar": "ChipInput-inkbar-13", "input": "ChipInput-input-3", + "inputAdornedEnd": "ChipInput-inputAdornedEnd-23", + "inputAdornedStart": "ChipInput-inputAdornedStart-22", "inputRoot": "ChipInput-inputRoot-2", - "label": "ChipInput-label-9", - "labelShrink": "ChipInput-labelShrink-10", - "labeled": "ChipInput-labeled-8", - "outlined": "ChipInput-outlined-5", + "label": "ChipInput-label-10", + "labelShrink": "ChipInput-labelShrink-11", + "labeled": "ChipInput-labeled-9", + "outlined": "ChipInput-outlined-6", "root": "ChipInput-root-1", - "standard": "ChipInput-standard-6", - "underline": "ChipInput-underline-15", + "standard": "ChipInput-standard-7", + "underline": "ChipInput-underline-16", } } clearInputValueOnChange={false} + color="default" defaultValue={ Array [ "foo", @@ -57,10 +64,10 @@ exports[`uncontrolled mode matches the snapshot 1`] = ` className="ChipInput-root-1" classes={ Object { - "fullWidth": "MuiFormControl-fullWidth-21", - "marginDense": "MuiFormControl-marginDense-20", - "marginNormal": "MuiFormControl-marginNormal-19", - "root": "MuiFormControl-root-18", + "fullWidth": "MuiFormControl-fullWidth-27", + "marginDense": "MuiFormControl-marginDense-26", + "marginNormal": "MuiFormControl-marginNormal-25", + "root": "MuiFormControl-root-24", } } component="div" @@ -73,276 +80,21 @@ exports[`uncontrolled mode matches the snapshot 1`] = ` variant="standard" >
- - -
- - foo - - - - - - - - - - -
-
-
- - -
- - bar - - - - - - - - - - -
-
-
+ + + + } value="" > + + + + } value="" > - + + + + } type="text" value="" > - + + + + } type="text" value="" > - -
- -
-
-
-
+ +
+ + foo + + + + + + + + + + +
+
+
+ + +
+ + bar + + + + + + + + + + +
+
+
+ +
+ +
diff --git a/stories/index.js b/stories/index.js index 2412d9b..9963875 100644 --- a/stories/index.js +++ b/stories/index.js @@ -4,6 +4,7 @@ import { action } from '@storybook/addon-actions' import Avatar from '@material-ui/core/Avatar' import Chip from '@material-ui/core/Chip' // import AutoComplete from '@material-ui/core/AutoComplete' +import { InputAdornment, Icon } from '@material-ui/core' import { green } from '@material-ui/core/colors' import ChipInput from '../src/ChipInput' import CustomizedChipInput from './examples/CustomizedChipInput' @@ -288,3 +289,49 @@ storiesOf('ChipInput', module) .add('with "filled" variant full width', () => ( )) + .add('with start adornment', () => ( + search
} + /> + )) + .add('with start adornment and "outlined" variant', () => ( + search} + /> + )) + .add('with start adornment and "filled" variant', () => ( + search} + /> + )) + .add('with end adornment', () => ( + search} + /> + )) + .add('with end adornment and "outlined" variant', () => ( + search} + /> + )) + .add('with end adornment and "filled" variant', () => ( + search} + /> + ))