-
-
Notifications
You must be signed in to change notification settings - Fork 208
Add support for start and end adornments #255
base: master
Are you sure you want to change the base?
Changes from all commits
51958ec
9672d01
cf83b48
54c5c61
68546c5
14dcc4b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
<link href="https://fonts.googleapis.com/css?family=Roboto:400,500,700" rel="stylesheet"> | ||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"> | ||
<style> | ||
body { | ||
font-family: 'Roboto', sans-serif; | ||
} | ||
</style> | ||
</style> |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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,25 +36,59 @@ 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: { | ||
display: 'inline-block', | ||
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 = ( | ||
<React.Fragment>{chipComponents}</React.Fragment> | ||
) | ||
} else { | ||
if (variant === 'standard') { | ||
InputProps.disableUnderline = true | ||
} | ||
InputMore.startAdornment = ( | ||
<React.Fragment>{chipComponents}</React.Fragment> | ||
) | ||
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 ( | ||
<FormControl | ||
|
@@ -510,7 +608,14 @@ class ChipInput extends React.Component { | |
{label && ( | ||
<InputLabel | ||
htmlFor={id} | ||
classes={{root: cx(classes[variant], classes.label), shrink: classes.labelShrink}} | ||
classes={{ | ||
root: cx( | ||
classes[variant], | ||
classes.label, | ||
{ | ||
[classes.adornedStart]: renderedStartAdornment | ||
}), | ||
shrink: classes.labelShrink}} | ||
shrink={shrinkFloatingLabel} | ||
focused={this.state.isFocused} | ||
variant={variant} | ||
|
@@ -530,15 +635,25 @@ class ChipInput extends React.Component { | |
[classes.underline]: !disableUnderline && variant === 'standard', | ||
[classes.disabled]: disabled, | ||
[classes.labeled]: label != null, | ||
[classes.error]: error | ||
[classes.error]: error, | ||
[classes.adornedStart]: renderedStartAdornment, | ||
[classes.adornedEnd]: renderedEndAdornment | ||
})} | ||
> | ||
{variant === 'standard' && chipComponents} | ||
{renderedStartAdornment} | ||
<InputComponent | ||
ref={this.input} | ||
classes={{ | ||
input: cx(classes.input, classes[variant]), | ||
root: cx(classes.inputRoot, classes[variant]) | ||
input: cx(classes.input, classes[variant], | ||
{ | ||
[classes.hasChips]: hasChips | ||
}), | ||
root: cx(classes.inputRoot, classes[variant], | ||
{ | ||
[classes.inputAdornedStart]: renderedStartAdornment, | ||
[classes.inputAdornedEnd]: renderedEndAdornment, | ||
[classes.hasChips]: hasChips | ||
}) | ||
}} | ||
id={id} | ||
value={actualInputValue} | ||
|
@@ -555,6 +670,7 @@ class ChipInput extends React.Component { | |
{...InputProps} | ||
{...InputMore} | ||
/> | ||
{renderedEndAdornment} | ||
</div> | ||
{helperText && ( | ||
<FormHelperText | ||
|
@@ -580,6 +696,8 @@ ChipInput.propTypes = { | |
chipRenderer: PropTypes.func, | ||
/** Whether the input value should be cleared if the `value` prop is changed. */ | ||
clearInputValueOnChange: PropTypes.bool, | ||
/** The color to render the chip based on the color palette **/ | ||
color: PropTypes.oneOf(['default', 'primary', 'secondary']), | ||
/** Data source for auto complete. This should be an array of strings or objects. */ | ||
dataSource: PropTypes.array, | ||
/** Config for objects list dataSource, e.g. `{ text: 'text', value: 'value' }`. If not specified, the `dataSource` must be a flat array of strings or a custom `chipRenderer` must be set to handle the objects. */ | ||
|
@@ -593,6 +711,8 @@ ChipInput.propTypes = { | |
disabled: PropTypes.bool, | ||
/** Disable the input underline. Only valid for 'standard' variant */ | ||
disableUnderline: PropTypes.bool, | ||
/** End `InputAdornment` for this component. */ | ||
endAdornment: PropTypes.node, | ||
/** Props to pass through to the `FormHelperText` component. */ | ||
FormHelperTextProps: PropTypes.object, | ||
/** If true, the chip input will fill the available width. */ | ||
|
@@ -601,6 +721,8 @@ ChipInput.propTypes = { | |
fullWidthInput: PropTypes.bool, | ||
/** Helper text that is displayed below the input. */ | ||
helperText: PropTypes.node, | ||
/** A function of the type `({ inputAdornment, additionalClasses }) => 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,20 +759,33 @@ ChipInput.defaultProps = { | |
allowDuplicates: false, | ||
blurBehavior: 'clear', | ||
clearInputValueOnChange: false, | ||
color: 'default', | ||
disableUnderline: false, | ||
newChipKeyCodes: [13], | ||
variant: 'standard' | ||
} | ||
|
||
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) => ( | ||
<Chip | ||
key={key} | ||
className={className} | ||
style={{ pointerEvents: isDisabled ? 'none' : undefined, backgroundColor: isFocused ? blue[300] : undefined }} | ||
onClick={handleClick} | ||
onDelete={handleDelete} | ||
label={text} | ||
color={color || 'default'} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The default value for |
||
/> | ||
) | ||
|
||
export const defaultInputAdornmentRenderer = (inputAdornment, additionalClasses) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not using this function would probably break a lot of things. Is a prop for customizing the renderer actually needed ? 🤔 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The biggest item for this would be to resolve the classes for styling. It was loosely based on what was happening from any of the inputs in Material-ui compared to BaseInput. When you say not using this function, can you clarify what issue you are seeing? It is a default renderer function and if someone were to override it, I would be expecting them to resolve the styling issues in their manual implementation anyhow. I was opting for customization capability more than anything. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But I could easily just hide this from being a prop if that is what you are going after. Let me know if you want me to do that instead |
||
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 ( | ||
<InputAdornment {...inputAdornment.props} classes={{root: rootClass, filled: filled, positionStart: positionStart, positionEnd: positionEnd}} /> | ||
) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The other new props, i.e.
startAdornment
,endAdornment
andadornmentRenderer
should also be added and documented here.